security: harden against npm supply-chain attacks (install gates, Scorecard, secret scan)#68
Merged
Merged
Conversation
…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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🔦 Lighthouse audit
Run |
This was referenced May 13, 2026
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
bunfig.toml→install.minimumReleaseAge = 1209600(14 days). Bun refuses to resolve any package version younger than 14 days onbun install/bun add/bun update/ Vercel build. The observed safe horizon for npm worms to surface and get yanked.renovate.json→minimumReleaseAge: "14 days"— symmetric with rotation #1 at PR-proposal time. Renovate won't propose what Bun would refuse to install.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)..github/workflows/ci.yml→ dropcontinue-on-erroronactions/dependency-review-action. A PR introducing a new high-severity advisory now hard-fails the check, matching the existingbun auditgate..github/workflows/ci.yml→ newsecret-scanjob (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..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.runs-on: ubuntu-24.04(wasubuntu-latest). Runner-image bumps land as deliberate PRs instead of silent infrastructure drift.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 stalenext.config.ts→next.config.mjsreference.One-time setup needed after merge
Follow-ups noted
First-principles audit surfaced several items beyond the npm-worm scope. Tracked as separate issues:
/api/subscribeMDXRenderer'snew Function()evalscript-srcoff'unsafe-inline'to nonce-basedCross-Origin-Opener-PolicyandCross-Origin-Resource-PolicyheadersCLAUDE.mdVerification
bun installclean on a fresh~/.bun/install/cache (trustedDependencies allows esbuild + sharp postinstall scripts)bun run buildsucceeds (next/image + velite paths intact)bun audit --ignore=GHSA-w5hq-g745-h8pq --ignore=GHSA-52f5-9888-hmc6cleanbun run check:ci+bun run typecheckcleanbun 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)