Two MCP Command Injections Got CVEs and a GHSA Each — and Neither Will Ever Show Up in npm audit

On 30 September 2026, NVD published two OS command injection CVEs in Model Context Protocol servers within seventy-three minutes of each other. GitHub minted advisories for both the same morning. Both are real, both are reproducible from the public issue reports, and one carries a CVSS v3.1 base score of 9.9.

Both are also, as of this writing, functionally invisible to the tooling most teams rely on to find exactly this class of problem. We queried OSV for the npm packages these projects publish under and got zero results for both. A developer running npm audit against either dependency today gets a clean report.

The two bugs, briefly

CVE-2026-102911 affects zosmaai/pi-llm-wiki (598 GitHub stars), a self-maintaining knowledge-base extension for the pi agent. The wiki_capture_source MCP tool accepts a url argument and interpolates it into a shell command string. NVD records CWE-77 and CWE-78, CVSS v3.1 9.9 (Critical) and CVSS v4.0 8.6 (High), with exploit maturity E:P — proof-of-concept public. Affected: up to and including 0.11.7; fixed in 0.11.8.

CVE-2026-102906 affects 0xshariq/github-mcp-server, a much smaller project (10 stars) exposing 29 Git operations as MCP tools. The git_remove tool embeds an attacker-controlled filename into a string passed to child_process.exec. CVSS v3.1 6.3 (Medium), v4.0 2.1 (Low). NVD notes the project "implements a rolling release," so no version boundary exists, and records that the maintainer was notified through an issue report and has not responded. That issue, filed 31 August 2026, is still open. The repository's last commit is dated 25 March 2026.

We read the code, and the interesting part is the guard that exists

The pi-llm-wiki bug is the textbook shape. We pulled the file at the commit the reporter cited. In extensions/llm-wiki/lib/source-extractors.ts, the extractor invoked sh with:

["-c", `uvx --from 'markitdown[docx,pdf]' markitdown "${source}" 2>/dev/null || echo ""`]

That is a URL, supplied through an MCP tool call, interpolated inside double quotes inside a sh -c string. The reporter demonstrated it by launching macOS Calculator. The fix is the correct one — drop the shell entirely and pass argv directly — and it shipped with 187 lines of new security regression tests plus a CodeQL workflow. Good response.

The github-mcp-server case is more instructive, because the author did write a sanitiser. sanitizeInput() strips shell metacharacters and collapses .. sequences. executeGitCommand() refuses anything not starting with git and caps command length. There is a comment reading "Sanitize command to prevent injection attacks."

None of it applies to the vulnerable path. Counting occurrences in src/github.ts at the commit NVD names: 26 call sites build a command via template literal; 6 of them call sanitizeInput first. gitRemove is one of the twenty that do not — it composes git reset HEAD "${file}" and hands it straight to execAsync. The git prefix check passes, because the command genuinely does start with git. The length cap passes. The sanitiser is never reached.

This is the failure mode we keep seeing in MCP servers specifically, and it is not ignorance of the bug class. It is a defence applied per-call-site in a file with twenty-six call sites. The same structural pattern produced the code-ollama grep_search injection, where a tool classified as read-only executed shell, and it is why Backslash found a risk finding in 29% of the top 8,000 MCP servers.

The advisory gap is the real story

GitHub issued GHSA-jcgm-7m3g-h3cx for the pi-llm-wiki flaw at 06:31 UTC and GHSA-7v7v-c4gg-gf73 for github-mcp-server at 03:31 UTC on 30 September 2026. We pulled both through GitHub's advisory API. Both have type: "unreviewed" and — the part that matters — an empty vulnerabilities array. No package name. No ecosystem. No affected version range.

An unreviewed GHSA is an automatic CVE mirror. It has not been through GitHub's curation step, which is where a human maps the free-text advisory onto a concrete package coordinate. Until that mapping exists, the advisory is a document, not a dependency check.

We verified the downstream consequence directly against OSV:

  • @zosmaai/pi-llm-wiki@0.11.7 — the exact affected version — returns 0 vulnerabilities.
  • @0xshariq/github-mcp-server@2.5.0, the current npm release of an unmaintained and unpatched project, returns 0 vulnerabilities.
  • As a control on the same query in the same session, lodash@4.17.15 returns 6.

The query path works. There is simply nothing to match. OSV, and therefore npm audit, Dependabot alerts, and every scanner that consumes those feeds, cannot connect "zosmaai pi-llm-wiki up to 0.11.7" — a vendor-and-product string in prose — to @zosmaai/pi-llm-wiki on npm. Nobody has told it they are the same thing.

Why this hits MCP servers harder than it hits ordinary npm packages

The gap is not unique to AI tooling. What is unusual is the density of MCP projects landing in it, and there are structural reasons.

  • The CNA is a bulk vulnerability database, not the vendor. Both CVEs were assigned by VulDB from public issue reports. VulDB publishes vendor/product strings; it is not positioned to assert npm coordinates, and the projects themselves are not CNAs and did not file.
  • Repository identity and package identity diverge. The CVE names zosmaai pi-llm-wiki. The install target is @zosmaai/pi-llm-wiki. For github-mcp-server the CVE identifies the affected version by a 40-character commit SHA, because the project ships rolling releases — a commit hash has no expressible relationship to a semver range at all.
  • MCP servers are frequently installed outside the dependency graph. A server added to an agent host config, run via npx, or installed through an agent's own extension mechanism is often not in any package.json a scanner will ever read. Even a perfectly mapped advisory would miss it.

That third point deserves weight. We have written repeatedly about MCP servers occupying a privileged position — gateways that aggregate blast radius, native hosts that drive an authenticated browser. The governance gap OX measured across 15,465 servers has a concrete mechanical expression here: the component with the most dangerous capability in the stack is the one your SCA tool is least likely to know you have installed.

A fix that shipped 29 days before anyone could look it up

The pi-llm-wiki timeline is worth laying out, because it inverts the usual complaint.

  • 29 August 2026 — 0.11.7 published to npm.
  • 31 August 2026 — issue #185 filed publicly, with full data-flow trace, suggested CVSS 9.8, and a working PoC.
  • 1 September 2026 — fix merged (PR #186) and 0.11.8 released the same day. Roughly 29 hours from report to shipped patch.
  • 30 September 2026 — CVE-2026-102911 published. GHSA follows hours later.

The maintainer did everything right and did it in a day. But the fix reached users as a single line in the v0.11.8 release notes — "fix(source-extractors): prevent OS command injection via sh -c (CWE-78) #185" — sitting among twelve dependabot version bumps and a reverted search filter. There was no advisory, no severity label, no scanner signal. Anyone who skimmed that changelog saw a routine maintenance release.

Four further releases have shipped since; npm's current latest is 0.12.4. Most users have long since moved past the problem without ever knowing it existed. That is a fine outcome for them and a bad one for everybody assessing risk, because the thirty-day window in which 0.11.7 was pinned, publicly exploitable, and completely unflagged is exactly the window an attacker reading GitHub issues had to themselves.

What to actually do

  • Upgrade pi-llm-wiki to 0.11.8 or later. 0.12.4 is current. If you pin MCP extension versions — and you should — check the pin, not the changelog.
  • Remove 0xshariq/github-mcp-server or stop exposing git_remove. There is no patch, the maintainer has not responded to a month-old report, and the repository has been static since March. This is not a wait-for-a-fix situation. Note there is also a widely used, actively maintained official GitHub MCP server; confirm which one you actually have installed before acting.
  • Inventory MCP servers separately from your dependency graph. Enumerate what is in your agent host configs and extension directories and diff it against what your SCA tool believes is installed. The delta is your unmonitored surface, and for most teams it is not small.
  • Do not read a clean npm audit as an absence of known vulnerabilities in this ecosystem. For MCP servers specifically, treat it as an absence of mapped vulnerabilities. Query NVD and the GitHub advisory database by vendor and product name for the servers you run.
  • Watch the issue tracker, not just the release feed. Both of these CVEs existed as detailed public issues — with PoCs — for a month before an identifier was assigned. The highest-signal source ran a month ahead of the feeds.
  • Maintainers: request the package mapping. If a CVE lands against your project, a curated GHSA with a correct ecosystem and version range is what converts it into an alert your users will actually see. Unreviewed advisories do not do that work.

The pattern worth naming

The vulnerability disclosure pipeline assumes a chain: vendor knows their product, CNA records it, GHSA maps it to a package, scanner alerts the user. Bulk CNAs filing against small open-source projects from public issue reports break the third link, and without it the first two links produce documents nobody reads.

None of this is new. What is new is the volume of AI agent tooling flowing through it — projects that are young, fast-moving, published under scoped npm names that do not match their repository names, installed by mechanisms that bypass dependency manifests entirely, and holding shell, filesystem, and credential access on behalf of an agent. A 9.9 that no scanner can see is worse than a 7.5 that every scanner reports, and right now the AI tooling ecosystem is producing the former at a rate the mapping process is not keeping up with.

Sources: