# PNPM Audit Remediation

Shared reference for `post`, `build`, and `ship`. Canonical policy for running, fixing, resolving, or
investigating `pnpm audit` vulnerabilities. Load it when audit work is in scope. Do not load a
separate `pnpm-audit` skill.

## Planning (`post`)

Treat audit work as a buildable remediation task unless the user asks for investigation-only or
planning-only output. `post` owns only audit task framing; do not run `pnpm audit`, `pnpm install`,
package scripts, or other implementation commands from `post`. Accept pasted audit output when
available; otherwise require `build` to run the initial audit before editing.

Capture in the Build Launch Prompt:

1. audit mode: investigation-only, remediation plan only, or fix-and-commit
2. target repository path
3. whether user-provided audit output exists, and a concise advisory/path summary if so
4. current override metadata status when known from read-only inspection of `package.json`
5. explicit instruction that `build` must hard-stop before remediation if any `pnpm.overrides` key lacks matching `x-crossplatformai.pnpmOverridesMetadata`
6. preferred fix order: direct dependency or owning workspace package upgrades, then scoped `pnpm.overrides`, then justified `pnpm.auditConfig.ignoreCves`
7. compat overrides that must not be removed, reclassified, or version-changed unless explicitly approved
8. required authoritative final QA: `pnpm --dir "<repo>" install` and
   `pnpm --dir "<repo>" audit`; include lint only if the repo defines a lint script; any earlier run
   is preliminary
9. expected ship handoff evidence for staged overrides or ignores: metadata changes, CVE/GHSA IDs, vulnerable ranges, patched versions/ranges, `npm view` verification, and ignore justification

Default to one commit for audit remediation. Split only when dependency upgrades and override/ignore
policy cleanup are large or conceptually separate enough that one commit would be unsafe to review.

## Build execution

Require a confirmed target repository path before non-read commands. Verify the repo path exists,
contains `package.json`, and is the intended target. Inspect the repo's `package.json` scripts before
invoking `lint`, `test`, `build`, or `typecheck`. Use `pnpm --dir "<repo-path>"` for all pnpm
commands.

### Fix strategy

Prefer direct dependency upgrades before forcing transitive resolutions. For each advisory:

1. prefer upgrading the direct dependency or workspace package that owns the vulnerable path
2. if an upgrade is not viable or does not clear the advisory, use a scoped `pnpm.overrides` entry
3. if no safe fix exists, or the advisory does not apply in this repo's usage context, use `pnpm.auditConfig.ignoreCves` with explicit justification

Before choosing an upgrade, confirm the candidate fixes the advisory: check the patched version/range;
check whether the candidate version of the owning direct dependency resolves the transitive
dependency to a non-vulnerable version; check release notes/changelog; inspect commits only when
notes are missing and the path is still ambiguous. `pnpm audit` after reinstall is the final
verification — research reduces wasted attempts but does not replace re-audit. Do not force a large
risky major bump for a low-severity advisory without strong reason.

### Override metadata convention

Every entry in `pnpm.overrides` must have a matching entry in the top-level
`x-crossplatformai.pnpmOverridesMetadata` key of `package.json` (`x-crossplatformai` is outside
`pnpm`, so pnpm ignores it). Each metadata entry needs:

1. `type`: `"security"` or `"compat"`
2. `reason`: plain-English explanation
3. `cve`: the CVE or GHSA ID, required when `type` is `"security"` and an advisory ID exists

Type rules: `security` is added to patch a CVE — re-evaluate each run and remove when stale. `compat`
prevents a breaking API/version mismatch unrelated to a CVE — never remove automatically. Missing
metadata is a hard-stop before remediation: report `Override <key> has no entry in
x-crossplatformai.pnpmOverridesMetadata. Cannot determine intent. Please add a metadata entry with
type and reason before audit remediation continues.`

Do not reclassify an existing override's `type` if it already has metadata. Do not modify an existing
override's version range unless explicitly confirmed safe. `compat` overrides must use caret ranges
(`^X.Y.Z`) or exact pins, never open-ended `>=`; `security` overrides may use `>=` to enforce a
minimum patched version. Before writing any version to `pnpm.overrides`, verify it exists:
`npm view <package>@<version> version`. Never write a nonexistent version; use the nearest available
patched version.

### Audit workflow

1. run or consume the initial audit (`pnpm --dir "<repo-path>" audit` when no current user output is approved)
2. record each advisory's severity, package, vulnerable versions, patched versions, advisory IDs, and vulnerable path
3. group advisories by the direct dependency or workspace package that owns the vulnerable path
4. read `package.json`; classify each `pnpm.overrides` key as `security`, `compat`, or missing; hard-stop on missing metadata
5. remove stale `security` overrides only when the advisory is confirmed absent from current output, remove the matching metadata and `ignoreCves` entry, and notify the user: `Removed <key> - advisory <ID> no longer present in audit output`
6. never remove `compat` overrides or overrides with missing metadata
7. reinstall and re-audit after stale cleanup
8. resolve remaining advisories through the smallest safe upgrade expected to clear them
9. if an attempted update fails verification, breaks something, or does not remove the advisory, revert/adjust while preserving unrelated user changes
10. in a workspace, update the manifest that actually owns the dependency
11. add overrides only for vulnerabilities still present after researched updates
12. add `ignoreCves` only after documenting why an update was not viable and why an override was unsafe, ineffective, or unnecessary

### Scoped override safety

Never use blanket overrides for packages spanning multiple major versions (e.g. `ajv` v6 and v8,
`minimatch` v3 and v10) — incompatible APIs can break consumers. Decide blanket vs scoped by reading
each consumer's declared range: if all consumers declare compatible ranges and the patched version
fits, a blanket override may be safe; if consumers span major versions, use scoped overrides. Common
signs a blanket override broke something: `TypeError: X is not a function`, `NOT SUPPORTED: option X`,
`Cannot set properties of undefined`.

### IgnoreCves

Use `pnpm.auditConfig.ignoreCves` only for genuinely unfixable cases, false positives, or advisories
that do not apply in this repo's usage context. Look up the CVE ID from the advisory URL when needed;
document why it is ignored and which upgrade/override paths were rejected; verify
`pnpm --dir "<repo-path>" audit` exits 0 after adding the ignore; include the justification in the
ship handoff.

### Audit QA and ship evidence

Focused install/audit runs may prove candidate readiness, but they are not final evidence when their
inputs later change. Against the completed diff, authoritative `pnpm --dir "<repo-path>" install`
and `pnpm --dir "<repo-path>" audit` must exit 0 before invoking
`ship`, unless the approved scope is investigation-only with no commit requested. If either command
changes manifests, lockfiles, or other content, classify the mutation and rerun the affected final
manifest. Follow lockfile diff hygiene before declaring complete.
Run `pnpm lint` only if the repo defines a `lint` script; otherwise document lint was unavailable.

The ship handoff must include: initial audit evidence; advisory summary and vulnerable paths;
dependency upgrades attempted/applied; stale security overrides removed; security overrides added with
CVE/GHSA ID, vulnerable range, patched version/range, scoped selector rationale, and `npm view`
verification; compat overrides observed and confirmation they were not removed/reclassified/
version-changed without approval; `ignoreCves` added with justification and rejected paths; final
install/audit/lint results; lockfile hygiene confirmation when `pnpm-lock.yaml` changed; confirmation
every override has matching metadata.

## Ship verification and refusal

`ship` refuses when: staged remediation includes `pnpm.overrides` entries without matching
`x-crossplatformai.pnpmOverridesMetadata`; a `compat` override was removed, reclassified, or
version-changed without explicit approval; a staged `compat` override uses an open-ended `>=` range;
`pnpm.auditConfig.ignoreCves` is added without documented justification and accepted audit evidence;
a staged security override lacks CVE/GHSA ID when one exists, vulnerable range, patched version/range,
reason, scoped selector rationale when needed, or `npm view` verification. `ship` also verifies the
handoff reports final audit exit code 0 (unless investigation-only) and includes the user-notification
text for each removed stale override.
