Repository navigation
test(api): keep the RI-exchange carve-out test offline under mutation (#175) - #1208
Conversation
…LeanerCloud#175) TestExecuteExchange_PlainAdminIsRefused drives the real executeExchange handler but injected no account resolver. With {execute, ri-exchange} dropped from adminCarvedOuts (the mutation this test exists to catch), the handler passed the gate and authorizeAWSExchangeExecution ran a live sts:GetCallerIdentity from ambient credentials. AWS_EC2_METADATA_DISABLED only cut off the IMDS fallback, so on a machine with credentials in the environment the mutated test still called STS for real. Inject the handler's existing reshapeAccountResolver seam with a stub that records it was reached and returns a sentinel error, plus an executeExchangeFn that fails the test if called. Under the mutation the test now fails in ~0s on its own "got past the gate" assertion, with no network I/O; unmutated it passes as before. The t.Setenv is dropped as it no longer guards anything. Closes LeanerCloud#175 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reached
This review includes 1 billable file and costs up to $0.25. Or wait 59 minutes for your next included review. View limit details
Comment |
|
Independent review 2 (claude-fleet, cold): APPROVE 900c742 Scope: test-only, in internal/api/ri_exchange_carveout_test.go (+39/-17). It closes #175. Root cause: the code matches the issue, and injecting the existing seam fixes the root cause, not a symptom.
Findings (none blocking):
Attacked:
Verified locally, with no cloud credentials, under the heavy lock:
Reviewed by an independent agent with no author context (second reviewer). Review only: nothing was pushed, and the GitHub review state is unchanged. |
|
VERDICT: APPROVE 900c742 Reviewed head 900c742 against origin/main 6776e27. Findings (none blocking):
Correctness: both paths past the gate resolve the account through Attacked:
|
|
Local verification at
CI on this head: CI Success and pre-commit green. cristim's independent review approved this head. Migrations in this batch (000136-000139) are applied in numeric order. Nothing was run against any cloud. |
|
Marked ready and merged by IAmJSD's agent at IAmJSD's request (IAmJSD is acting owner while the maintainers are away), after cristim's independent review approving |
Root cause
TestExecuteExchange_PlainAdminIsRefused(internal/api/ri_exchange_carveout_test.go) drives the realexecuteExchangehandler, but it builtHandler{config, auth}with no account resolver. When{ActionExecute, ResourceRIExchange}is removed fromadminCarvedOuts(the mutation this test exists to catch), the handler gets pastrequirePermissionand callsauthorizeAWSExchangeExecution→resolveReshapeCloudAccountIDOrUnattributed→resolveReshapeCloudAccountID. Withh.reshapeAccountResolver == nilthat falls through toresolveAWSCloudAccountID→resolveAWSCallerIdentity, which runsawsconfig.LoadDefaultConfigplus a livests:GetCallerIdentity.The existing
t.Setenv("AWS_EC2_METADATA_DISABLED", "true")only blocked the IMDS fallback. On a machine with AWS credentials in the environment, the mutated test still called STS for real. This was confirmed on main by pointing STS at a dead port:Fix (test-only)
The test now injects the seams
Handleralready has, so no production code changes:reshapeAccountResolveris a stub that records it was reached and returns a sentinelerrGateBypassed. A handler that has regressed stops at the first step past the gate, with no network I/O. Two new assertions name the failure:assert.False(gateBypassed)("a plain admin got past the execute:ri-exchange gate into cloud account scope resolution") andassert.NotErrorIs(err, errGateBypassed). These come before the existing 403ClientErroridentity check, which is still there.executeExchangeFncallst.Errorif it is ever reached. While the resolver returns an error it can't be reached. It stays as a backstop in case a future change moves scope resolution after the AWS call.t.Setenv("AWS_EC2_METADATA_DISABLED", ...)is dropped because it no longer guards anything. The doc comment is updated to describe the real path past the gate: scope resolution, then the AWS exchange call.Sibling tests in the same file: I checked all of them and none needed the change. The other three (
TestRIExchangeCarveOut_PlainAdminIsRefused,_ExchangerIsAllowed,_AdminKeepsNonCarvedVerbs) callrequirePermissiondirectly. Nothing in them goes past the gate or touches AWS, mutated or not.Verification
This is unit/mock evidence only. No real cloud calls are made, and none are needed.
All runs used
AWS_EC2_METADATA_DISABLED=true AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null. The local~/.aws/configdefault profile on this machine is invalid, and these settings also guarantee no ambient credential source.Unmutated (
go test -count=1 -v -run '^(TestExecuteExchange_PlainAdminIsRefused|TestRIExchangeCarveOut_.*)$' ./internal/api/): all 4 tests PASS.Mutated:
{ActionExecute, ResourceRIExchange}: true,was commented out ininternal/auth/types.go, then reverted afterwards.AWS_ENDPOINT_URL_STS=http://127.0.0.1:9 AWS_REGION=us-east-1and static dummy credentials (AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE), so any real STS call would show up as a connection-refused error with retries. The result is the same three assertion failures,(0.00s). There is noFailed to resolve source account ID via STSlog line, while main logs one (3.17s, refusedPOSTto127.0.0.1:9). STS is not reached.Other checks:
go test -count=1 ./internal/api/: okgo test ./internal/mocks/ -count=1: ok (TestNoUnfailableMockAssertionsand the other guards)go vet ./internal/api/: okgolangci-lintv2.10.1run ./internal/api/: 0 issuesgofmt: cleangocyclo -over 10 -ignore "_test\.go": silent (only a test file changed)git secrets --scanin an isolated temp repo: cleanscripts/test-git-secrets-allowlist.sh: 42 PASS, 0 FAILOut of scope
exchange.ExecuteExchange" point is the follow-up the test's existing comment calls "#1760". That number is from the old tracker and doesn't resolve in this repo or in cloud-commitments-cli, so I didn't link it. This PR only makes the carve-out test hermetic, using seams that already exist.Maintainer: please mirror the issue's triage labels onto this PR:
impact/internal,type/chore,triaged,priority/p3,severity/low,urgency/eventually,effort/s.Closes #175
🤖 Generated with Claude Code