From 39f95f7e04b3889ffa058e6d0565e41b4f72cd6d Mon Sep 17 00:00:00 2001 From: Parfii-bot Date: Fri, 24 Apr 2026 03:14:09 +0800 Subject: [PATCH] fix(cortex-ui): strip whitespace from token; drop credentials:'include' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live e2e test caught a paste-inserted whitespace in URL token param — copy-paste from terminal had inserted %20%20%20 into middle of the 64-char hex token, which passed URL parsing but failed byte-level auth::tokens_match on the daemon → 403. Two fixes: 1. `sanitize_token()` strips ALL whitespace (spaces, tabs, newlines, zero-width) from token before use, applied on both URL-param and localStorage read paths. Defensive even against future Setup-form paste mishaps — Setup input itself could also be whitespace-dirty. 2. `credentials: 'include'` → `credentials: 'omit'`. Bearer auth rides on an explicit header; we don't need cookies. `include` triggers browser quirks (Safari especially) around credentialed cross-origin fetches that can strip or mismangle Authorization on redirects. 3. Error message now includes response body preview — `"403 Forbidden — {\"error\":{\"code\":\"forbidden\",\"message\":\"bearer token rejected\"}}"` — so the next failing setup surfaces root-cause. Tests unchanged (10 passing). Rebuild hash: index-7ZqAoBoM.js. Co-Authored-By: Claude Opus 4.7 (1M context) --- _ts_packages/packages/cortex-ui/src/lib/api.ts | 11 +++++++++-- .../packages/cortex-ui/src/lib/config.ts | 16 +++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/_ts_packages/packages/cortex-ui/src/lib/api.ts b/_ts_packages/packages/cortex-ui/src/lib/api.ts index 1b19610..c389247 100644 --- a/_ts_packages/packages/cortex-ui/src/lib/api.ts +++ b/_ts_packages/packages/cortex-ui/src/lib/api.ts @@ -13,9 +13,16 @@ async function api( Authorization: `Bearer ${c.token}`, 'Content-Type': 'application/json', }, - credentials: 'include', + // Bearer auth via explicit header; no cookies needed. credentials:'omit' + // is the safe cross-origin default and avoids Safari quirks around + // credentials:'include' stripping Authorization on some redirects. + credentials: 'omit', }); - if (!res.ok) throw new Error(`${res.status} ${res.statusText}`); + if (!res.ok) { + let body = ''; + try { body = (await res.text()).slice(0, 200); } catch { /* ignore */ } + throw new Error(`${res.status} ${res.statusText}${body ? ` — ${body}` : ''}`); + } return res.json() as Promise; } diff --git a/_ts_packages/packages/cortex-ui/src/lib/config.ts b/_ts_packages/packages/cortex-ui/src/lib/config.ts index dba36bb..c3ae0d7 100644 --- a/_ts_packages/packages/cortex-ui/src/lib/config.ts +++ b/_ts_packages/packages/cortex-ui/src/lib/config.ts @@ -7,12 +7,22 @@ const DEFAULT_DAEMON = 'http://localhost:9797'; const KEY_DAEMON = 'kei-cortex-daemon'; const KEY_TOKEN = 'kei-cortex-token'; +/** Strip ALL whitespace (spaces, tabs, newlines, zero-width) from a token. */ +function sanitize_token(raw: string): string { + return raw.replace(/\s+/g, ''); +} + export function load_config(): CortexConfig | null { const params = new URLSearchParams(window.location.search); - const override = params.get('daemon'); + const url_daemon = params.get('daemon')?.trim(); + const url_token_raw = params.get('token'); + const url_token = url_token_raw ? sanitize_token(url_token_raw) : null; + if (url_daemon) localStorage.setItem(KEY_DAEMON, url_daemon); + if (url_token) localStorage.setItem(KEY_TOKEN, url_token); const daemon_url = - override ?? localStorage.getItem(KEY_DAEMON) ?? DEFAULT_DAEMON; - const token = params.get('token') ?? localStorage.getItem(KEY_TOKEN) ?? ''; + url_daemon ?? localStorage.getItem(KEY_DAEMON) ?? DEFAULT_DAEMON; + const stored_token = localStorage.getItem(KEY_TOKEN); + const token = url_token ?? (stored_token ? sanitize_token(stored_token) : ''); if (!token) return null; return { daemon_url, token }; }