fix(ai-llm-proxy): enforce server-side Origin check (403 on mismatch)#480
Merged
Merged
Conversation
The proxy validated the oCIS bearer token and set CORS response headers, but CORS headers are advisory and browser-enforced only - they do not stop a non-browser or cross-site client from reaching the LLM. The repo security requirement mandates an explicit server-side Origin check. Add an authoritative Origin gate: a request whose Origin header is present but does not match the origin derived from OCIS_URL is rejected with 403 before any token validation or LLM call. A request with no Origin header cannot be a cross-site browser request and stays gated by the mandatory oCIS token, so it is allowed through. The check is a no-op when OCIS_URL is unset (the server already refuses to start without it outside tests). Adds isOriginAllowed unit tests (matching origin, cross origin, scheme/port mismatch, missing header, unconfigured). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: David Walter <david.walter@kiteworks.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
This was referenced Jun 25, 2026
LukasHirt
approved these changes
Jun 25, 2026
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.
Problem
ai-llm-proxyvalidates the oCIS bearer token and sets CORS response headers, but has no server-sideOrigincheck. CORS headers are advisory and enforced only by the browser, so they do not stop a non-browser or cross-site client from reaching the LLM. The repo security requirement is explicit: any LLM-calling path "must perform an explicit server-side origin check and return403for unexpected origins — CORS headers alone are browser-enforced and insufficient."This came up while reviewing the new AI extensions (#475, #476, #477), which all correctly route through this proxy and rely on it being the single enforcement point — but the enforcement was missing.
Fix
Add an authoritative server-side
Origingate inhandleRequest, before token validation and the LLM call:Originheader present but not matching the origin derived fromOCIS_URL→ 403.Originheader → allowed through to token validation. A cross-site browser request always carriesOrigin, so a missing one cannot be a cross-site browser attack, and the mandatory oCIS bearer token still guards it.OCIS_URLis unset (the server already refuses to start without it outside tests).The expected origin is computed once from
OCIS_URLvianew URL(OCIS_URL).origin, so it compares correctly against the scheme/host/portOriginheader.Testing
isOriginAllowedunit tests: matching origin, cross origin, scheme mismatch, port mismatch, missing header, unconfigured.pnpm --filter ai-llm-proxy test:unit→ 25 passed.pnpm --filter ai-llm-proxy build+lintclean.Risk
Low. Genuine calls from the oCIS web extensions are same-origin POSTs that always carry
Origin: <OCIS_URL origin>, so they are unaffected. Only requests from a different (or unexpected) origin are newly rejected — which is the intent.