mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-28 22:20:35 +00:00
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
52f6ff078c
|
fix(api): survive a malformed CSRF cookie (#7589)
* fix(api): survive a malformed CSRF cookie `readCsrfCookie` percent-decodes a cookie the gateway sets verbatim. Cookies are shared by every app on the same host (ports included), so a stray or truncated `%` escape from another local app, a proxy, or a stale value makes `decodeURIComponent` throw `URIError` inside `fetch` and `getCsrfHeaders` — every state-changing call fails with an exception before the gateway can answer 403. Fall back to the raw cookie value when decoding fails, matching `getLocaleFromCookie` (#7553). * test(api): exercise sibling CSRF cookies in the DOM --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com> |
||
|
|
2f4f4d4b6c
|
feat(authz): admin user-management UI and disabled-account copy (#4063 gap 3) (#6641)
* feat(authz): admin user-management UI and disabled-account copy (#4063 gap 3) Frontend slice of the account lifecycle backend (#6566): - Settings gains an admin-only Users page (core/auth/admin-users.ts -> GET/PATCH /api/v1/admin/users): email, read-only role, disabled state, and a suspend/restore toggle per row; the Gateway's rejection detail (last-active-admin 409) renders verbatim. The nav entry is hidden for non-admins and the page guards independently; ?settings=users joins the deep-link whitelist (now an exhaustive Record over SettingsSection). - A session that turns out disabled maps account_disabled to a login redirect that states the reason on all three surfaces: the SSR AuthResult tag (consumed exhaustively by the workspace/artifacts/auth layouts), the shared fetcher's 401 interception, and AuthProvider.refreshUser. No next target is preserved - a disabled account cannot sign back in. Login failures surface the localized account_disabled copy when the code is present. - userSchema stays disabled-optional (absent = not disabled); zh/en locales and docs updated in the same change. * fix(authz): address review on admin user UI and disabled copy (#4063 gap 3) Review follow-ups on PR #6641: - Direct /login visits with a suspended session now surface the SSR account_disabled verdict through AuthDisabledContext instead of discarding it (willem-bd): the auth layout wraps pages with the provider and the login form seeds its reason from it — no redirect, so no loop. Regression tests cover both verdict states. - admin-users.ts parses both endpoints against userSchema / z.array(userSchema) (zhfeng): malformed payloads fail the load instead of rendering phantom rows; toggle error copy is scoped to GatewayApiError with a localized fallback for anything else. - The localized account_disabled copy is login-only (zhfeng): sign-up keeps the backend message verbatim so a suspended account's email cannot be probed through the register surface. - Settings page folds the load banner and toggle error into a single alert region (zhfeng); adds coded-envelope 409, malformed-row, and load-failure tests; fetcher.ts documents the 401 body-consumption contract. - prettier across touched files (CI format gate). The admin-users query key keeps user?.id, matching the model-settings-page precedent (converge later per review). |
||
|
|
73e3699347
|
feat(frontend): render markdown artifacts in the "open in new window" view (#5056)
* feat(frontend): render markdown artifacts in the new window The artifacts panel's "open in new window" action handed the browser the raw Gateway response. For markdown that is a `text/markdown` body the browser can only show as source, so the new window was a text dump rather than a reader. Route markdown artifacts to a new `/artifacts/view` page that renders them with the same components the panel uses (SafeStreamdown + the artifact rehype chain + citation links/panel), including the truncated-preview banner and its "load full file" action. Everything else keeps the raw Gateway URL — notably HTML/SVG, which the Gateway deliberately serves as a download so active content never executes in the application origin. - `core/artifacts/viewer.ts` centralizes which stored artifacts are markdown (`.skill` archives included, since they hold a SKILL.md), so the panel and the viewer route cannot drift. - `ArtifactFilePreview` and its siblings move out of `artifact-file-detail.tsx` into `artifact-file-preview.tsx`; otherwise the standalone route would pull the CodeMirror editor into its bundle. - The window title comes from the route's `generateMetadata`, not `document.title`, which the App Router overwrites after hydration. - The viewer reads content through `useStandaloneArtifactContent`, which shares `useArtifactContent`'s query key but not its `useThread` dependency, since a detached window has no thread context. Claude-Session: https://claude.ai/code/session_013AiCrC5SBc3HdFYNxsp1EC * fix(frontend): keep the artifact target across re-authentication Review found the standalone viewer unrecoverable from an expired session. The window's target lives entirely in `?path=...&thread_id=...`, and both auth paths dropped it: - The layout guard redirected to `/login` with no `next` at all. A layout cannot read `searchParams`, so the guard moves into the page, which can — and rebuilds the full viewer address for `next`. The layout loses its AuthProvider along the way: nothing under this route reads `useAuth`, and the guard now makes a single `getServerSideUser` call per request. - The shared fetch wrapper built `next` from `window.location.pathname`, which silently truncated the query string. It now carries `search` too, so any route holding state in the query survives a 401, not just this one. `validateAuthNextPath` already accepts a query string. `buildArtifactViewerURL` is split out of `resolveArtifactOpenURL`: the guard needs the route itself, never the Gateway fallback that the latter takes for non-markdown targets. Tests: the login round trip (unit — the rebuilt URL survives `validateAuthNextPath` and parses back to the same target), the fetch wrapper preserving the query on 401 (unit), and the expired-session window reaching `/login` with the artifact intact (E2E). The E2E asserts on the popup's navigation *requests*, since `(auth)/layout` answers `/login` with a server redirect under DEER_FLOW_AUTH_DISABLED and no navigation commits. `tests/unit/core/models/api.test.ts` stubbed `window.location` without `search`; a real Location always has it. Claude-Session: https://claude.ai/code/session_013AiCrC5SBc3HdFYNxsp1EC * fix(frontend): keep public showcase artifacts out of the auth gate Review found that the viewer's access check regressed `/showcase`. Those pages render with `isMock`, their artifacts are served by the unauthenticated demo route, and the raw artifact URL this window replaced stayed public — so gating the window unconditionally bounced every logged-out showcase visitor to /login for a document that is already public. `requiresAuthenticatedViewer` exempts a mock target only when `resolveStaticDemoArtifact` would actually serve it. The allowlist is the authority rather than the flag: `mock=true` is caller-supplied, so a target the demo route answers with 404 — a non-allowlisted path, or a thread that is not a demo thread — still needs a session. Covered in `tests/e2e-auth/`, since the default E2E config disables auth and cannot see this: a public showcase artifact renders without a session, while a non-allowlisted path and a missing mock flag both land on /login. Verified the positive case goes red without the exemption. Claude-Session: https://claude.ai/code/session_013AiCrC5SBc3HdFYNxsp1EC |