Skip to content

fix(deps): Dependabot lockfiles bypass the minimumReleaseAge cooldown #441

Description

@EricAndrechek

Summary

pnpm-workspace.yaml sets minimumReleaseAge: 10080 (7 days) as a supply-chain defense against compromised releases. Dependabot doesn't know about it, so the lockfiles it generates can contain packages published inside that window — and nothing in CI catches it.

Evidence

Dependabot recreated #416 on current main. A local pnpm install refused the lockfile outright:

[ERR_PNPM_NO_MATURE_MATCHING_VERSION]
@esbuild/openbsd-arm64@0.28.2 was published at 2026-08-08T19:57:56.000Z,
within the minimumReleaseAge cutoff (2026-08-03T19:23:49.523Z)
…and 34 more

35+ entries younger than the cooldown. Fixing it required a full rebuild (pnpm clean --lockfile + pnpm install), which re-resolved to policy-respecting versions (astro 7.1.6 rather than a fresher build, Starlight 0.41.6, etc.).

Why CI does not catch this

CI installs with pnpm install --frozen-lockfile, which installs exactly what the lockfile pins and does not re-check minimumReleaseAge. The policy only bites on a non-frozen local install — i.e. only when a human or agent happens to regenerate. A Dependabot npm PR merged as-is would therefore pull sub-cooldown packages into main with every check green.

This is the inverse of the usual lockfile-drift failure mode: the lockfile is internally consistent, so nothing complains — it just quietly violates the stated policy.

Suggested fix

Add a CI check that fails when the committed lockfile contains entries published inside the cooldown. Options, roughly in order of preference:

  1. A make target that resolves each lockfile entry's publish date against the registry and compares to minimumReleaseAge, wired into the Lint job. Self-contained, no new dependencies.
  2. A scheduled/PR job that runs pnpm install --no-frozen-lockfile --lockfile-only in a scratch tree and fails if the lockfile changes — catches cooldown violations and ordinary drift together.
  3. Teach the Dependabot flow to regenerate: a workflow that reacts to dependabot/npm_and_yarn/** pushes, rebuilds the lockfile under policy, and commits the result back to the PR branch.

Option 1 is the tightest gate; option 3 removes the manual step but needs write-back permissions on Dependabot branches.

Interim workaround

Regenerate the lockfile locally before merging any Dependabot npm PR:

pnpm clean --lockfile && pnpm install

Scope check on what has already merged

main is currently clean — make verify (which runs pnpm install --frozen-lockfile) passes at 3c7d62b, and #439's lockfile was regenerated locally as part of the version-holds fix before merge. This issue is about the general gap, not a known live breach.

Related

Activity

  1. coderabbitai commented on Aug 10, 2026

    @coderabbitai
    🔗 Related PRs

    #134 - fix(ci): only bypass admin review for dependabot patch/minor [merged]
    #165 - build(pnpm): migrate to pnpm 11 + add minimumReleaseAge + pin Node 22 [merged]
    #308 - ci: add build provenance for npm, go binaries, and the ghcr image [closed]
    #312 - ci: split ci into a job dag and isolate docs deploys from pr code [closed]
    #340 - ci: consolidate Dependabot npm updates to the pnpm workspace root [closed]


    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  2. EricAndrechek commented on Sep 9, 2026

    @EricAndrechek
    MemberAuthor

    New evidence (2026-09-09): the failure mode has flipped from silent to blocking

    Dependabot PR #571 (astro 7.1.6 → 7.2.8) hit this, and it changes the picture this issue describes.

    The "Why CI does not catch this" section is now stale. pnpm 11.21.0 verifies the lockfile against the policy even under --frozen-lockfile — the exact install CI runs:

    ✗ Lockfile failed supply-chain policy check (1126 entries in 2.1s)
    [ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION] 21 lockfile entries failed verification:
      @clack/core@1.5.0 was published at 2026-09-07T20:12:19.000Z, within the minimumReleaseAge cutoff (2026-09-02T11:09:06.217Z)
      rolldown@1.2.7 was published at 2026-09-02T13:30:45.000Z, within the ...
      postcss@8.5.28 was published at 2026-09-03T15:05:10.000Z, within the ...
      …and 18 more
    

    Note the error code changed too — ERR_PNPM_NO_MATURE_MATCHING_VERSION (this issue's original evidence) → ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION.

    So suggested fix option 1 is effectively delivered upstream: the gate exists, and a sub-cooldown lockfile can no longer merge green. On #571 it took down pnpm-install inside make verify, failing Lint, Docs build, Unit tests, E2E tests and Coverage in one shot (run 34292840574).

    The severity therefore inverts: this is no longer "a Dependabot npm PR merged as-is would quietly pull sub-cooldown packages into main". It is now recurring CI friction that hard-blocks Dependabot npm PRs, with no self-service path for the bot. That makes option 3 (regenerate the lockfile under policy and commit back to the dependabot/npm_and_yarn/** branch) the only remaining item of real value here.

    A Dependabot cooldown: would NOT fix this

    Worth recording, since it is the obvious first thing to reach for. Dependabot's cooldown gates the direct dependency being bumped. astro@7.2.8 was published 2026-08-26 — 14 days old, comfortably past a 7-day cooldown. All 21 violations were transitive: rolldown@1.2.7 plus its 15 platform binding packages (09-02), postcss@8.5.28 and tinyexec@1.3.1 (09-03), @clack/core@1.5.0 + @clack/prompts@1.8.0 (09-07).

    The churn comes from Dependabot re-resolving the whole subtree with an age-blind resolver, not from the age of the package it set out to bump. No cooldown: value would have prevented it.

    Refinement to the interim workaround

    The documented workaround (pnpm clean --lockfile && pnpm install) works, but it re-resolves everything — on this tree that was a 1294+/901− lockfile diff that also swept in unrelated bumps and collided with the in-flight npm-deps PR #574.

    A targeted install is enough and much tighter: bump the manifest, then plain pnpm install. pnpm's resolver honors minimumReleaseAge when resolving the new subtree while leaving every untouched pin alone — 545+/116− here, no collateral bumps:

    # in docs/package.json: "astro": "^7.1.1" -> "^7.2.10"
    pnpm install
    pnpm install --frozen-lockfile   # confirms the result is policy-clean

    That resolved astro@7.2.10 (published 2026-08-31, already past cooldown) with rolldown@1.2.2 / postcss@8.5.25, and the docs site builds clean on it. Filed as the fix for #571.

  3. EricAndrechek commented on Oct 9, 2026

    @EricAndrechek
    MemberAuthor

    Status check (2026-10-09), so the remaining decision is clear:

    • Already done on main: .github/dependabot.yml carries a 7-day npm cooldown (with the @wave-rf/* / @wavehouse/* excludes), and the development guide and CHANGELOG describe it.
    • CI does catch a lockfile inside the release-age window now (measured): on the head of the open npm-deps Dependabot PR, pnpm install --lockfile-only --frozen-lockfile printed "Verifying lockfile against supply-chain policies (1086 entries)" and passed. So the "CI does not catch this" premise above no longer holds; pnpm 11's frozen-lockfile verification fails loudly instead.
    • What remains is the transitive case: a cooldown only gates the package being bumped, and Dependabot's resolver doesn't know minimumReleaseAge, so a fresh transitive dependency can still land in its lockfile and then fail verification. The fix for that is the write-back workflow (option 3 above): regenerate the lockfile with pnpm on the Dependabot branch and push it back. That push has to come from a token that triggers CI (a GITHUB_TOKEN push does not), and none of the repo's existing secrets is a fit. Creating one is a maintainer decision, so it is not built.

    A docs PR will follow that writes down the targeted fix for a red Dependabot lockfile (bump the manifest, plain pnpm install, then --frozen-lockfile) and references this issue without closing it.


    — Posted by Claude Code on behalf of @EricAndrechek

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions