Missing authentication on MCP HTTP endpoint allows unauthenticated privileged tool calls
GHSA advisory for jovancoding/network-ai disclosing that its MCP HTTP transport (bin/mcp-server.ts, lib/mcp-transport-sse.ts) accepts JSON-RPC tools/call requests with zero authentication, session, or origin checks, and binds to 0.0.0.0 by default. The PoC shows an unauthenticated attacker can enumerate 22 registered tools and invoke privileged ones - config_get/config_set to read and mutate live orchestrator configuration, agent_list/agent_spawn/agent_stop, token_create/token_revoke, budget_set_ceiling, and blackboard_write/delete - with curl requests against a local Docker deployment. Remediation recommended enforcing bearer-token auth in _handlePost() before dispatch, defaulting to 127.0.0.1 binding, and gating state-mutating tools behind authorization checks.
What it is
GHSA-fj4g-2p96-q6m3 for jovancoding/network-ai: the MCP HTTP transport dispatches JSON-RPC tools/call requests straight to the orchestrator's tool registry with no authentication, session, origin, or token check, while binding to 0.0.0.0 by default. Anyone with network reach can enumerate and invoke privileged management tools.
How it works
The advisory pins the failure to specific lines:
bin/mcp-server.ts:75— binds0.0.0.0by default.lib/mcp-transport-sse.ts:379—_handlePost()parses the JSON-RPC body and callsthis._bridge.handleRPC(rpc)with no auth check.lib/mcp-transport-sse.ts:155—handleRPC()dispatchestools/calldirectly toprovider.call(toolName, toolArgs).lib/mcp-tools-control.ts:231—config_setmutates runtime config in place viathis._config[key] = parsed.
The exposed endpoints are /tools (inventory) and /mcp (JSON-RPC). config_get at lib/mcp-tools-control.ts:80 leaks live runtime config including auditLogPath, trustConfigPath, grantTokenTTL, and blackboardPath.
Preconditions / Affected
Any deployment reachable on a routable interface — the default 0.0.0.0 bind plus the missing auth gate is the whole exposure. PoC was run against a local Docker build bound to localhost:13001.
What to do
The advisory's own PoC doubles as a verification check. List tools:
curl http://localhost:13001/toolsRead and mutate live config unauthenticated:
curl http://localhost:13001/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"config_get","arguments":{}}}'
curl http://localhost:13001/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"config_set","arguments":{"key":"defaultTimeout","value":"12345"}}}'A 200 with "applied": true confirms the mutation persisted on the next read.
Remediation, in order: enforce a shared-secret/bearer token inside _handlePost() before handleRPC(); default bind to 127.0.0.1 and require explicit opt-in (warning on startup) for non-loopback binding without auth; as defense in depth, gate the state-mutating tools (config_set, agent_spawn, agent_stop, token_create, token_revoke, budget_set_ceiling, fsm_transition, blackboard_write, blackboard_delete) behind a verified caller identity.
Caveats
Testing was local-Docker only; no third-party deployment was tested, and the local build required a minor Dockerfile fix (the code path under test was unmodified). The agent_list PoC returned an empty array because the test environment had no agents — not because a control blocked it.
Technologies named here
- MCP HTTP/SSE transport
- Jovancoding/Network-AI
- curl
- Docker
- Model Context Protocol
- TypeScript
Techniques named here
- Missing Authentication for Critical Function
- Unauthenticated privileged tool enumeration and invocation
- Live configuration read/mutation
- Agent enumeration
Related in AI Security
