server-action-audit 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Del Prudence
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.fr.md ADDED
@@ -0,0 +1,54 @@
1
+ # server-action-audit
2
+
3
+ **Trouvez les Server Actions Next.js que n'importe qui peut appeler.**
4
+
5
+ Chaque fonction `"use server"` est un point d'accès HTTP public. N'importe qui peut l'appeler avec une requête POST, hors de votre interface, avec les arguments de son choix. La [documentation Next.js](https://nextjs.org/docs/app/guides/data-security) est claire : vérifier la connexion au niveau de la page ne protège pas les Server Actions qu'elle contient. Chaque action doit vérifier la session elle-même.
6
+
7
+ Cette vérification s'oublie facilement, et rien ne vous le rappelle. `server-action-audit` liste toutes les Server Actions de votre application et signale celles qui ne vérifient pas qui les appelle : dans le terminal, en CI, et dans votre éditeur grâce à ESLint.
8
+
9
+ ```sh
10
+ npx server-action-audit
11
+ ```
12
+
13
+ ![server-action-audit liste chaque Server Action et signale celles qui ne sont pas protégées](docs/demo.svg)
14
+
15
+ Aucune configuration nécessaire. L'outil lit votre code, il ne l'exécute jamais. Testé sur cinq grosses applications Next.js open source : 456 Server Actions, environ une seconde par application.
16
+
17
+ ## Ce qu'il reconnaît
18
+
19
+ - **Les actions** : les exports des fichiers `"use server"`, et les actions déclarées dans un composant (`<form action={async () => { "use server" … }}>`).
20
+ - **Les bibliothèques d'authentification** : Auth.js / NextAuth, Clerk, Supabase, Better Auth, Lucia, Kinde, iron-session, Firebase Admin, WorkOS AuthKit.
21
+ - **Vos propres fonctions** (`getCurrentUser()`, `requireUser()`…) : il les suit d'un fichier à l'autre, à travers les alias `@/` du tsconfig, les ré-exports et `cache()` de React.
22
+ - **Les clients d'actions** : next-safe-action et zsa. L'outil lit leur middleware, y compris celui d'un client dérivé d'un autre client.
23
+ - **Seuls les contrôles du chemin principal comptent** : une vérification placée dans une branche `if` ou dans une fonction de rappel ne protège pas l'action.
24
+ - **La validation des entrées** : il prévient quand une action utilise ses arguments sans les valider (Zod, Valibot, Yup, ou un schéma next-safe-action / zsa).
25
+
26
+ ## Actions publiques
27
+
28
+ Login, inscription, newsletter : certaines actions sont publiques exprès. Indiquez-le avec un commentaire juste avant l'action, ou en haut de son corps :
29
+
30
+ ```ts
31
+ // Tout le monde peut s'abonner. @public-action
32
+ export async function subscribe(email: string) {}
33
+ ```
34
+
35
+ ## Configuration, ESLint et CI
36
+
37
+ Tout est décrit dans le [README en anglais](README.md#configuration) :
38
+ - le fichier `server-action-audit.config.json` ;
39
+ - le plugin ESLint (`server-action-audit/eslint`) ;
40
+ - les options `--fail-on`, `--json` et `--quiet`.
41
+
42
+ ## Limites
43
+
44
+ C'est une analyse statique, fondée sur les noms. Un rapport sans erreur veut dire « rien d'évident ne manque », pas « l'application est sûre ».
45
+
46
+ - **Elle ne vérifie pas ce que vous faites de la session.**
47
+ - **Elle ne vérifie pas l'appartenance** : être connecté ne veut pas dire avoir le droit de supprimer *ce* contenu.
48
+ - **Elle ne compte pas le middleware**, car la documentation de Next.js recommande de vérifier dans chaque action.
49
+
50
+ Un faux positif, ou une action protégée qui n'est pas reconnue ? [Ouvrez une issue](https://github.com/i6moons/server-action-audit/issues).
51
+
52
+ ## Licence
53
+
54
+ [MIT](LICENSE) © Del Prudence ([@i6moons](https://github.com/i6moons))
package/README.md CHANGED
@@ -1,3 +1,164 @@
1
- # Temporary Holding Version
1
+ # server-action-audit
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Find the Next.js Server Actions that anyone can call.**
4
+
5
+ [![CI](https://github.com/i6moons/server-action-audit/actions/workflows/ci.yml/badge.svg)](https://github.com/i6moons/server-action-audit/actions/workflows/ci.yml)
6
+ [![npm](https://img.shields.io/npm/v/server-action-audit?color=cb3837)](https://www.npmjs.com/package/server-action-audit)
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
8
+
9
+ Every `"use server"` function is a public HTTP endpoint. Anyone can call it with a POST request, from outside your UI, with any arguments. The [Next.js docs](https://nextjs.org/docs/app/guides/data-security) put it plainly: an auth check on the page does not protect the Server Actions defined in it, so each action has to check the session itself.
10
+
11
+ That check is easy to forget, and nothing reminds you. `server-action-audit` lists every Server Action in your app and flags the ones without an authorization check. It runs in your terminal, in CI, and in your editor through ESLint.
12
+
13
+ ```sh
14
+ npx server-action-audit
15
+ ```
16
+
17
+ ![server-action-audit lists every Server Action and flags the unprotected ones](docs/demo.svg)
18
+
19
+ No config needed. It reads your code; it never runs it. Tested on five large open-source Next.js apps: 456 Server Actions, about a second per app.
20
+
21
+ ## What it understands
22
+
23
+ | | |
24
+ | --- | --- |
25
+ | **Actions** | Exports of `"use server"` files (functions, arrow functions, `export { a as b }`, default exports) and inline actions (`<form action={async () => { "use server" … }}>`) |
26
+ | **Auth libraries** | Auth.js / NextAuth v5 (`auth()`), NextAuth v4 (`getServerSession`), Clerk (`auth()`, `currentUser()`, `auth.protect()`), Supabase (`supabase.auth.getUser()`, `getClaims()`), Better Auth (`auth.api.getSession()`), Lucia (`validateRequest()`), Kinde, iron-session, Firebase Admin (`verifyIdToken`, `verifySessionCookie`), WorkOS AuthKit (`withAuth()`) |
27
+ | **Your own helpers** | `getCurrentUser()`, `requireUser()`, `verifySession()`… and any helper that calls one of the above. It follows them across files (relative imports and tsconfig `paths` like `@/lib/dal`), through re-exports and React `cache()`, up to 4 levels deep. Only checks on the main path count: a check inside an `if` branch or a callback does not |
28
+ | **Inline actions** | An action declared in a component that uses the component's session (`const session = await auth()` in the page, `if (!session) return` in the action) |
29
+ | **Action clients** | [next-safe-action](https://next-safe-action.dev) and [zsa](https://github.com/IdoPesok/zsa). It reads the client's middleware, so `actionClient.action(…)` is protected when `actionClient` checks the session, and so is a client derived from it (`actionClient.use(…)`). Guards that keep logged-in users out (`throwIfAuthenticated`) do not count |
30
+ | **Wrappers** | `withAuth(async () => …)`, `validatedActionWithUser(schema, …)`: wrappers whose name says auth, or that you list in the config |
31
+ | **Input validation** | Warns when an action takes arguments and nothing parses them (Zod, Valibot, Yup, or a next-safe-action / zsa schema) |
32
+
33
+ ## Public actions
34
+
35
+ Some actions are public on purpose: login, sign-up, newsletter. Say so with a comment, right before the action or at the top of its body:
36
+
37
+ ```ts
38
+ // Anyone can subscribe. @public-action
39
+ export async function subscribe(email: string) {
40
+ const { email: valid } = SubscribeSchema.parse({ email })
41
+ // …
42
+ }
43
+ ```
44
+
45
+ They show up as `○ public` and never fail the audit. A real auth check always wins over the marker.
46
+
47
+ ## Configuration
48
+
49
+ Teach it your own names in `server-action-audit.config.json`, or under `"serverActionAudit"` in package.json:
50
+
51
+ ```json
52
+ {
53
+ "authFunctions": ["getMe", "session.require"],
54
+ "authWrappers": ["withTeam"],
55
+ "authClients": ["teamActionClient"],
56
+ "publicActions": ["subscribe"],
57
+ "ignore": ["app/legacy/**"]
58
+ }
59
+ ```
60
+
61
+ - **`authFunctions`**: a plain name matches the end of the call (`getMe` matches `getMe()` and `lib.getMe()`). A dotted name matches the end of the path (`session.require` matches `ctx.session.require()`).
62
+ - **`authWrappers`**: functions that wrap an action with an auth check.
63
+ - **`authClients`**: next-safe-action or zsa clients with auth middleware.
64
+ - **`publicActions`**: action names to treat as public.
65
+ - **`ignore`**: globs, relative to the project root.
66
+
67
+ When an action isn't recognized, the report tells you which name to add.
68
+
69
+ ## ESLint
70
+
71
+ The same analysis, in your editor. ESLint 9 or 10, flat config:
72
+
73
+ ```js
74
+ // eslint.config.mjs
75
+ import serverActionAudit from 'server-action-audit/eslint'
76
+
77
+ export default [
78
+ // …your config
79
+ serverActionAudit.configs.recommended,
80
+ ]
81
+ ```
82
+
83
+ | Rule | Default | |
84
+ | --- | --- | --- |
85
+ | `server-action-audit/require-auth` | error | Server Action without an authorization check |
86
+ | `server-action-audit/require-input-validation` | warn | Server Action that uses its arguments without validating them |
87
+
88
+ Options go in `settings.serverActionAudit` (shared by both rules) or on each rule. They use the same format as the config file, without `ignore`.
89
+
90
+ ## CI
91
+
92
+ The CLI exits with code 1 when it finds an unprotected action, so any CI can run it:
93
+
94
+ ```sh
95
+ npx server-action-audit
96
+ ```
97
+
98
+ ### GitHub Actions
99
+
100
+ Problems show up as annotations on the pull request, right on the line of the action:
101
+
102
+ ```yaml
103
+ # .github/workflows/security.yml
104
+ name: Security
105
+ on: [pull_request]
106
+ jobs:
107
+ server-actions:
108
+ runs-on: ubuntu-latest
109
+ steps:
110
+ - uses: actions/checkout@v7
111
+ - uses: i6moons/server-action-audit@v0
112
+ with:
113
+ path: . # where your Next.js app is
114
+ fail-on: unprotected
115
+ ```
116
+
117
+ For GitHub code scanning (the Security tab), upload the SARIF output:
118
+
119
+ ```yaml
120
+ - run: npx server-action-audit --format sarif > server-actions.sarif || true
121
+ - uses: github/codeql-action/upload-sarif@v4
122
+ with:
123
+ sarif_file: server-actions.sarif
124
+ ```
125
+
126
+ ### Options
127
+
128
+ | Option | |
129
+ | --- | --- |
130
+ | `--fail-on unprotected` | Default. Fail on actions without an auth check |
131
+ | `--fail-on unvalidated` | Also fail on unvalidated input |
132
+ | `--fail-on never` | Report only |
133
+ | `--format pretty` | Default. Human-readable report |
134
+ | `--format json` | Machine-readable output (`--json` works too) |
135
+ | `--format github` | GitHub Actions annotations |
136
+ | `--format sarif` | SARIF 2.1.0, for code scanning |
137
+ | `--quiet` | Show problems only |
138
+ | `--config <file>` | Another config file |
139
+
140
+ ## What it does not check
141
+
142
+ This is a static check that works on names. Treat a green report as "nothing obviously missing", not as proof that your app is secure.
143
+
144
+ - **It does not check what you do with the session.** `await auth()` counts as a check even if you then ignore the result. Make sure you stop when there is no user.
145
+ - **It does not check ownership.** Being logged in is not the same as being allowed to delete *this* post. Check that the resource belongs to the user.
146
+ - **Middleware is not counted.** The Next.js docs recommend checking inside each action, and middleware-only protection has been bypassed before ([CVE-2025-29927](https://nextjs.org/blog/cve-2025-29927)). If you rely on it anyway, mark those actions with `@public-action` or list your middleware helper in `authFunctions`.
147
+ - **Unusual patterns can slip through.** Dynamic imports, auth checks hidden in packages from `node_modules`, or a helper whose name contains `auth` without doing any check.
148
+
149
+ Found a false positive or a false negative? [Open an issue](https://github.com/i6moons/server-action-audit/issues) with the smallest snippet that reproduces it.
150
+
151
+ ## API
152
+
153
+ ```ts
154
+ import { scanProject, formatPretty } from 'server-action-audit'
155
+
156
+ const report = scanProject(process.cwd(), { authFunctions: ['getMe'] })
157
+ console.log(report.summary) // { actions, protected, unprotected, public, unvalidated }
158
+ ```
159
+
160
+ ## License
161
+
162
+ [MIT](LICENSE) © Del Prudence ([@i6moons](https://github.com/i6moons))
163
+
164
+ [Lire en français](README.fr.md)