# Pkgxray – inspect what gets installed, not what executes

> Source: <https://github.com/adamsjack711-ux/pkgxray>
> Published: 2026-07-22 12:31:02+00:00

**pkgxray — pre-install security for npm packages, MCP servers, and AI agents.**

Use local, zero-dependency package static analysis to inspect npm packages and
Model Context Protocol (MCP) servers before installation or connection. pkgxray
reports cited `SAFE`

, `REVIEW`

, or `BLOCK`

evidence without executing package
code during normal scans.

**Static analysis** · **Supply-chain intelligence** · **Prompt-injection detection** ·
**MCP security** · `SAFE`

/ `REVIEW`

/ `BLOCK`

Real runs: guard clears express@4.21.0, then blocks a sample modeled on
the 2024 @solana/web3.js compromise.

[▶ 60-second walkthrough](#demo)

```
npx --yes pkgxray@1.0.4 guard npm:express@4.21.0
```

This downloads pkgxray through npm's temporary `npx`

cache, stages the target
tarball in quarantine, and performs the static and supply-chain checks. It does
not globally install pkgxray, run `npm install`

, execute lifecycle scripts, or
execute package code.

```
Decision: **SAFE**
Grade: **A+** (99/100)

No high- or medium-risk indicators were found in the provided evidence.

Notes:
- **INFO npm-vs-github-clean** — npm tarball matches the linked GitHub repo
  at the published version. (15/16 files match GitHub @4.21.0)
…
```

Real output, abridged. A BLOCK verdict instead lists every finding with
the file and evidence that produced it.

Point it at a package, get a verdict with cited evidence — before a single
line of that package runs. `guard`

stages the package in a sandboxed
quarantine, audits the staged copy, and only promotes it when policy allows.
It never runs `npm install`

, lifecycle scripts, build steps, or package code.

| Verdict | Exit | Meaning |
|---|---|---|
`SAFE` |
`0` |
No high- or medium-risk indicators were found; default policy permits promotion. |
`REVIEW` |
`3` |
Evidence is incomplete or a privileged capability needs human review. |
`BLOCK` |
`2` |
High-severity cited evidence requires rejection or deep investigation. |

`SAFE`

is not a proof that a package is harmless; static analysis cannot see a
payload downloaded only at runtime. See the [threat model](/adamsjack711-ux/pkgxray/blob/main/docs/threat-model.md).

From a repository checkout:

```
npx --yes pkgxray@1.0.4 --file examples/onboarding-malicious.json --format markdown
```

The fixture contains inert source text that models a split-string SSH-key read
and network exfiltration. It is never executed. The command returns `BLOCK`

(exit `2`

) and cites the matching file and evidence.

[Scan pull requests and schedule dependency rechecks](/adamsjack711-ux/pkgxray/blob/main/docs/reference.md#monitoring-pkgxray-recheck).[Expose pkgxray's tools to an MCP-capable coding agent](/adamsjack711-ux/pkgxray/blob/main/docs/mcp.md#the-pkgxray-mcp-server).[Evaluate the experimental Hookshot install gate](/adamsjack711-ux/pkgxray/blob/main/examples/hookshot).

AI coding assistants install packages and connect to MCP servers at machine
speed, often without a human ever reading the code. Sonatype reported
**454,648 newly identified malicious open-source packages across monitored
ecosystems in 2025**. Its Q4 report counted 394,877 in that quarter and said
99.8% of Q4 malware originated from npm
([annual figure](https://www.infosecurity-magazine.com/news/454000-malicious-open-source/);
[Q4 scope](https://www.sonatype.com/blog/open-source-malware-index-q4-2025-automation-overwhelms-ecosystems)).
Traditional antivirus inspects what *executes*; **pkgxray inspects what gets
installed**.

`npm audit`

and OSV-Scanner answer an essential question — *does this package
have a known CVE?* — and pkgxray asks it too (via OSV, before anything
downloads). But a freshly trojaned package has no CVE yet, so pkgxray also
analyzes **trust**: what the code actually does, whether the published npm
artifact matches the tagged GitHub source, whether the provenance attestation
is consistent with the claimed repository, and whether the docs carry a
prompt-injection payload aimed at the agent reading them.

It is intentionally conservative: verdicts come from deterministic heuristics
(no LLM in the verdict path, so injected text can't steer them), only
citable evidence is reported, and the **zero-heuristic-false-block calibration
on the top-1000 most-downloaded packages** is
[regression-gated in CI](/adamsjack711-ux/pkgxray/blob/main/docs/benchmark.md). That claim is scoped to the
most-installed set — it is *not* a claim of zero false blocks on every package;
the newer MCP/agent-tooling ecosystem is over-blocked and being reconciled
per-case ([details](/adamsjack711-ux/pkgxray/blob/main/docs/benchmark.md#scope-of-the-claim-read-this-first)).

| Threat | Coverage | How pkgxray sees it |
|---|---|---|
| Credential theft | ✅ | reads of `.ssh` / `.aws` / `.npmrc` / `.env` / keychains / wallets, incl. split-fragment paths (`".s"+"sh"` ) |
| Prompt injection | ✅ | tiered detection in docs, comments, metadata; deterministic verdict path can't be steered |
| Unicode smuggling | ✅ | invisible tag-block characters + Trojan Source bidi / zero-width |
| Base64 payloads | ✅ | encoded envelopes in docs/comments; blobs decoded into computed-arg `eval` / `new Function` / `child_process` |
| Exfiltration & loaders | ✅ | cross-file correlation: stage-2 loaders, `curl | sh` , `process.env` harvesting near a network sink, EtherHiding |
| Persistence | ✅ | writes to shell rc files, cron, launch agents |
| Obfuscation | ✅ | packed blob + computed-arg execution; minification alone is deliberately not flagged |
| Known CVEs | ✅ | OSV batch pre-check before download; never mutable by config |
| Trojaned updates / maintainer takeover | ✅ | `recheck` verdict-drift + version-drift monitoring |
| Artifact divergence | ✅ | published npm tarball diffed against the tagged GitHub source |
| MCP capability abuse | ✅ | capability-surface mismatch in the manifest audit (a `get_weather` that also takes a `command` ) |
| Runtime tool drift | ✅ | `mcp-proxy` re-audits on `tools/list_changed` ; pinned-manifest drift is denied |
| Sequence-level tool-call chains | ◑ | `mcp-proxy` gates each call and scans results; no cross-call flow analysis —
|
| Dependency confusion / typosquats | ◑ | callback beacons, repo-mismatch and provenance-mismatch signals; no name-similarity heuristic |

✅ detected · ◑ partial / indirect

**Known blind spot:** pkgxray reasons about bytes in the tarball. A package
that downloads its real payload *after* install can ship a clean tree —
pkgxray flags the capability when its shape is unambiguous, but pair it with
runtime sandboxing when that risk matters. Full analysis:
[docs/threat-model.md](/adamsjack711-ux/pkgxray/blob/main/docs/threat-model.md).

**Continuous monitoring**—diffs installed deps against a stored verdict baseline and pre-vets newer versions`pkgxray recheck`

**MCP vetting**—`pkgxray mcp`

audits a server's tool manifest before you connect;`--pin`

/`--recheck`

catch the rug-pull;`pkgxray-mcp`

gives any agent the audit tools directly**Runtime gate**—wraps a live MCP server on the wire: denied tools stripped, ~0.05 µs per-call verdict, injection scan of tool results`pkgxray mcp-proxy`

**Install gate**— a[hookshot](https://github.com/CorridorSecurity/hookshot)hook runs`guard`

on every package an agent tries to install, across Claude Code, Cursor, Windsurf, Factory Droid, and Codex ()`examples/hookshot/`

**Policy engine**— one`.pkgxray.json`

read by every surface; tighten freely, every loosening is printed; CVEs can never be allowed away; fail closed**Opt-in behavioral canary**—runs lifecycle scripts in an OS sandbox with decoy credentials; it can`pkgxray canary`

*confirm*malice, never*clear*a package

| Verdict | Meaning | You should |
|---|---|---|
🟢 `SAFE` |
No high- or medium-risk indicators. | Install. Only `safe` promotes out of quarantine by default. |
🟡 `REVIEW` |
Incomplete evidence, or a privileged capability that needs a human. | Inspect the quarantined copy before promoting. |
🔴 `BLOCK` |
High-severity, cited evidence. | Do not install. Every finding names the file and evidence. |

Exit codes are stable and CI-friendly: ** 0** safe/allow ·

**block ·**

`2`

**review. The full signal-to-severity mapping is in the**

`3`

[severity policy](/adamsjack711-ux/pkgxray/blob/main/docs/reference.md#severity-policy-what-lands-in-block--review--info).

**Vet an npm package before installing**

```
pkgxray guard npm:some-package@1.2.3 [--format json]
pkgxray guard ./ext --promote-to ./approved/ext   # local dir, promote if policy allows
```

**Vet an MCP server before connecting** — full guide: [docs/mcp.md](/adamsjack711-ux/pkgxray/blob/main/docs/mcp.md)

```
pkgxray mcp --package npm:some-mcp-server@1.4.2 npx some-mcp-server
pkgxray mcp --recheck npx some-mcp-server   # catch the rug-pull
```

**Enforce in CI/CD**

```
pkgxray audit package-lock.json [--deep]    # also: yarn.lock, pnpm-lock.yaml, package.json
npx pkgxray recheck package-lock.json       # scheduled: exits non-zero only on a regression
```

A ready-made [GitHub Actions integration](/adamsjack711-ux/pkgxray/blob/main/docs/integrations/github-actions.md)
and the self-hostable cache server (`PKGXRAY_CACHE_URL`

) are documented in the
[reference](/adamsjack711-ux/pkgxray/blob/main/docs/reference.md#monitoring-pkgxray-recheck).

**Guard AI coding agents**

pkgxray is published on the [MCP Registry](https://registry.modelcontextprotocol.io)
as `io.github.adamsjack711-ux/pkgxray`

. Add it to any MCP client — locally
installed (`pkgxray-mcp`

) or zero-install via `npx`

:

```
{
  "mcpServers": {
    "pkgxray": {
      "command": "npx",
      "args": ["--yes", "--package", "pkgxray@1.0.4", "pkgxray-mcp"],
      "env": { "PKGXRAY_MCP_ALLOWED_ROOTS": "/absolute/path/to/project" }
    }
  }
}
```

The [MCP guide](/adamsjack711-ux/pkgxray/blob/main/docs/mcp.md#the-pkgxray-mcp-server) explains the operator-owned
filesystem boundary. Product-specific setup is in the
[coding-agent integration guide](/adamsjack711-ux/pkgxray/blob/main/docs/integrations/coding-agents.md). Gate
installs with the [Hookshot integration](/adamsjack711-ux/pkgxray/blob/main/examples/hookshot) and wrap MCP servers with
[ pkgxray mcp-proxy](/adamsjack711-ux/pkgxray/blob/main/docs/mcp.md#per-call-runtime-gate-pkgxray-mcp-proxy).

One optional `.pkgxray.json`

, read by every surface. Zero config means
maximum strictness.

```
{
  "policy": "safe-only",              // or "allow-review" (a loosening — warns)
  "failOn": "review",                 // CI exit threshold
  "scanErrorPolicy": "fail-closed",   // a scan that errors → review, never safe

  "allow": [
    { "pkg": "left-pad@1.3.0", "sha256": "e0b0…",
      "reason": "reviewed 2026-07", "expires": "2026-10-01" }
  ]
}
```

Precedence, `mute`

/ `mcp`

blocks, and enforced invariants:
[docs/configuration.md](/adamsjack711-ux/pkgxray/blob/main/docs/configuration.md) ·
`.pkgxray.example.json`

The 60-second walkthrough — the SAFE run, the blocked trojan with its exit code, then a lockfile audit:

## pkgxray-demo.mp4

All captures are real runs — reproduction steps in

`docs/screenshots/`

, which also shows the
MCP proxy, hookshot install gate, and browser extension in action.`npm audit`

and [OSV-Scanner](https://google.github.io/osv-scanner/) match
dependencies against known CVEs — a different question, answered well.
pkgxray is designed to run *alongside* them, not replace them (it queries OSV
itself, before anything downloads). The comparison that matters is against
tools in the same lane — behavioral supply-chain vetting:

| Capability | Socket.dev | OpenSSF Package Analysis | Cisco MCP Scanner | pkgxray |
|---|---|---|---|---|
| Fully local, zero-dependency, no account or cloud upload | — ¹ | ◑ ² | ◑ ³ | ✅ |
| Static behavior analysis of package code | ✅ | ✅ | ✅ | ✅ |
| Sandboxed execution (dynamic analysis) | — | ✅ ⁴ | ◑ (optional Docker) | ◑ (opt-in `canary` ) ⁴ |
| npm ↔ GitHub artifact divergence | unknown | — | — | ✅ |
| Deterministic verdict path — no LLM an injection can steer | — ⁵ | ✅ | ◑ ⁵ | ✅ |
| Pre-install gate with a quarantined copy to review | ◑ ⁶ | — | — | ✅ |
| MCP server vetting before connect | — ⁷ | — | ✅ | ✅ |
| Per-call runtime gating of live MCP traffic | — | — | — ⁸ | ✅ (`mcp-proxy` ) |
| Verdict-drift monitoring vs. a stored baseline | ✅ (cloud-side) | — | — | ✅ (local `recheck` ) |

Comparison made 2026-07-21 against each tool's public documentation;
unknown means not publicly documented — not verified either way.

¹ Socket's analysis runs in its cloud; Socket Firewall needs no account but consults Socket's hosted intelligence on every install. ² Open source and self-hostable, but built as a registry-scale analysis pipeline (Docker/gVisor), not an install-time developer gate. ³ The YARA analyzer runs locally; the LLM-as-judge and Cisco AI Defense analyzers require API keys. ⁴ Both detonate packages in an OS sandbox. pkgxray's opt-in

[runs two phases — install-time lifecycle scripts and the import of the package entry point — with decoy credentials, so the malicious-on-first-](/adamsjack711-ux/pkgxray/blob/main/docs/canary-threat-model.md)

`canary`

`require`

(flatmap-stream) shape that is
pkgxray's stated [blind spot](/adamsjack711-ux/pkgxray/blob/main/docs/threat-model.md#known-blind-spot)is triggered and observed. Egress is now kernel-confined on both platforms:

`sandbox-exec`

on macOS and, on Linux with `bubblewrap`

+ `iproute2`

, a private
network namespace (`bwrap+netns`

) where a raw-socket dial that bypasses the
proxy is refused by the kernel (`ENETUNREACH`

) while proxied egress is still
captured. That tier engages only after a runtime
[self-test proves it](/adamsjack711-ux/pkgxray/blob/main/docs/canary-threat-model.md#isolation-levels)in the environment (verify with

`node scripts/verify-netns-confinement.js`

); absent the
tooling it falls back to observe-only and says so. Still ◑ — not for a
confinement gap, but by design: canary is opt-in and confirm-only (it proves
malice, never clears a package) and detonates without the package's
dependencies installed, whereas OpenSSF Package Analysis runs registry-scale and
default-on. Run them as complements — pkgxray before install, full dynamic
analysis where that risk matters.
⁵ Socket's LLM-based code inspection is a headline feature
(“AI-detected potential malware”, human-confirmed); Cisco's YARA-only mode
is deterministic, its LLM analyzer is not.
⁶ Socket Firewall blocks risky packages at install time; it does not stage a
quarantined copy for human review.
⁷ Socket's MCP offering exposes its package-scoring API to agents; it does
not vet arbitrary MCP servers at connect time.
⁸ Cisco MCP Scanner is analysis-only per its docs — it does not proxy or gate
live MCP traffic.Acquisition (OSV pre-check → fetch) → sandboxed quarantine → static analysis → policy → verdict. The same engine backs every surface: CLI, MCP server, runtime proxy, install hook, browser extension, and CI cache server. Principles: never execute untrusted code · citable evidence only · minimize false positives · fail closed · zero runtime dependencies.

Details: [docs/architecture.md](/adamsjack711-ux/pkgxray/blob/main/docs/architecture.md) ·
[docs/design.md](/adamsjack711-ux/pkgxray/blob/main/docs/design.md)

**Local static analysis: ~25 ms**— a full guard of`express`

is ~1.3–1.5 s cold-cache, almost all network round-trips (Apple M1, Node 26)**Known-vulnerable packages block at the OSV pre-check**, before download** Calibration**(precision, recall, the 0-heuristic-false-block gate on the top-1000 most-downloaded —[scope](/adamsjack711-ux/pkgxray/blob/main/docs/benchmark.md#scope-of-the-claim-read-this-first)) is measured by a committed[benchmark corpus](/adamsjack711-ux/pkgxray/blob/main/benchmark)that fails CI when it regresses

Full numbers: [docs/reference.md#performance](/adamsjack711-ux/pkgxray/blob/main/docs/reference.md#performance) ·
methodology: [docs/benchmark.md](/adamsjack711-ux/pkgxray/blob/main/docs/benchmark.md)

| Doc | What it covers |
|---|---|
|

[threat-model.md](/adamsjack711-ux/pkgxray/blob/main/docs/threat-model.md)[mcp.md](/adamsjack711-ux/pkgxray/blob/main/docs/mcp.md)[mcp-registry.md](/adamsjack711-ux/pkgxray/blob/main/docs/mcp-registry.md)[configuration.md](/adamsjack711-ux/pkgxray/blob/main/docs/configuration.md)`.pkgxray.json`

schema and invariants[reference.md](/adamsjack711-ux/pkgxray/blob/main/docs/reference.md)`recheck`

, JSON output, cache server[benchmark.md](/adamsjack711-ux/pkgxray/blob/main/docs/benchmark.md)[compatibility.md](/adamsjack711-ux/pkgxray/blob/main/docs/compatibility.md)[json-schema.md](/adamsjack711-ux/pkgxray/blob/main/docs/json-schema.md)`--format json`

schemaStart at the [documentation index](/adamsjack711-ux/pkgxray/blob/main/docs/README.md). Longer-term plans:
[project status](/adamsjack711-ux/pkgxray/blob/main/docs/project-status.md), [adoption playbook](/adamsjack711-ux/pkgxray/blob/main/docs/adoption.md),
and GitHub issues.

```
npm test                 # zero-dep node --test suite
npm run benchmark        # calibration corpus: precision/recall + 0-false-block gate
npm run build:browser    # build the MV3 browser extension
```

Contributions are welcome; read [CONTRIBUTING.md](/adamsjack711-ux/pkgxray/blob/main/CONTRIBUTING.md) and the
[Code of Conduct](/adamsjack711-ux/pkgxray/blob/main/CODE_OF_CONDUCT.md) before opening a pull request.

Releases are published to npm with provenance (SLSA attestation), gated on the
test suite, the calibration benchmark, and pkgxray's own supply-chain guard.
To report a vulnerability in pkgxray itself, see [SECURITY.md](/adamsjack711-ux/pkgxray/blob/main/SECURITY.md).
