Skip to content

fix(web): require same-site requests for cookie-authenticated writes and set frame headers on app pages - #2997

Merged
richiemcilroy merged 3 commits into
mainfrom
fix/same-site-cookie-writes
Oct 23, 2026
Merged

richiemcilroy merged 3 commits into
mainfrom
fix/same-site-cookie-writes

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Non-GET/HEAD/OPTIONS requests to /api that arrive with Sec-Fetch-Site: cross-site, or with an Origin outside the app's own host and WEB_URL when fetch metadata is missing, no longer pick up the session cookie in the Hono auth helpers, the Effect getCurrentUser used by the HttpApi and RPC auth middleware, or the App Router write handlers; these requests are now treated as anonymous rather than rejected, and bearer requests are unchanged. The proxy now also sets the /login frame headers on /dashboard, /onboarding, /invite, /cli/authorize and /mcp/authorize.

Testing

  • npx vitest run __tests__/unit/cookie-write-site.test.ts __tests__/unit/proxy-frame-headers.test.ts __tests__/unit/proxy-self-hosted.test.ts __tests__/unit/dev-reset-transcript.test.ts
  • npx vitest run __tests__/unit (all pass except share-stage-identity.test.ts, which fails the same way on main)
  • cd apps/web && npx tsc --noEmit
  • bun run biome check --write on touched files

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; the previous cookie-authentication bypass is fixed.

Summary

The PR limits cookie-authenticated writes from other sites and adds frame headers to signed-in app pages.

  • Cross-site API writes no longer use a browser session cookie for identity.
  • Dashboard and authorization pages now receive frame headers.

Reviews (2) · Last reviewed commit: "fix(web): run same-site check on the coo..." · Reviewed by Greptile

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

Comment thread packages/database/auth/request-site.ts Outdated

export const isCrossSiteCookieWrite = (request: SiteCheckedRequest) => {
if (READ_ONLY_METHODS.has(request.method.toUpperCase())) return false;
if (request.headers.has("authorization")) return false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Dummy headers allow cookie writes

isCrossSiteCookieWrite skips the check for any Authorization header, but callers still use the session cookie when the header is not a valid bearer token. A page served from http://localhost:3000 can pass the allowed credentialed preflight and send Authorization: Bearer other. With the user's cookie and a valid owned appId, /api/developer/credits/checkout then creates a checkout session as that user.

Apply the site check whenever authentication falls back to cookies. Only a successfully authenticated bearer should bypass it.

How this was verified: The checkout route allows the localhost origin and Authorization, the session cookie uses sameSite: "none", and getAuth reads that cookie after the header skips the site check.

Knowledge Base Used: Web API and domain services

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/database/auth/request-site.ts
Line: 39

Comment:
**Dummy headers allow cookie writes**

`isCrossSiteCookieWrite` skips the check for any `Authorization` header, but callers still use the session cookie when the header is not a valid bearer token. A page served from `http://localhost:3000` can pass the allowed credentialed preflight and send `Authorization: Bearer other`. With the user's cookie and a valid owned `appId`, `/api/developer/credits/checkout` then creates a checkout session as that user.

Apply the site check whenever authentication falls back to cookies. Only a successfully authenticated bearer should bypass it.

**How this was verified:** The checkout route allows the localhost origin and `Authorization`, the session cookie uses `sameSite: "none"`, and `getAuth` reads that cookie after the header skips the site check.

**Knowledge Base Used:** [Web API and domain services](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/web-api-and-domain-services.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
cap-status Ready Ready Preview Oct 23, 2026 9:42pm UTC

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

@richiemcilroy

Copy link
Copy Markdown
Member Author

@greptileai review

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

@richiemcilroy
richiemcilroy merged commit dbdf7cd into main Oct 23, 2026
40 checks passed

This branch was successfully deployed

2 active deployments
Preview – cap-web — 3beb785d Deployed Oct 23, 2026 by vercel[bot]
Preview – cap-status — 3beb785d Deployed Oct 23, 2026 by vercel[bot]
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