test(api): pin reshape security regressions — STS-error fail-closed + empty-region normalization (closes #151)#230
Conversation
… empty-region normalization (closes #151) Adds two regression-guard tests covering the security paths PR #79 + follow-up commits f44b065 and afc3aa1 hardened. Both fixes landed without coverage; a future refactor could silently break either gate. Test 1 — TestResolveAWSCloudAccountID_FailsClosedOnSTSError Pins commit f44b065's multi-tenant safety contract: when STS GetCallerIdentity fails (transient AWS error, denied perm, expired token), resolveAWSCloudAccountID propagates the error rather than returning ("", nil). The empty-with-no-error path would have purchaseRecLookupFromStore skip the AccountIDs filter and surface another tenant's recs — exactly the leak the production fix prevents. Mock injection: a failing http.RoundTripper installed on the handler's pre-sealed aws.Config makes STS GetCallerIdentity fail without hitting the network. Static dummy credentials + an explicit region let the SDK's signer compose the request before the failing transport runs (otherwise the SDK would error with MissingRegion / NoCredentialProviders before we reached the STS branch). The test asserts the wrapped error AND that ListCloudAccounts was never invoked — proving the resolver short-circuited before the DB read. Test 2 — TestGetReshapeRecommendations_EmptyRegionUsesConfigRegion Pins commit afc3aa1's region-normalization fix: when ?region= is empty (or omitted), the handler adopts cfg.Region (the value the AWS clients are actually talking to) before threading region through to the recs lookup. Without normalization the recs query lands as Region:"" — an unscoped read that surfaces alternatives from every region onto the reshape page. Mock injection mirrors the integration-test helpers but lives in the unit-test file (no //go:build integration tag). MockConfigStore captures every ListStoredRecommendations filter via mock.Run so the assertion can pin filter.Region == cfg.Region. A guard require.NotEmpty(capturedFilters) prevents a silent-pass failure mode if the analyzer ever stops invoking the closure for synthetic RIs. Would-fail-if-reverted verification (manual, before commit): - Commenting out the `if region == "" { region = cfg.Region }` block in handler_ri_exchange.go made Test 2 fail with the captured filter showing Region:"" instead of "us-west-2". - Changing the STS-error branch in exchange_lookup.go from `return "", fmt.Errorf(...)` back to `return "", nil` made Test 1 fail with "An error is expected but got nil". No production code modified — additive tests only.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR adds two security regression test functions pinning the AWS STS error fail-closed behavior and region-normalization path, ensuring multi-tenant recommendation leaks are prevented and empty region parameters correctly default to the handler's configured region. ChangesReshape Security Regression Tests
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Review rate limit: 1/5 review remaining, refill in 41 minutes and 18 seconds. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
) A fork that pushes a PR, pings CodeRabbit, then exits leaves CR threads unresolved — exactly what happened to PRs #228, #229, #230, etc., where CR posted Actionable findings + Nitpicks that were never triaged. This adds a paragraph to the CR-loop section explicitly addressing the delegation case: subagent prompts MUST include the full loop with the exit criteria spelled out. Stops the failure mode where the rule is correctly stated for humans but doesn't get mirrored into fork prompts.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Summary
Closes #151. Adds two regression-guard tests covering security paths PR #79 + follow-up commits hardened — both fixes landed without coverage and a future refactor could silently break either gate.
This PR is tests-only. Production code is unchanged.
Pinned regressions
Test 1 —
TestResolveAWSCloudAccountID_FailsClosedOnSTSErrorPins commit
f44b06567's multi-tenant safety contract: when STSGetCallerIdentityfails (transient AWS error, denied IAM permission, expired token),resolveAWSCloudAccountIDpropagates the error rather than returning("", nil). The empty-with-no-error path would havepurchaseRecLookupFromStoreskip theAccountIDsfilter and surface another tenant's recs — exactly the leak the production fix prevents.http.RoundTripperinstalled on the handler's pre-sealedaws.Configmakes STSGetCallerIdentityfail without hitting the network.MissingRegion/NoCredentialProvidersBEFORE the failing transport runs and the test would pass for the wrong reason."resolve source aws account for reshape scope"(the production prefix) ANDListCloudAccountswas never invoked — proving the resolver short-circuited before the DB read.Test 2 —
TestGetReshapeRecommendations_EmptyRegionUsesConfigRegionPins commit
afc3aa1ff's region-normalization fix: when?region=is empty (or omitted), the handler adoptscfg.Region(the value the AWS clients are actually talking to) before threading region through to the recs lookup. Without normalization the recs query lands asRegion:""— an unscoped read that surfaces alternatives from every region onto the reshape page.//go:build integrationtag).MockConfigStorecaptures everyListStoredRecommendationsfilter viamock.Runso the assertion can pinfilter.Region == cfg.Region.require.NotEmpty(capturedFilters)fires if the analyzer ever stops invoking the closure for synthetic RIs, preventing a vacuously-passing test.Would-fail-if-reverted verification (manual, before PR open)
This is the hard evidence that the tests pin the security behaviour:
if region == "" { region = cfg.Region }block ininternal/api/handler_ri_exchange.gomade Test 2 FAIL with the captured filter showingRegion:""instead of"us-west-2".internal/api/exchange_lookup.gofromreturn "", fmt.Errorf("resolve source aws account for reshape scope: %w", err)back toreturn "", nilmade Test 1 FAIL with"An error is expected but got nil".The temporary reverts were never committed.
Out of scope
Issue #151 also lists two other test cases (no-AWS-context fall-through,
ListCloudAccountserror propagation). The user request capped this PR's scope at the two security-critical paths above. The other two cases can land in a follow-up if desired.Test plan
go test ./internal/api/... -run 'TestResolveAWSCloudAccountID_FailsClosedOnSTSError|TestGetReshapeRecommendations_EmptyRegionUsesConfigRegion' -v -count=1— both PASS.go test ./internal/api/... -count=1— 1011 tests PASS (no regressions).go vet ./...— clean.gofmt -l .— empty output.git diffagainst base shows only the two test additions, no production-code changes (+221, -0across two*_test.gofiles).Summary by CodeRabbit