Security audit - 2026-07-27
- Reviewer: discord-mcp maintainers
- Scope: dependency advisories only. The manual code review shipped as part of the v0.13.0 correctness audit (token redaction, untrusted-wrap completeness, confirmation gates, audit redaction) is not re-litigated here.
- Supersedes: Security audit - 2026-05-01, whose “1 site-only moderate” figure was accurate on that date and had gone three months stale by this one.
The number that gates CI
Section titled “The number that gates CI”pnpm audit --audit-level=high --prod # exit 0| Severity | Count in the production tree |
|---|---|
| critical | 0 |
| high | 0 |
| moderate | 1 |
| low | 0 |
| total | 1 |
Before v0.13.0 this command reported 27 high advisories and exited 1.
The single remaining moderate is a path-traversal advisory in
@hono/node-server, reached via @modelcontextprotocol/sdk. It belongs to
the SDK’s Streamable-HTTP transport, which this project does not instantiate -
see reachability below.
What remediation actually consisted of
Section titled “What remediation actually consisted of”@opentelemetry/*bumped past the releases carrying the vulnerableprotobufjs/@grpc/grpc-jsranges, and@modelcontextprotocol/sdkbumped past the one carrying vulnerableajv→fast-uri.- Narrow
pnpm.overridesforwsandundici- pinned bydiscord.js/@discordjs/ws, and the only advisories that were on a default live code path.protobufjswas deliberately not blanket-overridden: forcing a major across the@grpc/grpc-jsrange is a wire-format risk for no benefit, and it sits behindOTEL_ENABLEDregardless. - Three OpenTelemetry packages that
mcp-corenever imports were dropped outright, and two more moved todevDependencies- the smallest production tree is the cheapest thing to audit. astro,@astrojs/starlightandsharpmoved fromdependenciestodevDependenciesin thesiteworkspace. The docs site isprivate: trueand is never published; its build tooling does not belong in anyone’s production tree.
The full tree, including dev tooling
Section titled “The full tree, including dev tooling”pnpm audit # 17 advisories| Severity | Count | Packages |
|---|---|---|
| critical | 2 | vitest |
| high | 5 | astro, brace-expansion, sharp |
| moderate | 6 | @hono/node-server, astro, turbo |
| low | 4 | astro, esbuild, turbo |
Every entry except @hono/node-server is build- and test-time only.
vitest, esbuild, turbo, astro, sharp and brace-expansion are
absent from both published tarballs. They are real advisories for a
contributor running this repo; they are not supply-chain exposure for anyone
installing @discord-mcp/cli.
CI gates on --prod for exactly that reason, and now runs weekly on a
schedule rather than only on push - the absence of that schedule is why three
months of drift went unnoticed.
Why the raw count overstates exposure
Section titled “Why the raw count overstates exposure”pnpm audit walks the dependency graph. It cannot tell that the vulnerable
code is never loaded by what discord-mcp actually ships.
Not reachable - the SDK’s HTTP transport, which this project does not ship
Section titled “Not reachable - the SDK’s HTTP transport, which this project does not ship”discord-mcp is stdio-only at v1.0.0 (see
v1.0.0 readiness).
@modelcontextprotocol/sdk bundles a Streamable-HTTP server transport whose
dependency subtree is installed but never imported by startStdio. The one
remaining production advisory lives entirely inside it.
Shipping an HTTP transport in a v1.x minor makes that subtree live. When that happens this analysis must be redone before the release, not after.
Not reachable by default - the OpenTelemetry export path
Section titled “Not reachable by default - the OpenTelemetry export path”OTEL_ENABLED defaults to false, and with it off mcp-server skips SDK
boot entirely, so the gRPC/protobuf exporter stack is never imported.
Operators who turn telemetry on should treat advisories in that subtree as
live and pin accordingly.
Reachable - and therefore fixed rather than explained away
Section titled “Reachable - and therefore fixed rather than explained away”undici is the HTTP client behind every Discord REST call and ws is the
WebSocket layer behind the optional Gateway. Both sit on a default or
near-default path, so both were overridden to patched ranges rather than
rationalised. Re-check both on every audit: a future advisory on either lands
squarely on live code.
Summary
Section titled “Summary”| Bucket | Live in a default stdio deployment? |
|---|---|
| SDK HTTP-transport subtree (1 moderate) | No - HTTP transport not shipped |
| OTLP exporter subtree | No - OTEL_ENABLED=false by default |
| Build and test tooling (16 advisories) | No - absent from both tarballs |
0 critical and 0 high in the production tree. 0 advisories with a demonstrated exploit path from Discord content, agent input, or operator config in the default stdio configuration.
Action items
Section titled “Action items”| Priority | Finding | Status |
|---|---|---|
| P0 | - | None |
| P1 | - | None |
| P2 | @hono/node-server path traversal via the SDK | Blocking for any future HTTP transport; inert today. Bump the SDK when a release ships @hono/node-server >= 2.0.5. |
| P2 | Build-tooling advisories (vitest, astro, turbo, sharp) | Not shipped. Bump on the normal dependency cadence; dependabot now watches the workflow actions and can be extended to npm. |
Reproducing
Section titled “Reproducing”pnpm install --frozen-lockfilepnpm audit --audit-level=high --prod # the CI gate - exit 0pnpm audit # full tree, including devpnpm why @hono/node-server # confirm the SDK HTTP-transport pathTwo adjacent supply-chain guards run in the same CI job:
pnpm exec publint --strict packages/mcp-corepnpm exec publint --strict packages/mcp-servernode .github/scripts/assert-no-workspace-protocol.mjsThe last one guards a different failure class entirely: pnpm rewrites the
workspace: protocol on pack, npm does not, and a published manifest
containing "@discord-mcp/core": "workspace:*" fails every consumer install
and cannot be repaired without a deprecate-and-republish.
Related
Section titled “Related”- Security audit - 2026-05-01 - the superseded point-in-time record, including the manual code review.
- Reference → Environment variables -
OTEL_ENABLEDdefaults tofalse. - Architecture → Confirmation - the destructive-operation gate.