Skip to content

fix(server-auth-actions): accept member-expression auth calls like auth0.getSession() (#239)#261

Merged
aidenybai merged 4 commits into
fix/nextjs-get-handler-side-effects-206from
fix/server-auth-actions-member-call-239
May 16, 2026
Merged

fix(server-auth-actions): accept member-expression auth calls like auth0.getSession() (#239)#261
aidenybai merged 4 commits into
fix/nextjs-get-handler-side-effects-206from
fix/server-auth-actions-member-call-239

Conversation

@NisargIO
Copy link
Copy Markdown
Member

@NisargIO NisargIO commented May 15, 2026

Summary

Stacked on top of #260.

server-auth-actions only recognised auth calls when the callee was a bare Identifierauth(), getSession(), getUser(), etc. Every member-expression flavour (auth0.getSession(), supabase.auth.getSession(), clerkClient.getUser(userId), request.auth()) slipped through and the whole server action got flagged as missing an auth check. One reporter in #239 hit 139 false positives — essentially every server action in the repo.

Route every CallExpression through the existing getCalleeName utility, which already resolves both Identifier and MemberExpression callees to a single name. walkAst already descends into AwaitExpression / ChainExpression children, so awaited (await auth0.getSession()) and optional-chained (auth0?.getSession()) variants fall out for free with no extra branching.

This is more elegant than #240 (which adds a second else if branch duplicating the identifier check) and reuses code we already have.

Now accepted as an auth check

  • await auth0.getSession() — the verbatim repro
  • auth0.getSession() (non-awaited)
  • await supabase.auth.getSession() (chained member expression)
  • await clerkClient.getUser(userId)
  • await auth0?.getSession() (optional chaining)
  • await auth() / await getSession() (the original Identifier forms — still works)

Still flagged

  • Server actions with no auth-related call inside the first 10 statements.
  • Server actions calling unrelated .something() member methods.
  • Auth checks that happen after the lookahead window.

Test plan

  • Added packages/react-doctor/tests/regressions/server-auth-actions-member-call.test.ts with 6 false-negative cases and 3 true-positive cases.
  • nr typecheck, nr lint, nr test (738/738), nr format all pass.
  • Existing server-auth-actions regressions in packages/react-doctor/tests/run-oxlint/nextjs.test.ts still pass.

Closes #239. Supersedes #240.

Eval results

Re-ran against the larger sandbox corpus (500 repos, scanned via Vercel Sandbox in parallel) on main vs parent #260 vs this branch:

main (8a3a1be) parent #260 (71ecf0a) this PR (3b9af6a)
Total diagnostics (all rules) 66,276 66,276 66,276
server-auth-actions 54 54 54
Net diff in any rule 0 0

Zero functional changes across 500 repos. The 43 line-level diffs vs main are 100% message-text encoding artifacts (same file/line/column/rule, em-dash rendering); no diagnostic actually moves. All 54 server-auth-actions hits stay flagged — these are real true positives in supabase/supabase, payloadcms/payload, unkeyed/unkey, and others: server actions performing prisma.* / form-submit / database mutations with no auth call in the lookahead window.

The patterns this PR unblocks (auth0.getSession(), supabase.auth.getSession(), clerkClient.getUser(), optional-chained and awaited variants) don't appear in the 500-repo sample as actual missed false positives, so the fix is exercised by the new server-auth-actions-member-call.test.ts regression suite (6 false-negative + 3 true-positive cases) and the verbatim repro from #239. Corpus-wide result: 0 regressions in any rule across 66,276 diagnostics.

Made with Cursor


Note

Medium Risk
Updates the server-auth-actions security rule’s AST matching and adds config-driven behavior, which can change which server actions are flagged (false positives/negatives) but does not affect runtime application code.

Overview
Fixes server-auth-actions so top-of-action auth checks are recognized when invoked via member/chain/optional-chain or TS-wrapped callees (e.g. auth0.getSession(), supabase.auth.getSession(), auth!(), (auth as T)()), and expands what export shapes are inspected (named exports, export const ... = async ..., and export default async function). It also tightens detection by not counting auth calls inside nested/unused helpers and by disambiguating overly-generic method names like .getUser() using an auth-ish receiver heuristic.

Adds a new user config key serverAuthFunctionNames that is forwarded from react-doctor config into oxlint settings and treated as an allowlist of custom auth guards. Introduces shared getReactDoctor*Setting helpers for safe typed reads from context.settings, updates no-secrets-in-client-code to use them, documents the new config in the README/types, and adds regression tests covering the new server-auth-actions behaviors.

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

@reactreview
Copy link
Copy Markdown

reactreview Bot commented May 15, 2026

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

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

@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-doctor-website Ready Ready Preview, Comment May 16, 2026 7:32am

Comment thread .changeset/server-auth-actions-member-call.md Outdated
@NisargIO NisargIO force-pushed the fix/server-auth-actions-member-call-239 branch from 978df73 to 5bff156 Compare May 16, 2026 00:59
@NisargIO NisargIO closed this May 16, 2026
NisargIO and others added 2 commits May 15, 2026 18:06
…uth0.getSession()` (#239)

`containsAuthCheck` only recognised auth calls with a bare `Identifier`
callee (`auth()`, `getSession()`, …), so every `auth0.getSession()`,
`supabase.auth.getSession()`, `clerkClient.getUser()` was missed and the
whole server action got flagged. One reporter hit 139 false positives —
essentially every server action in the repo.

Route every CallExpression through the existing `getCalleeName` utility,
which already resolves both Identifier and MemberExpression callees to a
single name. `walkAst` already descends into `AwaitExpression` /
`ChainExpression` children, so `await auth0.getSession()` and
`auth0?.getSession()` fall out for free with no extra branching.

Closes #239. Supersedes PR #240.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
cursoragent and others added 2 commits May 16, 2026 00:31
…g allowlist

The rule previously only matched bare identifier calls like auth() or getSession(), so common patterns from real auth libraries (auth0.getSession(), ctx.auth.getUser(), clerkClient.getUser(), session.auth()) were treated as missing an auth check.

Now any call whose final property is in AUTH_FUNCTION_NAMES is accepted. Generic names like getUser are gated on a receiver-substring pattern (auth/clerk/session/jwt/firebase/supabase/...) so analytics.getUser() is still flagged.

Adds a project-level allowlist via ReactDoctorConfig.serverAuthFunctionNames so codebases that wrap their auth library (requireWorkspaceMember, ensureSignedIn, ...) can opt-in. User-provided names are treated as distinctive and accepted as both bare and member calls.

Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
…lee wrappers, and prune nested helpers

Extends the rule beyond `export async function` declarations:
- `export default async function name() {}` / anonymous default
- `export const fn = async () => {}` and `async function() {}`
- Concise-body arrows (`async () => auth()`)

Unwraps TS-only callee wrappers before identifier/member analysis so
`auth!()`, `(auth as AuthFn)()`, and `<T>(auth)()` are recognized, and
also unwraps `ChainExpression` defensively for optional-chained member
calls (`auth0?.getSession()`).

Prunes nested function bodies during the auth-check scan — a call to
`auth()` inside a hoisted or expression-bound helper that the action
never invokes no longer protects the action.

Drops two dead alternatives (`iron-session`, `better-auth`) from
`AUTH_OBJECT_PATTERN` because they contain `-` and can never match a JS
identifier emitted by `buildDottedReceiverSource`.

Consolidates the duplicated react-doctor settings reader into a shared
`utils/get-react-doctor-setting.ts` (one for strings, one for string
arrays) and updates both `server-auth-actions` and
`no-secrets-in-client-code` to consume it.

Documents `serverAuthFunctionNames` in the README config table.

Adds 12 regression tests covering every new declaration form, both TS
callee wrappers, optional-chained member calls, concise-body arrows,
and the nested-helper pruning. Suite now 22 tests, all passing.

Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
@aidenybai aidenybai merged commit 21474ff into fix/nextjs-get-handler-side-effects-206 May 16, 2026
5 checks passed
@aidenybai aidenybai deleted the fix/server-auth-actions-member-call-239 branch May 16, 2026 07:44
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.

3 participants