Commit 61d78da
feat(scanner): fetch published npx/uvx package source for scanning (MCP-2206) (#658)
* feat(scanner): fetch published npx/uvx package source for scanning (MCP-2206)
Package-runner servers (npx/uvx) are the primary quarantine/scan target,
but a server quarantined on add is never run locally, so the local package
cache misses and the scan degraded to tool_definitions_only — no real
source-level analysis for exactly the untrusted code that most needs it.
Add a resolution fallback that fetches the PUBLISHED package source without
executing it, wired into both Pass 1 (Resolve) and Pass 2 (ResolveFullSource):
- npm (npx): `npm pack <spec> --ignore-scripts` downloads the published
tarball with no lifecycle scripts; extracted as source_method=npm_pack.
- PyPI (uvx): `uv pip download --no-deps` (fallback `pip download`) fetches
the wheel (preferred) or sdist; unpacked without building/setup.py as
source_method=pip_download.
Security: a scanner must never execute the code it scans. The fetch only
downloads + unpacks archives. Extraction is hardened against path traversal
(zip-slip), symlink escape, and decompression bombs (bounded file count and
total size). On missing toolchain / offline / fetch failure it falls through
to tool_definitions_only with no regression.
Enabled by default; air-gapped deployments can disable network egress via
security.scanner_fetch_package_source=false.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* docs(oas): regenerate OpenAPI for scanner_fetch_package_source field (MCP-2206)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(security): force --only-binary on python package fetch — never build/execute sdist (MCP-2391)
A plain 'pip download' / 'uv pip download' of an sdist invokes the
package's PEP 517 build backend (setup.py egg_info) to resolve metadata,
which executes code from the package being scanned — violating the
download-and-unpack-NEVER-execute invariant of the source fetch.
- Add --only-binary=:all: to uvDownloadArgs and pipDownloadArgs so only a
prebuilt wheel is ever fetched; a package with no wheel fails the fetch
and falls back to tool-definitions-only.
- Refuse a non-wheel archive in fetchPythonPackage (defense-in-depth);
drop the sdist extraction branch.
- Add positive tests asserting --only-binary=:all: on both Python builders
(the prior NoExecution tests only asserted absence of verbs).
- Align comments + docs (security-scanner-plugins.md, swagger.yaml).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>1 parent 9f4930c commit 61d78da
10 files changed
Lines changed: 891 additions & 4 deletions
File tree
- docs/features
- internal
- config
- security/scanner
- server
- oas
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
241 | 241 | | |
242 | 242 | | |
243 | 243 | | |
244 | | - | |
| 244 | + | |
| 245 | + | |
245 | 246 | | |
246 | 247 | | |
247 | | - | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
248 | 262 | | |
249 | 263 | | |
250 | 264 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1768 | 1768 | | |
1769 | 1769 | | |
1770 | 1770 | | |
| 1771 | + | |
| 1772 | + | |
| 1773 | + | |
| 1774 | + | |
| 1775 | + | |
| 1776 | + | |
| 1777 | + | |
| 1778 | + | |
| 1779 | + | |
| 1780 | + | |
| 1781 | + | |
| 1782 | + | |
| 1783 | + | |
| 1784 | + | |
| 1785 | + | |
| 1786 | + | |
| 1787 | + | |
| 1788 | + | |
| 1789 | + | |
| 1790 | + | |
| 1791 | + | |
| 1792 | + | |
1771 | 1793 | | |
0 commit comments