You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
crossOriginRefusal (apps/web/app/lib/auth/same-origin.ts, added in #96 for api.user-api-keys) should also guard the other cookie-authenticated mutating routes, which currently rely on SameSite=Lax alone:
api.skill-favorite.ts
api.skill-vote.ts
api.skill-rate.ts
api.skill-review.ts (action)
Routes that also accept a Bearer API key must apply the check only when the request is authenticated by the session cookie, so CLI/API-key clients (no Origin) keep working. Add a test per route: foreign Origin returns 403, same-origin and header-less requests pass.
Triage: P1 security hardening. These are ambient-cookie mutations behind SameSite=Lax only. Each one reads the body with request.json(), which does not require Content-Type: application/json, so a sibling subdomain can send a no-preflight text/plain POST that the cookie rides along on.
Findings while scouting:
All four listed routes (favorite, vote, rate, review) authenticate with onlygetSession, so none of them has a Bearer path. The "Session/Key" entries in CLAUDE.md were wrong. The check runs unconditionally there, before the session is read.
The same gap exists on other routes, so they are fixed in the same PR:
POST /api/skills/register (authenticateRequest falls back to the session). It is checked only when method === "session", so CLI publish with a key is unaffected.
Release state, personal-kit writes and publisher provision, all through resolveLimitedActor. They are checked only when there is no Authorization and no X-Admin-Secret, and the refusal is 403 cross_origin.
Activity
mrgoonie commentedon Oct 19, 2026
Triage: P1 security hardening. These are ambient-cookie mutations behind
SameSite=Laxonly. Each one reads the body withrequest.json(), which does not requireContent-Type: application/json, so a sibling subdomain can send a no-preflighttext/plainPOST that the cookie rides along on.Findings while scouting:
getSession, so none of them has a Bearer path. The "Session/Key" entries inCLAUDE.mdwere wrong. The check runs unconditionally there, before the session is read.POST /api/skills/register(authenticateRequestfalls back to the session). It is checked only whenmethod === "session", so CLI publish with a key is unaffected.resolveLimitedActor. They are checked only when there is noAuthorizationand noX-Admin-Secret, and the refusal is403 cross_origin.The work is stacked on #96 (branch
sx-bugs-47-49-61-79), which introducessame-origin.tsand is not merged yet.Generated by Claude Code
refactor(releases): move capped JSON body reading out of release-stat…