Repository navigation
Commit 722c64e
LAV-4493: ALTER USER implicit current-user targeting (#4681)
* LAV-4493: investigator: capture requires disposable-user authentication without proxy rewriting
Dispatch 1 ended voluntarily blocked at 2026-10-14T06:52:35, not from exhaustion. Recovered implementer JSONL item_31 and driver.log agree. Guarded captures requested Implicit_User_63254ed5 on Standard and Implicit_User_19e34f11 on Enterprise, but both authenticated as SNAPSHOT_TESTING. Initial unguarded captures mutated QUERY_TAG on SNAPSHOT_TESTING and completed UNSET; those invalid captures and all implementation edits were reverted.
Verified October 14, 2026: after git fetch origin, HEAD and origin/main both equal 2fd2e760a0459a48096d7524a715f9851e710d37; git status --porcelain and git diff origin/main --stat are empty. The parser still requires parse_identifier() in vendor/sqlparser/src/parser/alter.rs:153, so this is not already fixed.
Independent read-only identity probes: SNOWFLAKE_USER=LAV4493_INVESTIGATOR_IDENTITY_PROBE SNOWFLAKE_ROLE=PUBLIC make snowsql SQL='SELECT CURRENT_USER(), CURRENT_ROLE(), CURRENT_ACCOUNT()' returned SNAPSHOT_TESTING | PUBLIC | OD47041. With SNOWFLAKE_ACCOUNT=FS76419 and SNOWFLAKE_HOST=$SNOWFLAKE_HOST2 the same probe returned SNAPSHOT_TESTING | PUBLIC | FS76419. No objects were created and no ALTER statements were executed during investigation.
Repository contract docs/agentic-dev-workflow.md:1209 documents credential replacement and the dummy worker key. Current localstack/spiral source read through the permitted GitHub GET API confirms internal/proxy/snowflake.go:202 calls login.encode with the configured Snowflake user, and :372-374 overwrites LOGIN_NAME, AUTHENTICATOR and TOKEN. This is authentication rewriting, not a missing package or host-allowlist refusal.
Remote-history/sibling discovery found the identical restriction in investigator note 0e9f5aae1 (LAV-4352) and human direct-Cloud capture precedent 6ff891a22 (LAV-3996). Neither supplies a worker-side authentication-preserving capture path; no owning proxy-fix issue was identified in available evidence. Live Linear lookup is unavailable. Changing only CURRENT_ROLE cannot satisfy authenticated-user isolation.
Human action required: provide an authorized Cloud capture path preserving the disposable user's LOGIN_NAME and key-pair JWT, OR capture and commit the new disposable-user tests and snapshots from a direct-Cloud environment, then resume LAV-4493. Capture with TEST_TARGET=SNOWFLAKE_CLOUD and --snapshot-update; require SELECT CURRENT_USER() to equal the requested disposable identity before any SET/UNSET. Cover unnamed/named SET and UNSET, quoted identity and unchanged second user. Never reuse invalid SNAPSHOT_TESTING captures or mutate the credential-bearing production user. No implementation or waiver is supplied by this note.
Co-authored-by: Przemek Denkiewicz <przemyslaw.denkiewicz@localstack.cloud>
* LAV-4493: capture Cloud baselines for unnamed ALTER USER self-targeting
A disposable key-pair user runs ALTER USER [<name>] SET/UNSET QUERY_TAG
on itself, with a CURRENT_USER() guard before any mutation. Captured
against real Snowflake from a direct-Cloud environment (the worker proxy
substitutes the login identity): the unnamed form changes exactly what
the named form does, only on the authenticated user; a bystander and the
connecting user stay unchanged. The unnamed cases are strict xfail until
the parser accepts an omitted target.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* LAV-4493: resolve unnamed ALTER USER to the authenticated user
Preserve an omitted target as None in the parser and AST display, then
resolve it at execution with the schema-qualified Snowflake CURRENT_USER()
function. Explicit targets keep their existing literal-name resolution;
lookahead preserves even action-keyword user names instead of confusing
them with the omitted-target form. No PostgreSQL role is used for targeting.
Enable the human-captured disposable-user QUERY_TAG SET/UNSET snapshots
from b159f3ea7 without re-capturing or changing any snapshot. Both quoted
and unquoted users leave the bystander and connecting user unchanged.
SHOW/DESCRIBE projections and independently owned action implementations
are unchanged.
Extend the existing vendored parser test with omission/round-trip and
keyword-name cases. Its existing exact named-user assertion now wraps the
same Ident in Some to match the optional AST field; its expected identity
and all compat test assertions are unchanged.
Validation:
- Before implementation, the two unnamed compat cases failed on QUERY_TAG
parsing while both named cases passed.
- make check: passed.
- make test-vendored-sqlparser: passed (run explicitly because make check's
committed vendor-diff gate skips changes not yet committed).
- make test-compat PYTEST_PATH='tests/queries/access_control/test_alter_user.py tests/queries/access_control/test_desc_user.py' PYTEST_XDIST_ARGS='': 45 passed.
Co-authored-by: Przemek Denkiewicz <przemyslaw.denkiewicz@localstack.cloud>
## Test matrix
Unnamed SET / unquoted identity / success + unchanged other users -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-unquoted]
Unnamed UNSET / unquoted identity / reset to inherited empty default -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-unquoted]
Unnamed SET / quoted mixed-case identity / success + unchanged other users -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-quoted]
Unnamed UNSET / quoted mixed-case identity / reset to inherited empty default -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-quoted]
Named SET / unquoted identity / equivalent outcome and isolation -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-unquoted]
Named UNSET / unquoted identity / equivalent reset and isolation -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-unquoted]
Named SET / quoted mixed-case identity / equivalent outcome and isolation -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-quoted]
Named UNSET / quoted mixed-case identity / equivalent reset and isolation -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-quoted]
Named target / missing user / existing error -> tests/queries/access_control/test_alter_user.py::test_alter_user_nonexistent_error
Named target / IF EXISTS missing user / SET and UNSET no-op -> tests/queries/access_control/test_alter_user.py::test_alter_user_if_exists_missing_comment
Named target / IF EXISTS existing user / SET and UNSET state -> tests/queries/access_control/test_alter_user.py::test_alter_user_if_exists_existing_comment
Named target / RENAME round-trip / unchanged existing behavior -> tests/queries/access_control/test_alter_user.py::test_alter_user_rename_round_trip
Named target / property SET and UNSET / unchanged DESCRIBE readback -> tests/queries/access_control/test_desc_user.py::test_desc_user_after_alter_set_and_unset
Parser / SET and UNSET / omitted name with and without IF EXISTS round-trips -> vendor/sqlparser/tests/sqlparser_common.rs::test_parse_alter_user
Parser / action-keyword names / unquoted and quoted named SET and UNSET round-trips -> vendor/sqlparser/tests/sqlparser_common.rs::test_parse_alter_user
Parser / action-keyword names / existing target-only statements round-trip -> vendor/sqlparser/tests/sqlparser_common.rs::test_parse_alter_user
The resource-index ALTER USER syntax was enumerated. Independent action,
authentication, MFA, authorization, policy, tag, and parameter semantics
remain with their existing owners as specified; the SQL-visible target
resolution proof uses only the already captured QUERY_TAG mutations.
* LAV-4493: preserve the existing compat-test import
Keep the existing is_real_snowflake import unchanged and add the disposable
key-pair helper as a separate import. The branch's aggregate diff now only
adds test dependencies and new tests rather than replacing an existing
compat-test line. No waiver, assertion change, or snapshot migration is
needed; the human-captured Cloud baselines remain unchanged.
Validation:
- make check: passed, including vendored sqlparser tests.
- Focused ALTER USER and DESCRIBE USER compat replay: 45 passed.
- Existing-test removal predicate against origin/main and the working tree:
zero violations (the rejected import was the only violation).
- git diff --check: passed.
Co-authored-by: Przemek Denkiewicz <przemyslaw.denkiewicz@localstack.cloud>
## Test matrix
Unnamed SET / unquoted identity / outcome and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-unquoted]
Unnamed UNSET / unquoted identity / inherited default and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-unquoted]
Unnamed SET / quoted identity / outcome and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-quoted]
Unnamed UNSET / quoted identity / inherited default and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-quoted]
Named SET / unquoted identity / equivalent outcome and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-unquoted]
Named UNSET / unquoted identity / equivalent inherited default -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-unquoted]
Named SET / quoted identity / equivalent outcome and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-quoted]
Named UNSET / quoted identity / equivalent inherited default -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-quoted]
Named target / missing user / error -> tests/queries/access_control/test_alter_user.py::test_alter_user_nonexistent_error
Named target / IF EXISTS missing user / SET and UNSET no-op -> tests/queries/access_control/test_alter_user.py::test_alter_user_if_exists_missing_comment
Named target / IF EXISTS existing user / SET and UNSET -> tests/queries/access_control/test_alter_user.py::test_alter_user_if_exists_existing_comment
Named properties / SET and UNSET / DESCRIBE readback -> tests/queries/access_control/test_desc_user.py::test_desc_user_after_alter_set_and_unset
Parser / omitted target and action-keyword named targets / round-trip -> vendor/sqlparser/tests/sqlparser_common.rs::test_parse_alter_user
Re-enumerated the offline ALTER USER resource-index syntax. This feedback
round changes imports only; independently owned actions and parameter
semantics remain outside this target-resolution issue's contract.
* LAV-4493: select ALTER USER tests when their readback helper changes
Extend the existing test_desc_user.py dependency-selection rule to include
its new test_alter_user.py importer. Preserve the existing user-actions
selection and all test assertions, imports, and human-captured snapshots.
Reproduced the feedback failure before the change with the existing
transitive importer regression test. After the fix:
- scripts/test_select_compat_tests.py: 228 passed.
- make check: passed, including vendored sqlparser tests.
- make test-compat PYTEST_PATH="tests/queries/access_control/test_alter_user.py tests/queries/access_control/test_desc_user.py" PYTEST_ARGS="-k 'alter_user or desc_user_after_alter_set_and_unset'": 26 passed.
- git diff --check: passed.
Co-authored-by: Przemek Denkiewicz <przemyslaw.denkiewicz@localstack.cloud>
## Test matrix
Changed helper / direct and transitive importer selection -> scripts/test_select_compat_tests.py::test_changed_imported_test_module_selects_all_its_importers
Unnamed SET / unquoted identity / outcome and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-unquoted]
Unnamed UNSET / unquoted identity / inherited default and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-unquoted]
Unnamed SET / quoted identity / outcome and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-quoted]
Unnamed UNSET / quoted identity / inherited default and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[unnamed-quoted]
Named SET / unquoted identity / equivalent outcome and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-unquoted]
Named UNSET / unquoted identity / equivalent inherited default -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-unquoted]
Named SET / quoted identity / equivalent outcome and isolated state -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-quoted]
Named UNSET / quoted identity / equivalent inherited default -> tests/queries/access_control/test_alter_user.py::test_alter_user_self_target_query_tag[named-quoted]
Named target / missing user / error -> tests/queries/access_control/test_alter_user.py::test_alter_user_nonexistent_error
Named target / IF EXISTS missing user / SET and UNSET no-op -> tests/queries/access_control/test_alter_user.py::test_alter_user_if_exists_missing_comment
Named target / IF EXISTS existing user / SET and UNSET -> tests/queries/access_control/test_alter_user.py::test_alter_user_if_exists_existing_comment
Named properties / SET and UNSET / DESCRIBE readback -> tests/queries/access_control/test_desc_user.py::test_desc_user_after_alter_set_and_unset
Parser / omitted target and action-keyword named targets / round-trip -> vendor/sqlparser/tests/sqlparser_common.rs::test_parse_alter_user
Re-enumerated the offline ALTER USER resource-index syntax. This round changes
only CI test selection; independent action and parameter semantics remain
outside the target-resolution contract. No Cloud recapture was performed.
---------
Co-authored-by: spiral <spiral@localhost>
Co-authored-by: Przemek Denkiewicz <przemyslaw.denkiewicz@localstack.cloud>
Co-authored-by: Przemek Denkiewicz <przemyslaw.denkiewicz@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>1 parent 9714238 commit 722c64e
3 files changed
Lines changed: 55 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17772 | 17772 | | |
17773 | 17773 | | |
17774 | 17774 | | |
17775 | | - | |
17776 | | - | |
| 17775 | + | |
| 17776 | + | |
17777 | 17777 | | |
17778 | 17778 | | |
17779 | 17779 | | |
| |||
17929 | 17929 | | |
17930 | 17930 | | |
17931 | 17931 | | |
17932 | | - | |
| 17932 | + | |
| 17933 | + | |
| 17934 | + | |
17933 | 17935 | | |
17934 | 17936 | | |
17935 | 17937 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
150 | 150 | | |
151 | 151 | | |
152 | 152 | | |
153 | | - | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
154 | 180 | | |
155 | 181 | | |
156 | 182 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19372 | 19372 | | |
19373 | 19373 | | |
19374 | 19374 | | |
| 19375 | + | |
| 19376 | + | |
| 19377 | + | |
| 19378 | + | |
| 19379 | + | |
| 19380 | + | |
| 19381 | + | |
| 19382 | + | |
| 19383 | + | |
| 19384 | + | |
| 19385 | + | |
| 19386 | + | |
| 19387 | + | |
| 19388 | + | |
| 19389 | + | |
| 19390 | + | |
| 19391 | + | |
| 19392 | + | |
| 19393 | + | |
| 19394 | + | |
| 19395 | + | |
| 19396 | + | |
19375 | 19397 | | |
19376 | 19398 | | |
19377 | 19399 | | |
19378 | 19400 | | |
19379 | 19401 | | |
19380 | 19402 | | |
19381 | | - | |
| 19403 | + | |
19382 | 19404 | | |
19383 | 19405 | | |
19384 | 19406 | | |
| |||
0 commit comments