-
Notifications
You must be signed in to change notification settings - Fork 30.6k
Description
Link to the code that reproduces this issue
https://github.com/RhysSullivan/next-unused-server-actions
To Reproduce
- run next dev
- go to localhost:3000
- replace "getSignedInUser" with "getAllUsersPrivateDoNotLeak" in page.tsx
- open dev tools to the network tab
- press the button, copy the next action header value
- revert step 3, changing page.tsx back to just using "getSignedInUser"
- ensure "getAllUsersPrivateDataDoNotLeak" is not used anywhere in the app
- run next build && next start
- make a POST request to localhost:3000, with a json body of an empty array, and the copied next-action header
- notice that despite being an unused function on the client, getAllUsersPrivateDoNotLeak still has an endpoint created for it resutling in data leaking
Current vs. Expected behavior
Current behavior is for all exported functions in a file with "use server" at the top of it to be turned into endpoints.
While this is called out in the docs
A Server Action can be defined with the React "use server" directive. You can place the directive at the top of an async function to mark the function as a Server Action, or at the top of a separate file to mark all exports of that file as Server Actions.
This can lead to developers accidentally exposing endpoints that they didn't mean to and don't ever consume on the client.
A scenario where this can occur: A developer not familiar with this functionality adds a utility function to their actions file, and exports it to test it / use in other locations throughout the app. Now they've accidentally created an endpoint anyone can hit without realizing it.
It's also trivial to detect these unintentionally exposed functions.
For open source projects, all someone has to do is write a script to go through open source NextJS apps and find all exported functions in files with "use server" that aren't consumed on the client
For closed source projects, attackers just have to look at the JS payload to find all server actions which aren't assigned to a variable
Out of curiosity, I did an initial look to see if any open source projects hit this footgun and have already found one, so I think this needs to be addressed.
The expected behavior would be if an exported function from a file with "use server" is not consumed on the client, it should not be turned into an endpoint that can be hit
This shouldn't impact existing functionality at all, and will help accidentally exposing internal functions that are written in a file with the "use server" directive
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 23.0.0: Fri Sep 15 14:42:57 PDT 2023; root:xnu-10002.1.13~1/RELEASE_ARM64_T8112
Available memory (MB): 16384
Available CPU cores: 8
Binaries:
Node: 18.17.0
npm: 9.6.7
Yarn: 1.22.22
pnpm: 8.15.4
Relevant Packages:
next: 14.2.0-canary.46 // Latest available version is detected (14.2.0-canary.46).
eslint-config-next: N/A
react: 18.2.0
react-dom: 18.2.0
typescript: 5.1.3
Next.js Config:
output: N/AWhich area(s) are affected? (Select all that apply)
App Router
Which stage(s) are affected? (Select all that apply)
next dev (local), next build (local), next start (local)
Additional context
To help visualize, here is a screenshot. Top left you can see the actions, top right you can see the only action being used on the client is the public action. Bottom left you can see prod Next running. Bottom right you can see the request succeeding to the endpoint
