Repository navigation
fix(purchase): refuse recommendations owned by another account - #2196
Merged
Merged
Conversation
Cost Explorer returns linked-account recommendations to a payer, but a purchase call buys in the caller's own account. The purchase path never compared rec.Account with the credentials' account, so a payer run could commit the payer account to a linked account's usage. Both purchase entry points now resolve the caller account with STS GetCallerIdentity (failing the run if it cannot), and validatePurchasePreconditions rejects a recommendation whose account differs, or when the caller account is unknown, on dry runs too. Refs #2191 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Contributor
Drop the global toolCfg.CallerAccountID assignment from TestMain so deleting the lookup from an entry point fails a test, set the account per test where the purchase loops are called directly, and fix a lint finding and the defaultTestAccount placement. Refs #2191 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Member
Author
|
Independent review (claude-opus-5-5) of 8e75ee6: APPROVE This re-verifies the PR after my block on fb5df32. The delta is one commit, 8e75ee6, touching Earlier findings
Guard (fails closed)
Probes, run here at the head
Live dry runNo
CI on 8e75ee6 is green: all required checks pass, and gosec and Trivy are skipped. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2191
Replaces #2193 (rebuilt from a clean branch with synthetic account IDs only).
What
Recommendations are fetched with linked-account scope, so a payer run gets rows for other accounts, but
PurchaseCommitmentbuys in the caller's own account. Nothing comparedrec.Accountwith the credentials' account, so a purchase would commit the payer account to a member account's usage.withCallerAccountresolves the account with STSGetCallerIdentityintoConfig.CallerAccountID. It runs in both purchase entry points (runPurchaseAndReport,runToolFromCSV), on dry runs too, and fails the run if the account cannot be resolved.validatePurchasePreconditions(main pipeline and CSV loop) rejects a recommendation whoseAccountdiffers from the caller's, and fails closed when the caller account is unknown. The row is recorded as a failed result and audit entry; other rows proceed. A row with an empty account (CSV may omit it) has nothing to compare.go.mod:service/stsmoves from indirect to direct.Tests (mocks only; no purchase API is called)
TestCheckPurchaseAccount(including the unknown-caller case),TestPurchaseSingleRec_RefusesOtherAccountsRecommendation(dry and real),TestProcessPurchaseLoop_RefusesOtherAccountsRecommendationWithoutPurchasing(the mock client sees exactly onePurchaseCommitment, for the caller's own row),TestWithCallerAccount.TestMainstubs the STS fetcher with a synthetic account; existing audit-path and CSV tests set or use that account, and the two multi-account end-to-end expiry tests now expect the guard's refusal for rows of other accounts.Fail before (guard body neutered): the three guard tests fail with "An error is expected but got nil" / "Should be false". After:
go test ./cmdpasses (29s, skippingTestQueryMajorEngineVersions_ErrorHandling, which hangs locally in this session independent of this change).go vetand the pre-commit hooks pass.Live dry run (read-only, no
--purchase)Under payer-account credentials, a member-account EC2 row is now refused in the dry run with "recommendation is for account X but the credentials are for account Y; cross-account purchases are not supported ...", and the CSV row has
Success=false. Before this change the same row was reported as a successful dry-run purchase.🤖 Generated with Claude Code
Summary by CodeRabbit