Skip to content

feat(mcp-scan): apply npx dependency overrides during the security scan - #831

Merged
danbarr merged 3 commits into
mainfrom
feat/mcp-scan-npm-overrides
Aug 5, 2026
Merged

feat(mcp-scan): apply npx dependency overrides during the security scan#831
danbarr merged 3 commits into
mainfrom
feat/mcp-scan-npm-overrides

Conversation

@danbarr

@danbarr danbarr commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Stacked on #669. Addresses the npx half of @samuv's P1 review comment, which I had deferred there.

What

The scan runs the package directly rather than the built image, so it never saw spec.overrides and exercised a different dependency tree than the one that ships. #669 fixed this for uvx constraints; npx overrides were skipped with a logged note, because npm honors overrides only from a package.json it installs into and the scan has no project directory.

This stages a throwaway project containing the server package plus the overrides block, runs npm install in it, and runs the scanner with that directory as its working directory so npx resolves the installed tree. It also passes npx --no-install, which is load-bearing: without it npx may silently fetch an un-overridden copy and quietly void the whole thing.

Why I changed my mind on deferring

My deferral argument in #669 was that these overrides swap a vulnerable but working dependency for a patched one, so they change neither startup nor the tool surface. That holds for a correct override. It does not hold for a mistaken one.

While prototyping, a deliberately crude zod override broke context7's startup outright (ERR_PACKAGE_PATH_NOT_EXPORTED). So applying overrides in the scan makes it a check on the override itself: a startup-breaking override now fails the scan before the image publishes, instead of shipping a broken container. That is a better reason to do this than the vulnerability-parity argument, and it is the one I would not have found without building it.

Cost

Roughly 3 seconds of npm install, paid only by servers that declare overrides. Measured 3.0s for context7, 5.4s end to end for onchain-mcp including the scan.

Scope caveat worth recording

12 of 30 npx servers carry security.insecure_ignore: true because they need real credentials to start, and process_scan_results.py treats empty scan output as a warning and proceeds. For those, overrides have no observable effect on the scan no matter what we do here. That set includes brightdata-mcp, which is the #469 case #668 was opened for, plus mcp-server-neon, mcp-jetbrains, and browserbase-mcp-server.

So this is genuinely useful for scannable servers, and inert for the ones that need tokens. Documented in docs/adding-servers.md so the next person does not have to rediscover it.

Second commit

onchain-mcp is the first npx spec to carry overrides, which means CI actually exercises the new path rather than repeating #669's situation where the mechanism shipped with no spec using it.

It also fixes a real failure from #830: @bankless/onchain-mcp exact-pins the SDK at 1.7.0, carrying two high advisories with no reachable fix. I picked 1.26.0 over the minimum 1.25.2 because GHSA-345p-7cg4-v4c7 affects >=1.10.0,<=1.25.3, which 1.7.0 predates, so the smaller bump would have traded two advisories for a third.

Verification

  • npm install staging applies the override: SDK resolves to 1.26.0 from a pinned 1.7.0.
  • Scan passes with the override applied: exit 0, all 10 onchain-mcp tools enumerated, all SAFE.
  • grype --fail-on high --only-fixed on the built image: no high or critical findings, exit 0.
  • Regressions clean: uvx constraints path unchanged, and specs without overrides produce byte-identical scan config.

🤖 Generated with Claude Code

@toolhive-release-app

toolhive-release-app Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🔒 MCP Security Scan Results

✅ onchain-mcp

  • Status: Passed
  • Tools scanned: 10
  • Result: No security issues detected

Summary: Scanned 1 MCP server(s), all passed security checks. ✅

@danbarr

danbarr commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

CI came out as expected.

onchain-mcp is green across verify-provenance, mcp-security-scan, and build-containers, so the new npx override path is exercised in CI rather than only locally, and the SDK 1.26.0 override clears the Grype gate in the real pipeline.

The 10 remaining build-containers failures are pre-existing and unrelated to this PR, all catalogued in #830 (adb-mysql-mcp-server is #828). They show up here because the stack cumulatively touches cmd/dockhand/**, which triggers a full catalog rebuild. Previous run had 11; the one that flipped is the one this PR fixes.

@danbarr
danbarr requested a review from samuv August 5, 2026 17:45
samuv
samuv previously approved these changes Aug 5, 2026

@samuv samuv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good

stack merge was automatically disabled August 5, 2026 18:43

Pull Request is not mergeable

stack merge was automatically disabled August 5, 2026 18:44

Pull Request is not mergeable

Base automatically changed from feat/dep-overrides to main August 5, 2026 18:49
@danbarr
danbarr dismissed samuv’s stale review August 5, 2026 18:49

The merge-base changed after approval.

danbarr added 3 commits August 5, 2026 14:49
The scan runs the package directly rather than the built image, so it did
not see spec.overrides and exercised a different dependency tree than the
one that ships. uvx constraints were already reapplied; npx overrides were
skipped with a note, because npm honors "overrides" only from a
package.json it installs into and the scan has no project directory.

Stage a throwaway project containing the server package plus the overrides
block, npm install it, and run the scanner with that directory as its
working directory so npx resolves the installed tree. Pass npx
--no-install as well, so npx fails loudly instead of silently fetching an
un-overridden copy of the package.

Beyond matching what ships, this makes the scan a check on the override
itself: one that breaks the server's startup now fails the scan before the
image is published rather than producing a broken container.

Measured cost is roughly 3 seconds of npm install, paid only by servers
that declare overrides.
@bankless/onchain-mcp exact-pins the SDK at 1.7.0, which carries two high
advisories that fail the build-containers Grype gate. Neither fix is
reachable without overriding the pin.

Picked 1.26.0 rather than the minimum 1.25.2: GHSA-345p-7cg4-v4c7 affects
>=1.10.0,<=1.25.3, which 1.7.0 predates, so the smaller bump would have
traded two advisories for a third.

Verified: grype --fail-on high --only-fixed reports no high or critical
findings on the built image, and the server still enumerates all 10 tools
in the security scan. This is also the first npx spec to carry overrides,
so it exercises the scan path added in the previous commit.

Refs #830
The npm staging happens before the scanner's own try/finally, so a failure
there escaped as an uncaught traceback and left the temp project behind.
A runner without npm on PATH produced a raw FileNotFoundError, and an
install that hit the 300s timeout would have done the same.

Pre-check for npm and for the package fields, and clean up the temp
project on any staging failure.
@danbarr
danbarr force-pushed the feat/mcp-scan-npm-overrides branch from c398bee to d52fef2 Compare August 5, 2026 18:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants