Skip to content

fix(#206): skip response.headers.set() in nextjs-no-side-effect rule#233

Open
william-garden wants to merge 1 commit into
millionco:mainfrom
william-garden:fix/issue-206-v2
Open

fix(#206): skip response.headers.set() in nextjs-no-side-effect rule#233
william-garden wants to merge 1 commit into
millionco:mainfrom
william-garden:fix/issue-206-v2

Conversation

@william-garden
Copy link
Copy Markdown

@william-garden william-garden commented May 14, 2026

Summary

Issue #206: False positive - nextjs-no-side-effect-in-get-handler flags Response.headers.set()

Problem

The nextjs-no-side-effect-in-get-handler rule was incorrectly flagging response.headers.set() calls inside GET route handlers as security-risk side effects. These calls only shape the outbound response (Cache-Control, X-Deprecated, CORS headers) and are idiomatic Next.js — they do not mutate server state.

Solution

Added isResponseHeadersCall() function to detect response.headers.set(), response.headers.append(), and response.headers.delete() calls and skip them during side effect detection.

Changes

  • packages/react-doctor/src/plugin/utils/is-response-headers-call.ts (new)
  • packages/react-doctor/src/plugin/utils/find-side-effect.ts - skip response.headers calls
  • packages/react-doctor/src/plugin/utils/index.ts - export new function

Note

Low Risk
Low risk predicate change to a lint rule to reduce false positives; main risk is potentially missing a real side effect if code matches the new response.headers.* call shape unexpectedly.

Overview
Updates side-effect detection used by nextjs-no-side-effect-in-get-handler to ignore response.headers.set(), .append(), and .delete() calls so response header shaping is no longer reported as a server-state mutation.

Adds a new isResponseHeadersCall AST predicate and exports it via the utils index, then short-circuits findSideEffect when this call pattern is encountered.

Reviewed by Cursor Bugbot for commit a5bacbb. Bugbot is set up for automated code reviews on this repo. Configure here.

…ect rule

The nextjs-no-side-effect-in-get-handler rule was incorrectly flagging
response.headers.set() calls as security-risky side effects. These calls only
shape the outbound response (Cache-Control, X-Deprecated, CORS headers)
and are idiomatic Next.js - they don't mutate server state.

This fix adds isResponseHeadersCall() to detect response.headers.set(),
response.headers.append(), and response.headers.delete() calls and skip them
during side effect detection.
@reactreview
Copy link
Copy Markdown

reactreview Bot commented May 14, 2026

🔴 React Review0/100 (unchanged) · No new issues

Reviewed by react-review for commit a5bacbb. Configure here.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 14, 2026

@william-garden is attempting to deploy a commit to the Million Team on Vercel.

A member of the Team first needs to authorize it.

NisargIO added a commit that referenced this pull request May 16, 2026
… in GET handlers (#206)

Rewrites `nextjs-no-side-effect-in-get-handler` precision so it no longer
floods Next.js codebases with false positives from `response.headers.set()`
and request-scoped `new Map/Set/Headers/URLSearchParams/FormData(...)`
mutations, while still catching real CSRF-relevant writes: drizzle/prisma
ORM mutations, module-level mutable state, mutating fetch, and all three
forms of `cookies().set/delete()` including aliased `const cs = await
cookies(); cs.set(...)`.

Also folds in the safe handler-resolution improvements from PR #251 (cron
route skip and depth-bounded `const GET = withAuth(handler)` resolution).

Supersedes #209, #211, #233, #238.

Co-authored-by: Cursor <cursoragent@cursor.com>
NisargIO added a commit that referenced this pull request May 16, 2026
… in GET handlers (#206)

Rewrites `nextjs-no-side-effect-in-get-handler` precision so it no longer
floods Next.js codebases with false positives from `response.headers.set()`
and request-scoped `new Map/Set/Headers/URLSearchParams/FormData(...)`
mutations, while still catching real CSRF-relevant writes: drizzle/prisma
ORM mutations, module-level mutable state, mutating fetch, and all three
forms of `cookies().set/delete()` including aliased `const cs = await
cookies(); cs.set(...)`.

Also folds in the safe handler-resolution improvements from PR #251 (cron
route skip and depth-bounded `const GET = withAuth(handler)` resolution).

Supersedes #209, #211, #233, #238.

Co-authored-by: Cursor <cursoragent@cursor.com>
@NisargIO
Copy link
Copy Markdown
Member

Working on a related fix in #260 that takes a slightly broader angle — covers response.headers.set/append/delete (your case) plus the wider class: any chain ending in .headers, locally-scoped Map/Set/Headers/URLSearchParams/FormData bindings, .searchParams, aliased headers(), cron routes, and depth-bounded handler resolution for export const GET = withAuth(handler) patterns. Thanks for the report — your repro and test cases were super useful while validating the broader fix.

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.

2 participants