Skip to content

Your First Scan

This is the 5-minute walkthrough from "never heard of ActionScope" to "I just found a CRITICAL finding in my own repo." No AWS credentials needed for the static-analysis path.

1. Install

pip install actionscope

That's it. Python 3.10+ required. No external services; the install is a single Python package.

If you want the optional --aws-verify mode that makes read-only AWS IAM API calls, install with the aws extra:

pip install "actionscope[aws]"

You can do this later — the static-analysis path works without it.

2. Pick a repository to scan

You have three options, from fastest to most realistic:

cd /path/to/your/repo-with-github-actions
actionscope scan .

This is the most useful — ActionScope only takes 1–2 seconds on a repo of any size, so it costs you nothing to try.

git clone https://github.com/r12habh/ActionScope
cd ActionScope
actionscope scan tests/fixtures/coverage_repo

The bundled coverage_repo is a small synthetic repository designed to trigger every detector. Best for understanding what ActionScope can find before you point it at your own code.

Pick any public repo that uses aws-actions/configure-aws-credentials. Clone it and scan:

git clone <repo-url>
cd <repo>
actionscope scan .

The GitHub Code Search query finds thousands.

3. Read the output

This is what scanning the bundled coverage_repo fixture produces. Your own repo's output will be smaller — coverage_repo is deliberately overloaded to demonstrate every detector at once.

The header

╭──────────────────────────────────────────────────────────────────╮
│  ActionScope — Blast Radius Report                               │
│  Path: ./tests/fixtures/coverage_repo                            │
│  Workflows: 6 | Credential Sources: 2                            │
│  Observed Risk: 🔴 CRITICAL                                       │
│  Coverage: COMPLETE                                              │
│  Gate: REPORT ONLY                                               │
╰──────────────────────────────────────────────────────────────────╯
  • Workflows: how many .yml files in .github/workflows/ produced at least one finding
  • Credential Sources: how many aws-actions/configure-aws-credentials steps ActionScope found
  • Observed Risk: the maximum severity supported by evidence the scan inspected
  • Coverage: whether evidence was resolved completely. PARTIAL means some permissions or delegated workflow behavior remains unknown
  • Gate: the CI policy decision. Scans are report-only unless --fail-on is set

Known-compromised actions

⛔ KNOWN COMPROMISED ACTIONS (1 found)

⛔ CRITICAL: actions-cool/issues-helper@v3
   Workflow: triage.yml → triage → Compromised helper
   Status: Compromised 2026-05-18T19:10:24Z — documented supply-chain compromise
   Impact: Mutable tags may run credential-stealing code in this job
   Fix:    Remove this action OR pin to a verified pre-compromise SHA
   Advisory: https://www.stepsecurity.io/blog/...

ActionScope ships a curated database of compromised actions and flags any uses: reference that matches. Mutable-tag references (@v3) produce CRITICAL findings. Full-SHA pins to an action with a known compromise, where the SHA is not in the published affected-refs list, produce HIGH findings so a human can verify whether the pinned commit predates the compromise. See the full Compromised Actions Database for every entry.

AWS blast radius per workflow

Workflow: deploy.yml → Job: deploy → Step: Configure AWS credentials
AWS Role: arn:aws:iam::123456789012:role/deployer-role
Policy Match: terraform (high)
Auth: OIDC ✓

┌─────────────────────────┬────────────────────────┬─────────┐
│ Action                  │ Access                 │ Risk    │
├─────────────────────────┼────────────────────────┼─────────┤
│ iam:CreatePolicyVersion │ Permissions management │ 🔴 CRIT │
│ iam:PassRole            │ Permissions management │ 🔴 CRIT │
│ ec2:TerminateInstances  │ Write                  │ 🟠 HIGH │
│ s3:PutObject            │ Write                  │ 🟡 MED  │
└─────────────────────────┴────────────────────────┴─────────┘

🔴 Privilege Escalation Paths Detected:
  ↳ IAM PassRole + Wildcard Resource
  ↳ Create New IAM Policy Version

This is the blast radius — what deploy.yml can actually do in AWS if it's compromised. The table is derived from the IAM policies attached to the role assumed by the workflow.

The Policy Match line tells you where ActionScope got the policy from:

Value What it means
terraform (high) Found an aws_iam_role_policy resource in *.tf files
json (medium) Found a .json policy file in iam/ or policies/
aws_verified Fetched live from AWS via --aws-verify
not_found No matching IAM policy in the repo
dynamic_reference Role ARN computed at runtime — can't statically resolve

Other detectors

The rest of the output includes:

  • OIDC trust policy issues: wildcard subjects, missing aud, branch- instead of environment-scoping
  • GitHub Environment issues: deploy jobs missing environment protection rules
  • Script injection: ${{ github.event.pull_request.title }} inside run: blocks
  • Artifact poisoning: workflow_run jobs that execute downloaded artifacts
  • AI agent prompt injection: Claude Code / Copilot / Gemini agents with write permissions on PR triggers
  • Unpinned actions: external uses: references not pinned to a full 40-char SHA
  • GITHUB_TOKEN permissions: write-capable scopes that elevate risk

4. Add IAM policy files for deeper analysis

If your Policy Match column shows not_found, ActionScope didn't find the IAM policy attached to your deploy role. Two ways to fix that:

Option A — same-repo Terraform / JSON:

If your IAM policies live as Terraform (*.tf with aws_iam_role / aws_iam_role_policy resources) or as JSON files under iam/ or policies/ in the same repo, ActionScope picks them up automatically. No flag needed.

Option B — separate infrastructure repo:

If your IAM lives in a different repo, scan that repo separately and correlate manually:

actionscope scan /path/to/infra-repo --output-format json > infra.json

Or skip ahead to live AWS verification.

5. Use --aws-verify for live IAM policy data

When the policy isn't in the same repo, you can have ActionScope fetch the live attached policies via read-only IAM API calls:

pip install "actionscope[aws]"
export AWS_PROFILE=my-profile          # or use any standard AWS auth
actionscope scan . --aws-verify

This uses these IAM actions and nothing else:

  • iam:GetRole
  • iam:ListAttachedRolePolicies
  • iam:GetPolicy
  • iam:GetPolicyVersion
  • iam:ListRolePolicies
  • iam:GetRolePolicy

The exact minimum-required JSON policy is in AWS Verification Permissions. All calls are read-only — ActionScope makes no IAM changes.

6. Add ActionScope to your CI/CD

The most useful path: run ActionScope on every PR, post results as a comment, and upload SARIF to the GitHub Security tab.

Add this to .github/workflows/actionscope.yml:

name: ActionScope
on: [push, pull_request]

permissions:
  contents: read
  security-events: write   # for SARIF upload to the Security tab
  pull-requests: write     # for PR comments

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: r12habh/ActionScope@v0
        with:
          fail-on: high          # block new, high-confidence HIGH/CRITICAL findings
          new-only: true         # only newly gate-eligible findings
          min-confidence: high   # avoid blocking on heuristic matches
          save-state: true       # seed baseline from the default branch
          comment-pr: true       # post findings as a PR comment
          upload-sarif: true     # send to the Security tab

You get:

  • A PR comment on every pull request, with an exact finding delta versus the trusted default-branch baseline
  • First-class Code Scanning alerts in the Security tab via SARIF
  • CI failure only for new, high-confidence HIGH/CRITICAL findings

The Action is report-only if fail-on is omitted. A first new-only run is NOT EVALUATED until a trusted default-branch run seeds the baseline.

What's next