Skip to content

chore: apply npm supply-chain best practices#382

Merged
auxesis merged 4 commits intomainfrom
chore/tighten-supply-chain-security
May 1, 2026
Merged

chore: apply npm supply-chain best practices#382
auxesis merged 4 commits intomainfrom
chore/tighten-supply-chain-security

Conversation

@auxesis
Copy link
Copy Markdown
Contributor

@auxesis auxesis commented May 1, 2026

Apply the relevant controls from lirantal/npm-security-best-practices as enforced config, plus a vitest gate that fails CI if a practice regresses. Future agents can't silently weaken these settings.

Config:

  • Bump pnpm from 10.14.0 to 10.33.2 (needed for blockExoticSubdeps).
  • Adjust pnpm to ensure a 7-day cooldown before new package versions are eligible for installation
  • Forbids git/tarball transitive deps (blockExoticSubdeps)
  • pin default + @cipherstash registry to npmjs.org.
  • Adjust dependabot to ensure a 7-day cooldown for minor/patch, 14-day for majors, grouped dev + @types/* updates
  • Require @cipherstash/developers review on supply-chain critical paths (GitHub Actions workflows, dependabot.yml, pnpm-workspace.yaml, pnpm-lock.yaml, .npmrc, the new skill).

Docs:

  • Add a skill and AGENTS.md section for supply chain security

Tests:

  • Add tests for supply chain security config listed above

Summary by CodeRabbit

  • New Features

    • Added automated supply-chain security validation tests.
  • Chores

    • Updated CI Node.js runtime to v22.
    • Bumped pnpm to 10.33.2 and enforced frozen lockfile installs.
    • Pinned registry resolution to the public npm registry.
  • Security / Maintenance

    • Added Dependabot scheduling and grouping for npm and GitHub Actions.
    • Added repository ownership rules for critical supply-chain paths.
  • Documentation

    • Expanded supply-chain security guidance and operational runbooks.

Apply the relevant controls from lirantal/npm-security-best-practices
as enforced config plus a vitest gate that fails CI if a practice
regresses. Future agents can't silently weaken these settings.

Config:

- Bump packageManager: pnpm@10.14.0 → 10.33.2 (needed for
  blockExoticSubdeps).
- pnpm-workspace.yaml: minimumReleaseAge: 10080 (7-day cooldown
  before new package versions are eligible), blockExoticSubdeps:
  true (forbids git/tarball transitive deps).
- .npmrc: pin default + @cipherstash registry to npmjs.org. Auth
  tokens stay in user-level ~/.npmrc or env vars.
- .github/dependabot.yml: 7-day cooldown for minor/patch, 14-day
  for majors, grouped dev + @types/* updates, covers both npm and
  github-actions.
- .github/CODEOWNERS: @cipherstash/developers owns supply-chain
  critical paths (workflows, dependabot.yml, pnpm-workspace.yaml,
  pnpm-lock.yaml, .npmrc, the new skill).
- .github/workflows/tests.yml: Node 20 → 22, pnpm install →
  pnpm install --frozen-lockfile in both jobs.

Test gate (e2e/tests/supply-chain.e2e.test.ts, 12 cases):
asserts each invariant above plus that every pnpm-lock.yaml
resolution points at registry.npmjs.org (substitutes for
lockfile-lint, which doesn't support pnpm-lock.yaml). Negative-
control verified — breaking each practice produces a clean failure
naming the regression.

Docs:

- New skills/stash-supply-chain-security/SKILL.md with the full
  guide (enforced / documented / deferred).
- AGENTS.md gains a "Supply Chain Security" section pointing at
  the skill and lists the new skill in Repository Layout.

Practices 11 (provenance attestations) and 12 (OIDC trusted
publishing) are deferred — they need npmjs.com-side Trusted
Publisher configuration before release.yml can drop NPM_TOKEN.
Tracked in a separate issue.

Source: https://github.com/lirantal/npm-security-best-practices
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 1, 2026

🦋 Changeset detected

Latest commit: 33e19a6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Warning

Rate limit exceeded

@auxesis has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 32 minutes and 53 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b081e0bd-2834-47cf-83f2-c636e3087b88

📥 Commits

Reviewing files that changed from the base of the PR and between 653fe21 and 33e19a6.

📒 Files selected for processing (2)
  • .changeset/tighten-supply-chain-security.md
  • e2e/tests/supply-chain.e2e.test.ts
📝 Walkthrough

Walkthrough

Adds repository-level supply-chain security controls: CODEOWNERS, Dependabot config, pnpm workspace policy, .npmrc registry pinning, CI changes (Node 22 + frozen-lockfile), repository packageManager bump, new supply-chain E2E tests, and documentation/skill material describing and enforcing these controls.

Changes

Cohort / File(s) Summary
GitHub ownership & automation
.github/CODEOWNERS, .github/dependabot.yml
Adds CODEOWNERS assigning @cipherstash/developers to repository and critical supply-chain paths; adds Dependabot config for npm and GitHub Actions with schedules, cooldowns, grouping, labels, and an ignore rule.
CI workflows
.github/workflows/tests.yml
Updates CI jobs to run on Node.js 22 and enforces pnpm install --frozen-lockfile in install steps.
pnpm workspace policy
pnpm-workspace.yaml
Introduces minimumReleaseAge (10080 min) and blockExoticSubdeps: true to prevent git/tarball subdependencies and enforce release-age cooldown.
Registry & package manager
.npmrc, package.json
Pins default and @cipherstash scope registries to https://registry.npmjs.org/; bumps packageManager to pnpm@10.33.2.
E2E tests & test deps
e2e/tests/supply-chain.e2e.test.ts, e2e/package.json
Adds comprehensive supply-chain E2E tests validating pnpm/packageManager, workspace policies, lockfile resolution, .npmrc contents, CI frozen-lockfile and Node version, Dependabot cooldowns, and CODEOWNERS coverage; adds yaml devDependency.
Docs & operational guidance
AGENTS.md, skills/stash-supply-chain-security/SKILL.md
Updates prerequisites (pnpm version), documents enforced supply-chain controls, practices, runbooks, and adds a new supply-chain security skill doc.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant Repo as Repository
    participant Dependabot as Dependabot
    participant CI as CI (GitHub Actions)
    participant Registry as npm Registry

    Dev->>Repo: Push PR (code / deps)
    Repo->>CI: Trigger tests/workflows
    CI->>CI: Run `pnpm install --frozen-lockfile` (Node 22)
    CI->>Repo: Report pass/fail
    Dependabot->>Repo: Open scheduled update PRs (npm / actions)
    Repo->>CI: Dependabot PR triggers same CI checks
    CI->>Registry: Fetch packages from https://registry.npmjs.org/
    CI->>Repo: Fail if lockfile exotic subdeps or CI/frozen-lockfile checks fail
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • coderdan

Poem

🐰 I hopped through lockfiles late at night,

Pinned registries shining bright.
Dependabot waits three days before a peep,
CI freezes locks while secrets sleep.
A tiny rabbit guards the supply-chain tight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: applying npm supply-chain best practices through enforced configuration, security controls, and CI-gated tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/tighten-supply-chain-security

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 32 minutes and 53 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
e2e/tests/supply-chain.e2e.test.ts (1)

91-93: ⚡ Quick win

Make install-step detection resilient to pnpm flags before install.

The current matcher only catches pnpm install. Variants like pnpm --filter ... install won’t be checked.

Suggested refactor
 const installSteps = job.steps.filter(
-  (s) => typeof s.run === 'string' && /\bpnpm\s+install\b/.test(s.run),
+  (s) => typeof s.run === 'string' && /\bpnpm\b[^\n]*\binstall\b/.test(s.run),
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/supply-chain.e2e.test.ts` around lines 91 - 93, The install-step
filter only matches the exact "pnpm install" token; update the predicate used in
installSteps (the job.steps.filter callback) to use a more flexible regex that
allows flags/args before install, e.g. replace /\bpnpm\s+install\b/ with a
pattern like /\bpnpm\b(?:\s+\S+)*\s+install\b/ so commands such as "pnpm
--filter=... install" or "pnpm -w install" are recognized.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/tests/supply-chain.e2e.test.ts`:
- Around line 100-105: The test currently skips jobs missing an
actions/setup-node step; update the test in the "every job runs on Node 22"
block to first detect whether a job runs pnpm (scan job.steps for run commands
containing 'pnpm' or uses of pnpm actions) and if so assert that a setup step
exists (the found variable `setup` must be truthy) and then assert that
setup.with?.['node-version'] === '22'; for jobs that don't run pnpm you may
still skip the Node check as before but do not use a blind continue for
pnpm-using jobs—fail the test when a pnpm job lacks `actions/setup-node` and
ensure the assertion references `workflow.jobs`, `job.steps`, and the `setup`
variable.
- Around line 123-124: Update the test "github-actions ecosystem is also
covered" so it doesn't just assert existence of an update with package-ecosystem
=== 'github-actions' but also asserts that the matched update enforces the
cooldown policy: locate the update via db.updates.find(u =>
u['package-ecosystem'] === 'github-actions') and then assert the cooldown is
applied (for your implementation check the update's cooldown flag or timestamp
field — e.g. expect(u.cooldown).toBe(true) or
expect(u.cooldownUntil).toBeGreaterThan(Date.now()) — whichever field your
cooldown implementation uses).
- Around line 129-140: The test currently checks substring presence in
CODEOWNERS via the `co` variable and `toContain(path)` which can match comments
or unrelated entries; instead parse CODEOWNERS lines (from `co`), locate the
rule line that applies to each path in the array (match exact path or path
prefix like 'path/' rules), extract the owner list from that rule, and assert
that the owner list includes the expected owner string
`@cipherstash/developers`; update the test inside the `it('protects supply-chain
critical paths'...)` block to perform this owner identity check for each `path`
rather than using `toContain(path)`.

---

Nitpick comments:
In `@e2e/tests/supply-chain.e2e.test.ts`:
- Around line 91-93: The install-step filter only matches the exact "pnpm
install" token; update the predicate used in installSteps (the job.steps.filter
callback) to use a more flexible regex that allows flags/args before install,
e.g. replace /\bpnpm\s+install\b/ with a pattern like
/\bpnpm\b(?:\s+\S+)*\s+install\b/ so commands such as "pnpm --filter=...
install" or "pnpm -w install" are recognized.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 17894716-c1cd-4f24-abe9-f2ca57a7c647

📥 Commits

Reviewing files that changed from the base of the PR and between e3c54c4 and 80a800a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • .github/CODEOWNERS
  • .github/dependabot.yml
  • .github/workflows/tests.yml
  • .npmrc
  • AGENTS.md
  • e2e/package.json
  • e2e/tests/supply-chain.e2e.test.ts
  • package.json
  • pnpm-workspace.yaml
  • skills/stash-supply-chain-security/SKILL.md

Comment thread e2e/tests/supply-chain.e2e.test.ts Outdated
Comment thread e2e/tests/supply-chain.e2e.test.ts Outdated
Comment thread e2e/tests/supply-chain.e2e.test.ts Outdated
Address review feedback on e2e/tests/supply-chain.e2e.test.ts.
Each finding passed against the current (correct) config but
would let real regressions slip through:

- Node 22 check silently skipped jobs without actions/setup-node.
  Now: detect pnpm-using jobs (uses pnpm/action-setup or runs
  `pnpm`) and require both setup-node and node-version: '22'.

- github-actions ecosystem was only checked for existence in
  dependabot.yml. Now: mirror the npm assertion and require
  cooldown.default-days >= 3.

- CODEOWNERS substring match could be satisfied by comment-only
  mentions (e.g. `# pnpm-workspace.yaml is critical`). Now: strip
  blank/comment lines, find a rule containing each path, and
  assert the rule's owner column includes @cipherstash/developers.

- Install regex /\bpnpm\s+install\b/ missed `pnpm --filter=foo
  install` and `pnpm -w install`. Now: allow flag tokens between
  `pnpm` and `install` while avoiding false matches like `pnpm
  run install-x`.

Each tightening was verified by breaking the relevant control
and confirming the test fails naming the regression. 12/12 still
pass against the unchanged config.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
e2e/tests/supply-chain.e2e.test.ts (1)

151-157: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Critical CODEOWNERS path coverage is incomplete.

Line 151’s required path list omits /.github/CODEOWNERS and /skills/stash-supply-chain-security/, so ownership regressions there won’t fail this gate.

Suggested patch
     for (const path of [
-      'pnpm-workspace.yaml',
-      'pnpm-lock.yaml',
-      'dependabot.yml',
-      '.npmrc',
-      '.github/workflows/',
+      '/pnpm-workspace.yaml',
+      '/pnpm-lock.yaml',
+      '/.github/dependabot.yml',
+      '/.npmrc',
+      '/.github/workflows/',
+      '/.github/CODEOWNERS',
+      '/skills/stash-supply-chain-security/',
     ]) {

As per coding guidelines, "Validate supply chain controls against the e2e/tests/supply-chain.e2e.test.ts test to prevent silent regressions in CI."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/supply-chain.e2e.test.ts` around lines 151 - 157, The path list in
the for loop inside the supply-chain test (for (const path of [...]) in
e2e/tests/supply-chain.e2e.test.ts) is missing critical entries; update that
array to include '/.github/CODEOWNERS' and
'/skills/stash-supply-chain-security/' so the test will fail when ownership
changes touch those locations, preserving the existing format and separators
used in the list.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/tests/supply-chain.e2e.test.ts`:
- Around line 28-31: The test 'pnpm-workspace.yaml sets minimumReleaseAge ≥ 3
days' currently asserts ws.minimumReleaseAge ≥ 4320 (3 days) which is weaker
than the intended 7-day policy; update the expectation in the test (the it block
and its assertion referencing ws.minimumReleaseAge and the numeric literal 4320)
to assert the configured 7-day minimum (10080) so CI enforces the correct
supply-chain control.

---

Duplicate comments:
In `@e2e/tests/supply-chain.e2e.test.ts`:
- Around line 151-157: The path list in the for loop inside the supply-chain
test (for (const path of [...]) in e2e/tests/supply-chain.e2e.test.ts) is
missing critical entries; update that array to include '/.github/CODEOWNERS' and
'/skills/stash-supply-chain-security/' so the test will fail when ownership
changes touch those locations, preserving the existing format and separators
used in the list.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cd5e90a5-4645-4fe6-8c1d-7a2228691c8c

📥 Commits

Reviewing files that changed from the base of the PR and between 80a800a and 653fe21.

📒 Files selected for processing (1)
  • e2e/tests/supply-chain.e2e.test.ts

Comment thread e2e/tests/supply-chain.e2e.test.ts Outdated
auxesis added 2 commits May 2, 2026 00:40
- Bump cooldown threshold from 4320 (3 days) to 10080 (7 days) in test,
  to match the policy
- Add CODEOWNERS and skills to the CODEOWNERS path list, to ensure
  review coverage
@auxesis auxesis merged commit 3dd9d78 into main May 1, 2026
7 checks passed
@auxesis auxesis deleted the chore/tighten-supply-chain-security branch May 1, 2026 18:01
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.

2 participants