fix(server-auth-actions): accept member-expression auth calls like auth0.getSession() (#239)#261
Merged
aidenybai merged 4 commits intoMay 16, 2026
Conversation
|
🔴 React Review — 0/100 (unchanged) · No new issues Reviewed by react-review for commit fcd7f8b. Configure here. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
aidenybai
reviewed
May 15, 2026
978df73 to
5bff156
Compare
…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>
Closed
3 tasks
bd9778a to
71ecf0a
Compare
5bff156 to
3b9af6a
Compare
This was referenced May 16, 2026
…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>
21474ff
into
fix/nextjs-get-handler-side-effects-206
5 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on top of #260.
server-auth-actionsonly recognised auth calls when the callee was a bareIdentifier—auth(),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
CallExpressionthrough the existinggetCalleeNameutility, which already resolves bothIdentifierandMemberExpressioncallees to a single name.walkAstalready descends intoAwaitExpression/ChainExpressionchildren, 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 ifbranch duplicating the identifier check) and reuses code we already have.Now accepted as an auth check
await auth0.getSession()— the verbatim reproauth0.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
.something()member methods.Test plan
packages/react-doctor/tests/regressions/server-auth-actions-member-call.test.tswith 6 false-negative cases and 3 true-positive cases.nr typecheck,nr lint,nr test(738/738),nr formatall pass.server-auth-actionsregressions inpackages/react-doctor/tests/run-oxlint/nextjs.test.tsstill pass.Closes #239. Supersedes #240.
Eval results
Re-ran against the larger sandbox corpus (500 repos, scanned via Vercel Sandbox in parallel) on
mainvs parent #260 vs this branch:main(8a3a1be)server-auth-actionsZero functional changes across 500 repos. The 43 line-level diffs vs
mainare 100% message-text encoding artifacts (same file/line/column/rule, em-dash rendering); no diagnostic actually moves. All 54server-auth-actionshits stay flagged — these are real true positives insupabase/supabase,payloadcms/payload,unkeyed/unkey, and others: server actions performingprisma.*/ 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 newserver-auth-actions-member-call.test.tsregression 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-actionssecurity 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-actionsso 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 ..., andexport 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
serverAuthFunctionNamesthat is forwarded fromreact-doctorconfig into oxlintsettingsand treated as an allowlist of custom auth guards. Introduces sharedgetReactDoctor*Settinghelpers for safe typed reads fromcontext.settings, updatesno-secrets-in-client-codeto use them, documents the new config in the README/types, and adds regression tests covering the newserver-auth-actionsbehaviors.Reviewed by Cursor Bugbot for commit fcd7f8b. Bugbot is set up for automated code reviews on this repo. Configure here.