apm audit
Synopsis
Section titled “Synopsis”apm audit [PACKAGE] [OPTIONS]Description
Section titled “Description”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
--stripor 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. Whenapm_modules/is absent butapm.lock.yamlis present, CI mode self-hydrates a lock-pinned scratch install forconfig-consistencyand drift without mutating the checkout. Auto-discovers org policy from your project’s git remote unless--no-policyis 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.
Options
Section titled “Options”Content scan
Section titled “Content scan”| Flag | Default | Description |
|---|---|---|
--file PATH | unset | Scan an arbitrary file instead of installed packages. Bypasses drift detection. |
--strip | off | Remove critical and warning severity characters in place. Preserves emoji and ZWJ inside emoji sequences. |
--dry-run | off | Preview what --strip would remove without modifying files. |
--no-drift | off | Skip the install-replay drift check (reduces coverage). Mutually exclusive with --strip and --file. |
--verbose, -v | off | Show info-level findings and per-file detail. No effect in --ci mode. |
External scanners (experimental)
Section titled “External scanners (experimental)”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.
| Flag | Default | Description |
|---|---|---|
--external NAME | unset | Ingest 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 PATH | unset | SARIF file to ingest for --external sarif. |
--external-llm / --no-external-llm | adapter default | Force 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 TEXT | unset | Extra 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).
Output
Section titled “Output”| Flag | Default | Description |
|---|---|---|
--format, -f text|json|sarif|markdown | text | Output format. sarif targets GitHub Code Scanning. markdown is for GitHub step summaries and is not allowed with --ci. |
--output, -o PATH | stdout | Write 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.
CI gate
Section titled “CI gate”| Flag | Default | Description |
|---|---|---|
--ci | off | Run lockfile consistency checks and drift as a CI gate. Cannot be combined with --strip, --dry-run, --file, or PACKAGE. |
--policy SOURCE | auto | Policy 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-policy | off | Skip policy discovery and enforcement. Equivalent to APM_POLICY_DISABLE=1. Overridden when --policy is passed explicitly. |
--no-cache | off | Force a fresh policy fetch (skip the policy cache). Only relevant with policy discovery active. |
--no-fail-fast | off | Run every check even after the first failure, for a full diagnostic report. |
Examples
Section titled “Examples”Default audit (content scan plus drift)
Section titled “Default audit (content scan plus drift)”apm auditScan a specific installed package
Section titled “Scan a specific installed package”apm audit https://github.com/owner/repoScan an arbitrary file outside APM
Section titled “Scan an arbitrary file outside APM”apm audit --file .cursorrulesRemediate findings
Section titled “Remediate findings”# Preview what --strip would removeapm audit --strip --dry-run
# Strip critical and warning severity characters in placeapm audit --strip
# In CI, remediate first and then rerun the gateapm audit --stripapm audit --ci --no-driftReports
Section titled “Reports”# SARIF to stdout (for GitHub Code Scanning upload)apm audit -f sarif
# JSON to a fileapm audit -f json -o results.json
# Markdown for a GitHub Actions step summaryapm audit -f markdown -o "$GITHUB_STEP_SUMMARY"
# Auto-detect format from extensionapm audit -o report.sarifExternal scanners
Section titled “External scanners”# 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 flagsapm audit --external skillspector --external-args "--model gpt-4o"
# Ingest a SARIF file from any scannerapm audit --external sarif --external-sarif report.sarifCI gate
Section titled “CI gate”# 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 sourceapm audit --ci --policy orgapm 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 SARIFapm audit --ci -f jsonapm audit --ci -f sarif -o audit.sarifFor the full workflow, see Enforce in CI.
Behavior
Section titled “Behavior”Severity levels (content scan)
Section titled “Severity levels (content scan)”| Severity | Examples | Effect |
|---|---|---|
| Critical | Tag 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. |
| Warning | Zero-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 BOM | Exit 2 if no critical findings. Removed by --strip. |
| Info | Non-breaking and unusual whitespace, emoji presentation selector (U+FE0F), ZWJ between emoji characters | Exit 0. Shown only with --verbose. Preserved by --strip. |
Drift detection
Section titled “Drift detection”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.
Deployment-owner integrity
Section titled “Deployment-owner integrity”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 checks (--ci)
Section titled “CI checks (--ci)”--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.
Mutual exclusions
Section titled “Mutual exclusions”--no-driftcannot be combined with--stripor--file.--cicannot be combined with--strip,--dry-run,--file, orPACKAGE.--cidoes not support--format markdown.--externalcannot be combined with--strip,--dry-run, or--ci;--external-sarifrequires--external sarif;--external-llm/--no-external-llmand--external-argsrequire--external.
Exit codes
Section titled “Exit codes”Content scan mode
Section titled “Content scan mode”| Code | Meaning |
|---|---|
0 | Clean, info-only findings, drift-only (advisory) in bare audit, or successful --strip. |
1 | Critical findings detected, or an invalid canonical deployment-owner reference in apm.lock.yaml (always hard-fails, unlike ordinary drift). |
2 | Warning-only findings, or usage error (mutually exclusive flags). |
3 | Configuration or infrastructure error (feature not enabled, scanner not found, malformed SARIF). |
CI gate mode (--ci)
Section titled “CI gate mode (--ci)”| Code | Meaning |
|---|---|
0 | All checks passed. |
1 | One or more checks failed (including deployment-owner integrity, drift, hash drift, or policy violations). |
Related
Section titled “Related”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 --ciinto branch protection.