Skip to content

security: harden against npm supply-chain attacks (install gates, Scorecard, secret scan)#68

Merged
lwwmanning merged 7 commits into
mainfrom
claude/dazzling-morse-869032
May 13, 2026
Merged

security: harden against npm supply-chain attacks (install gates, Scorecard, secret scan)#68
lwwmanning merged 7 commits into
mainfrom
claude/dazzling-morse-869032

Conversation

@lwwmanning
Copy link
Copy Markdown
Contributor

@lwwmanning lwwmanning commented May 13, 2026

Summary

In light of the recent npm worm class (Shai-Hulud, mini-Shai-Hulud, the May 2026 TanStack incident, etc.), this PR closes the pre-detection gaps in our supply-chain defense — the failure mode where a compromised package is published, propagates, and gets yanked all within the window before any audit catches it. Existing post-detection gates (bun audit, dependency-review, SHA-pinned actions) catch what's already known; these layers catch what hasn't been observed yet.

Changes

  1. bunfig.tomlinstall.minimumReleaseAge = 1209600 (14 days). Bun refuses to resolve any package version younger than 14 days on bun install / bun add / bun update / Vercel build. The observed safe horizon for npm worms to surface and get yanked.
  2. renovate.jsonminimumReleaseAge: "14 days" — symmetric with rotation #1 at PR-proposal time. Renovate won't propose what Bun would refuse to install.
  3. package.json"trustedDependencies": ["esbuild", "sharp"] — explicit lifecycle-script allowlist replaces Bun's default ~366-package name-based allowlist. Shrinks the PackageGate-class transitive-name-collision surface from ~366 → 2 (esbuild's native bin via velite; sharp's libvips for next/image).
  4. .github/workflows/ci.yml → drop continue-on-error on actions/dependency-review-action. A PR introducing a new high-severity advisory now hard-fails the check, matching the existing bun audit gate.
  5. .github/workflows/ci.yml → new secret-scan job (TruffleHog v3.95.2). Defense-in-depth on top of GitHub's push protection; re-scans diffs on every PR + push to main, catches secrets that slipped past push protection.
  6. .github/workflows/scorecard.yml (new) — OpenSSF Scorecard weekly grade on supply-chain hygiene (pinned deps, branch protection, token permissions, dangerous workflow patterns). SARIF posts to the Security tab; aggregate score publishes to https://scorecard.dev.
  7. All workflows → runs-on: ubuntu-24.04 (was ubuntu-latest). Runner-image bumps land as deliberate PRs instead of silent infrastructure drift.
  8. CLAUDE.md — replaces the "Audit advisories" preamble with a 10-layer "Supply chain hardening" section that walks the defense-in-depth stack in order of which gate trips first. The existing four audit-advisory entries (postcss / mdast-util-to-hast / uuid / tmp) move under as a subsection, content unchanged. Also fixes a stale next.config.tsnext.config.mjs reference.

One-time setup needed after merge

  • Settings → Code security → enable "Code scanning" so the Scorecard SARIF upload lands in the Security tab. Without it the upload step no-ops silently.

Follow-ups noted

First-principles audit surfaced several items beyond the npm-worm scope. Tracked as separate issues:

Verification

  • bun install clean on a fresh ~/.bun/install/ cache (trustedDependencies allows esbuild + sharp postinstall scripts)
  • bun run build succeeds (next/image + velite paths intact)
  • bun audit --ignore=GHSA-w5hq-g745-h8pq --ignore=GHSA-52f5-9888-hmc6 clean
  • bun run check:ci + bun run typecheck clean
  • bun run verify — 17/17 passed (sitemap, robots, RSS, OG, canonicals, JSON-LD, theme parity, /api/subscribe, internal links)
  • bun run test:e2e — 16/16 passed (desktop + mobile smoke)

…hain attacks

Closes the local-install and PR-time gaps that the existing audit gates
don't cover:

- bunfig.toml: install.minimumReleaseAge = 1209600s (14d). Refuses to
  resolve any package version younger than 14 days during `bun install`,
  `bun add`, `bun update`, or a Vercel build. 14d is the observed safe
  horizon for the npm-worm class (Shai-Hulud, mini-Shai-Hulud, the May
  2026 TanStack incident) to surface and get yanked.
- package.json: trustedDependencies = ["esbuild", "sharp"]. Replaces
  Bun's default ~366-package name-based allowlist with an explicit
  two-package allowlist. sharp builds libvips for next/image; esbuild's
  postinstall builds its native bin (pulled in via velite). Shrinks the
  PackageGate-class transitive-name-collision surface from ~366 to 2.
- renovate.json: minimumReleaseAge "14 days". Symmetric with the
  bunfig gate at PR-proposal time so Renovate won't even open a PR for
  a version Bun would refuse to install.
- ci.yml: drop continue-on-error on actions/dependency-review-action.
  A PR introducing a new high-severity advisory now hard-fails the
  check, matching the existing bun audit gate.

Signed-off-by: Will Manning <will@willmanning.io>
ubuntu-latest drifts whenever GitHub rolls the image. A silent change to
the base image (system openssl, libc, default Node/Bun, apt sources,
toolchain) can flip CI from green to red without any change in this repo
— or worse, mask a real breakage by upgrading something the code
incidentally depends on. Pinning makes runner-image bumps a deliberate
PR.

The Node version inside the runner image can still drift; .nvmrc pins
that separately. Comment in the setup-node block updated to describe
both layers.

Signed-off-by: Will Manning <will@willmanning.io>
Weekly grade on supply-chain hygiene — branch protection, pinned
dependencies, signed releases, token permissions, dangerous workflow
patterns, dependency-update tooling, vulnerability backlog, code review
coverage. Findings land in the Security tab as SARIF; aggregate score
also publishes to https://scorecard.dev so the badge tracks drift over
time.

Triggers: weekly schedule, push to main (workflow changes), branch
protection rule changes, and manual dispatch.

All third-party actions SHA-pinned per repo policy. Workflow uses
permissions: read-all at the workflow level; the analysis job opts up
to security-events: write (for SARIF upload) and id-token: write (for
the OIDC-signed publish to scorecard.dev).

Signed-off-by: Will Manning <will@willmanning.io>
Defense-in-depth on top of GitHub push protection. Push protection
covers high-entropy / known-provider tokens at push time; this job
re-scans on every PR + push to main using TruffleHog's verified
detector set, which:

  - catches secrets that slipped past push protection (low-entropy
    formats, detector patterns added after the secret was committed)
  - re-checks history when push protection itself is bypassed via the
    "I'll fix it later" allow path

--results=verified,unknown drops trufflehog's "unverified" tier
(pattern-matched but couldn't reach the verifier endpoint) to keep
noise down without losing coverage of secrets whose verifiers don't
recognize them yet. fetch-depth: 0 because the action diffs base..head
and needs the base ref in the checkout.

Signed-off-by: Will Manning <will@willmanning.io>
Replaces the existing "Audit advisories" preamble with a "Supply chain
hardening" section that walks the full defense-in-depth stack in order
of which gate trips first:

  1. bunfig minimumReleaseAge → 2. renovate minimumReleaseAge →
  3. trustedDependencies → 4. bun audit → 5. dependency-review →
  6. trufflehog → 7. Scorecard → 8. SHA-pinned actions →
  9. pinned ubuntu-24.04 runner → 10. patch/minor-only auto-merge

The existing four audit-advisory entries (postcss, mdast-util-to-hast,
uuid, tmp) move under "Audit advisories" as a subsection — same
content, just framed as the current state of layer 4.

Also fixes a stale reference: the key-files list pointed at
next.config.ts; the actual file is next.config.mjs.

Signed-off-by: Will Manning <will@willmanning.io>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vortex Ready Ready Preview, Comment May 13, 2026 2:57pm

Request Review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 13, 2026

🔦 Lighthouse audit

URL Perf A11y Best practices SEO Report
/ 🟢 100 🟢 100 🟢 96 🟢 100 report
/blog 🟢 100 🟢 100 🟢 96 🟢 100 report
/blog/btrblocks-compressor 🟢 100 🟢 96 🟢 96 🟢 100 report

Run 25807198809 · 2026-05-13 14:58 UTC

Every check Scorecard runs (branch protection state, pinned dependencies
in main, code-review-coverage history, vulnerability backlog) reflects
main as it currently sits — running on a PR would just re-grade main,
not preview the PR's effect. Adding the rationale inline so a future
contributor doesn't naively add `pull_request:` to the on: block.

Signed-off-by: Will Manning <will@willmanning.io>
Links to the scorecard.dev viewer for this repo. Badge image will 404
until the first Scorecard run completes after this PR merges (the
workflow's `push: branches: [main]` trigger fires the first run; the
weekly schedule keeps it refreshed thereafter). Brief broken-badge
window while indexing settles is acceptable; the badge then tracks
supply-chain hygiene drift over time and gives a one-glance status
signal to anyone landing on the repo page.

Signed-off-by: Will Manning <will@willmanning.io>
@lwwmanning lwwmanning enabled auto-merge (squash) May 13, 2026 14:57
@lwwmanning lwwmanning merged commit 1114b9b into main May 13, 2026
9 of 10 checks passed
@lwwmanning lwwmanning deleted the claude/dazzling-morse-869032 branch May 13, 2026 15:25
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.

1 participant