@eeacms/volto-chatbot 2.0.3 及之前版本存在未經驗證的代理漏洞
GHSA-W9X9-J56R-H98R: GHSA-w9x9-j56r-h98r: Unauthenticated Confused Deputy Proxy in @eeacms/volto-chatbot
@eeacms/volto-chatbot 2.0.3 及之前版本存在未經驗證的混淆代理漏洞,攻擊者可利用通配符代理路由,借用伺服器端憑證呼叫 Danswer 和 Halloumi 後端的管理 API。漏洞 CVSS v3.1 評分為 8.2,已有公開概念驗證程式;2.0.4 版本加入路徑允許清單修補漏洞。
GHSA-w9x9-j56r-h98r: Unauthenticated Confused Deputy Proxy in @eeacms/volto-chatbot
Vulnerability ID: GHSA-W9X9-J56R-H98R
CVSS Score: 8.2
Published: 2026-10-29
The @eeacms/volto-chatbot package for Plone Volto contains an unauthenticated confused deputy vulnerability (CWE-862) in versions 2.0.3 and earlier. The package registers Express middleware on wildcard proxy routes (**/_v1_da/** and **/_v1_ha/**) without verifying client authentication or restricting target endpoints. When processing incoming HTTP requests, the middleware automatically attaches server-side administrative credentials before forwarding the requests to upstream Danswer (Onyx) or Halloumi (LLMGW) API servers. This allows unauthenticated network attackers to execute arbitrary administrative API calls against the backend infrastructure.
TL;DR
Versions 2.0.3 and earlier of @eeacms/volto-chatbot expose unauthenticated wildcard proxy routes that automatically attach server-side API keys to outgoing requests, allowing unauthenticated attackers to invoke arbitrary administrative endpoints on upstream Danswer and Halloumi backends.
⚠️ Exploit Status: POC
Technical Details
- Advisory ID: GHSA-w9x9-j56r-h98r
- Weakness Class: CWE-862 (Missing Authorization / Confused Deputy)
- CVSS v3.1 Score: 8.2 High (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N)
- Attack Vector: Network (Unauthenticated HTTP requests to /_v1_da/** or /_v1_ha/**)
- Affected Package: @eeacms/volto-chatbot (<= 2.0.3)
- Patched Version: 2.0.4
- Exploit Status: Public Proof-of-Concept Available
- CISA KEV Status: Not Listed
Affected Systems
- @eeacms/volto-chatbot
- Plone Volto deployments using @eeacms/volto-chatbot
-
@eeacms/volto-chatbot: <= 2.0.3 (Fixed in:
2.0.4)
Code Analysis
Commit: a459b36
Introduce ALLOWED_PROXY_PATHS and ALLOWED_HALLOUMI_PATHS allowlists and isPathAllowed check to prevent Confused Deputy attacks.
diff --git a/src/halloumi/middleware.js b/src/halloumi/middleware.js
index 4579a3e2..7c999108 100644
--- a/src/halloumi/middleware.js
+++ b/src/halloumi/middleware.js
@@ -1,11 +1,19 @@
import debug from 'debug';
import { getVerifyClaimResponse } from './generative';
+import { isPathAllowed } from '../middleware';
const log = debug('halloumi');
const MSG_INVALID_CONFIGURATION =
'Invalid configuration: missing LLMGW_TOKEN or LLMGW_URL';
+// Allowed paths for _v1_ha proxy.
+// When adding new endpoints, update this list.
+const ALLOWED_HALLOUMI_PATHS = [
+ { path: '/generate', methods: ['POST'] },
+ { path: '/classify', methods: ['POST'] },
+];
+
const LLMGW_URL = process.env.LLMGW_URL;
const LLMGW_TOKEN = process.env.LLMGW_TOKEN;
@@ -32,6 +40,14 @@ const classifyModel = {
export default async function middleware(req, res, next) {
const path = req.url.replace('/_v1_ha/', '/');
+ // Reject paths not on the allowlist — prevents Confused Deputy attacks
+ if (!isPathAllowed(path, req.method, ALLOWED_HALLOUMI_PATHS)) {
+ res.statusCode = 404;
+ res.statusMessage = 'Not Found';
+ res.send({ error: 'Not Found' });
+ return;
+ }
+
if (!(LLMGW_TOKEN && LLMGW_URL)) {
res.send({
error: MSG_INVALID_CONFIGURATION,
diff --git a/src/middleware.js b/src/middleware.js
index 32cdb2a2..b2ae29f0 100644
--- a/src/middleware.js
+++ b/src/middleware.js
@@ -8,6 +8,30 @@ const log = debug('volto-chatbot');
const MOCK_STREAM_DELAY = parseInt(process.env.MOCK_STREAM_DELAY || '0');
+// Allowed paths for _v1_da proxy.
+// When adding new endpoints, update this list.
+const ALLOWED_PROXY_PATHS = [
+ { path: '/persona', methods: ['GET'] },
+ { pathPattern: /^\/persona\/\d+$/, methods: ['GET'] },
+ { path: '/chat/create-chat-session', methods: ['POST'] },
+ { path: '/chat/send-message', methods: ['POST'] },
+ { path: '/chat/create-chat-message-feedback', methods: ['POST'] },
+];
+
+/**
+ * Check whether a stripped path matches the allowlist.
+ * Strips query strings before comparison.
+ */
+function isPathAllowed(strippedPath, method, allowedPaths) {
+ const cleanPath = strippedPath.split('?')[0];
+ return allowedPaths.some(
+ (entry) =>
+ (entry.path === cleanPath ||
+ (entry.pathPattern && entry.pathPattern.test(cleanPath))) &&
+ entry.methods.includes(method),
+ );
+}
+
let cached_auth_cookie = null;
let last_fetched = null;
let maxAge;
@@ -224,9 +248,18 @@ async function send_danswer_request(
}
}
+export { isPathAllowed, ALLOWED_PROXY_PATHS };
export default async function middleware(req, res, next) {
const path = req.url.replace('/_v1_da/', '/');
+ // Reject paths not on the allowlist — prevents Confused Deputy attacks
+ if (!isPathAllowed(path, req.method, ALLOWED_PROXY_PATHS)) {
+ res.statusCode = 404;
+ res.statusMessage = 'Not Found';
+ res.send({ error: 'Not Found' });
+ return;
+ }
+
const reqUrl = `${process.env.DANSWER_URL || ''}/api${path}`;
const username = process.env.DANSWER_USERNAME;
Commit: eaf18b7
Release 2.0.4 changelog commit.
Exploit Details
- GitHub Security Advisory: Official advisory containing both cURL reproduction commands and a complete Python proof-of-concept script (poc.py).
Mitigation Strategies
- Upgrade
@eeacms/volto-chatbotto version 2.0.4 or newer to enforce strict proxy endpoint allowlisting. - Restrict upstream service account privileges (
DANSWER_API_KEYandLLMGW_TOKEN) to the minimum role required for public chat operations. - Implement reverse proxy or WAF path filtering rules to block access to administrative sub-paths under
/_v1_da/and/_v1_ha/.
Remediation Steps:
- Update
package.jsonto reference@eeacms/volto-chatbotversion^2.0.4and runyarn upgrade @eeacms/volto-chatbot@^2.0.4ornpm update @eeacms/volto-chatbot. - Audit the Danswer backend logs for historical requests originating from the Volto proxy IP address targeting
/api/admin/*or other non-chat routes. - Rotate
DANSWER_API_KEY,DANSWER_PASSWORD, andLLMGW_TOKENif unauthorized access to upstream configuration endpoints is suspected. - Reconfigure the Danswer API key used by Volto to use a non-administrative role.
References
- GitHub Advisory Database: GHSA-w9x9-j56r-h98r
- Repository Security Advisory: GHSA-w9x9-j56r-h98r
- Vendor Fix Commit a459b36dbd2c67e6397cc81910e27b9d611cca18
- Release Tag 2.0.4
- Release 2.0.4 Changelog Commit
- GitHub Repository: eea/volto-chatbot
Read the full report for GHSA-W9X9-J56R-H98R on our website for more details including interactive diagrams and full exploit analysis.
來源:DEV Community · dev.to