Skip to content

nextjs-no-side-effect-in-get-handler false positive: Headers.set() on a freshly constructed response #1808

Description

@ItalianScallian

Diagnostic

  • Rule: react-doctor/nextjs-no-side-effect-in-get-handler
  • Severity: error
  • Category: Security
  • Version: react-doctor 0.9.14

Message

This GET handler's side effect (headers.set()) is prone to CSRF vulnerabilities, since prefetching or a forged request can trigger it.

Repro

function downloadHeaders({ filename, contentType, contentLength }: {
  filename: string;
  contentType: string;
  contentLength?: string | null;
}): Headers {
  const headers = new Headers({
    'Content-Type': contentType,
    'Content-Disposition': `attachment; filename="${filename}"`,
  });
  if (contentLength) headers.set('Content-Length', contentLength);
  return headers;
}

export async function GET(request: Request) {
  const denied = await authorize();
  if (denied) return denied;
  const upstream = await fetch(/* ... */);
  return new Response(upstream.body, {
    headers: downloadHeaders({ filename: 'x', contentType: 'video/mp4', contentLength: '123' }),
  });
}

Why this looks wrong

headers.set() is called on a Headers instance the helper just constructed with new Headers(...) for the response about to be returned. It never touches an external or shared Headers/cookie store, request state, or anything a forged/prefetched GET could observe or influence beyond the handler's own return value. The rule appears to pattern-match on any .headers.set() (or similar) call reachable from a GET handler, without distinguishing a locally constructed response object from shared/external state — the actual CSRF-relevant case.

Suggested fix

Scope the rule to Headers.set()/similar calls on an object that either (a) isn't a local new Headers(...)/new Response(...) freshly constructed within the handler's own call graph, or (b) is known to mutate state outside the handler's own response (cookies, a database write, an external API call with side effects, etc.).

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