Skip to content

JulietSecurity/abom

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

abom logo

Actions Bill of Materials — map your GitHub Actions supply chain

Build Go Report Card GitHub release License: Apache 2.0


SBOMs exist for your application dependencies. ABOMs should exist for your CI/CD pipelines.

abom recursively resolves every GitHub Action in your workflows — including actions nested inside composite actions, reusable workflows, and actions that silently embed tools like Trivy in their own code — and builds a complete dependency tree.

$ abom scan . --check
WORKFLOW                  STEP                      ACTION                             REF  STATUS
.github/workflows/ci.yml  Checkout v4               actions/checkout                   v4   tag
.github/workflows/ci.yml  Setup Node.js             actions/setup-node                 v4   tag
.github/workflows/ci.yml  Set up Docker Buildx      docker/setup-buildx-action         v3   tag
.github/workflows/ci.yml  Scan for vulnerabilities  crazy-max/ghaction-container-scan  v3   COMPROMISED (ABOM-2026-001 (detected via action inputs))

Compromised actions found: 1 (1 direct, 0 transitive)

grep can't find this. The workflow never mentions trivy-action. But crazy-max/ghaction-container-scan downloads and runs Trivy internally. abom detects it by analyzing the action's metadata.

Why this exists

The Trivy supply chain compromise (CVE-2026-33634) exposed a blind spot: organizations grepped their workflows for trivy-action and found nothing — while compromised code ran in their pipelines through transitive and embedded dependencies.

Every post-incident guide from CrowdStrike, Wiz, Snyk, and Microsoft tells you to grep your workflows. None of them address transitive action dependencies.

abom was built to close that gap.

We hope the Trivy project recovers quickly — this tool exists to address a gap in incident response, not to pile on. Supply chain attacks can happen to any project.

Features

  • Recursive resolution — follows composite actions and reusable workflows through the full dependency chain
  • Tool wrapper detection — identifies actions that embed known tools (Trivy, Grype, Snyk, etc.) by analyzing action.yml inputs and descriptions
  • Remote scanning — scan any public GitHub repo without cloning: abom scan github.com/org/repo
  • Advisory database — built-in + auto-updated database of known-compromised actions
  • Standard BOM formats — output as CycloneDX 1.5 or SPDX 2.3 for integration with Dependency-Track, Grype, and other tooling
  • SHA verification — optionally verify that pinned SHAs are actually reachable from the upstream repo, catching fork-sourced and force-pushed-away commits (--verify-shas)
  • Ref resolution — optionally resolve tag and branch refs to the commit SHA they point to at BOM-generation time, turning a mutable-tag BOM into a stable evidentiary record (--resolve-refs)
  • CI gate — exits non-zero when compromised actions are found or (with --fail-on-warnings) when any advisory warning is emitted
  • Fast — caches resolved actions locally and fetches action content directly from raw.githubusercontent.com to avoid API rate limits

Installation

Homebrew:

brew install JulietSecurity/tap/abom

Go:

go install github.com/julietsecurity/abom@latest

Binary releases:

Download prebuilt binaries for Linux, macOS, and Windows from GitHub Releases.

The basics

Scan a repository and see every action in your supply chain:

# Local repo
abom scan .

# Remote repo — no clone needed
abom scan github.com/your-org/your-repo

Check for known-compromised actions:

abom scan . --check

Generate standard BOM formats:

# CycloneDX 1.5 — for Dependency-Track, Grype, etc.
abom scan . -o cyclonedx-json

# SPDX 2.3 — for compliance tooling
abom scan . -o spdx-json

# Native JSON — full dependency tree with metadata
abom scan . -o json

Use as a CI gate:

- name: Check Actions supply chain
  run: abom scan . --check

Block on fork-sourced SHA pins as well:

- name: Check Actions supply chain
  run: abom scan . --check --verify-shas --fail-on-warnings
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Verifying pinned SHAs

Pinning an action to a SHA (e.g. actions/checkout@a1b2c3...) is the recommended defense against tag-swap attacks. But GitHub's object store is shared across a repo and its forks — so a SHA that exists only on a fork (or was force-pushed out of the upstream's history) will still resolve successfully when a workflow runs. The pin protects you from tag mutation, but not from a commit that was never in the upstream's ref graph.

--verify-shas hits the GitHub commits API for each SHA-pinned reference and emits a warning when the SHA isn't reachable from the claimed repo's refs. It doesn't change resolution behavior — ABOM still builds the same dependency tree GitHub would — it just surfaces the discrepancy.

abom scan . --verify-shas --github-token $GITHUB_TOKEN

Combine with --fail-on-warnings to block CI on the finding:

abom scan . --verify-shas --fail-on-warnings --github-token $GITHUB_TOKEN

What a warning means: the SHA is not reachable from owner/repo's refs. That may be a fork-only commit, a force-pushed-away commit, or a mistaken pin. It does not necessarily mean the SHA was tampered with.

Exit codes: 0 clean, 1 compromised action (or runtime error), 2 warnings emitted with --fail-on-warnings. When both conditions hold, exit 1 wins.

Rate limit caveat: --verify-shas makes an extra API call per unique SHA. Anonymous requests are capped at 60/hour — set --github-token (or GITHUB_TOKEN) for a realistic 5000/hour budget.

Resolving tag and branch refs

Git tags are mutable. A workflow pinned to actions/checkout@v4 today may resolve to a different commit next week if the maintainer re-points the tag. This means a BOM with tag refs is only a semi-reliable record of what actually ran. Branches (main, master) are even more mutable.

For teams generating BOMs as audit or compliance evidence, --resolve-refs calls the GitHub commits API to look up the commit SHA each tag or branch currently points to and records it in resolved_sha alongside the original ref. The original pinning is preserved so contributor intent stays visible.

abom scan . --resolve-refs --github-token $GITHUB_TOKEN

Output (JSON) gets a stable record of what was actually resolved at generation time:

{
  "uses": "actions/checkout@v4",
  "ref": "v4",
  "ref_type": "tag",
  "resolved_sha": "34e114876b0b11c390a56381ad16ebd13914f8d5"
}

Scope: tag and branch refs only. SHA-pinned refs are already immutable and skipped. Docker and local actions are also skipped.

Interaction with --verify-shas: orthogonal. --verify-shas walks SHA-pinned refs; --resolve-refs populates resolved_sha for tag and branch refs. Running both together populates both signals.

Rate limit caveat: same as --verify-shas — one API call per unique tag or branch ref, so a token is effectively required for any workflow with more than a handful of tagged actions.

How detection works

abom finds compromised dependencies through three layers that grep will never reach:

Layer What it catches How
Direct uses: aquasecurity/trivy-action@v0.20.0 Parses workflow YAML
Transitive Composite action A calls action B which calls trivy-action Fetches and parses action.yml recursively
Embedded crazy-max/ghaction-container-scan has a trivy_version input Scans action metadata for known tool signatures

Output formats

Format Flag Use case
Table -o table Human-readable dependency tree (default)
JSON -o json Native ABOM format for automation
CycloneDX 1.5 -o cyclonedx-json Dependency-Track, Grype, standard BOM tooling
SPDX 2.3 -o spdx-json Compliance, license scanning, SBOM aggregation

Advisory database

abom --check compares your ABOM against known-compromised actions:

  • Built-in — a snapshot ships with every release. Works fully offline.
  • Auto-updated — fetches the latest data from advisories.juliet.sh at runtime. Source and contributions at JulietSecurity/abom-advisories.
  • Community-curated — anyone can submit a PR to add a new advisory.

SHA-pinned actions and advisories. When --check encounters a SHA-pinned action that matches an advisory's affected package, abom resolves the SHA to its upstream tag (via git ls-remote, which doesn't consume REST API quota) and compares that tag against the advisory's affected version ranges. Refs at fixed versions are cleared; refs that fall inside the affected range are flagged with a definitive advisory ID instead of the older "verify manually" annotation. No extra flags required — this runs automatically whenever --check is used with network access.

See advisories.juliet.sh/db/advisories.json for the current advisory list.

All flags

Flag Short Description Default
--output -o Output format: table, json, cyclonedx-json, spdx-json table
--file -f Write output to file instead of stdout stdout
--check Flag known-compromised actions false
--depth -d Max recursion depth for transitive deps 10
--verify-shas Verify pinned SHAs are reachable from upstream repo refs false
--resolve-refs Resolve tag and branch refs to current commit SHAs false
--fail-on-warnings Exit 2 if any warnings were emitted false
--github-token GitHub token for API requests (also reads GITHUB_TOKEN)
--no-network Skip resolving transitive dependencies (local parsing only) false
--offline Use built-in advisory data only, skip remote fetch false
--no-cache Force fresh advisory database fetch false
--quiet -q Suppress progress output, only print results false

Contributing

We welcome contributions. If you discover a compromised action or a wrapper that abom should detect:

License

abom is released under the Apache 2.0 License.


Built and maintained by Juliet Security
Website · GitHub · Contact

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages