Limitations and Finding Calibration¶
Status: This document ships with ActionScope v0.4.0. Last updated: 2026-06-08
ActionScope reports security exposure from static analysis. That is valuable, but users need clear calibration: some findings are confirmed risks, some are exposure signals that need human review, and some are edge cases that may not apply to your setup.
This guide explains what ActionScope can and cannot detect, how to interpret confidence levels, and how to tune results for your environment.
Static mode vs --aws-verify mode¶
ActionScope has two analysis modes:
| Mode | What it does | Confidence |
|---|---|---|
| Static (default) | Reads workflow files, Terraform, and JSON policies locally. Infers blast radius from IAM action names and resource patterns. | Exposure signal — may over-report |
--aws-verify |
Fetches live IAM policies from AWS (read-only) and cross-references with workflow usage. | Higher confidence — grounded in actual policy state |
When to use static mode¶
- Quick triage of a new repo
- CI pipelines where AWS credentials are not available
- Auditing workflow files before they reach production
When to use --aws-verify¶
- You have read-only AWS credentials configured
- You want to confirm that a detected exposure is actually exploitable
- You need to present findings to a security team with confidence
What --aws-verify cannot do¶
- It cannot read IAM policies that the verification role cannot access
(requires read-only
iam:Get*/iam:List*permissions) - It cannot inspect organization-level SCPs (Service Control Policies) unless the role has
organizations:Describe* - It cannot verify cross-account trust relationships unless the role has access to the trusted account
Understanding policy_source: not_found¶
When ActionScope reports policy_source: not_found, it means:
- The workflow references an IAM role (e.g.,
role-to-assume: arn:aws:iam::123456789012:role/my-role) - ActionScope could not find a matching policy file in the repository
(static mode), or the AWS API returned access denied (
--aws-verifymode)
What to do next¶
- If you control the role: Run with
--aws-verifyand ensure the attached role hasiam:GetRolePolicyandiam:ListAttachedRolePolicies - If you don't control the role: Treat the finding as a review-needed signal. The role may have more permissions than what's visible
- If the role is cross-account: The finding is likely a true positive — cross-account trust with overly broad permissions is a known risk pattern
Confidence levels explained¶
| Label | Meaning | Action |
|---|---|---|
| CRITICAL | Confirmed, severe vulnerability or known-compromised dependency requiring immediate remediation. | Fix immediately / emergency remediation |
| HIGH | Confirmed vulnerability. The workflow pattern is a known attack vector and the IAM permissions confirm exploitability. | Fix immediately |
| MEDIUM | Security exposure. The pattern is concerning but may require additional context (e.g., branch protection, environment rules) to exploit. | Review within 1 sprint |
| LOW | Hardening opportunity. The finding represents a defense-in-depth improvement rather than an immediate risk. | Add to backlog |
| INFO | Informational. The finding is documented for completeness but does not represent a current risk. | No action required |
Likely true positives (fix these)¶
- OIDC trust with
repo:*subject and noaudrestriction run:block that interpolates${{ github.event.head_commit.message }}without sanitization- Unpinned third-party actions (e.g.,
uses: some-action@v1without SHA) workflow_runtrigger with artifact download from untrusted source- AI agent (
claude-code,copilot) in CI with access to secrets
Likely review-needed findings (context matters)¶
- Broad IAM permissions on a role used only in
push-to-mainworkflows (branch protection may mitigate) pull_request_targetworkflows that check out the PR ref (safe if you don't use secrets in the same step)- Terraform
aws_iam_rolewithResource: "*"(may be intentional for infrastructure provisioning) - Actions from verified publishers that are unpinned (lower risk than unverified publishers)
Known blind spots¶
External reusable workflows¶
When a workflow uses uses: org/repo/.github/workflows/reusable.yml@ref,
ActionScope can inspect the called YAML when --github-token or
GITHUB_TOKEN is supplied. Without a token, it deliberately makes no network
request and records the target as no_token rather than treating it as clean.
External inspection covers the workflow file and nested reusable calls. It
does not fetch the called repository's Terraform or IAM policy files, so AWS
role-to-policy correlation still depends on policy evidence in the scanned
repository or --aws-verify.
Mitigation: Supply a read-capable GitHub token, pin the call to a full SHA,
and use --aws-verify when the role policy lives outside the scanned repo. See
Reusable Workflow Inspection.
IAM policy context ActionScope cannot read¶
ActionScope can read inline policies and managed policies attached to the calling role. It cannot read:
- Permissions boundaries
- Session policies applied at assume-role time
- SCPs that restrict the role's effective permissions
Mitigation: Use --aws-verify with the read-only IAM permissions listed in
AWS Verify Permissions:
iam:GetRole, iam:ListAttachedRolePolicies, iam:GetPolicy,
iam:GetPolicyVersion, iam:ListRolePolicies, and iam:GetRolePolicy.
To evaluate permissions boundaries or SCP effects, use AWS's
iam:SimulatePrincipalPolicy separately as a manual follow-up.
YAML expression edge cases¶
ActionScope parses YAML workflow files but does not evaluate GitHub Expression syntax at runtime. Edge cases include:
- Dynamic job matrix expressions that conditionally set permissions
if:conditions that gate secret access on branch/environment- Reusable workflow inputs that override default permissions
Mitigation: Review findings in context of your branch protection rules.
Generated workflows¶
Some projects generate workflow files at build time (e.g., via
actions/setup-python matrix generation). ActionScope analyzes the
file as-is and may flag patterns that are generated safely at runtime.
Organization-level GitHub settings¶
ActionScope does not currently check:
- Organization-wide
GITHUB_TOKENpermission policies - Required workflows that enforce security settings
- Organization-level OIDC trust policies
Suppression and configuration¶
ActionScope does not currently support repository-local suppressions or custom risk tuning. That work is tracked in issue #23.
Until that lands, treat accepted findings as documented risk decisions in your own security review process rather than hiding them from the scan.
Examples¶
Example 1: OIDC wildcard subject (CRITICAL)¶
# .github/workflows/deploy.yml
jobs:
deploy:
permissions:
id-token: write
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/deploy
Finding: oidc-wildcard-subject — The OIDC subject claim allows any repo in the org.
The vulnerable configuration is in the AWS IAM role trust policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/*"
}
}
}
]
}
Why it's CRITICAL: Any repository in my-org can assume this role and
access AWS, not just the intended production repository.
Fix: Scope the subject to the specific repo and branch:
{
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"
}
}
Example 2: Script injection in run block (MEDIUM)¶
- name: Run tests
run: |
echo "Testing PR: ${{ github.event.pull_request.title }}"
Finding: script-injection — PR title is interpolated in a run: block.
Why it's MEDIUM: Exploitable only if the workflow runs on pull_request_target AND secrets are accessible in the same context.
Fix: Pass the value via an environment variable instead:
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: echo "Testing PR: $PR_TITLE"
Example 3: Unpinned action (LOW)¶
- uses: actions/checkout@v4
Finding: unpinned-action — Action is referenced by tag, not SHA.
Why it's LOW: actions/checkout is a GitHub-verified publisher. The risk is supply-chain, not immediate exploitation.
Fix: Pin to a full commit SHA:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
Further reading¶
- AWS Verify Permissions — How to set up live IAM verification
- CLI Reference — Full command-line options
- FAQ — Common questions
- Compromised Actions Database — Known-compromised actions