Dependencies
Renovate for updates, a 24-hour quarantine for new versions, an install-script allowlist, and a CI audit that blocks only advisories users would install.
Most supply-chain attacks on npm are caught and pulled within hours. We do not install a version until it is a day old, we do not run install scripts we did not approve, and publishing never runs dependency code. All of this is built into pnpm and Renovate; there are no custom dependency scripts.
Quarantine
pnpm-workspace.yaml:
minimumReleaseAge: 1440 # minutes: new versions wait 24 hours
minimumReleaseAgeStrict: true # fail instead of quietly excluding too-young versions
allowBuilds: # the only dependencies allowed to run install scripts
esbuild: truepnpm refuses to resolve a version younger than 24 hours, including transitive
ones. With minimumReleaseAgeStrict off, pnpm would instead add such a version
to minimumReleaseAgeExclude by itself and install it; strict mode fails in CI
and asks at the terminal. pnpm 11 turns strict mode on whenever
minimumReleaseAge is set; the starters write it out so nobody turns it off
by accident. Only list packages in allowBuilds (onlyBuiltDependencies on pnpm 10)
that really need a build step. Never set dangerouslyAllowAllBuilds.
Pin pnpm in package.json ("packageManager": "pnpm@11.x.y"). An older pnpm
can silently ignore these settings.
The quarantine applies to third-party versions only. Our own packages skip it, so a fix in one Lupinum library reaches the others the same day:
minimumReleaseAgeExclude:
- '@lupinum/*' # our own scope; published only by our protected workflowsThis is the one standing exclusion, decided on 2026-10-07 while the Ginko libraries release often. Review it when release frequency drops.
Updates
Renovate opens update pull requests. .github/renovate.json:
{
"extends": ["config:recommended", "helpers:pinGitHubActionDigests"],
"minimumReleaseAge": "1 day",
"internalChecksFilter": "strict",
"rangeStrategy": "replace",
"schedule": ["before 6am on Monday"]
}Renovate waits the same day before it proposes a version, and keeps GitHub
Actions pinned to full commit SHAs with a version comment. replace keeps the
ranges that consumers of a library see; a repository that publishes nothing
may use pin. The weekly schedule batches updates into one review session.
Renovate opens security updates outside the schedule; pnpm still makes the
new version wait 24 hours unless you exclude it (see the last section). Dependabot stays on
for security alerts only; its security updates are off (see
Set up a repository), so only
Renovate opens update pull requests.
When an update changes dependencies or peerDependencies of a published
package (not only the lockfile), users get the change with the next version.
Such a pull request needs a changeset that bumps that package, at least
patch, for example "Change the foo dependency to v2."; CI fails without
one. Push it to the Renovate branch. Dev dependencies and lockfile-only
updates need no changeset.
An agent may merge a green Renovate pull request for a patch or minor update. A major update, or an update to anything that runs in CI or at build time (bundlers, test runners, GitHub Actions), gets a quick read of the release notes first.
Audit
An advisory blocks only when users would install the vulnerable code. CI runs
node scripts/audit-deps.mjs as part of pnpm verify. It runs
pnpm audit --prod and sorts every finding by where it comes from:
| Finding | Result |
|---|---|
| High or critical, reached through the production dependencies of a published package | CI fails |
| Anything in the docs site, in dev tools, or below high | A warning in the CI log; Renovate fixes it on its normal schedule |
The docs site and the test tools never reach users, so an advisory there must
not block every pull request and release. Dependabot alerts stay on as
notifications; the audit lists them as advice (OPS-03), and CI decides what
blocks. When audit-deps.mjs fails:
- Update the affected package, usually through the open Renovate pull request.
If no fixed version exists, check whether the vulnerable code is reachable from the library. If it is not, ignore that one advisory with a reason and a date to look again:
yaml # pnpm-workspace.yaml auditConfig: ignoreGhsas: - GHSA-xxxx-xxxx-xxxx # not reachable from our code; recheck 2026-11-01- If it is reachable, treat it as a security report.
Only ignore an advisory that blocks. A warning needs no entry.
When the quarantine blocks something you need now
First try an older version that has the fix. If that is not possible, for an urgent security fix only, exclude one exact version:
minimumReleaseAgeExclude:
- some-package@1.2.3 # urgent fix for GHSA-…; remove after 2026-10-02Remove the line once the version is a day old. The audit reports every
exclusion except @lupinum/* as a warning until you do. Never exclude a range
or a whole third-party package.