Skip to content

fix(server): make CORS origins configurable and drop the credentialed wildcard (#448) - #449

Open
Anai-Guo wants to merge 1 commit into
theroyallab:mainfrom
Anai-Guo:fix/cors-allowed-origins
Open

fix(server): make CORS origins configurable and drop the credentialed wildcard (#448)#449
Anai-Guo wants to merge 1 commit into
theroyallab:mainfrom
Anai-Guo:fix/cors-allowed-origins

Conversation

@Anai-Guo

Copy link
Copy Markdown
Contributor

Summary

Fixes the CORS portion of #448. TabbyAPI ships:

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Because allow_credentials=True is combined with allow_origins=["*"], Starlette reflects the requesting origin (rather than sending a literal *). Any web page a user has open in their browser is also "connecting from localhost", so it can pass preflight, POST to http://127.0.0.1:5000, and read the responses. For an instance running with disable_auth: true — which the config sample explicitly recommends for localhost-only use — that page reaches the admin surface (/v1/model/load, /v1/download, /v1/model/list with the resolved model path, ...). Thanks @elfrost for the detailed write-up and repro.

Change

  • Add a network.allowed_origins config key so the CORS allowlist is configurable. Default is ["*"], preserving today's behavior so existing browser frontends keep working out of the box.
  • Set allow_credentials=False. TabbyAPI authenticates with a header/query token, never cookies, so credentialed CORS provides nothing and only enables the origin-reflection footgun. With this change the middleware sends a literal * (or the configured allowlist) instead of reflecting an arbitrary origin, and operators who want to lock down a shared instance can now set an explicit allowlist or an empty list [].
network:
  # Restrict to your own frontends, or [] to block all cross-origin callers.
  allowed_origins: ["http://localhost:8000"]

Note on the default

I kept the default permissive (["*"]) to avoid breaking existing browser UIs on upgrade, and scoped this PR to exposing the knob + removing the credentialed-wildcard footgun. If you'd prefer a secure-by-default posture (default [], browser callers opt in), that's a one-line change to the field default — happy to flip it if you want that instead.

The second item in #448 (the image_url server-side fetch / SSRF surface) is orthogonal and left for a separate change.

Testing

config_sample.yml is updated to document the new key. The repo's tests/ are live-server integration scripts (they need a loaded model), so there's no unit harness for middleware wiring to extend here; the change is config-driven and common/config_models.py + endpoints/server.py compile clean.

🤖 Generated with Claude Code

…dcard

The app shipped `CORSMiddleware(allow_origins=["*"], allow_credentials=True)`.
Because credentials are enabled, Starlette reflects the requesting origin
instead of sending `*`, so any web page open in the browser can preflight and
read responses from a loopback instance. With `disable_auth: true` (which the
config explicitly suggests for localhost-only use) that page reaches the admin
surface (`/v1/model/load`, `/v1/download`, model path listing, ...).

Expose a `network.allowed_origins` config key (default `["*"]`, so existing
browser frontends keep working) and set `allow_credentials=False`. TabbyAPI
authenticates with a header/query token, never cookies, so credentialed CORS
buys nothing and only enables the origin-reflection footgun. Operators who want
to lock a shared instance down can now set an explicit allowlist or `[]`.

Closes theroyallab#448 (CORS portion).

Signed-off-by: Anai-Guo <antai12232931@anaiguo.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant