Skip to content

fix(cloud-security): retry transient AssumeRole failures (first-scan "could not assume role") - #3055

Merged
tofikwest merged 1 commit into
mainfrom
tofik/aws-assume-role-first-scan
Jun 8, 2026
Merged

tofikwest merged 1 commit into
mainfrom
tofik/aws-assume-role-first-scan

Conversation

@tofikwest

@tofikwest tofikwest commented Jun 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Customer (with multiple AWS accounts, cross-account role auth) reported: the first AWS scan fails with a "Could not assume AWS role" finding; switching the scan engine and back, then re-running, works — on every account, with nothing changed in AWS. They correctly concluded it isn't the role (it assumes fine the second time) — it's our bug.

Root cause (investigated + adversarially verified)

Assuming the customer's cross-account role is a two-hop STS flow (task role → Comp roleAssumer → customer role) with no application-level retry, and the AWS SDK's default retry strategy only covers throttling / 5xx / network — not the classes that actually occur here:

  • IAM/STS eventual consistency: a new or just-edited role/trust-policy returns AccessDenied on the first assume and succeeds seconds later, with no AWS change.
  • The base/roleAssumer session can briefly return ExpiredToken.

A single transient failure surfaced as a sticky finding, written by the integration-platform checks path (resolveAwsSessionOrFail → ctx.fail({ title: 'Could not assume AWS role' }) in manifests/aws/checks/shared.ts) — not the Cloud Tests scan (which returns success:false with no finding). So both assume implementations are fixed.

The "switch switch" is incidental, not causal. updateMode only writes metadata.awsScanMode (idempotent, no cache or credential refresh) and both modes share the same assume path. What actually makes the retry succeed is the elapsed time before re-running.

Fix

  • New shared helper retryAssume / isRetryableAssumeError (manifests/aws/checks/assume-retry.ts): bounded exponential backoff + jitter, retrying only transient / eventual-consistency classes (AccessDenied, ExpiredToken, IDPCommunicationError, throttling, 5xx, network). Hard config errors (bad ARN, ValidationError) and a persistently-denied role still propagate; backoff is capped so a genuinely broken role fails within a few seconds.
  • Wrapped both AssumeRole hops in shared.ts (checks path — writes the finding) and aws-security.service.ts assumeRole (Cloud Tests scan path).
  • Exported retryAssume from the package barrel for apps/api.

Tests

  • assume-retry.test.ts: retry-then-succeed, no-retry on config errors, give-up after N attempts, and the classifier (AccessDenied/ExpiredToken/throttling/5xx/network = retryable; ValidationError/bad-ARN = not). 7 passing.
  • Full cloud-security jest suite green (311); typecheck clean.

Honest notes

  • Not yet live-verified. The exact STS class for this org (AccessDenied vs ExpiredToken) is already captured in evidence.error on the failing finding — the fix covers both. Pulling that error string would confirm which hop/class it was.
  • Safety: pure additive retry — happy path (assume succeeds first try) is byte-identical. Bounded attempts so a broken role can't hang the scan.

🤖 Generated with Claude Code


Summary by cubic

Retries transient AWS STS AssumeRole failures with bounded backoff to stop first‑scan “Could not assume AWS role” findings. Applies to both the checks path and the Cloud Tests scanner.

  • Bug Fixes
    • Added shared retryAssume and isRetryableAssumeError with capped exponential backoff + jitter; retries AccessDenied, ExpiredToken, throttling, 5xx, and network errors; skips config errors (e.g., ValidationError).
    • Wrapped both AssumeRole hops in the checks (shared.ts) and scanner (aws-security.service.ts) paths to ensure transient failures don’t surface as findings.
    • Exported from @trycompai/integration-platform for reuse in apps/api; added unit tests for retry behavior; no change to the success path.

Written for commit 28ca06e. Summary will update on new commits.

Review in cubic

…"could not assume role")

Customer-reported: the first AWS scan fails with a "Could not assume AWS role"
finding; switching the scan engine and back, then re-running, works — on every
account, with no change in AWS. Customer correctly concluded it's not the role
(it assumes fine the second time).

Root cause (verified): assuming the customer's cross-account role is a two-hop
STS flow with NO application-level retry, and the AWS SDK's default retry only
covers throttling/5xx/network — NOT the classes that actually occur here:
- IAM/STS eventual consistency on a new or just-edited role/trust-policy returns
  AccessDenied on the first assume and succeeds seconds later (no AWS change).
- The base/roleAssumer session can briefly return ExpiredToken.
So a single transient failure surfaced as a sticky finding. The "switch switch"
is INCIDENTAL — updateMode only writes metadata (idempotent, no cache/cred
refresh); what actually fixes it is the elapsed time before the retry.

The visible finding is written by the integration-platform checks path
(resolveAwsSessionOrFail in manifests/aws/checks/shared.ts), not the Cloud
Tests scan (which returns success:false with no finding) — so both assume
implementations are fixed.

- new shared helper retryAssume / isRetryableAssumeError
  (manifests/aws/checks/assume-retry.ts): bounded exponential backoff + jitter,
  retries only transient / eventual-consistency classes (AccessDenied,
  ExpiredToken, IDPCommunicationError, throttling, 5xx, network). Hard config
  errors (bad ARN, ValidationError) and a persistently-denied role still
  propagate; backoff capped so a genuinely broken role fails within seconds.
- wrap both AssumeRole hops in shared.ts (checks path) and
  aws-security.service.ts assumeRole (Cloud Tests scan path).
- export retryAssume from the package barrel for apps/api.
- tests: retry-then-succeed, no-retry on config errors, give-up after N, classifier.

Note: not yet live-verified; the exact STS class (AccessDenied vs ExpiredToken)
is already captured in evidence.error on the failing finding — the fix covers
both. cloud-security suite green (311); helper tests green; typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment Jun 8, 2026 6:58pm
comp-framework-editor Ready Ready Preview, Comment Jun 8, 2026 6:58pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
portal Skipped Skipped Jun 8, 2026 6:58pm

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

@tofikwest
tofikwest merged commit 7ea61c4 into main Jun 8, 2026
11 checks passed
@tofikwest
tofikwest deleted the tofik/aws-assume-role-first-scan branch June 8, 2026 19:57
@claudfuen

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.73.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

This branch was successfully deployed

2 active and 1 inactive deployments
Preview – comp-framework-editor — 28ca06e5 Deployed Jun 8, 2026 by vercel[bot]
Preview – app — 28ca06e5 Deployed Jun 8, 2026 by vercel[bot]
Preview – portal — 28ca06e5 Deployed Jun 8, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants