[GH-ISSUE #5758] Embed widgets answer requests from any origin by default (no deny-by-default for empty allowlist_domains) #5278

Closed
opened 2026-06-05 14:53:05 -04:00 by yindo · 0 comments
Owner

Originally created by @dmitrymaranik on GitHub (Jun 4, 2026).
Original GitHub issue: https://github.com/Mintplex-Labs/anything-llm/issues/5758

What happened?

When a chat embed is created without allowlist_domains, the value defaults to null. In server/models/embedConfig.js, parseAllowedHosts() returns null for an empty/missing allowlist, and in server/utils/middleware/embedMiddleware.js (canRespond) the origin check is skipped when allowedHosts === null:

const allowedHosts = EmbedConfig.parseAllowedHosts(embed);
if (allowedHosts !== null && !allowedHosts.includes(host)) { /* reject */ }

So an embed with no allowlist answers /api/embed/{uuid}/stream-chat from any origin. An operator who enables an embed for a single page unknowingly exposes that workspace's content to requests from every origin, and there's no warning at creation time and no way to make 'no allowlist' mean deny-all.

To be clear: public embeds are intentional and auth-less by design — this is not a request to require authentication. It's that the secure default and operator awareness are missing.

Proposed solution (additive, non-breaking)

  1. An opt-in server flag EMBED_REQUIRE_ALLOWLIST=true that makes the embed middleware reject requests to an embed whose allowlist is empty/null (deny-by-default). Unset preserves today's behavior.
  2. A creation-time console.warn when an embed is created with no allowlist.
  3. An .env.example entry documenting the flag.

I've opened a small PR implementing exactly this.

Disclosure

This was surfaced while testing a self-hosted AnythingLLM instance with an automated multi-tenant-isolation checker (Sectum AI); this report was drafted with AI assistance.

Originally created by @dmitrymaranik on GitHub (Jun 4, 2026). Original GitHub issue: https://github.com/Mintplex-Labs/anything-llm/issues/5758 ### What happened? When a chat embed is created without `allowlist_domains`, the value defaults to `null`. In `server/models/embedConfig.js`, `parseAllowedHosts()` returns `null` for an empty/missing allowlist, and in `server/utils/middleware/embedMiddleware.js` (`canRespond`) the origin check is **skipped** when `allowedHosts === null`: ```js const allowedHosts = EmbedConfig.parseAllowedHosts(embed); if (allowedHosts !== null && !allowedHosts.includes(host)) { /* reject */ } ``` So an embed with no allowlist answers `/api/embed/{uuid}/stream-chat` from **any** origin. An operator who enables an embed for a single page unknowingly exposes that workspace's content to requests from every origin, and there's no warning at creation time and no way to make 'no allowlist' mean deny-all. To be clear: public embeds are intentional and auth-less by design — this is **not** a request to require authentication. It's that the *secure default* and *operator awareness* are missing. ### Proposed solution (additive, non-breaking) 1. An opt-in server flag `EMBED_REQUIRE_ALLOWLIST=true` that makes the embed middleware reject requests to an embed whose allowlist is empty/null (deny-by-default). Unset preserves today's behavior. 2. A creation-time `console.warn` when an embed is created with no allowlist. 3. An `.env.example` entry documenting the flag. I've opened a small PR implementing exactly this. ### Disclosure This was surfaced while testing a self-hosted AnythingLLM instance with an automated multi-tenant-isolation checker ([Sectum AI](https://github.com/sectum-ai/sectum-ai)); this report was drafted with AI assistance.
yindo closed this issue 2026-06-07 16:36:38 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Mintplex-Labs/anything-llm#5278