Auto-PR: feat(dashboard): embed can no longer answer every website; customer owns the allowlist#123
Merged
Merged
Conversation
…wns the allowlist The public chat widget is the customer's brand and their capped LLM budget, but AnythingLLM treats an embed with no allowlist as "allow every origin" — parseAllowedHosts returns null and canRespond skips the origin check entirely. Three changes: EMBED_REQUIRE_ALLOWLIST="true" in compose.prod flips no-allowlist to deny-all, so the failure mode becomes a visibly dead widget instead of a stranger quietly spending the customer's budget. Upstream #5759 plus its 2026-06-17 inverted-logic fix; older images ignore the variable harmlessly, which is why the explicit allowlist set by provisioning remains the real protection. The dashboard gains an "Authorized websites" panel, so a customer adding a second site never needs an operator. Entries are normalised to scheme://host[:port] — lower-cased, path and trailing slash stripped, wildcards refused — because ALLM compares the browser Origin byte-for-byte, and the payload goes out as the comma-separated string ALLM actually parses. The instance's own origin is always retained, so the list can never be emptied back into the allow-any state. The list persists in a new dashboard_state volume; provisioning seeds it via EMBED_ALLOWLIST_DOMAINS. The embed snippet now carries data-greeting with the AI disclaimer and data-no-sponsor, so the PRD's non-negotiable disclosure is stated the moment the widget opens rather than depending on the model remembering to say it. Also fixes the /app/healthz false warning: the public health route was matched before the base path was stripped, so it only ever answered the container's internal /healthz — while Caddy routes only /app* to the dashboard, leaving the public probe to fall through to the auth gate and return 401. Verification: 16-check live test against a running dashboard + stub AnythingLLM covering the public health probe, auth and CSRF gates, wildcard rejection, case/path normalisation, self-origin retention, the empty-submission floor, state-file persistence, the outgoing payload shape and the snippet contents; node --check on the server and the dashboard's inline script; copier render + YAML parse of the compose template; authed UI rendered and eyeballed.
The flag landed 2026-06-06 with its condition backwards and was fixed 2026-06-17, so on v1.14.0/v1.14.1 leaving it UNSET makes a no-allowlist embed refuse every origin, and SETTING it opens the embed to all of them — the exact opposite of the intent. v1.15.0 (2026-06-25) is the first release with the correct logic. Records which versions the flag can be trusted on.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 Automated Pull Request — authored by
weown-bot(ecosystem service account)Opened by: @ncimino
Last pushed by: @ncimino
Branch:
feature/nik-embed-lockdown→mainContributors on this branch:
📋 Human Review Checklist — NIST CSF 2.0 Functions
Review per the 6 NIST CSF Functions. Frameworks referenced: NIST CSF 2.0, CIS Controls v8 IG1, CSA CCM v4, ISO/IEC 27001:2022, SOC 2, ISO/IEC 42001:2023. See
docs/COMPLIANCE_ROADMAP.md.🏛️ Govern (GV)
.github/CODEOWNERS)🔍 Identify (ID)
.github/SECURITY_ASSESSMENT.md)🛡️ Protect (PR)
--from-literal, never/tmp, always$(mktemp)— ISO A.8.24)restricted(NIST PR.IP, CIS 4)🕵️ Detect (DE)
livenessProbe+readinessProbe) configured🚨 Respond (RS)
.github/INCIDENT_RESPONSE.md)♻️ Recover (RC)
📚 Documentation & Versioning
CHANGELOG.mdupdated (per-directory or repo-level/CHANGELOG.md)#WeOwnVerversion bumped perdocs/VERSIONING_WEOWNVER.md📝 Recent Commits (full bodies for Copilot context)
e9147d9 Merge branch 'main' into feature/nik-embed-lockdown
Author: Nik
Date: Thu Jul 23 11:51:19 2026 -0600
7912ad3 docs(compose): EMBED_REQUIRE_ALLOWLIST is inverted before v1.15.0
Author: Nik
Date: Wed Jul 22 22:14:56 2026 -0600
The flag landed 2026-06-06 with its condition backwards and was fixed
2026-06-17, so on v1.14.0/v1.14.1 leaving it UNSET makes a no-allowlist embed
refuse every origin, and SETTING it opens the embed to all of them — the exact
opposite of the intent. v1.15.0 (2026-06-25) is the first release with the
correct logic. Records which versions the flag can be trusted on.
1b998e1 feat(dashboard): embed can no longer answer every website; customer owns the allowlist
Author: Nik
Date: Wed Jul 22 20:41:41 2026 -0600
The public chat widget is the customer's brand and their capped LLM budget, but
AnythingLLM treats an embed with no allowlist as "allow every origin" —
parseAllowedHosts returns null and canRespond skips the origin check entirely.
Three changes:
EMBED_REQUIRE_ALLOWLIST="true" in compose.prod flips no-allowlist to deny-all,
so the failure mode becomes a visibly dead widget instead of a stranger quietly
spending the customer's budget. Upstream #5759 plus its 2026-06-17 inverted-logic
fix; older images ignore the variable harmlessly, which is why the explicit
allowlist set by provisioning remains the real protection.
The dashboard gains an "Authorized websites" panel, so a customer adding a second
site never needs an operator. Entries are normalised to scheme://host[:port] —
lower-cased, path and trailing slash stripped, wildcards refused — because ALLM
compares the browser Origin byte-for-byte, and the payload goes out as the
comma-separated string ALLM actually parses. The instance's own origin is always
retained, so the list can never be emptied back into the allow-any state. The
list persists in a new dashboard_state volume; provisioning seeds it via
EMBED_ALLOWLIST_DOMAINS.
The embed snippet now carries data-greeting with the AI disclaimer and
data-no-sponsor, so the PRD's non-negotiable disclosure is stated the moment the
widget opens rather than depending on the model remembering to say it.
Also fixes the /app/healthz false warning: the public health route was matched
before the base path was stripped, so it only ever answered the container's
internal /healthz — while Caddy routes only /app* to the dashboard, leaving the
public probe to fall through to the auth gate and return 401.
Verification: 16-check live test against a running dashboard + stub AnythingLLM
covering the public health probe, auth and CSRF gates, wildcard rejection,
case/path normalisation, self-origin retention, the empty-submission floor,
state-file persistence, the outgoing payload shape and the snippet contents;
node --check on the server and the dashboard's inline script; copier render +
YAML parse of the compose template; authed UI rendered and eyeballed.
🔍 Copilot AI Review: Copilot is configured to auto-request review for bot-authored PRs. If an auto-created PR opens without an initial Copilot review, push a follow-up commit to the same open PR (
review_on_push: true) to trigger review automatically.👥 Required Reviewers: human approval enforced by branch protection + CODEOWNERS.
@nciminorequested automatically.📚 Review Guidelines:
.github/copilot-instructions.md(phase-aware compliance directives)🛠️ Workflow Operations:
.github/workflows/README.mdAuto-generated by
.github/workflows/auto-pr-to-main.yml