ci: restrict Dependabot auto-merge to direct devDependencies - #208
Conversation
The auto-merge workflow approved and merged every non-major Dependabot PR, including production dependencies, with nobody reading the diff. release-please then publishes main to npm, so a compromised minor or patch release of a production dependency could reach every consumer unreviewed. The existing exclusion list only covered a handful of renderer/validator packages. Gate auto-merge on `dependency-type == direct:development` in addition to the semver and exclusion-list checks. Production dependencies, GitHub Actions bumps (reported as production by Dependabot), and lockfile-only `indirect` updates are now held for a maintainer, and the "held for manual review" comment states which rule applied. The exclusion list stays as defense in depth for packages that render user content even when they appear as devDependencies. The policy is evaluated in a single step that emits `automerge` and `hold-reason` outputs, and the comment body now receives metadata via environment variables instead of interpolating expressions into the shell script. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155aTZRVhq6WYcU4B6u1Tqq
Cloudflare preview✅ Deployed |
|
CI: The
Why it is not this PR's:
No open PR fixes it yet. The fix is a lockfile-only bump and needs no pnpm update fast-uri --lockfile-only
pnpm audit --audit-level=high # → No known vulnerabilities foundI ran exactly that locally: it changes 4 lines in Generated by Claude Code |
Summary
The Dependabot auto-merge workflow now only auto-approves and auto-merges PRs whose updated packages are all direct devDependencies. Production dependencies, GitHub Actions bumps, and lockfile-only updates are held for a maintainer, and the "held for manual review" comment states which rule applied.
Why
dependabot-automerge.ymlapproved and merged every non-major Dependabot PR with no dependency-type filter and no reviewer. Production dependencies were included. Becauserelease-please.ymlpublishesmainto npm, a compromised minor or patch release of any production dependency could reach every consumer of@tightknitai/block-kitchenwith nobody having read the diff. The existing exclusion list only covered a handful of renderer/validator packages, so it mitigated the worst cases but not the general one.A compromised devDependency reaches CI and developer machines at worst, so those keep auto-merging (unless on the exclusion list).
What changed
Evaluate auto-merge policystep that emitsautomergeandhold-reasonoutputs. A PR is eligible only when it is not a major update,dependency-typeisdirect:development, and no package is on the exclusion list. Anything else, including an empty or unknowndependency-type, fails closed.slack-web-api-clientis a devDependency at the root today).run:scripts. Metadata reaches the comment body throughenv:instead of${{ }}inside the shell script.SECURITY-REVIEW.mdF-006 gets a dated addendum describing the tightened gate.Behavioral consequences worth knowing
dependabot/fetch-metadatareports the most sensitivedependency-typepresent (direct:production>direct:development>indirect), so a mixed group is held. This repo'sdependabot.ymlalready groups by dependency type, so groups are homogeneous in practice.react,react-dom,slack-web-api-client) are reported as production and will now be held. The demo is deployed publicly, so that seems right, but it is more manual work than before.indirectupdates (transitive security bumps in the lockfile) are held because Dependabot cannot say whether they reach the published package.Test plan
actionlinton the workflow: no findings.indirect(held), major dev and major prod (held), Actions bump (held), empty metadata (held).pnpm typecheck/pnpm lint/pnpm testnot run: no TypeScript or source files changed.mainafter merge: confirm a devDependency bump auto-merges and a production bump gets the held comment.Notes for reviewer
The suggested one-line fix of
dependency-type == 'development'would never match: fetch-metadata's actual output values aredirect:production,direct:development, andindirect. The gate here usesdirect:development.🤖 Generated with Claude Code
https://claude.ai/code/session_0155aTZRVhq6WYcU4B6u1Tqq
Generated by Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.