lpm audit
Scan dependencies for vulnerabilities and source risks, inspect secrets, and fix vulnerable direct dependencies.
Scan your dependencies before a release, or use an audit policy as a CI gate.
lpm audit [OPTIONS] [COMMAND]
lpm audit fix [OPTIONS]
lpm audit signatures [OPTIONS]Quickstart
lpm audit
lpm audit --level high --fail-on vuln
lpm audit fix --dry-runLPM CLI works with npm, LPM.dev Registry, and private registries. Only @lpm.dev/* packages always use LPM.dev Registry.
| Mode | Purpose |
|---|---|
lpm audit | Report vulnerabilities, source capabilities, suspicious source patterns, and registry quality findings. |
lpm audit --secrets | Scan installed package files for hardcoded credentials. This is a separate scan. |
lpm audit fix | Update vulnerable direct dependencies in an LPM CLI project, then reinstall. |
lpm audit signatures | Verify npm-compatible registry signatures for the installed inventory. |
The regular audit does not scan for secrets. Run both modes when you need both reports.
Examples
# Gate on High and Critical vulnerabilities
lpm audit --level high --fail-on vuln
# Include High source risks in the failure policy
lpm audit --fail-on all
# Scan installed packages for hardcoded credentials
lpm audit --secrets --fail-on secrets
# Save a report for a CI artifact
lpm audit --json > audit.json
# Preview, apply, then inspect remaining findings
lpm audit fix --dry-run
lpm audit fix
lpm audit
# Verify registry signatures
lpm audit signatures --jsonThese commands do not prompt. A preview never approves or applies a fix.
Configuration
There is no dedicated audit section in lpm.json or package.json > lpm. You can define an uncached task:
{
"tasks": {
"security:audit": {
"command": "lpm audit --fail-on vuln",
"cache": false
}
}
}lpm run security:auditcommand defines the invocation. cache: false keeps the security result fresh. An explicit lpm.json task command takes precedence over a same-name package.json script.
For task dependencies and environment configuration, see lpm.json and lpm run.
Audit after installation
lpm install --audit-after-install
lpm install --no-audit-after-installTo enable the audit by default, set the user configuration:
audit-after-install = trueThe precedence is the install flag, then LPM_AUDIT_AFTER_INSTALL, then the user configuration. The default is off.
The environment variable accepts 1, true, yes, or on to enable the audit. It accepts 0, false, no, or off to disable it.
The automatic audit is informational. Findings or incomplete coverage do not fail a successful install. An incomplete audit prints a warning instead of completed counts.
For a CI gate, run the standalone audit after lpm install.
Inventory and network requests
lpm audit and lpm audit fix act on the nearest directory with a package.json, as lpm install does. From a subdirectory, they print the directory they chose.
The regular audit reads lpm.lock, package-lock.json, pnpm-lock.yaml, yarn.lock, or Bun lockfiles. It also analyzes available local package source.
Without a supported lockfile, it walks real package directories under node_modules/, including nested dependencies. This inventory has weaker identity and reports degraded: true.
Malformed or unreadable package manifests stop fallback discovery. Linked package directories require a supported lockfile. A package manifest must be a regular file, not a link.
LPM CLI sends names and versions outside @lpm.dev/* to OSV.dev for vulnerability data. This includes names from private registries and proxies.
For @lpm.dev/*, it requests exact version metadata from LPM.dev Registry. Local source analysis does not upload source code.
OSV requests run on each invocation. Local source analysis can use cached results. The automatic audit uses the same network scope.
An unavailable service, incomplete response, inconsistent version identity, or unsupported advisory severity prevents a clean audit. Reconnect or correct the metadata, then rerun the command.
Severity and CI policies
lpm audit --level high --fail-on vuln--level filters both the report and the findings that can fail the command. It does not suppress coverage errors.
The severity order is critical, high, moderate, then info. medium aliases moderate, and low aliases info. The default reports every level.
--fail-on | Regular audit fails for | Secret scan fails for |
|---|---|---|
| Omitted | Retained vulnerabilities or Critical findings. | Any secret finding. |
vuln | Retained vulnerabilities. | Findings are informational. |
behavior | High or Critical non-vulnerability findings and capabilities. | Findings are informational. |
secrets | Findings are informational. | Any secret finding. |
all | Retained vulnerabilities, plus High or Critical non-vulnerability findings and capabilities. | Any secret finding. |
Non-vulnerability findings include registry security and quality findings. Quality scores below 40 are Moderate, and scores below 20 are High.
--fail-on all applies to the selected mode. It does not add the secret scan to a regular audit.
An incomplete scan exits with code 1, regardless of the policy. Findings that meet the policy also exit with code 1.
Invalid flag combinations produce a usage error. --level cannot accompany --secrets. Scan options cannot accompany fix or signatures.
With regular or secret JSON output, success: true means the scan completed. Findings can still produce a nonzero exit status. Use the process status for CI.
For individual capability selectors, see lpm query.
Source capabilities
The Capabilities section describes API use. A capability does not prove execution or malicious intent, and it does not increase security finding counts.
Explicit --fail-on=behavior and --fail-on=all policies still include high-severity capabilities.
The eval capability covers runtime evaluation and compilation through these APIs:
- Global
evalandFunction, including recognized aliases and indirect calls. - Node
vm.runInContext,vm.runInNewContext,vm.runInThisContext, andvm.compileFunction. - Node
vm.Scriptandvm.SourceTextModuleconstructors.
A constant body, such as Function('return this'), still uses runtime code generation.
A local class named Function or a method declaration named eval does not establish built-in evaluation.
Calls named eval on unresolved objects retain conservative capability evidence. The evidence states that built-in evaluation is not established.
Source evidence includes a package-relative path, a reason, and a file context. Precise matches also include a line, a byte column, and a short excerpt. Evidence contains a bounded set of examples. It does not list every call or prove that an API executes.
With --json, each package stores capabilities in capabilities, and total_capabilities counts them.
Each capability includes rule_id, name, policy_severity, source, and evidence.
Capabilities do not contribute to issues, total_issues, packages_with_issues, or security severity counts.
Source coverage
An audit can find no threats and still have incomplete coverage. Missing package sources, missing or invalid package manifests, unreadable files, unsupported syntax, and scan limits prevent a clean result.
An incomplete source audit prints Audit incomplete and exits with code 1, regardless of --fail-on or --level.
The source scan covers JavaScript and TypeScript files. It keeps these resource limits:
- Full analysis stops at 4 MiB for one file. Larger files receive a sample from each end.
- One package has a 50 MiB scan budget and a 5,000-file limit.
- A sampled file does not count as a fully scanned file.
If sources are missing, install the dependencies with the project's package manager, then rerun lpm audit.
For Yarn PnP, provide a node_modules installation to enable local source analysis.
If a file exceeds a limit or uses unsupported syntax, review that file separately. Increasing --level does not complete the scan.
With --json, incomplete source coverage sets success: false. The behavioral_coverage object includes:
| Field | Meaning |
|---|---|
complete | Every selected package source scan completed within its supported scope. |
scanned_packages | Packages with a local analysis, including partial analyses. |
partial_packages | Packages with analysis limits, input failures, or unsupported source syntax. |
unavailable_packages | Packages without accessible source files. |
packages | Coverage gaps with package identity, path, reason, file count, and byte count. |
Gap reasons are source_unavailable, source_missing_or_unreadable, scan_limit_reached, source_read_failed, or unsupported_source_syntax.
Oversized-file evidence includes the file path, full size, sampled byte count, and detected signals.
The top-level scanned field counts discovered packages. It does not mean that every package received complete source analysis.
Quoted examples and comments do not establish executable capabilities. Obfuscation checks distinguish executable identifiers and decoder calls from quoted examples. Encoded strings can still contribute to obfuscation evidence. Executable template expressions remain part of the scan. Bounded parser loops and ordinary locale argument validation do not establish protestware. Source analysis does not execute package code or fully track values between functions and modules. Runtime mutation, custom wrappers, unsupported syntax, and sampled files can hide behavior. Static checks cannot prove that a package is safe. LPM CLI refreshes local cached analyses when scanner rules change. Previously stored LPM.dev Registry tags require a publication rescan before they change.
Credential exfiltration
The Critical :credential-exfiltration finding identifies supported uploads of credential-file contents, private-key material, or the complete process environment.
It checks fetch request headers and bodies with fixed HTTP URLs, Node HTTP request bodies, and supported repository-content uploads.
It follows supported assignments, encodings, local helpers, and bundled classes within complete files. Evidence points to the upload call.
Sampled file ranges cannot establish this finding.
Normal token authentication, public addresses, signatures, and local secret scanning do not establish this finding. The detector recognizes selected secret names and wallet APIs. It does not prove that a value contains credentials or that an upload executes. Dynamic endpoints, arbitrary wrappers, runtime mutation, separate files, and files beyond the scan limits can hide these flows.
Credential-file checks cover supported reads of .env, .npmrc, AWS credentials, SSH private keys, Kubernetes config, and Git credential files.
A file read alone does not establish an upload. The finding requires a supported path from its contents to a network payload.
Downloaded and encrypted code
The High :downloaded-execution finding identifies a supported path from a downloaded response to code evaluation or a launched file.
The High :encrypted-execution finding identifies a supported path from decrypted bytes to code evaluation or a launched file.
These findings describe risky code paths. They do not establish that an external stage is malicious or that an activation condition occurs.
Ordinary downloads, local data decryption, and unrelated process launches do not establish these findings. The checks require recognized APIs and supported value relationships within a complete source file. Concealed API names, callbacks, runtime mutation, and separate modules can prevent detection.
Broad filesystem deletion
The Critical :destructive-filesystem finding identifies supported recursive deletion of home directories, filesystem roots, or current project contents.
It includes supported helpers that enumerate a broad directory and remove its entries.
Cleanup restricted to a build directory or selected cache entries does not establish this finding.
A warning does not prove that deletion occurs during installation. The code can require an explicit call or depend on a remote response. Review the evidence and activation conditions before deciding whether the behavior is acceptable.
Foreign lockfiles
lpm audit reads one fixed snapshot of each selected foreign lockfile. It rejects malformed files and unsupported versions instead of using a degraded inventory.
The audit supports npm lockfile versions 1, 2, and 3. It supports pnpm lockfile major versions 5, 6, and 9.
Each foreign lockfile has these limits:
- The file size limit is 64 MiB.
- The package-entry limit is 200,000.
npm lockfiles have these additional limits:
- The package-path limit is 32 KiB.
- The package nesting limit is 256 levels.
- The dependency limit for one package is 10,000.
- The total dependency-edge limit is 1,000,000.
If a lockfile exceeds a limit, the audit exits non-zero. It does not use an ancestor lockfile or the node_modules/ fallback.
For bun.lockb, LPM CLI copies the selected file before conversion. The Bun converter has these limits:
- The time limit is 30 seconds.
- The converted output limit is 64 MiB.
- The diagnostic-output limit is 64 KiB.
If Bun exceeds a limit, the audit stops the converter and exits non-zero.
JSON package identity
lpm audit --jsonEach object in packages includes name, version, instance_id, and path.
For an LPM CLI-managed package, instance_id identifies the exact lockfile instance. It is null when the selected inventory does not contain an exact instance ID.
The path value identifies the directory that the audit analyzed. It points to the exact installed package materialization when that path is available.
For a file: or link: dependency, path identifies the original live source directory. The value is null when discovery cannot resolve an analyzed directory.
Two rows can have the same name and version. Their instance_id, path, and findings can differ.
Use a non-null instance_id as the row key. Otherwise, use a non-null path. If both are null, the report lacks a stable unique key.
The combination of name and version is not necessarily unique.
Fixing vulnerabilities
lpm audit fix --dry-run
lpm audit fix
# Equivalent compatibility form
lpm audit --fix --dry-runThe fixer supports dependencies, devDependencies, optionalDependencies, and npm aliases. It requires an LPM CLI lockfile and installed direct dependencies.
The fixer updates the package.json that the command selected, in the nearest directory at or above the current one.
For each direct dependency, the fixer checks the installed name and version against the selected lockfile instance. Modern lockfiles preserve exact root identities.
If a legacy lockfile leaves the source ambiguous, the fixer skips that dependency. It does not guess a source or send the ambiguous name to a registry.
The fixer chooses the lowest newer stable version that the available advisory data identifies as unaffected. The target can cross a major version boundary.
An applied fix pins the manifest to the exact planned version. An npm alias retains its npm: identity. Review the preview and run your project tests afterward.
Transitive-only findings remain unchanged. Non-registry protocols, such as workspace:, catalog:, file:, git, and JSR, cannot be rewritten automatically.
The report lists skipped dependencies and their reasons. Any skipped dependency makes the command exit nonzero, even when other fixes succeed.
Recovery
Application holds the project install lock. Before writing, the fixer checks that the manifest and selected lockfile still match the plan.
A concurrent file change stops the fix. Rerun the preview with the current files.
The fixer saves package.json, lpm.lock, and lpm.lockb, then installs the exact targets and checks their vulnerability status again.
A failed install or verification restores those files. The transaction does not restore node_modules/.
If the installed tree differs after failure, restore it from the restored lockfile:
lpm install
lpm audit--dry-run leaves the manifest, lockfiles, and installed tree unchanged. With --json, the fixer emits one report without nested install output.
Registry signatures
lpm audit signaturesThe command verifies npm registry package signatures for the installed tree. LPM CLI reads stored signatures and publication times from lpm.lock when available.
If stored evidence is incomplete, LPM CLI gets exact metadata from the authorized registry. A signature with an expiring key requires a publication time.
For foreign lockfiles, the recorded tarball URL must match an authorized registry path. This path can match npm or LPM.dev Registry.
The path can also match a registry that is configured in .npmrc.
LPM CLI keeps the configured registry path and its authentication. It never trusts an unconfigured origin from a foreign lockfile.
Human output is intentionally compact:
! Registry signatures · 1 verified · 1 not verified
unsigned-pkg@1.0.0 missing dist.signaturesA clean tree exits zero:
✓ Registry signatures verified · 247 verifiedUnder --json, the envelope includes aggregate counts and one row per package:
{
"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 include non-registry sources and @lpm.dev/* packages from LPM.dev Registry.
A foreign @lpm.dev/* name from another authorized registry still requires signature verification. Any not_verified row makes the command exit with code 1.
Secret scanning
lpm audit --secrets
lpm audit --secrets --fail-on secrets --jsonThis mode scans installed packages for API keys, tokens, private keys, and credential files. It does not scan your root project files.
A node_modules/ installation is required. The scanner follows supported installed package links within allowed project and store locations. Dangling package links fail the scan.
Selected text files have a 2 MiB read limit. The operation limits are 100,000 packages, 250,000 files, 512 MiB, and 10,000 findings.
Unreadable inputs, directory errors, or exceeded limits stop the scan with an error. An informational finding policy does not suppress these errors.
The JSON report contains packagesScanned, packagesWithSecrets, and findings. Each match includes a credential type, a description with its file path, line, and severity.
Matched credential values are not included in the report. A detected pattern still needs review because example data can resemble a real credential.
If a real credential is present, revoke it and replace the affected package. Removing the file does not revoke the credential.
For your own package before upload, lpm publish also checks the prepared package files for secrets.
Flags
| Flag | Effect |
|---|---|
--level <LEVEL> | Minimum severity for the regular report and failure decision. Values: info, moderate, high, critical. Aliases: low, medium. |
--fail-on <POLICY> | Finding policy for the selected scan. Values: vuln, behavior, secrets, all. |
--secrets | Select the separate installed-package secret scan. |
--fix | Alias for lpm audit fix. Conflicts with scan options. |
--dry-run | With --fix, preview without file changes. |
fix --dry-run | Preview fixes with the subcommand form. |
--json | Emit machine-readable output for the selected mode. |
See global flags for other shared options.
See also
- Security audit — security layers and source tags.
- lpm query — inspect dependencies with selectors.
- lpm install — restore the installed tree and enable automatic audits.
- lpm publish — check package files before upload.