# lpm audit (/docs/packages/audit)



```bash
lpm audit
```

Walks every installed package and reports:

1. **Confirmed vulnerabilities** — npm packages are cross-referenced against [OSV.dev](https://osv.dev); `@lpm.dev/*` packages get vulnerability data from the registry's per-version metadata.
2. **Behavioral flags** — static analysis surfaces risky behaviors (`eval`, `child_process`, network access, filesystem writes, native code, etc.). Registry-provided behavioral metadata for `@lpm.dev/*` packages is normalized into the same severity ladder, including info-level tags such as WebSockets and URL string literals. See the full tag list under [`lpm query`](/docs/packages/query).
3. **Quality score** — for `@lpm.dev/*` packages. Scores below 40 surface as moderate; below 20 surface as high.
4. **Dependency-confusion warnings** — when an installed `@lpm.dev/*` package's bare name collides with a popular npm package (`react`, `lodash`, etc.), the audit warns about the registry-confusion risk.
5. **Optional secret scan** — with `--secrets`, scans the installed source for hardcoded API keys, tokens, and private keys.

Works in any project, not just LPM.dev Registry-published ones. Discovery walks `lpm.lock`, `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, or `bun.lock` (text form). Without a supported lockfile, it recursively walks nested `node_modules/` package directories without following symlinks and reports that inventory as degraded.

The vulnerability scan fails closed. If OSV.dev is unavailable, returns an incomplete batch, or omits advisory details that cannot be hydrated, `lpm audit` exits non-zero and never reports a clean audit. The same applies when the LPM.dev Registry cannot return exact metadata for an installed `@lpm.dev/*` version. Under `--json`, an incomplete OSV scan returns the audit envelope with `success: false`, `osv_degraded: true`, and `osv_degraded_reason`; other registry failures use the standard error envelope.

The `signatures` subcommand is a focused check for npm registry package signatures. It verifies the installed package set against npm-compatible `dist.signatures` metadata and exits non-zero when any registry package cannot be verified.

## Examples [#examples]

```bash
lpm audit
lpm audit --level high              # only show high-severity findings
lpm audit --secrets                 # also scan for hardcoded credentials
lpm audit --fail-on vuln            # CI: exit non-zero only on confirmed vulns
lpm audit --fail-on behavior        # CI: exit non-zero only on critical behaviors
lpm audit --secrets --fail-on secrets # CI: exit non-zero only on hardcoded secrets
lpm audit --fail-on all             # CI: exit non-zero on either (default)
lpm audit signatures                # verify npm registry package signatures
lpm audit signatures --json         # machine-readable signature report
lpm audit fix                       # bump vulnerable direct deps and reinstall
lpm audit --fix --dry-run           # plan the same fixes without writing files
lpm audit --json                    # structured output for tooling
```

## CI gating [#ci-gating]

`lpm audit` exits non-zero when its findings trip the active `--fail-on` policy. The implicit default is conservative — high-severity behavioral signals like `eval` don't fail CI unless you ask for them explicitly with `--fail-on all`.

An incomplete vulnerability scan always exits non-zero, regardless of `--fail-on`. A network or advisory-data failure is not evidence that the installed tree is clean.

| Policy                      | Exit code 1 when                                                                                                                                                                     |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| (no flag, implicit default) | Vulnerabilities **or** critical behavioral flags (obfuscation, protestware, high-entropy strings). Excludes `high` behaviors so existing pipelines that tolerate `eval` don't break. |
| `vuln`                      | Any confirmed vulnerability — OSV (npm) or registry-side advisory (`@lpm.dev`)                                                                                                       |
| `behavior`                  | Any critical **or** high behavioral flag                                                                                                                                             |
| `secrets`                   | Any hardcoded-secret finding from `lpm audit --secrets`                                                                                                                              |
| `all` (explicit)            | Vulnerabilities + critical behaviors + high behaviors + secrets — the strict union                                                                                                   |

For finer control, use [`lpm query`](/docs/packages/query) — its CSS-like selectors target individual flags or combinations.

## Fixing vulnerabilities [#fixing-vulnerabilities]

```bash
lpm audit fix
lpm audit --fix
lpm audit fix --dry-run
```

`lpm audit fix` is the canonical form; `lpm audit --fix` is an alias for npm-compatible muscle memory. Both read the package exposed at each direct root `node_modules` path and match its exact name and version to the lockfile, so transitive copies cannot be selected merely because they have a higher matching version. If the lockfile contains multiple source identities for that same installed name and version, the dependency is reported as source-ambiguous and skipped rather than guessing or sending its name to a metadata endpoint. They support `dependencies`, `devDependencies`, `optionalDependencies`, npm aliases such as `npm:lodash@^4`, and registry advisories for `@lpm.dev/*` packages.

For npm packages, the fixer evaluates every relevant OSV affected range against available registry versions and chooses the lowest newer version proven unaffected. This preserves maintained release lines when a patch exists instead of jumping to the highest fixed version from another major line. On apply, the manifest entry is pinned to that exact planned version (npm aliases retain their `npm:` identity), and verification rejects any different installed version; dry-run and apply therefore report the same target. Complex or non-registry source protocols such as `catalog:`, `workspace:`, `file:`, git, and JSR are reported as skipped rather than rewritten into semver dependencies. Transitive-only findings are not changed.

The fix pass currently operates only in LPM CLI-managed projects; it does not rewrite npm / pnpm / yarn / bun lockfiles. Fix and dry-run planning hold the project's install transaction lock against other LPM operations. Immediately before the first write, a real fix also compares the exact `package.json` bytes read for planning and aborts without overwriting if an editor or another process changed them. It then snapshots `package.json`, `lpm.lock`, and `lpm.lockb`, invalidates the install cache, reinstalls silently, and rechecks the exact version installed against OSV.dev or exact LPM.dev Registry metadata. `--json` therefore emits one audit-fix document without nested install output. A failed install or verification restores the snapshotted files; because `node_modules/` is not copied as part of the transaction, run `lpm install` after a failed fix if the installed tree needs to converge with the restored lockfile.

`--dry-run` reports the same plan without mutating `package.json`, `lpm.lock`, or `node_modules`.

## Registry signatures [#registry-signatures]

```bash
lpm audit signatures
```

Verifies npm registry package signatures for the installed tree. LPM CLI reads `lpm.lock` when present; for lockfiles from other package managers it uses the discovered installed package list and hydrates npm metadata as needed.

Human output is intentionally compact:

```text
! Registry signatures · 1 verified · 1 not verified
  unsigned-pkg@1.0.0  missing dist.signatures
```

A clean tree exits zero:

```text
✓ Registry signatures verified · 247 verified
```

Under `--json`, the envelope includes aggregate counts and one row per package:

```json
{
  "success": false,
  "scanned": 2,
  "verified": 1,
  "not_verified": 1,
  "skipped": 0,
  "packages": [
    { "name": "signed-pkg", "version": "1.0.0", "status": "verified", "signatures_verified": 1 },
    {
      "name": "unsigned-pkg",
      "version": "1.0.0",
      "status": "not_verified",
      "reason": "missing_signatures",
      "reason_detail": "missing dist.signatures",
      "signatures_verified": 0
    }
  ]
}
```

Statuses are `verified`, `not_verified`, or `skipped`. Skipped rows cover packages outside this signature surface, such as non-registry sources and `@lpm.dev/*` packages. Any `not_verified` row makes the command exit with code `1`.

## Severity levels [#severity-levels]

```bash
lpm audit --level high
```

Suppresses anything below the threshold. Ladder from most-to-least severe: `critical` → `high` → `moderate` (alias `medium`) → `info` (alias `low`). Default is `info` (show everything). Any other value is rejected as a usage error before the audit starts.

## Secret scanning [#secret-scanning]

```bash
lpm audit --secrets
```

Scans the installed source tree under `node_modules/` for hardcoded API keys, access tokens, private keys, and similar credentials — the same scanner that runs as part of `lpm publish`. Useful as a final check before shipping a Docker image or deploying.

Requires `node_modules/` to exist (errors with a `run \`lpm install\` first`hint otherwise).`--secrets`still runs as its own scan mode, but it participates in`--fail-on\`:

* `lpm audit --secrets` uses the command's default gate policy (`all`), so a detected secret exits non-zero.
* `lpm audit --secrets --fail-on secrets` gates on secret findings only.
* `lpm audit --secrets --fail-on vuln` or `--fail-on behavior` keeps the secret scan informational.
* `--level` is still ignored in secrets mode.

## Flags [#flags]

| Flag                                       | Effect                                                                                                          |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| `--level <critical\|high\|moderate\|info>` | Minimum severity to report (default: `info`; aliases: `medium`, `low`)                                          |
| `--fail-on <vuln\|behavior\|secrets\|all>` | CI exit-code policy (`secrets` applies to `--secrets`; `all` includes secret findings too)                      |
| `--secrets`                                | Scan installed source for hardcoded secrets (separate scan mode; consults `--fail-on`, still ignores `--level`) |
| `--fix`                                    | Alias for `lpm audit fix`                                                                                       |
| `--dry-run`                                | With `--fix`, show the fix plan without changing files                                                          |

Plus the [global flags](/docs/commands#global-flags) — `--json` is especially useful for piping into CI gates.

## Subcommands [#subcommands]

| Subcommand   | Effect                                                                                        |
| ------------ | --------------------------------------------------------------------------------------------- |
| `fix`        | Update vulnerable direct dependency instances to verified unaffected versions, then reinstall |
| `signatures` | Verify npm registry package signatures for the installed tree                                 |

## See also [#see-also]

* [`lpm query`](/docs/packages/query) — inspect packages by behavioral tag
* [Security audit](/docs/packages/security-audit) — concept overview and tag reference
* [`lpm publish`](/docs/packages/publish) — runs the same secret scanner before upload
