Skip to content

apm audit

Terminal window
apm audit [PACKAGE] [OPTIONS]

apm audit is the explicit security and integrity tool. It runs in two modes:

  • Content scan mode (default). Scans deployed files across the project for hidden Unicode, including governed files absent from the lockfile and lockfile-recorded files outside the current target directories. It replays the install pipeline into a scratch tree to detect drift (hand-edits to deployed files, missing integrations, orphaned files vs the lockfile). Can also remediate findings with --strip or scan an arbitrary file with --file.
  • CI gate mode (--ci). Runs lockfile consistency checks plus drift in machine-readable form (text, JSON, or SARIF) suitable for branch-protection gates. When apm_modules/ is absent but apm.lock.yaml is present, CI mode self-hydrates a lock-pinned scratch install for config-consistency and drift without mutating the checkout. Auto-discovers org policy from your project’s git remote unless --no-policy is set.

Both modes also enforce the lockfile’s canonical deployment ownership; see Deployment-owner integrity.

This is the explicit power tool. Built-in protection against critical Unicode findings already runs automatically in apm install, apm compile, and apm unpack; you do not need to call apm audit to be safe by default. See Drift and secure by default for the consumer-side overview and Enforce in CI for the gating workflow. For marketplace plugin transitive-dependency pinning, see apm marketplace audit.

PACKAGE, when supplied, scans one installed dependency by its lockfile key (the repo URL), so only files recorded for that package are included. Omit it to scan deployed files across the whole project. See Baseline CI checks for the exact lockfile, target-directory, symlink, and .apm/ scope boundaries.

FlagDefaultDescription
--file PATHunsetScan an arbitrary file instead of installed packages. Bypasses drift detection.
--stripoffRemove critical and warning severity characters in place. Preserves emoji and ZWJ inside emoji sequences.
--dry-runoffPreview what --strip would remove without modifying files.
--no-driftoffSkip the install-replay drift check (reduces coverage). Mutually exclusive with --strip and --file.
--verbose, -voffShow info-level findings and per-file detail. No effect in --ci mode.

Gated by the external-scanners experimental flag (apm experimental enable external-scanners). Folds findings from any SARIF 2.1.0 scanner into the report. CLI-driven and install-method-neutral — no pip extra; works with the APM binary. See External scanners.

FlagDefaultDescription
--external NAMEunsetIngest findings from an external SARIF-native scanner (repeatable). Names: skillspector (invokes the CLI on PATH), sarif (ingests a file via --external-sarif). Cannot be combined with --strip, --dry-run, or --ci.
--external-sarif PATHunsetSARIF file to ingest for --external sarif.
--external-llm / --no-external-llmadapter defaultForce a scanner’s LLM-powered analysis on or off for this run (overrides config). SkillSpector default is offline --no-llm. LLM mode makes outbound API calls and needs OPENAI_API_KEY or NVIDIA_INFERENCE_KEY; missing the key fails closed. Requires --external.
--external-args TEXTunsetExtra scanner CLI flags as a single shlex-split string (e.g. "--model gpt-4o"). Allowlist-validated per adapter; secret-looking or out-of-cwd tokens are rejected fail-closed. Overrides config args. Requires --external.

When LLM mode is active APM prints a [!] egress banner before the scan noting that outbound API calls will be made. --external-llm / --external-args used without --external <name> raise a usage error (exit 2).

FlagDefaultDescription
--format, -f text|json|sarif|markdowntextOutput format. sarif targets GitHub Code Scanning. markdown is for GitHub step summaries and is not allowed with --ci.
--output, -o PATHstdoutWrite the report to a file. Format is auto-detected from extension (.sarif, .sarif.json, .json, .md) when --format is omitted.

-f json in default (non---ci) mode includes a top-level "passed" boolean (exit_code == 0), alongside the existing summary and finding list. Deployment-owner integrity findings render in every format — text, JSON, SARIF, and markdown — as a deployment-owner category entry naming the locator, its invalid owner(s), and the remediation.

FlagDefaultDescription
--cioffRun lockfile consistency checks and drift as a CI gate. Cannot be combined with --strip, --dry-run, --file, or PACKAGE.
--policy SOURCEautoPolicy source for --ci. Accepts org (auto-discover from the project’s git remote), owner/repo, an https:// URL, or a local file path. Experimental. Without --ci it is ignored with a warning.
--no-policyoffSkip policy discovery and enforcement. Equivalent to APM_POLICY_DISABLE=1. Overridden when --policy is passed explicitly.
--no-cacheoffForce a fresh policy fetch (skip the policy cache). Only relevant with policy discovery active.
--no-fail-fastoffRun every check even after the first failure, for a full diagnostic report.
Terminal window
apm audit
Terminal window
apm audit https://github.com/owner/repo
Terminal window
apm audit --file .cursorrules
Terminal window
# Preview what --strip would remove
apm audit --strip --dry-run
# Strip critical and warning severity characters in place
apm audit --strip
# In CI, remediate first and then rerun the gate
apm audit --strip
apm audit --ci --no-drift
Terminal window
# SARIF to stdout (for GitHub Code Scanning upload)
apm audit -f sarif
# JSON to a file
apm audit -f json -o results.json
# Markdown for a GitHub Actions step summary
apm audit -f markdown -o "$GITHUB_STEP_SUMMARY"
# Auto-detect format from extension
apm audit -o report.sarif
Terminal window
# Invoke SkillSpector on PATH (offline by default)
apm audit --external skillspector
# Opt into LLM-powered analysis (needs an API key; makes network calls)
apm audit --external skillspector --external-llm
# Pass allowlisted scanner flags
apm audit --external skillspector --external-args "--model gpt-4o"
# Ingest a SARIF file from any scanner
apm audit --external sarif --external-sarif report.sarif
Terminal window
# Default CI gate (auto-discovers org policy)
apm audit --ci
# CI gate, baseline checks only (no policy)
apm audit --ci --no-policy
# CI gate with an explicit policy source
apm audit --ci --policy org
apm audit --ci --policy ./apm-policy.yml
# Full diagnostic report (don't stop at first failure)
apm audit --ci --no-fail-fast
# CI gate as JSON or SARIF
apm audit --ci -f json
apm audit --ci -f sarif -o audit.sarif

For the full workflow, see Enforce in CI.

SeverityExamplesEffect
CriticalTag characters (U+E0001-E007F), bidi overrides (U+202A-E, U+2066-9), variation selectors 17-256 (U+E0100-E01EF, the Glassworm vector)Exit 1. Removed by --strip. Blocks apm install / apm compile / apm unpack by default.
WarningZero-width spaces and joiners (U+200B-D), variation selectors 1-15 (U+FE00-FE0E), bidi marks (U+200E-F, U+061C), invisible operators (U+2061-4), annotation markers (U+FFF9-B), deprecated formatting (U+206A-F), soft hyphen (U+00AD), mid-file BOMExit 2 if no critical findings. Removed by --strip.
InfoNon-breaking and unusual whitespace, emoji presentation selector (U+FE0F), ZWJ between emoji charactersExit 0. Shown only with --verbose. Preserved by --strip.

The default audit replays the install pipeline into a scratch tree and diffs the result against the working tree. It catches hand-edits, missing integrations, orphaned files, and unrecorded files. unrecorded applies when replay produced the same normalized bytes as the project but no exact or directory deployed_files claim covers the path. Shared merge-hook targets are exempt and differing bytes report modified. unrecorded findings fail --ci; run apm install, then commit the regenerated apm.lock.yaml.

Drift is whole-project only; --file and explicit PACKAGE runs skip it. Use --no-drift to opt out with reduced coverage. In bare apm audit, drift findings are advisory and do not change the exit code (see Exit codes).

Bare apm audit keeps replay cache-only, so a cache miss produces an informational skip. apm audit --ci instead self-hydrates one lock-pinned scratch replay through install/audit_replay.py; materialization failures fail closed without mutating the checkout.

Before content scanning or drift, both modes validate the lockfile’s canonical deployments rows: every owners entry and the active_owner must resolve to a current lock dependency key, the workspace self-owner (.), or local-bundle. A stale reference — typically left behind when a dependency was removed without running apm prune — is a hard failure, not drift: it always exits 1, in bare apm audit and --ci alike, and is reported under the deployment-ledger-owners check ID in --ci output. The finding names the owning locator and its invalid owner(s) with one remediation: Run 'apm prune', then rerun 'apm audit'. --strip refuses to modify content while any owner reference is invalid. Stale owner rows never authorize deleting files — only apm prune reconciles them, and only ever as metadata repair; see apm prune.

--ci runs the baseline lockfile consistency checks defined in src/apm_cli/policy/ci_checks.py: lockfile presence, canonical deployment-owner integrity (deployment-ledger-owners), ref consistency, deployed-files presence, no orphaned packages, skill-subset consistency, MCP config consistency, content integrity, and an advisory includes consent check. A lockfile is required when apm.yml declares APM or MCP dependencies. For an MCP-only project, normal apm install creates or repairs the resolved MCP lock state; frozen install fails without writing when that state is missing or stale. Content integrity scans hidden Unicode across the whole-project deployed-file scope and checks SHA-256 drift only where the lockfile provides a baseline. Drift replay runs alongside and contributes to the exit code unless --no-drift is set; --no-drift never disables hidden-Unicode scanning. On a cold cache, CI mode self-hydrates a scratch install from the lockfile pins instead of reporting a green skip, so setup-only CI can still catch stale committed deployed files without rewriting the checkout. Audit also reports unrecorded drift when replay produces governed files that no lockfile entry claims. Repos that gitignore deployed outputs still need those files present on disk for deployed-files-present, so the full-install CI pattern remains the right default there. With policy discovery active, declared policy rules are evaluated against the resolved manifest. See Baseline CI checks for the full reference.

  • --no-drift cannot be combined with --strip or --file.
  • --ci cannot be combined with --strip, --dry-run, --file, or PACKAGE.
  • --ci does not support --format markdown.
  • --external cannot be combined with --strip, --dry-run, or --ci; --external-sarif requires --external sarif; --external-llm / --no-external-llm and --external-args require --external.
CodeMeaning
0Clean, info-only findings, drift-only (advisory) in bare audit, or successful --strip.
1Critical findings detected, or an invalid canonical deployment-owner reference in apm.lock.yaml (always hard-fails, unlike ordinary drift).
2Warning-only findings, or usage error (mutually exclusive flags).
3Configuration or infrastructure error (feature not enabled, scanner not found, malformed SARIF).
CodeMeaning
0All checks passed.
1One or more checks failed (including deployment-owner integrity, drift, hash drift, or policy violations).
  • apm install — the built-in scan that blocks critical findings before deployment.
  • apm prune — reconciles the canonical deployment-owner references this check validates.
  • Drift and secure by default — consumer-side overview of the two-layer security model.
  • Enforce in CI — wiring apm audit --ci into branch protection.