Skip to content

Render not-found for an org slug the session does not belong to - #1825

Merged
RhysSullivan merged 1 commit into
mainfrom
fix-1713-foreign-org-slug-not-found
Aug 28, 2026
Merged

Render not-found for an org slug the session does not belong to#1825
RhysSullivan merged 1 commit into
mainfrom
fix-1713-foreign-org-slug-not-found

Conversation

@RhysSullivan

Copy link
Copy Markdown
Collaborator

Fixes #1713

Opening /<a-slug-you-do-not-own>/policies sometimes rendered the full authenticated console — sidebar, org switcher showing the user's OWN organization, page chrome — under an address naming an organization they cannot see, with a failing org-scoped query and a Retry button for a body.

Cause

AuthGate only reaches its not-found branch once auth.organization resolves to null. Until /account/me answers, useAuth seeds from the auth-hint cookie, and that cookie always names the organization the session last landed in — never the one in the URL. So the first paint answered a question about a different organization and built the whole shell out of it. Whether the user ever saw it came down to how fast the server replied, which is why it reproduced roughly one run in four on a loaded CI runner.

Fix

AuthGate now refuses to build the shell from an answer that does not name the organization the URL names:

if (pathnameOrgSlug != null && auth.organization.slug !== pathnameOrgSlug) return <BlankScreen />;

/account/me is scoped by the URL's slug, so once it resolves it can only agree or be null. A disagreement is therefore always an unresolved answer, never a verdict — hence blank rather than not-found, leaving the existing organization == null branch as the only thing entitled to declare a wrong address. The URL is never rewritten.

Cold-load speed is unchanged for the legitimate case: the hint names the slug already in the URL, so this matches on the very first paint and the shell renders with no round trip. Only a slug the hint does not name waits — a foreign one, and the single frame after an org switch, which now paints the organization the URL asked for instead of briefly showing the previous one. Bare and reserved console paths (/policies, /integrations, …) are unaffected: every console root is a reserved slug, so pathnameOrgSlug is null for them and canonicalization still runs as before.

OrgSlugGate's header comment is updated: the invariant it documents is now actually enforced by the host rather than assumed.

Why not server-side

The console is SPA mode — one static prerendered shell serves every authenticated document request, and the document gate can only redirect or append cookies, not vary the body. A redirect is exactly what the issue rules out, and authorizing the URL's slug in the gate would put a live membership lookup on every slugged document request, which is the cold-load cost this change is meant to avoid. So the block is client-side and the shell simply never mounts.

Tests

The org-slug scenario's unknown-slug step previously only waited for "Page not found", so a run that flashed an entire workspace first still passed. It now installs a MutationObserver before the page's own scripts run, records whether a console sidebar was ever attached, and asserts the landing state as a whole — pathname plus shell-ever-mounted. Both tenancy branches are pinned rather than one being skipped: multi-tenant hosts must 404 with no shell and the URL untouched, single-tenant hosts must canonicalize onto the instance org's shell.

Verified end to end:

  • vitest run --project cloud scenarios/org-slug-routing.test.ts — passes; screenshot shows the not-found page with /zz-no-such-org/policies still in the address bar.
  • Same run with the fix reverted — fails with shellEverMounted: true, deterministically. The assertion catches the flash it was written for.
  • vitest run --project selfhost scenarios/org-slug-routing.test.ts — passes; the canonicalize branch holds.
  • packages/react (363) and apps/cloud (344) unit suites, plus repo typecheck, lint, and format.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
executor-marketing 0c63e6e Commit Preview URL

Branch Preview URL
Aug 28 2026, 09:32 AM

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud 0c63e6e Aug 28 2026, 09:33 AM

@pkg-pr-new

pkg-pr-new Bot commented Aug 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor-js/cli

npm i https://pkg.pr.new/@executor-js/cli@1825

@executor-js/config

npm i https://pkg.pr.new/@executor-js/config@1825

@executor-js/execution

npm i https://pkg.pr.new/@executor-js/execution@1825

@executor-js/sdk

npm i https://pkg.pr.new/@executor-js/sdk@1825

@executor-js/codemode-core

npm i https://pkg.pr.new/@executor-js/codemode-core@1825

@executor-js/runtime-quickjs

npm i https://pkg.pr.new/@executor-js/runtime-quickjs@1825

@executor-js/plugin-file-secrets

npm i https://pkg.pr.new/@executor-js/plugin-file-secrets@1825

@executor-js/plugin-graphql

npm i https://pkg.pr.new/@executor-js/plugin-graphql@1825

@executor-js/plugin-keychain

npm i https://pkg.pr.new/@executor-js/plugin-keychain@1825

@executor-js/plugin-mcp

npm i https://pkg.pr.new/@executor-js/plugin-mcp@1825

@executor-js/plugin-onepassword

npm i https://pkg.pr.new/@executor-js/plugin-onepassword@1825

@executor-js/plugin-openapi

npm i https://pkg.pr.new/@executor-js/plugin-openapi@1825

executor

npm i https://pkg.pr.new/executor@1825

commit: 0c63e6e

@RhysSullivan
RhysSullivan marked this pull request as ready for review August 28, 2026 18:28
@RhysSullivan
RhysSullivan merged commit 06a7b75 into main Aug 28, 2026
44 checks passed
@RhysSullivan RhysSullivan mentioned this pull request Aug 28, 2026
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.

A foreign org slug renders the console shell instead of a not-found page

1 participant