GitHub Advisoryadvisory · npm
GHSA-hjwh-xvfw-qrwj SearXNG Basic Authentication Credentials Exposed Through MCP Logs and JSON-RPC Error Responses
MEDIUM
Official page ↗
- Ecosystem
- npm
- Packages
- mcp-searxng
- Severity
- Moderate
- CVSS vector
- CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
- Weaknesses
- CWE-209, CWE-532
- Reviewed
- 2026-08-19
Affected packages
• npm mcp-searxng: < 1.12.0 → fixed in 1.12.0
Description
Summary
mcp-searxng version 1.11.0 exposes SearXNG Basic Authentication credentials embedded in the `SEARXNG_URL` environment variable.
When the server starts in STDIO mode and an MCP client connects, the complete `SEARXNG_URL`, including its username and password, is sent to the client through an MCP `notifications/message` logging notification.
Additionally, when URL validation fails, the complete credential-bearing URL is included in the configuration error. This error is logged through MCP and returned to the client as a JSON-RPC error response.
For example, a value such as:
```text
http://username:[email protected]
```
is exposed without redaction.
A connected MCP client or anyone with access to captured server logs may recover the SearXNG credentials and use them to access the configured SearXNG instance.
The issue was confirmed in:
```text
mcp-searxng 1.11.0
```
Suggested severity: Medium
Details
mcp-searxng supports SearXNG Basic Authentication by embedding credentials in the URL userinfo component:
```text
https://username:[email protected]
```
The project contains a redaction function named `redactSearxngInstanceUrl()`, but it is not used in several logging and error-handling paths.
Startup console disclosure
In `src/index.ts:373-378`, the server retrieves the raw SearXNG URLs and writes them directly to stderr:
```typescript
const searxngInstances = getSearxngInstances();
if (searxngInstances.length > 0) {
console.error(`🌐 SearXNG URLs: ${searxngInstances.join("; ")}`);
}
```
`getSearxngInstances()` returns the unmodified environment-variable values.
Relevant code in `src/searxng-instances.ts:25-38`:
```typescript
export function parseSearxngUrls(
raw: string | undefined = process.env.SEARXNG_URL
): string[] {
if (raw === undefined) {
return [];
}
return raw
.split(";")
.map((entry) => entry.trim())
.filter((entry) => entry !== "");
}
export function getSearxngInstances(): string[] {
return parseSearxngUrls();
}
```
MCP logging notification disclosure
After the MCP client connects, `src/index.ts:388-393` sends the complete URL through the MCP logging interface:
```typescript
const searxngInstances = getSearxngInstances();
logMessage(
mcpServer,
"info",
`SearXNG URLs: ${
searxngInstances.length > 0
? searxngInstances.join("; ")
: "not configured"
}`
);
```
`logMessage()` passes this value to `sendLoggingMessage()` in `src/logging.ts:15-25`:
```typescript
mcpServer.sendLoggingMessage({
level,
data: notificationData
});
```
As a result, the connected MCP client receives a message containing the username and password:
```json
{
"method": "notifications/message",
"params": {
"level": "info",
"data": {
"message": "SearXNG URLs: http://username:[email protected]"
}
},
"jsonrpc": "2.0"
}
```
Configuration error disclosure
The URL validation function includes the complete unredacted value in error messages.
Relevant code in `src/searxng-instances.ts:44-52`:
```typescript
export function validateSearxngInstanceUrl(
value: string
): string | null {
try {
const url = new URL(value);
if (!["http:", "https:"].includes(url.protocol)) {
return `SEARXNG_URL invalid protocol for "${value}": ${url.protocol}`;
}
} catch {
return `SEARXNG_URL invalid format: ${value}`;
}
return null;
}
```
The validation error is aggregated by `validateEnvironment()` in `src/error-handler.ts:175-203`:
```typescript
const validationError =
validateSearxngInstanceUrl(searxngUrl);
if (validationError) {
issues.push(validationError);
}
```
The complete error is then thrown from `src/search.ts:689-693`:
```typescript
const validationError = validateEnvironment();
if (validationError) {
logMessage(mcpServer, "error", "Configuration invalid");
throw new MCPSearXNGError(validationError);
}
```
The tool handler in `src/index.ts:254-260` sends the error message and stack trace through MCP logging, then rethrows it:
```typescript
logMessage(
mcpServer,
"error",
`Tool execution error: ${
error instanceof Error
? error.message
: String(error)
}`,
{
tool: name,
args: args,
error:
error instanceof Error
? error.stack
: String(error)
}
);
throw error;
```
Rethrowing the error causes the same unredacted credential-bearing URL to be returned in the JSON-RPC error response.
Existing redaction function is not used
The project already contains a suitable redaction function in `src/searxng-instances.ts:57-69`:
```typescript
export function redactSearxngInstanceUrl(
raw: string
): string {
try {
const url = new URL(raw);
if (!url.username && !url.password) {
return raw;
}
url.username = "";
url.password = "";
return url.toString();
} catch {
return raw.replace(
/^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)[^/]*@/,
"$1"
);
}
}
```
However, this function is not applied before startup logging, MCP logging, or configuration error construction.
The MCP manifest also marks `SEARXNG_URL` as non-secret in `.mcp/server.json:20-25`:
```json
{
"name": "SEARXNG_URL",
"description": "URL of your SearXNG instance",
"isRequired": true,
"isSecret": false,
"format": "string"
}
```
Because credentials may be embedded in this variable, it should be classified as a secret.
PoC
The following proof of concept uses fake credentials. A real SearXNG server is not required.
Requirements
```text
Node.js 20 or newer
npm
mcp-searxng 1.11.0 source code
```
Build the application
```bash
unzip mcp-searxng-main.zip
cd mcp-searxng-main
npm ci
npm run build
```
Test 1: Credential disclosure through MCP logging
Create an MCP initialization request:
```bash
cat > /tmp/mcp-init.jsonl &1 | tee credential-log-leak.txt
```
Search the output for the credentials:
```bash
grep -nE \
'MCP_POC_USER_7391|MCP_POC_PASS_7391' \
credential-log-leak.txt
```
Observed result
The complete credential-bearing URL is exposed:
```text
SearXNG URLs: http://MCP_POC_USER_7391:[email protected]:9
```
It is also delivered to the MCP client:
```json
{
"method": "notifications/message",
"params": {
"level": "info",
"data": {
"message": "SearXNG URLs: http://MCP_POC_USER_7391:[email protected]:9"
}
},
"jsonrpc": "2.0"
}
```
This confirms that a connected MCP client can recover the configured username and password without accessing the host environment.
Test 2: Credential disclosure through JSON-RPC errors
Create initialization and tool-call requests:
```bash
cat > /tmp/mcp-error-poc.jsonl &1 | tee credential-error-leak.txt
```
Search the response:
```bash
grep -nE \
'MCP_POC_USER_7391|MCP_POC_PASS_7391' \
credential-error-leak.txt
```
Observed result
The complete URL is exposed in the MCP logging notification:
```text
Tool execution error: Configuration Issues: SEARXNG_URL invalid protocol for "ftp://MCP_POC_USER_7391:[email protected]…