Hi — this is not a vulnerability report, so I'm opening it in the open
rather than privately.
I wrote an open-source static scanner for MCP servers
(mcpaudit), ran it against this repo,
then read the source behind every hit. Full write-up below.
Happy to be told I got any of it wrong.
Result: nothing survived triage.
I scanned neondatabase/mcp-server-neon with my open-source static analyzer
mcpaudit, then read the source behind every hit. The scan produced four
findings; one turned out to be a bug in my own rule, and the other three are
false positives caused by a limitation I describe below. None of them are real.
What that does and does not mean: this is 14 lexical rule classes over the
JS/TS sources, no taint tracking, no execution. It says the obvious classes look
clean. It is not proof the codebase has no vulnerabilities, and I would not want
it cited as one.
This report is not a sales pitch. It is an honest account of what my tool missed and why. The value here is transparency about the limitations of single-file static analysis, not manufactured urgency.
The three findings, and why none of them are real
All three remaining findings were flagged as MCP008 (SSRF). They all stem from the same root cause: my analyzer cannot track constants across file boundaries.
1. mcp/neon-client.ts:154
export const fetchAsMcpServer: typeof fetch = (input, init) => {
const request = new Request(input, init);
request.headers.set('User-Agent', USER_AGENT);
return fetch(request);
};
Why it’s a false positive: this decorates a request the @neon/sdk has
already built, purely to attach a User-Agent. It does not choose a
destination — it forwards whatever it is handed. Flagging the forwarding call
puts the finding on the wrapper rather than on whatever decides the URL, which
is where an SSRF question would actually belong.
2. mcp/tools/handlers/docs.ts:12
import { NEON_DOCS_INDEX_URL } from '../../../lib/config';
const response = await fetch(NEON_DOCS_INDEX_URL, { ... });
Why it’s a false positive: NEON_DOCS_INDEX_URL is a constant imported from lib/config. My scanner has a "hoisted-literal" guard that treats bare identifiers as literals if they are assigned string literals in the same file. It does not resolve imports across files, so it sees an unknown identifier and flags it.
3. mcp/tools/handlers/docs.ts:51-52
const url = `${NEON_DOCS_BASE_URL}/${mdSlug}`;
const response = await fetch(url, { ... });
Why it’s a false positive: NEON_DOCS_BASE_URL is also an imported constant. The origin is fixed; only the path segment mdSlug is variable. This is a safe pattern (fixed origin + variable path), but my rule only recognizes it when the base URL appears as a literal string in the template. Since it’s an identifier, the rule doesn’t trigger.
The common cause: my analyzer is single-file
The root cause of all three false positives is that your code correctly extracts URLs into a config module. My analyzer is single-file and does not perform cross-file constant resolution.
This is a limitation of my tool, not a flaw in your code. Hoisting endpoints
into a config module is the better way to write it; my analyzer punishes you for
it because it reasons one file at a time and never follows the import. Resolving
imported constants is a real feature, not a regex I forgot to write, and I have
not built it.
A bug your code found in my scanner
The initial scan also flagged app/api/token/route.ts:556:
const [clientId, clientSecret] = credentials.split(':');
This is array destructuring, not a computed property assignment (which could lead to prototype pollution). My regex for detecting left-hand values incorrectly matched const [ because t is a word character. I have since updated the regex to exclude declaration keywords.
Method
- Tool:
mcpaudit (open-source static scanner)
- Scope: 82 files in
mcp-server-neon
- Technique: Pure static analysis. No taint tracking, no code execution, no network access.
- Limitations:
- Does not resolve constants across files.
- Does not track data flow across function boundaries.
- Relies on pattern matching, not semantic analysis.
Reproduction
You can reproduce this scan with:
npx allenwu-blip/mcpaudit ./mcp-server-neon
This was done voluntarily and without compensation. If you believe any of my judgments are incorrect, I welcome your feedback. The goal is to improve the tool, not to criticize the code.
Hi — this is not a vulnerability report, so I'm opening it in the open
rather than privately.
I wrote an open-source static scanner for MCP servers
(mcpaudit), ran it against this repo,
then read the source behind every hit. Full write-up below.
Happy to be told I got any of it wrong.
Result: nothing survived triage.
I scanned
neondatabase/mcp-server-neonwith my open-source static analyzermcpaudit, then read the source behind every hit. The scan produced fourfindings; one turned out to be a bug in my own rule, and the other three are
false positives caused by a limitation I describe below. None of them are real.
What that does and does not mean: this is 14 lexical rule classes over the
JS/TS sources, no taint tracking, no execution. It says the obvious classes look
clean. It is not proof the codebase has no vulnerabilities, and I would not want
it cited as one.
This report is not a sales pitch. It is an honest account of what my tool missed and why. The value here is transparency about the limitations of single-file static analysis, not manufactured urgency.
The three findings, and why none of them are real
All three remaining findings were flagged as
MCP008(SSRF). They all stem from the same root cause: my analyzer cannot track constants across file boundaries.1.
mcp/neon-client.ts:154Why it’s a false positive: this decorates a request the
@neon/sdkhasalready built, purely to attach a
User-Agent. It does not choose adestination — it forwards whatever it is handed. Flagging the forwarding call
puts the finding on the wrapper rather than on whatever decides the URL, which
is where an SSRF question would actually belong.
2.
mcp/tools/handlers/docs.ts:12Why it’s a false positive:
NEON_DOCS_INDEX_URLis a constant imported fromlib/config. My scanner has a "hoisted-literal" guard that treats bare identifiers as literals if they are assigned string literals in the same file. It does not resolve imports across files, so it sees an unknown identifier and flags it.3.
mcp/tools/handlers/docs.ts:51-52Why it’s a false positive:
NEON_DOCS_BASE_URLis also an imported constant. The origin is fixed; only the path segmentmdSlugis variable. This is a safe pattern (fixed origin + variable path), but my rule only recognizes it when the base URL appears as a literal string in the template. Since it’s an identifier, the rule doesn’t trigger.The common cause: my analyzer is single-file
The root cause of all three false positives is that your code correctly extracts URLs into a
configmodule. My analyzer is single-file and does not perform cross-file constant resolution.This is a limitation of my tool, not a flaw in your code. Hoisting endpoints
into a config module is the better way to write it; my analyzer punishes you for
it because it reasons one file at a time and never follows the import. Resolving
imported constants is a real feature, not a regex I forgot to write, and I have
not built it.
A bug your code found in my scanner
The initial scan also flagged
app/api/token/route.ts:556:This is array destructuring, not a computed property assignment (which could lead to prototype pollution). My regex for detecting left-hand values incorrectly matched
const [becausetis a word character. I have since updated the regex to exclude declaration keywords.Method
mcpaudit(open-source static scanner)mcp-server-neonReproduction
You can reproduce this scan with:
This was done voluntarily and without compensation. If you believe any of my judgments are incorrect, I welcome your feedback. The goal is to improve the tool, not to criticize the code.