ci: add 7-day lockfile age audit for package-lock.json - #882
Merged
Conversation
Fails PRs that add package-lock.json entries published less than 7 days ago. Catches lockfile-bypass paths the Dependabot cooldown doesn't cover (manual edits, --package-lock-only, escape flags). Set-diffs head vs merge-base resolutions (not just '+' lines) so lockfile reorders don't trigger false positives. @clickhouse/* preapproved — first-party. Skip via 'lockfile-age-skip' label.
motsc
force-pushed
the
chore/lockfile-age-audit
branch
from
June 23, 2026 10:23
2646b5f to
c8d9507
Compare
…okup
CodeQL flagged name.replace('/', '%2F') as only replacing the first '/'.
For valid npm names that's always one '/' max (scoped: @scope/pkg), so
no functional bug today — just hardens against future regressions.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new GitHub Actions workflow and Node.js script to enforce a “minimum lockfile entry age” policy for package-lock.json changes in PRs. The workflow diffs lockfile resolutions between the PR’s merge-base and HEAD, then queries the npm registry to fail PRs that introduce packages published within the last 7 days (with an opt-out label).
Changes:
- Introduces
scripts/ci/lockfile-age-audit.mjsto compute newly-added lockfile resolutions and validate their publish timestamps via the npm registry. - Adds
.github/workflows/lockfile-age-audit.ymlto run the audit on PR events, with alockfile-age-skiplabel escape hatch.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| scripts/ci/lockfile-age-audit.mjs | Implements the lockfile resolution diffing and npm publish-time checks. |
| .github/workflows/lockfile-age-audit.yml | Runs the audit in CI on PR updates and supports a label-based bypass. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| if (next) await checkOne(next) | ||
| } | ||
| } | ||
| await Promise.all(Array.from({ length: CONCURRENCY }, worker)) |
Collaborator
There was a problem hiding this comment.
this is likely gonna get us rate limited
2 tasks
peter-leonov-ch
added a commit
that referenced
this pull request
Jun 24, 2026
…rettify (#886) ## Summary Addresses CI failure and review feedback on the lockfile age gate (`scripts/ci/lockfile-age-audit.mjs`, introduced in #882). These changes are made in a fresh PR to `main` because #883 targets the `release` branch and cannot be modified. Two fixes, both in `scripts/ci/lockfile-age-audit.mjs`: 1. **Fail closed on non-registry sources** (Copilot review comment). Previously `extractNpmResolutions` silently `continue`d on any entry whose `resolved` host was not an allowed registry, so a PR could bypass the age gate entirely by pinning a new dependency from an alternative registry, a plain-`http` URL, or a `git+https` source. Now `isRegistryEntry` is replaced by `classifyResolved`: - `registry` — allowed HTTPS registry tarball → audited against the age gate (unchanged behavior). - `foreign` — a URL but not an allowed HTTPS registry host → **fails closed** (new). - `local` — no resolved URL (workspace source dirs, `file:` links) → skipped, nothing to age-check. The `lockfile-age-skip` PR label (handled in `.github/workflows/lockfile-age-audit.yml`) remains the intentional escape hatch. 2. **Prettier formatting** (Copilot review comment + the failing `code-quality` check). The file was written in single-quote / no-semicolon style; the repo's Prettier defaults (`.prettierrc` = `{}`) use double quotes + semicolons. Reformatted with `prettier --write`. ## Verification - `prettier --check` passes on the file (full-repo check is clean apart from an untracked local scratch dir). - `node --check` passes. - Manually exercised the fail-closed path against synthetic lockfiles: new deps resolved from an alternative host, `git+https`, and plain `http` all fail closed; unchanged legit registry entries are not flagged. ## Test plan - [x] `npm run prettier:check` clean for tracked files - [x] Fail-closed logic verified against synthetic base/head lockfiles 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
fails PRs that add
package-lock.jsonentries published less than 7 days ago. complements the Dependabot cooldown by catching anything that bypasses it (manual edits,--package-lock-only, escape flags), audits the lockfile diff so it isn't tool-specific.set-diff against
git merge-base origin/main HEAD, not+lines, so lockfile reorders don't false-positive. existing lockfile grandfathered. ~1 registry request per added package, concurrency 8.@clickhouse/*excluded.escape hatch is the
lockfile-age-skiplabel, for legit CVE bumps inside the window.failure looks like:
advisory only until repo admin adds
Lockfile age audit / auditas a required check on main. will follow up once this lands.companion: #881.