reference

IAM risk scanner

Cloud IAM is where a small mistake becomes a total compromise. The IAM risk scanner reads the IAM policy documents you commit — Terraform, CloudFormation, raw JSON, serverless configs — and flags the identity-and-access patterns that actually get people owned: loose GitHub OIDC trust, privilege-escalation paths, and admin-equivalent grants. 12 checks across three families.

It reads policy-as-code, not your live cloud

This scanner analyzes the IAM configuration in your repository. It does not connect to your AWS/GCP/Azure control plane, and it does not need read:orgor any elevated GitHub scope — just the read access every scan already has. (Organization MFA enforcement, which some tools fold into “IAM,” is a posturesignal here, not part of this scanner — see Posture score.) If it can't read the repo's files, the result is marked degraded rather than reported as a clean pass.

How it runs

From the repo tree, it selects files that look like they carry IAM policy — .tf, serverless.yml, and JSON/YAML whose path mentions iam/policy/role/trust/cloudformation/template — up to 80 files, 256 KB each. It extracts policy statements from each (JSON documents, Terraform jsonencode / aws_iam_policy_document / heredoc policies, and CloudFormation/SAM YAML), then runs the three check families over them. Findings deduct from a 100-point score: critical −20, high −10. The remaining score maps to a level: 90+ low, 70+ medium, 50+ high, below 50 critical.

GitHub OIDC trust

When a repo uses GitHub Actions OIDC to assume an AWS role, the role's trust policy is what decides who can assume it. A trust policy that is too loose lets workflows you do not control mint your credentials.

  • critical

    OIDC trust has no Condition block

    iam-oidc-no-condition

  • critical

    Wildcard repo/org in the sub claim

    iam-oidc-wildcard-repo

  • critical

    Trust accepts the pull_request context

    iam-oidc-pull-request-trust

  • high

    Wildcard ref/environment in the sub claim

    iam-oidc-wildcard-ref

Privilege escalation

These cross statements rather than auditing a policy in isolation — the IGA angle. They flag permission combinations that let a principal grant itself more than it started with.

  • critical

    iam:PassRole granted on Resource: *

    iam-passrole-wildcard

  • critical

    iam:PassRole + compute creation in the same file

    iam-passrole-with-create-compute

  • critical

    Policy can modify IAM policies on Resource: *

    iam-self-managing

  • high

    sts:AssumeRole allowed without a Condition

    iam-assume-role-no-condition

  • high

    Allow combined with NotAction (inverted logic)

    iam-not-action-allow

Admin equivalents

The table-stakes wildcard checks — grants that are administrator-equivalent in practice even when they don't attach the AdministratorAccess policy by name.

  • critical

    Allow with Action: * and Resource: * (or 3+ service:* on Resource: *)

    iam-action-resource-wildcard

  • high

    Sensitive service (iam/kms/secretsmanager/sts) with Resource: *

    iam-sensitive-service-wildcard

  • critical

    Resource policy with a wildcard Principal and no Condition

    iam-principal-wildcard

OIDC trust — vulnerable vs fixed

A trust policy with no condition can be assumed by any workflow on the public GitHub OIDC provider — including repos you don't own:

// FLAGGED: iam-oidc-no-condition (critical)
{
  "Effect": "Allow",
  "Principal": {
    "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
  },
  "Action": "sts:AssumeRoleWithWebIdentity"
}

Fixed — the sub claim is pinned to one repo and one ref, so only that workflow can assume the role:

// OK
{
  "Effect": "Allow",
  "Principal": {
    "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
  },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringEquals": {
      "token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"
    }
  }
}

The same statement with a sub of repo:my-org/*:* trips iam-oidc-wildcard-repo; a sub ending in :pull_request trips iam-oidc-pull-request-trust (a fork PR could assume it); and one pinned to the repo but ending in :* trips iam-oidc-wildcard-ref.

Privilege escalation — vulnerable vs fixed

iam:PassRoleon every resource lets a principal hand any role to compute it controls — a documented escalation path:

// FLAGGED: iam-passrole-wildcard (critical)
{ "Effect": "Allow", "Action": "iam:PassRole", "Resource": "*" }

Fixed — scoped to the exact role, and constrained to the service that may receive it:

// OK
{
  "Effect": "Allow",
  "Action": "iam:PassRole",
  "Resource": "arn:aws:iam::123456789012:role/my-app-task-role",
  "Condition": {
    "StringEquals": { "iam:PassedToService": "ecs-tasks.amazonaws.com" }
  }
}

If the same file also grants compute creation (lambda:CreateFunction, ec2:RunInstances, ecs:RunTask, glue:CreateJob), that combination alone trips iam-passrole-with-create-compute. Granting IAM-mutating actions (iam:AttachRolePolicy, iam:PutRolePolicy, …) on Resource: * trips iam-self-managing— the principal can attach AdministratorAccess to itself.

Admin equivalents — vulnerable vs fixed

The classic full wildcard:

// FLAGGED: iam-action-resource-wildcard (critical)
{ "Effect": "Allow", "Action": "*", "Resource": "*" }

Fixed — enumerate the actions and pin the resources:

// OK
{
  "Effect": "Allow",
  "Action": ["s3:GetObject", "s3:PutObject"],
  "Resource": "arn:aws:s3:::my-bucket/*"
}

A resource policy whose Principal is "*" (or { "AWS": "*" }) with no Condition trips iam-principal-wildcard— anyone in any AWS account can act on the resource. Constrain it with the specific principals, or add a Condition such as aws:PrincipalOrgID.

Where this fits

IAM-in-code findings are computed dynamically, so they don't appear in the static rule catalog. For a one-line summary of every detector, see Detectors. For deep cross-account IAM analysis against your live cloud, a dedicated CSPM/CIEM tool goes further than policy-as-code static analysis can.

Found an IAM pattern we should catch (or a false positive)? Open an issue.