Skip to content

Supply-chain hardening: blockExoticSubdeps, SHA-pinned actions, least-privilege CI, GHA cooldown - #69

Merged
CallumHoward merged 4 commits into
mainfrom
claude/youthful-mccarthy-yyoqkm
Sep 28, 2026
Merged

CallumHoward merged 4 commits into
mainfrom
claude/youthful-mccarthy-yyoqkm

Conversation

@CallumHoward

@CallumHoward CallumHoward commented Jul 3, 2026 •

Copy link
Copy Markdown
Owner

Supply-chain hardening for pnpm and CI.

  • 651345a blockExoticSubdeps: true: transitive deps must resolve from the registry, not git/tarball URLs. This is already the pnpm 11 default; setting it explicitly documents intent and lets the config guard catch a future default change.
  • 96a4b94 CI hardening:
    • ci.yml gets top-level permissions: contents: read.
    • Every action in both workflows is pinned to a tagged release's commit SHA with an exact # vX.Y.Z comment (all releases are >7 days old), so a moved or compromised tag can't change what runs. Majors are unchanged; Dependabot handles bumps.
    • persist-credentials: false on both checkouts. Neither workflow pushes with GITHUB_TOKEN; create-pull-request uses the App token.
    • diff-cover pinned to 10.5.1 (pipx run otherwise pulls latest from PyPI each run).
    • New github-actions Dependabot block with a 7-day cooldown, matching the npm cooldown.
  • 846415d src/lib/supply-chain-config.test.ts parses pnpm-workspace.yaml (new yaml devDep) and asserts trustPolicy, blockExoticSubdeps, minimumReleaseAge, minimumReleaseAgeStrict, and no global build allowlist. pnpm ignores unknown keys silently, so a typo'd or removed setting would otherwise go unnoticed.

Git/tarball deps and mutable action tags both skip registry and provenance checks, which minimumReleaseAge and trustPolicy don't cover.

Follow-ups (not in this PR)

  • Enable the repo setting that requires actions to be pinned to full-length SHAs (sha_pinning_required), so future unpinned uses: fail instead of relying on review.
  • diff-cover isn't tracked by Dependabot; bump it by hand.

Verification

  • pnpm install --frozen-lockfile passes the supply-chain policies; the only new dep is yaml.
  • Each config guard fails when its setting is broken and passes when restored.
  • Rebased onto main (cca6ad3); peers, typecheck, lint, format, fallow, tests and build pass locally.

🤖 Generated with Claude Code

@CallumHoward CallumHoward changed the title Block transitive git/tarball deps via pnpm blockExoticSubdeps Supply-chain hardening: blockExoticSubdeps, SHA-pinned actions, least-privilege CI, GHA cooldown Jul 3, 2026
@CallumHoward
CallumHoward force-pushed the claude/youthful-mccarthy-yyoqkm branch from ce371d4 to 56ffa9c Compare September 27, 2026 09:30
pnpm 11 already defaults this to true: only direct dependencies may resolve
from git or tarball URLs, so transitive deps can't bypass minimumReleaseAge
and trustPolicy. Setting it explicitly documents the intent and gives the
config guard something to assert against a future default change.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01KvPbYiZxbqWVnJyNkoVzky
…ooldown

- ci.yml: add top-level `permissions: contents: read` (least privilege)
- Pin every action to a tagged release's commit SHA with an exact
  `# vX.Y.Z` comment, so a moved/compromised tag cannot alter what runs
- `persist-credentials: false` on checkouts; neither workflow pushes with
  GITHUB_TOKEN
- Pin diff-cover, which `pipx run` otherwise fetches unpinned from PyPI
- dependabot.yml: add github-actions ecosystem with a 7-day cooldown, the
  min-release-age equivalent for Actions; Dependabot bumps the pinned SHAs
  and keeps the version comment current

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01KvPbYiZxbqWVnJyNkoVzky
Parses pnpm-workspace.yaml (via the yaml devDep) and asserts trustPolicy,
blockExoticSubdeps, minimumReleaseAge, minimumReleaseAgeStrict, and the
absence of a global build allowlist. pnpm silently ignores unknown keys, so
this fails CI if a protection is removed or misspelled instead of no-opping
unnoticed.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01KvPbYiZxbqWVnJyNkoVzky

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Explicit strict release-age and complete build-policy regression checks are still needed.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
What changed in this PR

Hardens pnpm dependency resolution and GitHub Actions against supply-chain risks.

Changes:

  • Adds pnpm policy configuration and regression tests.
  • SHA-pins workflow actions, restricts permissions, and disables persisted credentials.
  • Pins diff-cover and adds GitHub Actions Dependabot cooldowns.
File Summary
src/​lib/​supply-chain-config.test.ts Adds configuration guards; strict mode and build-policy assertions need strengthening, including wildcard rejection.
pnpm-workspace.yaml Enables exotic-subdependency blocking; explicitly configure strict release-age enforcement.
pnpm-lock.yaml Locks the new YAML dependency.
package.json Adds yaml as a development dependency.
.github/​workflows/​update-pnpm.yml Pins actions and disables persisted checkout credentials.
.github/​workflows/​ci.yml Hardens permissions, action references, credentials, and tooling.
.github/​dependabot.yml Adds GitHub Actions updates with cooldown.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/lib/supply-chain-config.test.ts Resolved
The implicit default only holds while minimumReleaseAge is set, and the
previous `not.toBe(false)` guard also passed when the key was absent.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@CallumHoward
CallumHoward merged commit 072fa0a into main Sep 28, 2026
1 check passed
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.

3 participants