Skip to content

chore(security): apply the Dependabot cooldown to npm installs - #48

Closed
devin-ai-integration[bot] wants to merge 1 commit into
masterfrom
devin/1786346940-npmrc-min-release-age
Closed

chore(security): apply the Dependabot cooldown to npm installs#48
devin-ai-integration[bot] wants to merge 1 commit into
masterfrom
devin/1786346940-npmrc-min-release-age

Conversation

@devin-ai-integration

Copy link
Copy Markdown

SUMMARY

.github/dependabot.yml sets cooldown: default-days: 7 for all six ecosystems. That cooldown binds Dependabot only. There was no .npmrc anywhere in the tree (find . -name .npmrc -not -path "*/node_modules/*" returned nothing), so any npm install — a developer's, or docker/docker-frontend.sh:36 in dev mode — could resolve a version published minutes earlier.

This adds min-release-age=7 in an .npmrc in each directory that has its own package-lock.json:

superset-frontend/.npmrc
superset-frontend/cypress-base/.npmrc
superset-websocket/.npmrc
superset-websocket/utils/client-ws-app/.npmrc
superset-embedded-sdk/.npmrc

npm reads project config only from the directory it runs in (the sibling of package.json), so one file per install path is required; a single root file would not apply to superset-frontend.

min-release-age was added in npm 11.10.0 (npm/cli#8965). superset-frontend/package.json and superset-websocket/package.json already require npm: ^11.13.0, and every .nvmrc in the repo is v24.16.0, which ships npm 11.13.0 (https://nodejs.org/dist/index.json). Older npm parses the key as an unknown config and ignores it — measured below — so nothing breaks on a stale toolchain.

Deliberate exclusions

  • docs/ — installs with yarn 1.22 ("packageManager": "yarn@1.22.22", docs/yarn.lock). Yarn 1 has no equivalent option.
  • Repository root — .github/workflows/release.yml:97 does echo ... > .npmrc in the root during publishing, which would clobber a committed root file. There is no root package.json, so no install happens there.
  • .github/actions/* — vendored third-party actions with committed dist/; nothing installs them locally.
  • superset/mcp_servicedependencies and devDependencies are both empty and there is no lockfile.

What this does not do

  • Only the resolver is affected. Versions already pinned in a package-lock.json are installed as-is, so npm ci behaviour is identical (measured below). The protection applies when a version is added or changed, which is where a fresh malicious release would enter.
  • min-release-age is mutually exclusive with npm's before; anyone who needs --before for an install must remove or override this key for that run.
  • npm has no per-package exclusion for this setting (Add package exclusion support for min-release-age npm/cli#8994). It is all-or-nothing: adding a dependency whose only usable release is under 7 days old requires npm install --min-release-age=0 for that command.
  • Dockerfile:70-77 bind-mounts only package.json and package-lock.json for its npm ci, and COPY superset-frontend happens afterwards, so the image build never sees the new file. No change there either way, since npm ci is lockfile-driven.
  • The npm publish paths (lerna publish from-package in release.yml, npm publish --access public in superset-embedded-sdk/release-if-necessary.js) were not exercised. min-release-age flattens to a before value used for version resolution, and npm publish --dry-run with the key set behaved normally in a scratch package, but a real publish was not run. The embedded-sdk release relies on there being no _authToken line in an .npmrc for OIDC trusted publishing; this file adds no auth line.
  • Measurement discontinuity: from this commit onward, the newest resolvable version in these directories lags the registry by 7 days. Any metric derived from "how current are our dependencies" — dependency freshness/lag dashboards, npm outdated diffs taken from a plain npm install — shifts by up to a week at this commit and is not comparable across it. Dependabot's own PRs are unaffected, since they were already on the same 7-day cooldown.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Not applicable — no UI change.

TESTING INSTRUCTIONS

Local runs, exact commands and output.

1. The key is recognised by npm 11 and ignored by npm 10. npm i -g --prefix /tmp/npmglobal npm@11 gave npm 11.19.0; the system npm is 10.8.2.

$ cd superset-frontend && /tmp/npmglobal/bin/npm config list
; "project" config from /home/ubuntu/repos/superset/superset-frontend/.npmrc

min-release-age = 7

; npm version = 11.19.0

The ASF header comments parse fine (npm's ini reader treats # as a comment). Under npm 10.8.2 the same file yields min-release-age = "7" — an unknown config, retained as a string and unused, with no warning or error.

2. It actually filters resolution. Scratch package, min-release-age=3650:

$ /tmp/npmglobal/bin/npm install lodash --no-audit --no-fund
added 1 package in 435ms
$ /tmp/npmglobal/bin/npm ls lodash
└── lodash@4.14.2

Same command with npm 10.8.2 and the same .npmrc resolves lodash@4.18.1 — i.e. the filter is npm-11-only, as documented above.

3. npm ci is unaffected. Scratch package with lodash@4.18.1 in package-lock.json and min-release-age=3650:

$ /tmp/npmglobal/bin/npm ci --no-audit --no-fund && /tmp/npmglobal/bin/npm ls lodash
added 1 package in 307ms
└── lodash@4.18.1

In this repo, npm ci --dry-run --no-audit --no-fund in superset-frontend produces the same package set with and without the new .npmrcdiff of the two logs shows only two reordered remove lines and the elapsed time (removed 12 packages in 4s vs 3s). npm ci --dry-run also succeeds in superset-websocket, superset-websocket/utils/client-ws-app, superset-frontend/cypress-base and superset-embedded-sdk.

4. Lint.

$ uvx pre-commit run
auto-walrus..........................................(no files to check)Skipped
check for added large files..............................................Passed
fix end of files.........................................................Passed
trim trailing whitespace.................................................Passed
oxfmt (frontend).....................................(no files to check)Skipped
oxlint (frontend)....................................(no files to check)Skipped
[... all remaining hooks: (no files to check)Skipped]

No pre-commit hook matches .npmrc, so the five staged files exercise only the whitespace/EOF/large-file hooks. pre-commit is not installed in this environment, hence uvx.

5. License check — this is the hook that a new dotfile can actually fail:

$ ./scripts/check_license.sh
Running license checks. This can take a while.
/home/ubuntu/repos/superset/.rat-excludes
ERROR: Ignored 18 lines in your exclusion files as comments or empty lines.
RAT checks passed.

.rat-excludes lists .nvmrc but not .npmrc, so each new file carries the ASF header in # comment form, matching .dockerignore. The script's own download of apache-rat 0.16.1 from Maven Central returned HTTP 429 here; the jar was fetched from archive.apache.org and placed at /tmp/lib/apache-rat-0.16.1.jar instead, which is the path the script expects.

6. Tests. No test covers .npmrc; there is nothing to add without asserting on npm's own behaviour. Ran the frontend suite against an unrelated file to confirm the toolchain is intact:

$ cd superset-frontend && npm run test -- src/utils/assetUrl.test.ts
PASS src/utils/assetUrl.test.ts (5.239 s)
Test Suites: 1 passed, 1 total
Tests:       10 passed, 10 total

Manual verification

cd superset-frontend
node -v            # expect v24.16.0
npm -v             # expect 11.13.x
npm config get min-release-age    # expect 7
npm view <a package published in the last day> versions
npm install <that package> --dry-run   # the fresh version is not selected
npm ci --dry-run                        # unchanged package set

ADDITIONAL INFORMATION

Adversarial self-review of the diff. What was checked, and what came back:

  • Does a single root .npmrc suffice? No — npm project config is read from the local prefix only. Hence one file per install path. Checked that a root file would also be destroyed by release.yml:97.
  • Is the config name right and supported? Verified against feat: add min-release-age npm/cli#8965 (merged, released in 11.10.0) and confirmed empirically with npm 11.19.0, rather than trusting the name.
  • Does it break npm ci in CI or Docker? Tested: no. npm ci reinstalled the lockfile version even at min-release-age=3650, and the superset-frontend dry-run package set is byte-identical modulo ordering.
  • Does it break on the npm actually present in CI images? Checked every .nvmrc (all v24.16.0 → npm 11.13.0) and both engines blocks (npm: ^11.13.0); confirmed npm 10.8.2 ignores the key.
  • Will the license check reject a new dotfile? Nearly missed — .rat-excludes excludes .nvmrc, not .npmrc. Added ASF headers and ran RAT to confirm.
  • Do the ASF header comments break npm's ini parsing? Checked with npm config list in a repo directory: the value parses.
  • Does anything else write these paths and get clobbered? Grepped workflows, Dockerfiles and docker/*.sh for npmrc: only release.yml (root, excluded) and a comment in embedded-sdk-release.yml about deliberately not writing an auth line. Adding a non-auth .npmrc in superset-embedded-sdk should leave OIDC publishing alone, but this is the one path a real release exercises and this PR does not.
  • Did I leave anything untracked behind? Yes, initially: RAT flagged superset-frontend/test-report.html, an artifact of my own jest run, not part of the diff. Removed; re-ran RAT clean. The diff is five files, 130 added lines, no deletions.
  • ignore-scripts? Out of scope here. npm ci --dry-run in superset-frontend reports install scripts pending approval for nx, unrs-resolver and others; restricting them needs an allowlist and belongs in its own change, as the issue notes.

AI DISCLOSURE

This change was authored with AI assistance (Devin). The commit message carries a Generated-by: trailer per the ASF generative tooling guidance.

AUTHOR'S NOTE

Link to Devin session: https://app.devin.ai/sessions/4a3cb5aceb3d4d148c943e5c8aa61590
Requested by: @jethac

.github/dependabot.yml sets a 7-day cooldown for every ecosystem, but that
binds Dependabot only. The repository had no .npmrc, so a developer's or CI's
`npm install` could resolve a version published minutes ago.

Add an .npmrc setting `min-release-age=7` to each directory with its own
package-lock.json: superset-frontend, superset-frontend/cypress-base,
superset-websocket, superset-websocket/utils/client-ws-app and
superset-embedded-sdk. npm reads project config only from the directory it
runs in, so one file per install path is required.

`min-release-age` needs npm >= 11.10.0; superset-frontend and
superset-websocket already require npm ^11.13.0, which node 24.16.0 (the
version in every .nvmrc) ships. Older npm ignores the key without erroring.
Lockfile-pinned versions are unaffected, so `npm ci` is unchanged.

docs/ is excluded: it installs with yarn 1.22, which has no equivalent option.
The repository root is excluded: .github/workflows/release.yml writes its own
root .npmrc during publishing and would clobber it.

Generated-by: Devin (https://devin.ai)
@jethac jethac self-assigned this Aug 10, 2026
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@jethac

jethac commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Upstream issue filed: apache#42983

Verified against apache/superset@3539c41dab before filing, so the defect is confirmed present on upstream master.

Note: duplicate of #33 — both address the missing .npmrc cooldown.

@devin-ai-integration

Copy link
Copy Markdown
Author

Confirmed the overlap with #33 — same defect, different coverage. Concrete differences, so whoever picks a winner can do it on facts:

Directories with .npmrc:

directory has own package-lock.json #33 #48
superset-frontend yes yes yes
superset-frontend/cypress-base yes yes yes
superset-websocket yes yes yes
superset-websocket/utils/client-ws-app yes no (named as out of scope) yes
superset-embedded-sdk yes no (named as out of scope) yes
docs no — yarn.lock, packageManager: yarn@1.22.22 yes, with a comment stating yarn ignores it no

Other differences:

Suggestion, not a decision: keep #33 (it has the better verification), add .npmrc to superset-websocket/utils/client-ws-app and superset-embedded-sdk there, and confirm RAT passes on its four files. I'll close #48 on your word rather than merge two overlapping changes.

@jethac

jethac commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Superseded by #33.

Same min-release-age=7 .npmrc change as #33, which is non-draft and also covers docs/ and the docker npm ci fix. Caveat for the human: #48 covers superset-embedded-sdk and superset-websocket/utils/client-ws-app, which #33 misses — graft those two files onto #33 before closing this. The two also disagree on the required npm version (11.10.0 vs 11.13.0); worth checking which is right.

Closing in favour of #33. Reviewed together with the other open PRs on this fork; reopen if this was the wrong call.

@jethac jethac added the superseded Replaced by another PR that does the same work more completely label Aug 13, 2026
@jethac jethac closed this Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

embedded superseded Replaced by another PR that does the same work more completely

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant