cdx-audit
June 30, 2026 · View on GitHub
cdx-audit helps security, engineering, and governance teams either prioritize upstream dependency review from existing CycloneDX BOMs or evaluate those BOMs directly with the built-in BOM audit rules. It answers two practical operational questions:
- which dependencies should we review first, and why?
- what direct BOM or OBOM findings should we surface from a saved BOM right now?
In predictive mode, it resolves supported package URLs back to source repositories, generates child SBOMs for those sources, and combines cdxgen rules with explainable, high-signal heuristics to surface the dependencies most likely to warrant attention.
In direct BOM audit mode, it evaluates the supplied BOM or OBOM itself with the same rule engine used by cdxgen --bom-audit, making it possible to re-audit a saved BOM later without regenerating it first.
Unlike cdxgen --bom-audit, which evaluates the BOM you just generated as part of BOM creation, cdx-audit starts from one or more existing BOMs. You can use it either to investigate upstream repositories behind supported dependencies or to run the direct BOM audit flow against the saved BOM itself.
Product positioning
cdx-audit is strongest when it can identify structural precursors that have repeatedly shown up in modern supply-chain incidents, for example:
- risky GitHub Actions workflow patterns
- dangerous publish paths or legacy token usage
- install-time execution or concealment signals
- weak or missing provenance signals
cdx-audit works best as part of a broader supply-chain assurance program alongside:
- reproducible builds
- registry-side integrity controls
- maintainer identity review
- provenance verification
- runtime behavior analysis
- human review
Use it as an evidence-backed prioritization layer that helps teams review faster, focus earlier, and explain why a dependency moved to the front of the queue.
Who should use this
AppSec analysts
Use cdx-audit to answer:
- Which third-party packages should I inspect before spending time elsewhere?
- Which findings are corroborated strongly enough to justify escalation?
- Which upstream workflows, repositories, or provenance signals should I inspect first?
Maintainers and package owners
Use cdx-audit to answer:
- Which dependency is most likely to deserve review before the next release?
- Which upstream workflow file or package behavior triggered the score?
- What is the next concrete review action for this dependency?
Platform, governance, and compliance teams
Use cdx-audit to answer:
- Which dependencies need risk triage across a portfolio of BOMs?
- Which results can be exported into SARIF or preserved as CycloneDX annotations?
- Which manual SCVS reviews should be supported with heuristic evidence?
When to use cdx-audit
Use cdx-audit when you already have one or more BOMs and want to either prioritize upstream dependency review based on explainable risk signals or re-run the direct BOM audit flow against saved BOMs and OBOMs.
It is especially useful when “where should we look first?” matters as much as “what evidence can we show for that decision?”.
Use BOM_AUDIT.md when you want to embed post-generation findings into the BOM being generated.
Use CDX_VALIDATE.md when the primary goal is structural validation, SCVS coverage, or CRA-oriented review.
Use cdx-audit to accelerate prioritization and escalation decisions. Final disposition should still account for provenance, internal policy, and analyst review.
Audit modes
cdx-audit supports two complementary ways to analyze existing BOMs:
- Predictive dependency audit (default)
- extracts supported package URLs from the input BOM
- resolves them to upstream repositories
- generates child SBOMs and prioritizes dependency review
- Direct BOM audit (
--direct-bom-audit)- evaluates the supplied BOM directly with the BOM audit rule engine
- works well for saved OBOMs, rootfs BOMs, or previously generated SBOMs
- defaults to
obom-runtimefor OBOM-like inputs and otherwise evaluates all direct BOM categories unless you narrow them with--categories
Supported scope
cdx-audit currently evaluates package URLs for:
- Cargo / crates.io (
pkg:cargo/...) - npm (
pkg:npm/...) - PyPI (
pkg:pypi/...)
Other ecosystems are skipped and reported as unsupported.
Current practical scope limits:
- only supported purls can be analyzed
- source resolution can fail or resolve to incomplete metadata
- clean source does not guarantee clean release artifacts
- some malicious behavior that lives only in runtime, registry, or maintainer infrastructure may be outside the tool's line of sight
cdx-audit ships with the main npm package.
npm install -g @cyclonedx/cdxgen --omit=optional --ignore-scripts --min-release-age=2
cdx-audit --help
Without a global install:
corepack pnpm dlx --package=@cyclonedx/cdxgen cdx-audit --help
GitHub Releases also publish standalone binaries such as cdx-audit-linux-amd64, cdx-audit-darwin-arm64, and cdx-audit-windows-amd64.exe together with matching .sha256 files.
Example with the GitHub CLI in GitHub Actions:
permissions:
contents: read
steps:
- name: Download cdx-audit binary from GitHub Releases
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release download v12.3.1 \
--repo cdxgen/cdxgen \
--pattern 'cdx-audit-linux-amd64' \
--pattern 'cdx-audit-linux-amd64.sha256'
sha256sum -c cdx-audit-linux-amd64.sha256
chmod +x cdx-audit-linux-amd64
./cdx-audit-linux-amd64 --help
For Linux, macOS, and Windows download snippets with hash verification, see CLI.md.
What the command does
- Load one BOM with
--bomor many BOMs from--bom-dir - Extract unique Cargo, npm, and PyPI package URLs from
components[] - Skip trusted-publishing-backed packages by default unless you override that behavior
- Resolve each supported purl to a source repository URL
- Clone or reuse the source under
--workspace-dir - Generate or reuse a child SBOM for that upstream repository
- Evaluate built-in rules and heuristics against the child SBOM
- Enrich results with provenance and publishing signals when registries expose them
- Score each target conservatively so stronger severities require corroboration
The result is a prioritized, explainable review queue for upstream investigation.
Quick start
# Audit one BOM
cdx-audit --bom bom.json
# Re-audit a saved OBOM directly with the BOM rule engine
cdx-audit --bom obom.json --direct-bom-audit
cdx-audit --bom aibom.json --direct-bom-audit --categories ai-bom
# Re-audit a saved OBOM directly and keep the scope explicit
cdx-audit --bom obom.json --direct-bom-audit --categories obom-runtime
# Audit a directory of BOMs and render JSON
cdx-audit --bom-dir ./boms --report json
# Export SARIF for code-scanning style review
cdx-audit --bom bom.json --report sarif --report-file audit.sarif
# Reuse clones and child SBOMs across runs
cdx-audit --bom bom.json --workspace-dir .cache/cdx-audit --reports-dir .reports/cdx-audit
# Focus on required dependencies only
cdx-audit --bom bom.json --scope required
# Limit the queue while keeping the default direct-runtime prioritization
cdx-audit --bom bom.json --scope required --max-targets 25
# Override trusted-publishing target selection
cdx-audit --bom bom.json --include-trusted
cdx-audit --bom bom.json --only-trusted
# Add your own purl prefix allowlist on top of the built-in well-known filter
cdx-audit --bom bom.json --allowlist-file ./audit-allowlist.json
# Explain risk scoring decisions in think mode
CDXGEN_THINK_MODE=true cdx-audit --bom bom.json --max-targets 10
CLI reference
| Option | Description |
|---|---|
--bom | Path to a single CycloneDX JSON BOM |
--bom-dir | Directory containing CycloneDX JSON BOMs |
--direct-bom-audit | Evaluate the supplied BOM(s) directly with the BOM audit rule engine |
--workspace-dir | Reuse git clones and cached child SBOMs between runs |
--reports-dir | Persist generated child SBOMs and per-target findings |
--rules-dir | Merge additional YAML rules into direct BOM audit and predictive child-SBOM evaluation |
--report | Output format: console, json, or sarif |
--report-file, -o | Write the final report to a file instead of stdout |
--categories | Comma-separated categories for predictive child-SBOM analysis or direct BOM audit |
--min-severity | Minimum target severity (predictive) or finding severity (direct BOM audit) to render |
--fail-severity | Exit with code 3 when any target or direct BOM finding reaches this severity |
--max-targets | Safety limit for the number of unique purls analyzed |
--scope | Target selection scope: all or required |
--include-trusted | Include targets already marked with trusted publishing metadata |
--only-trusted | Restrict analysis to trusted-publishing-backed targets |
--prioritize-direct-runtime | Keep direct runtime dependencies ahead of less actionable targets (enabled by default) |
--allowlist-file | Add a JSON array or newline-delimited purl-prefix allowlist on top of the built-in well-known filter |
--license-policy | Path to a license compliance policy YAML file (see below) |
License compliance auditing
Pass --license-policy <file.yml> to evaluate a license compliance policy
alongside the supply-chain audit. License violations are reported as a separate
table (and licenseViolations array in JSON output), independent of the
supply-chain findings.
Evaluation is comprehensive across both audit modes:
- The input BOM(s) are always evaluated, recursing through nested components.
- In predictive mode, the per-dependency child SBOMs generated from cloned upstream sources are also evaluated, so source-derived and deeper transitive licenses are covered, not just the licenses declared in the input BOM. Duplicate component/license findings across both sources are collapsed.
Both prohibited (error) and warning-level violations are listed. Any
prohibited license causes the run to exit with code 3, regardless of
--fail-severity. The policy file format is documented in
Lesson 17.
Exit behavior
| Code | Meaning |
|---|---|
0 | The run completed and no result met --fail-severity and no prohibited license was found |
1 | Configuration or runtime error |
3 | A result met or exceeded --fail-severity, or a prohibited license was found |
Target selection defaults
cdx-audit narrows target selection before cloning upstream repositories:
- only Cargo, npm, and PyPI purls are considered
- components with
scope: optionalorscope: excludedare skipped when--scope requiredis used - packages with trusted-publishing metadata such as
cdx:cargo:trustedPublishing=true,cdx:npm:trustedPublishing=true, orcdx:pypi:trustedPublishing=trueare skipped by default - built-in well-known purl prefixes such as
pkg:npm/%40babel,pkg:npm/npm, andpkg:npm/%40typesare skipped by default - when
--max-targetstrims the queue, direct runtime dependencies are prioritized by default - explicit
scope=requiredis treated as a stronger prioritization indicator than an implicit missing scope evidence.occurrenceslifts packages that are observed in more source locations- development-only and platform-specific packages remain deprioritized relative to runtime/general packages
- for Cargo targets, runtime-facing crates stay ahead of build-only workspace helper crates when the queue must be trimmed
Use the trusted-publishing switches to override the default:
--include-trustedincludes both trusted and non-trusted targets--only-trustedkeeps only trusted-publishing-backed targets
Passing both switches together is invalid.
You can also append your own purl prefix allowlist with --allowlist-file.
The file may be either:
- a JSON array such as
["pkg:npm/%40acme", "pkg:pypi/internal-tool"] - a newline-delimited text file with one prefix per line
Recommended filter combinations
cdx-audit --bom bom.json --scope required --max-targets 25keeps triage focused on required dependencies and caps the review queuecdx-audit --bom bom.json --include-trusted --max-targets 50includes trusted-publishing-backed packages when you want a broader baseline reviewcdx-audit --bom bom.json --only-trustedisolates the subset of packages already backed by trusted publishing metadatacdx-audit --bom bom.json --allowlist-file ./audit-allowlist.jsonappends internal or pre-approved package prefixes to the built-in well-known filter
Prioritization indicators
When cdx-audit has to choose which packages to inspect first, it uses a small set of explainable indicators:
- direct runtime dependency status from the root dependency graph
- explicit CycloneDX
scope=required - source evidence density from
evidence.occurrences - absence of development-only markers
- absence of platform-specific constraints
- for Cargo, runtime-facing member crates before build-only workspace helper crates
These indicators affect queue order, not the final risk severity. Final severity still comes from the findings observed in the generated child SBOM and the conservative scoring model.
Thought-log diagnostics
If you want a lightweight explanation for why a dependency stayed low risk or was considered risky, run cdx-audit with thought logging enabled:
CDXGEN_THINK_MODE=true cdx-audit --bom bom.json --scope required --max-targets 10
The thought log emits one short decision summary per package with:
- the final severity and score
- confidence and confidence label
- the number of findings and corroborating categories
- a short preview of the top reasons behind the decision
Cargo-specific predictive signals
Cargo support now folds several Cargo-native signals into prioritization and scoring:
- yanked crates and publisher/cadence drift from crates.io metadata
- native build surfaces from
build.rs, build dependencies, and-syshelpers - workspace-resolved member dependencies so build-only helpers can be deprioritized beneath runtime-facing crates
- exact GitHub Actions/setup/cache/build metadata when child SBOMs include formulation and workflow components
In practice this means cdx-audit can now explain not only that a Cargo dependency looks risky, but also whether that risk is:
- runtime-facing versus build-only
- reinforced by Cargo-native build surfaces
- reinforced by mutable Cargo setup actions or workflow steps that exercise native build logic
What each audience gets back
Console output
Best for maintainers and triage sessions.
The console report highlights:
- final severity
- affected package or grouped namespace
- why the dependency needs attention
- the next review step
- upstream escalation guidance when the dependency is maintained externally
When nothing crosses the configured threshold, the console output uses the empty state:
No dependencies require your attention.
JSON output
Best for automation and secondary reporting pipelines.
Use --report json when you want stable machine-readable results for dashboards, ticket enrichment, or internal triage workflows.
SARIF output
Best for code scanning platforms and centralized review queues.
cdx-audit includes:
- rule metadata and remediation text
- per-result
properties.nextAction properties.upstreamEscalationwhen the right fix lives with an external maintainerrelatedLocationsfor correlated local workflow receiver files when a sender → receiver dispatch edge was identified
CycloneDX annotations
When a cdx-audit result is written back into a BOM by downstream workflows, the annotation text preserves:
cdx:audit:nextActioncdx:audit:upstreamGuidancecdx:audit:dispatch:edgecdx:audit:dispatch:receiverFilescdx:audit:dispatch:receiverNames
These properties are useful in REPL.md, Dependency-Track, and other annotation-aware tooling.
Severity model
cdx-audit is intentionally conservative:
- isolated findings usually remain
lowormedium highrequires corroboration across stronger signals or categoriescriticalis reserved for rare compound patterns with strong confidence
This severity model is designed for operational use:
- lower scores generally indicate weaker or less corroborated signals
- higher scores generally indicate stronger signal convergence and a better candidate for early review
- stronger scores mean “review this sooner with higher confidence”, not “skip analyst validation”
Two rule families receive additional weight because they encode attacker-relevant, compound behavior rather than generic hygiene issues:
CI-019— explicit fork-context plus sensitive-context plus downstream dispatchINT-009— obfuscated npm lifecycle execution
This keeps prioritization focused on structurally higher-signal packages while avoiding alert floods from single weak detectors.
Scope and boundaries
cdx-audit is intentionally focused. It does not currently aim to:
- cover all ecosystems equally
- detect completely novel attack techniques with no structural precursor
- verify that release artifacts exactly match reviewed source
- see maintainer account takeovers unless they leave source-visible traces
- replace provenance, signatures, reproducibility, or registry protections
- replace manual investigation for high-impact decisions
Detection coverage
GitHub Actions and workflow abuse
cdx-audit looks for:
workflow_dispatchandrepository_dispatchlaunched from fork-reachable or privileged jobs- workflows that inspect fork or head-repository context before dispatching downstream automation
- explicit local sender ↔ receiver workflow correlation when the sender target can be matched uniquely inside the same repository
- dispatches triggered via
gh workflow run, GitHub API endpoints,actions/github-script, and common helper actions
This is one of the stronger parts of the tool today because many recent supply-chain incidents have left exactly these workflow-level breadcrumbs.
Correlated sender → receiver edges are preserved in the console summary, SARIF properties, SARIF related locations, and CycloneDX annotations.
npm install-time concealment
cdx-audit evaluates:
- obfuscated or base64-decoded npm lifecycle hooks
- install-time execution in
preinstall,install,postinstall,prepublish, andprepare - referenced JS or TS lifecycle files so hidden payloads outside
package.jsonare still visible
PyPI packaging heuristics
cdx-audit evaluates:
- suspicious encoded or dynamically executed logic in
setup.py - suspicious process or network behavior in package
__init__.py
The Python coverage is intentionally triage-oriented rather than full static analysis.
Provenance and publisher context
When registry metadata is available, cdxgen records and uses signals such as:
- trusted publishing
- provenance URLs
- publisher identity
- publish time
- cadence compression
- maintainer or uploader drift
Positive provenance evidence reduces the final score. Missing provenance is treated as weak context, not as proof of compromise.
Additional considerations
Like any source- and metadata-driven prioritization system, cdx-audit can underrepresent cases such as:
- clean-looking repositories with malicious release artifacts
- attacks that happen entirely through stolen maintainer credentials
- runtime-only payloads or environment-triggered behavior
- malicious infrastructure outside the source tree
- subtle abuse that does not resemble existing incident patterns
Performance and caching
- progress is written to
stderr, so JSON output onstdoutremains machine-readable --workspace-dirstores reusable clones and child SBOM caches--reports-dirpersists intermediate child artifacts and findings for later review- large target sets emit a preflight note so operators know when the run may take several minutes
Operational tips
For AppSec analysts
- start with
--scope requiredfor the highest-value triage pass - use
--report sarifwhen you want findings in a shared review queue - treat
CI-019andINT-009as escalation pivots, especially when corroborated - treat results as strong prioritization input for human review, not standalone proof
For maintainers
- start with the console report to get the next concrete action
- inspect sender and receiver workflows together when a dispatch edge is shown
- use
--workspace-dirduring repeated investigations to avoid recloning the same targets - keep provenance, release controls, and compensating controls in the loop even for lower-scored targets
For platform and compliance teams
- use JSON for portfolio automation
- combine
cdx-validatemanual SCVS reviews withcdx-auditevidence when you need workflow, provenance, or publisher context - preserve SARIF and CycloneDX annotations so the guidance travels with the BOM
Relationship to custom properties
The prioritization engine relies on the custom properties documented in CUSTOM_PROPERTIES.md, especially GitHub workflow metadata, provenance properties, and install-time execution indicators.