Skip to content

Unused server actions have endpoints created for them #63804

@RhysSullivan

Description

@RhysSullivan

Link to the code that reproduces this issue

https://github.com/RhysSullivan/next-unused-server-actions

To Reproduce

  1. run next dev
  2. go to localhost:3000
  3. replace "getSignedInUser" with "getAllUsersPrivateDoNotLeak" in page.tsx
  4. open dev tools to the network tab
  5. press the button, copy the next action header value
  6. revert step 3, changing page.tsx back to just using "getSignedInUser"
  7. ensure "getAllUsersPrivateDataDoNotLeak" is not used anywhere in the app
  8. run next build && next start
  9. make a POST request to localhost:3000, with a json body of an empty array, and the copied next-action header
  10. 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/A

Which 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
image

Metadata

Metadata

Assignees

Labels

bugIssue was opened via the bug report template.locked

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions