Skip to content

find-security-issues proposes HSTS/X-Frame-Options on Vercel-hosted apps — twice now, and headers aren't even in its pattern list #45

Description

@havardljohansen

tasks/find-security-issues.md has produced a "add HSTS + X-Frame-Options to next.config.*" PR twice on the Sanity/Next fleet — jotul-frontend#675 (merged, sat 26 days, reverted) and merkur-frontend#566 (blocked by the protected-paths gate, now inside bundle #576). Both were wrong for the same reason, and both cost review time.

The interesting part: security headers are not in the task's pattern list. Step 2 enumerates auth bypass, injection, XSS, exposed secrets, CSRF, rate limiting, and deserialization/SSRF/open redirects. Missing response headers appear nowhere — so the run went outside its own stated scope, twice, and step 3's "pick one real, non-speculative issue" didn't hold it in.

Needed from whoever owns the task prompts: an explicit negative in step 2. The positive list demonstrably didn't constrain this, so listing categories isn't enough — "missing security header" is the kind of finding a model reaches for when the real list turns up nothing that night.

Why both PRs were wrong

Verified 2026-08-26 with curl -sSI against production apexes:

  • Vercel already serves strict-transport-security: max-age=63072000 on every attached custom domain — confirmed on merkur-programmet.no, distriktssenteret.no and www.jotul.com, none of which set it in app code. So an app-level HSTS header can only add includeSubDomains / preload (effectively irreversible) or silently weaken the platform value. merkur#566's stated premise — "the custom domain only gets it if we set it" — is false.
  • X-Frame-Options is the wrong tool on this stack, not merely redundant. All seven synced clones set Content-Security-Policy: frame-ancestors including cross-origin Sanity Studio hosts for Presentation. Modern browsers ignore XFO when frame-ancestors is present, and legacy XFO can't express an allowlist, so SAMEORIGIN would break the Studio embed exactly where it takes effect.

jotul already worked this out and wrote it down — next.config.mjs:56 carries the full rationale as a comment. That reasoning never reached the boilerplate template, which is filed as sanity-nextjs-boilerplate#438.

Suggested wording for step 2

A short "not in scope" line, e.g.:

Not in scope: missing or weak response headers (HSTS, X-Frame-Options, CSP hardening) on platform-hosted apps. The host may already set them, and the irreversible directives (includeSubDomains, preload) are an ops decision, not an overnight fix. Report as an issue if you believe one is genuinely missing — do not open a PR.

The "report, don't PR" half matters: if a header genuinely is missing, that's still worth surfacing. It's the unattended code change that's wrong.

Adjacent

Step 1 dedupes against open night-shift security PRs only, so a proposal that was merged-and-reverted (jotul#675) or closed is invisible to the next run — which is how the same idea returned seven weeks later. That's #38's territory rather than this issue's, noting it because it's the mechanism that let the repeat through.

The path-based baseline exclude in bundles/_multi-runner.md is not the right lever here — it excludes directories, not finding types.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions