Supply-chain hygiene audit
A defense-in-depth audit against npm-ecosystem supply-chain attacks (typosquatting, hijacked maintainer accounts, malicious postinstall scripts). Walks the user through their developer machine + the current project + CI workflows, then offers fixes one-by-one with explicit confirmation.
Operating rules (read first)
- Audit before fix. Always run the full audit and present findings before proposing any change.
- Confirm every write. Show the file path and the exact content you will write. Ask y/n. Never batch-apply.
- Never execute remote installers. If Safe Chain is missing, print the install command and link; do not run yourself.
- Never auto-edit CI workflow files. YAML structure varies (matrices, reusable workflows). Print the snippet and the suggested path; let the user place it.
- Render policy values at runtime. Read from this skill's directory and substitute etc. into templates before showing them.
- Surface intentional overrides as warnings, not failures. is a deliberate choice — flag for review, recommend , do not propose flipping.
Phase 1 — Detect environment
Run these in parallel:
- — captures G1
- Read
~/.safe-chain/config.json
(may not exist) — captures G2
- the cwd for lockfiles to identify the package manager
- Check for — if absent, exit with "Not a JS project, nothing to audit."
Lockfile priority (first match wins):
| Lockfile | Manager | Extra check |
|---|
| bun | — |
| pnpm | Run to branch v9 vs v10+ |
| + | yarn berry | — |
| alone | yarn classic v1 | Emit warning, skip P2, continue with P1/P3/CI |
| npm | — |
For yarn classic: emit
⚠ yarn classic detected — no native min-release-age support. Recommend upgrading to Yarn Berry v3+ or switching to pnpm.
Do not propose P2 fixes.
Also check
— if it exists, run CI checks. If
/
/ etc. exist, emit one
⚠ non-GitHub CI detected — unchecked, see https://github.com/AikidoSec/safe-chain#cicd-integration
.
Phase 2 — Audit checklist
Present findings as a table. Format:
[✓/✗/⚠] ID — short label — current → expected
.
Global (machine-level):
| ID | Check |
|---|
| G1 | binary on PATH |
| G2 | ~/.safe-chain/config.json
has minimumPackageAgeHours ≥ 72
|
Project (repo-level):
| ID | Check |
|---|
| P1 | Lockfile present and not gitignored |
| P2 | PM-specific config has policy values (see below) |
| P3 | or mentions Safe Chain install |
P2 per manager (load thresholds from
):
- npm: contains , ,
- pnpm v10+: contains and an explicit key. Do not flag absence of — scripts are disabled by default in v10+.
- pnpm v9-: treat as npm — expect with ,
- yarn berry: contains and
- bun: contains section with
minimumReleaseAge = 259200
. Do not flag absence of script-disable — bun disables postinstalls by default.
handling: if found in
, emit
⚠ intentional override detected — ensure @lavamoat/allow-scripts is configured as the allowlist (https://github.com/LavaMoat/LavaMoat/tree/main/packages/allow-scripts)
. Mark as warning, not failure. Do not propose flipping.
| ID | Check |
|---|
| C1 | At least one workflow runs a frozen-lockfile install (, pnpm install --frozen-lockfile
, , bun install --frozen-lockfile
) AND has a Safe Chain install step before it |
| C2 | Safe Chain installer URL pins a specific version (matches policy.json#safeChainInstallerVersion
or another concrete version), not |
| C3 | The install step does not use bare / (without ) |
| C4 | SAFE_CHAIN_LOGGING: verbose
is set on the Safe Chain install step |
After the table, print:
N passing, M failing, K warnings
. If M+K > 0, ask:
"Walk through fixes interactively?" — y/n. If no, stop here.
Phase 3 — Interactive fix loop
Iterate failing checks in this order (least invasive first). For each: show the exact change, ask y/n, apply on yes, skip on no.
Order
-
G2 — Safe Chain global config
- If
~/.safe-chain/config.json
is missing: show the JSON, confirm, write it.
- If present but
minimumPackageAgeHours < 72
: read existing, show diff, ask before overwriting that key only (preserve other keys).
-
P2 — PM config
- Read existing config file if any.
- Compare key-by-key against the policy.
- For each missing/incorrect key: show old value vs new value, ask y/n.
- Use for surgical merges (preserve unrelated keys, comments, ordering). Only use if the file does not exist.
- For in : do not propose change. Print the warning text from Phase 2 and move on.
-
P3 — CONTRIBUTING/README mention
- If exists: offer to append
templates/contributing.snippet.md
to it.
- Else if exists: offer to append to README, OR create — ask which.
- Else: offer to create from the snippet.
-
C1–C4 — GitHub Actions (never edit workflow files automatically)
- For each failing CI check, print the rendered
templates/github-actions.snippet.yml
(with substituted from ).
- State which file the user should edit (e.g. ) and which step it should go before.
- Do not run Edit or Write on workflow files.
-
G1 — Safe Chain not installed
- Print:
Safe Chain is not on PATH. Install with:
followed by the official install command and the link https://github.com/AikidoSec/safe-chain#installation
.
- Do not execute.
After applying each fix, re-run that single check silently and confirm it now passes. If it still fails, surface why and stop the loop.
Phase 4 — Final summary
Print:
- Updated pass/fail counts after fixes.
- A one-line list of files changed in the repo (use if the cwd is a git repo, else list paths you wrote to).
- Call out
~/.safe-chain/config.json
separately if you touched it — it is not in the repo.
- If C1–C4 had failures, remind the user that CI workflow edits are still pending (you printed the snippet but did not apply it).
Templates and policy
Available in this skill directory:
- — version pins and threshold values. Always load before rendering templates.
- — full recommended body.
templates/pnpm-workspace.snippet.yaml
— keys to merge into .
templates/yarnrc.snippet.yml
— keys to merge into .
templates/bunfig.snippet.toml
— keys to merge into .
templates/contributing.snippet.md
— the "Security setup (required)" block.
templates/github-actions.snippet.yml
— Safe Chain + frozen install steps. Substitute from policy.json#safeChainInstallerVersion
before showing.
Templates are written verbatim into user repos. Do not paraphrase them.