Skip to content

fix(cloud-security): use credential report for iam-root-access-keys check#2846

Open
github-actions[bot] wants to merge 1 commit into
mainfrom
tofik/fix-iam-root-access-keys-credential-report
Open

fix(cloud-security): use credential report for iam-root-access-keys check#2846
github-actions[bot] wants to merge 1 commit into
mainfrom
tofik/fix-iam-root-access-keys-credential-report

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented May 14, 2026

Summary

  • The iam-root-access-keys Cloud Tests check was producing critical false positives. Customer-reported case: AWS account 615477685532 (Marsello-Prod-01). IAM Dashboard's Security Recommendations panel shows "Root user has no active access keys" ✓ — our scan reports "Root account has active access keys" at critical severity ✗.
  • Root cause: iam.adapter.ts:checkRootAccessKeys called GetAccountSummary and read AccountAccessKeysPresent. Per the AWS Security blog, that field returns 1 when root has access keys "even when that access key is not active" — so any leftover disabled root key triggers our critical finding even though AWS Console correctly ignores it.
  • Fix: Switch to GenerateCredentialReport + GetCredentialReport (same source AWS Console's recommendation panel uses). Parse the <root_account> row's access_key_1_active / access_key_2_active columns directly. Our finding now matches AWS Console exactly.
  • Extracted the check to its own module (iam-root-access-keys.ts) so it's unit-testable. Added 16 unit tests covering all branches — there were zero tests for the IAM adapter before.

What changed

  • New: apps/api/src/cloud-security/providers/aws/iam-root-access-keys.tscheckRootAccessKeys, plus exported helpers getCredentialReport (handles GenerateCredentialReport + polling with retry on CredentialReportNotReadyException / CredentialReportNotPresentException) and findRootAccountRow (CSV parser).
  • New: apps/api/src/cloud-security/providers/aws/iam-root-access-keys.spec.ts — 16 tests.
  • Modified: apps/api/src/cloud-security/providers/aws/iam.adapter.ts — drops the broken in-adapter checkRootAccessKeys (and the now-unused GetAccountSummaryCommand import), imports the new helper. File shrinks from 308 → 266 lines (back under the 300-line limit).

Why the new approach is correct

The credential report has explicit access_key_*_active columns:

user,arn,...,access_key_1_active,...,access_key_2_active,...
<root_account>,arn:aws:iam::123456789012:root,...,false,...,false,...

true = active key, false = inactive (or no key at that slot). This is exactly what AWS Console's IAM Dashboard → Security recommendations → Root user has no active access keys checks. The IAM managed policy SecurityAudit (which our auditor role already has) grants iam:GenerateCredentialReport and iam:GetCredentialReport, so customers don't need to update their IAM role.

Failure modes — handled

  • Report not yet generated: GenerateCredentialReport is called first; subsequent GetCredentialReport calls poll for up to ~10 seconds on CredentialReportNotReadyException.
  • Permission denied / unknown error: check returns [] (skip) rather than failing the entire scan.
  • Malformed CSV / no root row: check returns [] (skip).

Test plan

  • npx jest src/cloud-security/providers/aws — 22/22 pass (16 new + 6 existing ec2-vpc)
  • Direct tsc --noEmit on the changed files — zero errors. The package-level typecheck shows only pre-existing errors unrelated to this PR (people-invite.service.ts, variables.controller.spec.ts, training-certificate-pdf.service.spec.ts, etc.) plus a worktree-vs-main better-auth type duplication that resolves once merged.
  • Manual verification (you): rescan the customer's AWS account after merge and confirm iam-root-access-keys flips from failed → passed without IAM changes on the customer's side.

Tests added (covering all the relevant branches)

Scenario Expected Verifies
Root has only inactive keys (customer's exact case) passed=true, info severity The original false-positive class is fixed
Root has access_key_1_active=true passed=false, critical Active-key detection works
Root has access_key_2_active=true passed=false, critical Both columns checked
Both root keys active passed=false, critical OR logic correct
No <root_account> row in CSV [] (skip) No spurious findings
AccessDenied on GetCredentialReport [] (skip) Doesn't break the scan
Report not ready, then ready Returns CSV Polling/retry works
Report never ready within budget null Bounded retry
GenerateCredentialReport fails Still works if a recent report exists Resilience

What did NOT change

  • IAM role permissions required on the customer side — SecurityAudit already grants both credential-report APIs.
  • Finding ID (iam-root-access-keys), severity (critical when failed, info when passed), or remediation message.
  • Any other check in iam.adapter.ts (password policy, MFA, stale keys) — untouched.

🤖 Generated with Claude Code


Summary by cubic

Replaced the IAM root access keys check to use the IAM Credential Report so we only flag active root keys and match what the AWS Console shows. This fixes false positives when a root key exists but is disabled.

  • Bug Fixes

    • Use Credential Report via GenerateCredentialReport and GetCredentialReport from @aws-sdk/client-iam instead of GetAccountSummary.AccountAccessKeysPresent.
    • Read the <root_account> row and evaluate access_key_1_active/access_key_2_active.
    • Retry on CredentialReportNotReadyException (1s delay, up to 10 tries); safe-skip on permission errors or missing root row.
  • Refactors

    • Extracted the check to apps/api/src/cloud-security/providers/aws/iam-root-access-keys.ts with focused unit tests in iam-root-access-keys.spec.ts.
    • Updated IamAdapter to call checkRootAccessKeys and removed the GetAccountSummary usage/import.

Written for commit 0c9069e. Summary will update on new commits.

…heck

The check previously called GetAccountSummary and read AccountAccessKeysPresent,
which returns 1 when the root account has *any* access keys — active or
inactive. That produced a critical false positive for accounts that only had
a disabled root key sitting on them: AWS Console correctly said "Root user has
no active access keys" while our scan reported "Root account has active access
keys" at critical severity.

Switch to GenerateCredentialReport + GetCredentialReport (same source AWS
Console's IAM Dashboard recommendation panel uses). Parse the <root_account>
row's access_key_1_active / access_key_2_active columns directly so we report
exactly what AWS Console reports.

- Extract the check into apps/api/src/cloud-security/providers/aws/iam-root-access-keys.ts
  so it can be unit-tested in isolation.
- Drop the old in-adapter implementation and the now-unused GetAccountSummary
  import. iam.adapter.ts shrinks from 308 → 266 lines.
- Add 16 unit tests covering: customer's exact scenario (inactive-only key
  passes), each active-key column, both active, no root row (safe skip),
  permission errors (safe skip), and the polling/retry path on
  CredentialReportNotReadyException.

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

vercel Bot commented May 14, 2026

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

Project Deployment Actions Updated (UTC)
comp-framework-editor Ready Ready Preview, Comment May 14, 2026 0:40am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
app Skipped Skipped May 14, 2026 0:40am
portal Skipped Skipped May 14, 2026 0:40am

Request Review

@tofikwest tofikwest changed the title [dev] [tofikwest] tofik/fix-iam-root-access-keys-credential-report fix(cloud-security): use credential report for iam-root-access-keys check May 14, 2026
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

No issues found across 3 files

Confidence score: 5/5

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant