Dev.to Security 🔐 Cybersecurity 👁 0 📖 4 min read

GHSA-W9X9-J56R-H98R: GHSA-w9x9-j56r-h98r: Unauthenticated Confused Deputy Proxy in @eeacms/volto-chatbot

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 conta

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-chatbot to version 2.0.4 or newer to enforce strict proxy endpoint allowlisting.
  • Restrict upstream service account privileges (DANSWER_API_KEY and LLMGW_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:

  1. Update package.json to reference @eeacms/volto-chatbot version ^2.0.4 and run yarn upgrade @eeacms/volto-chatbot@^2.0.4 or npm update @eeacms/volto-chatbot.
  2. Audit the Danswer backend logs for historical requests originating from the Volto proxy IP address targeting /api/admin/* or other non-chat routes.
  3. Rotate DANSWER_API_KEY, DANSWER_PASSWORD, and LLMGW_TOKEN if unauthorized access to upstream configuration endpoints is suspected.
  4. Reconfigure the Danswer API key used by Volto to use a non-administrative role.

References

Read the full report for GHSA-W9X9-J56R-H98R on our website for more details including interactive diagrams and full exploit analysis.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.