npm datasource caches private packages from registries that omit Cache-Control (e.g. GitHub Packages)
#42792
Replies: 1 comment
-
|
PR #42822 |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
How are you running Renovate?
Self-hosted Renovate CLI
Which platform you running Renovate on?
GitHub.com
Which version of Renovate are you using?
43.138.1
Please tell us more about your question or problem
Summary
The npm datasource caches packument responses in Renovate's shared package
cache whenever the upstream registry omits a
Cache-Controlheader. ForGitHub Packages (
https://npm.pkg.github.com) — which never sendsCache-Controlon read — this means every private@foo/*response iscached for the full hard TTL (default 7 days). On concurrent webhook
fan-outs shortly after a publish, jobs hit the in-process soft-TTL window
(15 min default) and receive a stale packument with no network revalidation
— missing the just-published version.
Reproduction
Configure an npm host rule pointing at
https://npm.pkg.github.com.Run any job that resolves a private
@foo/barpackage.Inspect the Redis/Valkey package cache:
The key exists with a ~604800 s TTL.
Publish a new version of
@foo/bar.Within 15 minutes, trigger another resolve job. Logs show
localHit: 1, hit: 0, miss: 0and Renovate resolves the old version.Root cause
lib/modules/datasource/npm/get.tsconstructs its cache provider with:And
PackageHttpCacheProvider.cacheAllowed(before the fix) reads:For a GHP response:
cachePrivatePackagesisfalseby default — no short-circuit.checkCacheControlHeaderistrue, but GHP returns nocache-controlheader at all, soisString(resp.headers['cache-control'])is false and the entire branchis skipped — not treated as private.
checkAuthorizationHeaderis hardcodedfalsefor npm, so therequest-auth check is also skipped.
return true→ the response is cached.For
registry.npmjs.orgthe same path returnstruevia the public branch(
Cache-Control: public, max-age=…is present). So public npm cacheslegitimately; GHP caches by falling through every guard.
Note:
cachePrivatePackagesis a "force caching on regardless" adminoverride — not a "refuse to cache when private" gate. The actual privacy
gate for npm is supposed to be
Cache-Control, and it silently fails openwhen the header is missing.
Why this can't be fixed publish-side
Response headers from
https://npm.pkg.github.comare entirely controlledby GitHub.
npm publishexposes no flag that influences the read-sideCache-Control,ETag, orLast-Modifiedheaders, and GitHub does notdocument any opt-in. Visibility, token type, and workflow changes all leave
GHP's read-side headers unchanged. This is a Renovate-side correctness gap.
Proposed fix
Require an affirmative
Cache-Control: publicsignal before caching whencheckCacheControlHeader: true. A missing header should disallow cachingrather than fall through.
Impact:
registry.npmjs.org: unchanged — it sendsCache-Control: publicandcontinues to cache.
Cache-Control: stopcaching by default. Correctness restored; performance recoverable via
cachePrivatePackages: truefor admins who know their registry is safeto share.
checkCacheControlHeader: false.Logs (if relevant)
Logs
Beta Was this translation helpful? Give feedback.
All reactions