Skip to content

fix(audit-action): derive prod closure from lockfile instead of pnpm list#15

Merged
cherviakovtaskworld merged 3 commits into
mainfrom
cherviakovtaskworld/fix-prod-scope-lockfile
Jul 3, 2026
Merged

fix(audit-action): derive prod closure from lockfile instead of pnpm list#15
cherviakovtaskworld merged 3 commits into
mainfrom
cherviakovtaskworld/fix-prod-scope-lockfile

Conversation

@cherviakovtaskworld

Copy link
Copy Markdown
Contributor

Problem

pnpmBulkAuditor enumerates the dependency set with
pnpm list --prod --parseable --depth=Infinity. In a pnpm workspace (or with
shamefullyHoist: true), --parseable reports physical paths from the shared
.pnpm store at the workspace root, which contains every package (dev + prod,
all members). The --prod filter is effectively bypassed, so the audit counts
vulnerabilities in dev/build-time tooling as production findings.

Observed on tw-frontend (path: ./client): the audit reported 8 high, but
pnpm why --prod confirms 7 of them come from dev-only packages (rollup,
serialize-javascript, flatted, fast-uri, @babel/plugin-transform-modules-systemjs).
Only linkify-it is a genuine production dependency.

The pre-3.1.3 --json --depth=Infinity approach is not a fix either: pnpm
deduplicates subtrees in that output (deduped: true, no nested
dependencies), so packages reachable only through deduped nodes are dropped and
the audit under-reports.

Solution

Collect the dependency closure from pnpm-lock.yaml, the only source that is
independent of hoisting and store layout:

  1. findLockfile walks up from the target path to the workspace pnpm-lock.yaml.
  2. toImporterKey maps the target path to its importers.<key> entry.
  3. collectFromLockfile seeds from the importer's dependencies +
    optionalDependencies (plus devDependencies when include-dev-deps is set)
    and walks the snapshots graph, stripping peer/patch suffixes
    (1.2.3(react@18.3.1)1.2.3) and skipping link:/file:/git: refs.

mapToAuditMetadata, the bulk-advisory fetch, and the detailed/include-dev-deps
options are unchanged. yaml is added as a runtime dependency (bundled into
dist/index.js).

Verification

  • pnpm test — 37 passing. New unit tests cover toImporterKey, transitive
    closure, dev-subtree inclusion/exclusion, suffix stripping, link: skipping,
    and a missing-importer error; the end-to-end pnpmBulkAuditor tests now drive a
    lockfile fixture instead of pnpm list output.
  • Replayed against the real tw-frontend lockfile (client importer): closure
    size drops from 1097 (store enumeration) to 620 (actual prod closure);
    rollup/serialize-javascript/flatted/fast-uri/@babel/* are correctly
    absent, linkify-it and markdown-it correctly present.

cherviakovtaskworld and others added 3 commits July 1, 2026 18:58
…list

pnpm list --prod --parseable --depth=Infinity enumerates the shared
.pnpm store at the workspace root, so under a workspace or shamefullyHoist
the --prod filter leaks dev/build packages (rollup, esbuild, babel, ...)
and the audit over-reports vulnerabilities. The --json variant instead
deduplicates subtrees and under-reports.

Replace the pnpm list enumeration with a walk of pnpm-lock.yaml: resolve
the importer for the target path and traverse the snapshots graph from its
production (and optional dev) dependencies. This is hoisting- and
store-layout-independent. Adds yaml as a dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The self-test pinned pnpm/action-setup to 8.15.6, so pnpm install
regenerated the lockfile in the v6 format (no importers section for a
single-package repo). Consumers (tw-frontend, tw-backend) run pnpm 10 and
commit v9 lockfiles, which the new lockfile parser targets. Declare
packageManager: pnpm@10.33.0 and let pnpm/action-setup read it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pnpm 10 defaults auto-install-peers to true, but the committed lockfile
records false, so frozen installs failed with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
Declare it explicitly in .npmrc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

Vulnerabilities

Vulnerabilities were found in audit-action.

⚪ Info 0
🔵 Low 2 `>undici@6.25.0`
🟡 Moderate 1 `>undici@6.25.0`
🟠 High 1 `>undici@6.25.0`
🔴 Critical 0

@cherviakovtaskworld
cherviakovtaskworld merged commit 78ad9d2 into main Jul 3, 2026
1 check passed
@cherviakovtaskworld
cherviakovtaskworld deleted the cherviakovtaskworld/fix-prod-scope-lockfile branch July 3, 2026 07:14
cherviakovtaskworld added a commit that referenced this pull request Jul 3, 2026
* ci(audit-action): fix release trigger to main and bump to 3.2.1

The release workflow triggered on pull_request to `master`, but the
repository's default branch is `main`, so it never fired — no v3.2.x tag
was ever created despite package.json sitting at 3.2.0. Point the trigger
at `main` and bump the patch version so the lockfile-closure fix (#15) can
be published.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci(audit-action): skip release cleanly when version unchanged

Gate the release step on an output flag instead of letting `grep -q` fail
the job: merges to main that don't bump package.json now skip the release
steps (green) rather than reporting a failed workflow run. Add a
`merged == true` job guard so closed-but-unmerged PRs don't trigger it, and
let workflow_dispatch force a release of the current version.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants