Meteor Supply Chain Audit
Leading word: drift — all three sections below detect one thing: dependency state that has drifted from the locked, controlled baseline. Every finding is a drift report; every fix closes the gap.
Run the three sections in order. Each section ends with a pass/fail verdict and a remediation list. Assemble the full report only after all three sections are complete.
Repo Context Detection (run first, before all sections)
Before running any section, determine which repo context you are in. This controls which checks apply.
| Signal | Context |
|---|---|
.meteor/ directory present at repo root |
Meteor app repo — full audit applies |
No .meteor/ at root, only packages/*/package.js files |
Package-only repo — app-level checks are skipped |
Record the context in the report header. If the context is package-only, skip Section 1 entirely (no pnpm-lock.yaml, .npmrc, or workspace config is expected at this level) and apply the modified Section 2 rules described below.
Section 1 — Lockfile & Package Manager Drift
Skip this section entirely if the repo context is package-only. A package-only repo has no app-level lockfile or package manager config.
Goal: confirm exactly one pnpm-lock.yaml exists and no competing lockfiles have drifted into source control.
- Search the repo tree for lockfiles:
package-lock.json,npm-shrinkwrap.json,yarn.lock,pnpm-lock.yaml. - FAIL if any
package-lock.json,npm-shrinkwrap.json, oryarn.lockis tracked in git, EXCEPT for:package-lock.jsoninsidepackages/*/(legitimate for package-level dev tooling).npm-shrinkwrap.jsonunderpackages/*/.npm/package/(Section 2 covers them).
- FAIL if there is not exactly one
pnpm-lock.yamlat the application root. - Check
package.jsonfor"packageManager": "pnpm@<version>". FAIL if absent. - Check
app/.npmrcforengine-strict=true. FAIL if absent. - Check
pnpm-workspace.yamlorpackage.jsonforonlyBuiltDependencies/allowBuilds. List every package in the allowlist; flag any that are not native C++/binary packages. - Confirm
ignore-scriptsis absent or commented out in.npmrc(its presence silently breaks the native allowlist). - If the package manager is pnpm, check for the
minimumReleaseAgeoption (written asminimum-release-agein.npmrc, orminimumReleaseAgein pnpm workspace/package config). FAIL if absent. Advise the user to configure it (e.g.,minimum-release-age=7d) to block recently published, potentially compromised versions.
Section 1 done when: verdict is PASS or every FAIL has an entry in the remediation list.
Section 2 — Npm.depends Drift
Goal: inventory all local npm dependencies embedded in Meteor packages, verify their shrinkwrap files are present and current, and quantify version drift risk.
packages/<name>/.npm/package/npm-shrinkwrap.jsonis the only lock mechanism available forNpm.dependsdependencies. It is generated by Meteor's build tool on the firstmeteor runormeteor buildinside a Meteor app repo. It must be committed once generated. Itsnode_modules/sibling must not be.
Package-only repo note: The shrinkwrap is generated by the Meteor toolchain when the package is first consumed by a Meteor application — it cannot exist before that. Absence in a package-only repo is therefore not a failure; it is the expected state before the package has been run inside an app.
- Search
packages/*/package.jsforNpm.depends({...}). Collect every file and its full dependency map. - For each package found, apply the shrinkwrap-presence rule according to context:
- Meteor app repo: FAIL if
packages/<name>/.npm/package/npm-shrinkwrap.jsondoes not exist or is not committed to git. An absent shrinkwrap means versions are unpinned. - Package-only repo: INFO if
packages/<name>/.npm/package/npm-shrinkwrap.jsonis absent. Emit: "Shrinkwrap not yet generated. Run the package inside a Meteor app once (meteor runormeteor build), then commit the generated file." Do not raise a FAIL.
- Meteor app repo: FAIL if
- FAIL if
packages/<name>/.npm/package/node_modules/is tracked in git. Rungit ls-files 'packages/*/.npm/package/node_modules'to check. - Check
.gitignorefor the patterns below. FAIL if any are missing:
Do not gitignorepackages/*/.npm/package/node_modules/ .meteor/local/npm-shrinkwrap.json— it must be tracked. - For each committed shrinkwrap, run
git log -1 -- packages/<name>/.npm/package/npm-shrinkwrap.jsonandgit log -1 -- packages/<name>/package.js. FAIL if the shrinkwrap commit is older than the lastNpm.dependsedit — it is stale and risks resolving wrong versions. - For each dependency, check the version string. HIGH RISK if the string contains
^,~,*, orx— these are dynamic ranges that drift on rebuild. - Flag any
Npm.dependsentry pointing at a git URL (not a registry version) — flag for manual verification that the shrinkwrap resolves them correctly after any regeneration. - For each
Npm.dependsentry, record migration feasibility:- Migratable: package can move to root
package.json+tmeasday:check-npm-versionsinpackage.js. - Blocked: package requires Meteor package internals (must be available at
Npm.requirecall time before app boot).
- Migratable: package can move to root
- Collect every package+version pinned across all local shrinkwrap files. Emit this list as a separate "Unaudited by npm audit" appendix in the report — standard
npm auditnever sees these dependencies; they require manual vulnerability review.
For regeneration of a missing or stale shrinkwrap, see references/shrinkwrap-regeneration.md.
Section 2 done when: every Npm.depends entry has a shrinkwrap status, a staleness verdict, a risk rating, and a migration action; and the unaudited-packages appendix is populated.
Section 3 — CI/CD Enforcement Drift
Goal: confirm the pipeline enforces the locked state and separates trust boundaries.
Inspect
.github/workflows/. Verify everypnpm installstep uses--frozen-lockfile. FAIL if any install step is missing the flag.Verify the pipeline has a hard boundary between:
- Untrusted build — runs
pnpm installandmeteor buildwith zero secrets and read-only permissions. - Trusted deploy — handles Docker push, AWS credentials, etc.
FAIL if both stages share the same job or the same secrets scope.
- Untrusted build — runs
Confirm production
pnpm installinsidebundle/programs/serverruns inside the target Docker image (so native bindings compile against container glibc, not the host runner OS). FAIL if the install runs on the host runner and the bundle is copied in afterwards.
Section 3 done when: verdict is PASS or every FAIL has an entry in the remediation list.
Report Format
After all three sections, emit the report using the template in references/report-template.md.