Skip to content

fix(gateway-worker): serve GraphiQL under a strict CSP, and send nosniff and no referrer everywhere - #35

Merged
Hebilicious merged 1 commit into
audit/self-hosted-default-limitfrom
audit/graphiql-csp
Oct 14, 2026
Merged

Hebilicious merged 1 commit into
audit/self-hosted-default-limitfrom
audit/graphiql-csp

Conversation

@Hebilicious

@Hebilicious Hebilicious commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Closes #15

What changed

  • The page module. New apps/gateway/src/graphiql-page.ts renders Yoga's GraphiQL page. RenderedPages renders it once per isolate and again after a failed render. The page keeps Agris's icon.
  • The policy. The page is served with a Content-Security-Policy, built by graphiqlContentSecurityPolicy:
    • it allows only the page's inline scripts, each by its hash whatever its tag's attributes, plus the pinned @graphql-yoga/graphiql release's dist/ directory;
    • it lets the page connect only to the gateway and that release;
    • default-src, base-uri, object-src, form-action and frame-ancestors are 'none';
    • the page is also sent with X-Frame-Options: DENY.
  • Every public response carries X-Content-Type-Options: nosniff and Referrer-Policy: no-referrer, unless the response states its own (SECURITY_HEADERS in public-gateway.ts). The 429 response no longer sets nosniff itself.
  • Docs. GraphiQL stays on by default. docs/deploy.md explains the policy and when to set GRAPHIQL=disabled. The gateway README names the module.
  • Tests. public-gateway gains a policy step in public-gateway-graphiql and new scenarios public-gateway-graphiql-inline-scripts, public-gateway-graphiql-render-retry and public-gateway-security-headers.

How it was reproduced

I ran this layer's public-gateway feature against the parent's gateway (audit/self-hosted-default-limit), with the bundle built at the parent. I overlaid apps/gateway/src/graphiql-page.ts, the new module two scenarios test directly; the parent's gateway never imports it.

Scenario On the parent
public-gateway-graphiql fails: AssertionError: The response has no Content-Security-Policy
public-gateway-security-headers fails: Referrer-Policy is null, not 'no-referrer'
public-gateway-graphiql-inline-scripts passes
public-gateway-graphiql-render-retry passes
the 15 others pass

Not reproduced

  • public-gateway-graphiql-inline-scripts and public-gateway-graphiql-render-retry are unit tests of the new module, which was overlaid. They pass on the parent and say nothing about it.
  • A compromise of unpkg or of the npm artifact. The policy still trusts unpkg to serve the pinned release unchanged, as docs/deploy.md says.

Refs #21 (security audit tracking issue). Part of stack #42; review bottom-up, each layer shows only its own diff.

@Hebilicious
Hebilicious added this pull request to stack #37 October 13, 2026 20:10
@changeset-bot

changeset-bot Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 1acd6f3

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@Hebilicious
Hebilicious removed this pull request from stack #37 October 14, 2026 06:05
@Hebilicious
Hebilicious added this pull request to stack #42 October 14, 2026 06:05
@Hebilicious Hebilicious changed the title Serve GraphiQL under a strict Content-Security-Policy, and send nosniff and no referrer everywhere fix(gateway-worker): serve GraphiQL under a strict CSP, and send nosniff and no referrer everywhere Oct 14, 2026
…iff and no referrer everywhere

GraphiQL is served by default on the API origin and runs third-party code
from unpkg with no CSP (audit W1). src/graphiql-page.ts now renders Yoga's
page once per isolate (and again after a failed render) with a
Content-Security-Policy that allows only its inline scripts, each by its
hash whatever its tag's attributes, and the pinned GraphiQL release's dist/
directory; connects only to the gateway and that release; and forbids
framing (frame-ancestors 'none', X-Frame-Options DENY). Every public response
carries X-Content-Type-Options: nosniff and Referrer-Policy: no-referrer
unless it states its own. GraphiQL stays on by default; docs/deploy.md says
when to set GRAPHIQL=disabled.

public-gateway proves the policy against the served page, the hashing of
attributed inline scripts, the render retry and the headers on every query
form.

Refs #15
@Hebilicious
Hebilicious merged commit f76218d into main Oct 14, 2026
5 of 6 checks passed
@Hebilicious
Hebilicious deleted the audit/graphiql-csp branch October 14, 2026 08:56
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.

GraphiQL executes third-party unpkg JavaScript on the API origin with no SRI and no CSP

1 participant