@patchstack/connect 0.5.24 → 0.5.25
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/AGENT-INSTALL.md +117 -9
- package/README.md +30 -0
- package/dist/cli.js +522 -153
- package/dist/cli.js.map +1 -1
- package/dist/protect/templates/next-guard.ts +54 -0
- package/dist/protect.cjs +11 -3
- package/dist/protect.cjs.map +1 -1
- package/dist/protect.edge.js +12 -4
- package/dist/protect.edge.js.map +1 -1
- package/dist/protect.js +11 -3
- package/dist/protect.js.map +1 -1
- package/package.json +9 -1
package/AGENT-INSTALL.md
CHANGED
|
@@ -69,12 +69,12 @@ Every command at a glance — what it does, whether it reads your source, what i
|
|
|
69
69
|
| Command | What it does | Reads your source? | Writes to your project | Sends over the network |
|
|
70
70
|
|---|---|---|---|---|
|
|
71
71
|
| `scan` | Provision (or reuse) the site and POST the dependency list for vulnerability matching. Also runs automatically via `setup` and the install/build hooks. | No source analysis — lockfile only; `node_modules/` is enumerated when no lockfile can be read (e.g. `bun.lockb`) or when the lockfiles present disagree. It also reads the `<title>` of the root `index.html` and the `name` in `package.json`, to report what the site is called. During `prebuild` only, it reads the scaffolded guard and its co-located rules JSON to remove a previous map stamp. | `.patchstackrc.json` (public: site UUID + settings); `.patchstackrc.local.json` (the API key, created owner-only) and a `.gitignore` entry for it — the CLI says so if it could not add one; during `prebuild`, removal of a previous `_patchstack.build_id` from the guard's own rules file so a later build cannot carry stale coordinates; the widget `<script>` tag in the root HTML shell — only after a successful post; the production marker in a code root shell — before the post, since it needs no site UUID | Package names + versions; this site's public address and name, where the project or build environment states them |
|
|
72
|
-
| `setup` | One bounded command: `scan` → manage the widget → install + verify `protect` → wire the install/build scans. Never runs the project build. |
|
|
72
|
+
| `setup` | One bounded command: `scan` → manage the widget → install + verify `protect` → wire the install/build scans. Never runs the project build. | Local integration reads via `scan` and `protect` | Config, widget tag, production marker, guard/framework/route files, `package.json` scripts | Package names + versions and the site's public address and name (via `scan`); a claim token as a request header, only when you pass one |
|
|
73
73
|
| `map` | Local attack-surface analysis (entry points → inputs → sinks → evidence-backed flows). Never run by another command. | **Yes** — via the app's own TypeScript; a pre-bundle `--upload` also locates the co-located rules file imported by the scaffolded guard | Only the file named by `--out`; during a pre-bundle `--upload`, `_patchstack.build_id` in that existing rules file | Nothing — **unless `--upload`**: structure only (routes, parameter names, the package behind each sink, file:line) plus a SHA-256 identity derived from its policy content. Never source code or env values |
|
|
74
|
-
| `protect` | Install the always-on runtime guard; auto-wire known stacks, or scaffold a generic guard + print a wiring plan. `--check` verifies the guard is wired (exit 1 if not); `--demo` seeds a broad sample rule set. Runs automatically **only** via `setup` — never by `scan`, `guide`, `status`, or `mark-build`. |
|
|
75
|
-
| `demo node-serialize` | Production-backed walkthrough: confirm the vulnerable package is present, scan, wait for live rule `18843`, install + verify the guard, print test requests. Does not install the package or start/restart the app. |
|
|
76
|
-
| `demo-guide node-serialize` | Read-only companion: explains the prepare/run/prove/cleanup sequence and prints the next command. |
|
|
77
|
-
| `guide` | Print this project's live setup status (done/missing, with tailored commands), then the full guide. `--full` prints it even when setup is complete. |
|
|
74
|
+
| `protect` | Install the always-on runtime guard; auto-wire known stacks, or scaffold a generic guard + print a wiring plan. `--check` verifies the guard is wired (exit 1 if not); `--demo` seeds a broad sample rule set. Runs automatically **only** via `setup` — never by `scan`, `guide`, `status`, or `mark-build`. | Reads local wiring/route source for integration; does not produce an attack-surface map | Guard/framework files (e.g. `middleware.ts`, `src/patchstack/`, supported Next App Router handlers) | Nothing |
|
|
75
|
+
| `demo node-serialize` | Production-backed walkthrough: confirm the vulnerable package is present, scan, wait for live rule `18843`, install + verify the guard, print test requests. Does not install the package or start/restart the app. | Local integration reads via `scan` and `protect` | Same files as `scan` + `protect` | `scan` payload; polls the public Pulse rules endpoint (never the printed test requests) |
|
|
76
|
+
| `demo-guide node-serialize` | Read-only companion: explains the prepare/run/prove/cleanup sequence and prints the next command. | Reads local protection wiring for verification | Nothing | Nothing |
|
|
77
|
+
| `guide` | Print this project's live setup status (done/missing, with tailored commands), then the full guide. `--full` prints it even when setup is complete. | Reads local protection wiring for verification | Nothing | Nothing |
|
|
78
78
|
| `status` | Re-print the site UUID + dashboard URL and check whether the site still exists (active / removed / could not verify). | No | Nothing | Site-existence check |
|
|
79
79
|
| `init <site-uuid>` | Optional: pre-seed `.patchstackrc.json` with an existing UUID. | No | `.patchstackrc.json` only | Nothing |
|
|
80
80
|
| `mark-build` | Ensure the widget tag in built pages, and — **on a production build only** — stamp the live-site flag + build fingerprint. A local or preview build is stamped with neither, and has a stale marker removed; `--production` forces it for a build published by hand. Run as a `postbuild` step. | No | Build output only (`dist/ build/ out/ .output/public/ _site/`) — never source | The same manifest `scan` sent, plus one word for what it did with the marker — and only for a site already registered |
|
|
@@ -82,7 +82,7 @@ Every command at a glance — what it does, whether it reads your source, what i
|
|
|
82
82
|
| `login` | Recover a lost credential for an existing site: print an owner-approval link and poll (10 min). Approving **rotates** the credential. Not usable in CI. | No | New credential into `.patchstackrc.local.json` on approval | Device-code request + approval poll |
|
|
83
83
|
| `uninstall` | Signal Patchstack that the package is being removed: an unclaimed record is deleted, a claimed one is flagged. Does **not** touch local files. | No | Nothing local | Removal signal |
|
|
84
84
|
|
|
85
|
-
Only `map`
|
|
85
|
+
Only `map` produces an attack-surface analysis, and only `map --upload` sends that description. `protect` reads and edits local source to integrate the guard; its verifier, also used by setup guides, reads the wiring without executing the app. These integration reads transmit nothing. `scan` additionally reads two declarations the project makes about itself — the `<title>` in the root `index.html` and the `name` in `package.json` — to report what the site is called; during `prebuild` it also reads the scaffolded guard and its co-located rules JSON solely to remove a previous map stamp. `scan` transmits package names + versions, plus the site's own public address and name where the project states them — never source code, file paths, git history, or any environment variable value other than the published URL of this site. `scan --install-paths` additionally sends where each package sits in the dependency tree; it is off unless you pass it.
|
|
86
86
|
|
|
87
87
|
## Package and command behavior
|
|
88
88
|
|
|
@@ -94,12 +94,12 @@ Only `map` analyses your source, and only `map --upload` sends anything derived
|
|
|
94
94
|
- **Only `scan` looks for the address and the name.** They are resolved in the one code path that reports them, so `guide`, `status`, `login`, `uninstall`, `mark-build`, `init`, `protect`, `demo-guide` and `map` neither read the host's URL variables nor open `index.html` or `package.json` for this. `setup` and `demo` do, because both run `scan`.
|
|
95
95
|
- **The address is the one your visitors use, and, apart from the tool's own `PATCHSTACK_*` settings, it is the only env var value read.** A site provisioned by a scan from a developer machine has no address, so the dashboard shows a placeholder and Patchstack cannot check that the published page still carries what was scanned. `scan` therefore sends `url` when — and only when — it can know it: `url` in `.patchstackrc.json` or `PATCHSTACK_SITE_URL` if you set one, otherwise the single variable a host publishes to name its own **production** URL (`VERCEL_PROJECT_PRODUCTION_URL` on a Vercel production deployment, Netlify's `URL` in the production context, `RENDER_EXTERNAL_URL`, `RAILWAY_PUBLIC_DOMAIN` in a production environment). Preview and branch deployments are excluded, as are hosts that publish no production signal. An address that is not how the public reaches a website is dropped: any IP address (in either family, however it is written), any single-label host such as `localhost` or `production`, and the reserved suffixes (`.local`, `.internal`, `.test`, `.invalid`, `.home.arpa`, …). A `url` you set explicitly that fails those checks is refused with an error rather than replaced by a guess. When nothing qualifies, `url` is omitted from the payload rather than guessed. Patchstack only ever applies it to a site that still has no address; it never re-points a site whose address is already real.
|
|
96
96
|
- **The name is read from your project, never from the host environment.** `name` in `.patchstackrc.json` (or `PATCHSTACK_SITE_NAME`) if you set one; otherwise the `<title>` of the project's root `index.html` (`public/index.html` if there is no root one), read from the file as text — a title your app sets from script is not seen; otherwise the `name` in `package.json`, unless it is a template placeholder such as `vite_react_shadcn_ts`. It is omitted when nothing qualifies, and it only ever fills in a site that has no name yet — a name set in the dashboard is never replaced.
|
|
97
|
-
- **Only `map`
|
|
97
|
+
- **Only `map` produces an attack-surface analysis.** It parses your server source to report your app's attack surface. It runs only when you invoke it and prints to stdout. It transmits nothing unless you explicitly pass `--upload`, which sends that description of your app's structure to your own site's Patchstack endpoint — never source code, and never without that flag. `protect` separately parses supported wiring and route files for local integration, without producing or uploading a map. A `prebuild` scan reads the scaffolded guard and rules JSON only to identify and clear the reserved map stamp; it does not analyse them or transmit their contents.
|
|
98
98
|
- **`scan` makes up to three source edits:** the Patchstack Connector's `<script>` tag, the production marker, and — during `prebuild` only — removal of a previous `_patchstack.build_id` from the existing guard rules file. None runs on `--dry-run`; all are idempotent. `"widget": false` disables the first two, while stale-stamp removal is independent because it prevents old coordinates being attributed to a new build.
|
|
99
99
|
- The **widget tag** goes in the root HTML shell — the first of `index.html`, `public/index.html`, or `src/app.html` that exists — and only after a successful post, because it carries the site UUID.
|
|
100
100
|
- The **production marker** goes in a root shell that is JSX rather than HTML (e.g. `src/routes/__root.tsx`, `app/layout.tsx`), inside a `{/* #region patchstack */}` block placed above the widget tag. It is written *before* the post: it carries no site UUID and needs no network, and build scripts commonly chain `patchstack-connect scan || true`, where waiting on the server would mean an offline build silently ships without the flag. The marker is guarded by the framework's own production expression (`import.meta.env.PROD`, or `process.env.NODE_ENV === 'production'`), so it is inert in dev and preview builds. Without it a server-rendered site has no built HTML for `mark-build` to stamp, and the widget treats the published site as build mode. `mark-build` writes to build output only (`dist/`, `build/`, `out/`, `.output/public`), never to source. `guide`, `status`, and `init` write nothing except `init`'s own `.patchstackrc.json`.
|
|
101
101
|
- **`setup` runs `scan`, then `protect`, then edits `package.json` scripts:** provisioning happens first so the runtime guard can bake the real site UUID. It verifies the resulting framework seam, preserves existing commands, adds `scan` after dependency installs and before builds, adds `mark-build` after builds, and uses a direct build chain for Bun. It never runs the project build. If the widget or runtime guard needs a framework-specific manual merge, it prints the exact remaining step instead of overwriting user code.
|
|
102
|
-
- The package also exposes **`protect`** directly (runtime exploit guard; its templates live under `dist/protect/`). `setup` invokes it automatically; `scan`, `guide`, `status`, and `mark-build` do not. It writes only local files and auto-wires known stacks — **TanStack Start + Supabase** (patches the Supabase client + `src/start.ts`), **Next.js** (scaffolds
|
|
102
|
+
- The package also exposes **`protect`** directly (runtime exploit guard; its templates live under `dist/protect/`). `setup` invokes it automatically; `scan`, `guide`, `status`, and `mark-build` do not. It writes only local files and auto-wires known stacks — **TanStack Start + Supabase** (patches the Supabase client + `src/start.ts`), **Next.js** (scaffolds or composes middleware and adds request/response checks to supported App Router handlers), **SvelteKit** (`src/hooks.server.ts`), **Astro** (`src/middleware.ts`), **Nuxt** (`server/middleware/`), **NestJS** (`app.use(patchstackMiddleware)` in the bootstrap), **Fastify** (`app.register(patchstackFastify)`), and **Express** (`app.use(patchstackMiddleware)`). On **any other stack** it scaffolds a framework-agnostic guard under `src/patchstack/` and prints a wiring plan — then you finish the install by importing that guard into your server entry (`protectFetch(handler)` for a Web-Fetch server, or `app.use(patchstackMiddleware)` for Node/Express) and running `patchstack-connect protect --check` to confirm it is wired (exit 1 until it is). Passing `--demo` seeds a broad sample rule set (for demonstrations, not production).
|
|
103
103
|
- **`demo node-serialize` is an explicit production-backed walkthrough.** It requires `node-serialize@0.0.4` to already be present in the lockfile; it does not install the vulnerable dependency. It runs the same production `scan`, polls the configured site's public Pulse rules endpoint until rule `18843` is served, runs `protect`, verifies the generated guard, and prints exploit/benign test requests. It writes the same manifest/widget and guard files as those underlying commands. It does not start/restart the app and does not send the printed requests.
|
|
104
104
|
- **`map` is local unless you pass `--upload`.** It walks the project's server source (skipping `node_modules`, build output and dot-directories; it does not follow symlinks out of the project unless you pass `--follow-symlinks`), parses it with the project's **own** `typescript`, and prints JSON describing the attack surface: entry points, the inputs each reads, the sinks they can reach (database / file system / process / outbound HTTP) with the npm package behind each, and evidence-backed input→sink flows, each labelled with how the link was established — from an exact read at the sink's own call site, through a transformed or cross-module link, down to the two being present together with no proven link. Static analysis is best-effort, so the output reports the *detected* surface with coverage counters — not a completeness guarantee. Without `--upload` it writes nothing except the file named by `--out`, and it is never invoked by `scan`, `setup`, `guide`, `protect`, or `mark-build`.
|
|
105
105
|
- **`map --upload` is the only command that sends a description of your source.** (The runtime guard can also report rule detections, which carry route paths and parameter names — see "Runtime guard reporting" below.) It POSTs the same JSON document to `monitor/pulse/input-map/<your site uuid>` so Patchstack can pin protection rules to your app's own parameter names instead of guessing them. During a pre-bundle build hook it hashes the policy-relevant document (all fields except analyser timing and memory observations), writes the SHA-256 value as `_patchstack.build_id` in the existing rules file imported by the scaffolded guard, and sends the same value as `build_id`. Outside that lifecycle it sends no identity and changes no file, so any generated scoped rule remains detect-only. **No source code, no file contents, no environment variable values.** A map with no recognised entry points is still uploaded because its import inventory and coverage limits are evidence; a failure to reach Patchstack is reported and ignored rather than failing your build. Omit the flag and the command stays entirely local.
|
|
@@ -161,6 +161,11 @@ Only when the person asks you to publish the site. Two things decide whether Pat
|
|
|
161
161
|
|
|
162
162
|
An app with a server needs this for runtime protection: the guard fetches its rules with the key. Without it the deploy is still recognised as long as the packages have not changed since the last scan, but nothing is protected. Never put the key in a committed file, in a public variable (`NEXT_PUBLIC_*`, `VITE_*`), or in your reply.
|
|
163
163
|
- **A host that builds the site itself but is not named under "How each environment is labelled"** — ChatGPT Sites (`*.chatgpt.site`, published from Codex) is one — gives Patchstack nothing that says "production", so its builds report `local` and the live site is never recognised as deployed. In that host's environment settings for the published site, set two variables: `PATCHSTACK_API_KEY`, read from `.patchstackrc.local.json` as above, and `PATCHSTACK_ENVIRONMENT=production`. Set the label only where the published build reads it, never in a committed file, and not in settings a preview or the local dev server also reads. Then publish again: the deployed site keeps serving the build from before setup until it is rebuilt.
|
|
164
|
+
- **Give Patchstack the live address when the host does not.** Patchstack confirms a deploy by reading the build marker off the live page, either when a visitor's browser loads the widget or through its own scheduled check of the site's address. A new site may get no visitors for days, so the scheduled check is often the only confirmation, and it can only read an address Patchstack has. Vercel, Netlify, Render and Railway production builds report their address themselves (see "Package and command behavior"). Anywhere else, including ChatGPT Sites, Lovable or Bolt, a custom domain, GitHub Pages, Cloudflare Pages or a folder uploaded by hand, take the public address from the deploy output or ask the person for it. Then:
|
|
165
|
+
1. Add it to `.patchstackrc.json` as `"url": "https://your-app.example.com"`. Use the address visitors use, never a preview or a per-deployment URL. The address is public, so this file can be committed.
|
|
166
|
+
2. Run `npx @patchstack/connect scan` once, so Patchstack learns it.
|
|
167
|
+
|
|
168
|
+
Patchstack applies the address only to a site that has no real address yet, meaning a placeholder or a builder preview. An address it already holds is never replaced, so if the dashboard shows a different one, the person changes it in the site's settings. A private site cannot be read this way; see the next item.
|
|
164
169
|
- **Check the live site after deploying**, and tell the person what you found:
|
|
165
170
|
- `curl -s <live url> | grep -ac __PATCHSTACK_PROD__` prints `1` or more. `0` means the build did not know it was production, and the widget will treat the live site as a preview.
|
|
166
171
|
- A site published privately answers `curl` with the host's sign-in page, so the check above prints `0` whatever was deployed — check the status code first (`curl -s -o /dev/null -w "%{http_code}" <live url>`). On a `401`, `403` or a redirect to a sign-in page, ask the person to open the live site while signed in and run `window.__PATCHSTACK_BUILD__` in the browser console: a short build ID means the marker is live, `undefined` means it is not. Patchstack cannot read a private page either, so its dashboard shows the deploy as reported, not confirmed, until a signed-in visit lets the widget report the build. Tell the person that, rather than describing the deploy as confirmed.
|
|
@@ -324,7 +329,20 @@ It is server-only. Never put it in the widget tag, client bundles, or public env
|
|
|
324
329
|
npx @patchstack/connect protect --check
|
|
325
330
|
```
|
|
326
331
|
|
|
327
|
-
`setup` performs both steps automatically. The explicit commands are for manual setup or repair. If verification reports a generic or existing framework seam, complete the
|
|
332
|
+
`setup` performs both steps automatically. The explicit commands are for manual setup or repair. If verification reports a generic or existing framework seam, follow [Completing guard wiring](#completing-guard-wiring), complete the source edit and re-run `--check`; do not report protection as active until it exits successfully.
|
|
333
|
+
|
|
334
|
+
**Next.js:** `protect` uses the installed TypeScript parser to compose supported middleware while
|
|
335
|
+
preserving its original routing scope, and edits supported `app/**/route.ts`/`route.js` handlers to
|
|
336
|
+
check requests and filter returned responses. It writes a shared server-only `patchstack.next`
|
|
337
|
+
helper alongside `patchstack.rules.json`. Unsupported exports, complex matchers or handlers are
|
|
338
|
+
left unchanged and reported by `--check`; re-run `protect` after adding routes. An existing
|
|
339
|
+
`proxy.ts`/`proxy.js` requires manual integration: no competing middleware is scaffolded and
|
|
340
|
+
`--check` reports the gap. Middleware alone
|
|
341
|
+
cannot filter downstream page bodies. Rendered pages, Server Actions and Pages API response
|
|
342
|
+
filtering are not verified by this adapter. Keep Next.js patched: a framework middleware bypass
|
|
343
|
+
also bypasses a guard in middleware. Edge middleware needs `PATCHSTACK_API_KEY` in the server
|
|
344
|
+
environment; it cannot read `.patchstackrc.local.json`. Do not put credentials in public variables
|
|
345
|
+
or commit them. Source checks do not verify rule delivery or blocking in the running deployment.
|
|
328
346
|
|
|
329
347
|
`--check` reads the app's source. It can establish that the guard is imported and called on a request
|
|
330
348
|
path; it cannot establish that a request ever reaches it — an app can wire the guard onto one server
|
|
@@ -377,6 +395,96 @@ It is server-only. Never put it in the widget tag, client bundles, or public env
|
|
|
377
395
|
2. **The dashboard link** the scan printed — open it in a browser and sign in.
|
|
378
396
|
3. **`npx @patchstack/connect claim`** from the terminal, which prints a link to sign in with and then attaches the site.
|
|
379
397
|
|
|
398
|
+
## Completing guard wiring
|
|
399
|
+
|
|
400
|
+
Use this procedure when scaffolding leaves a manual step, and review automatic edits against it too.
|
|
401
|
+
The printed entry candidates and framework names are hints. A generated guard or a successful source
|
|
402
|
+
check is not proof that every deployed route passes through it.
|
|
403
|
+
|
|
404
|
+
1. **Find what actually serves requests.** Read the application's package, installed framework version,
|
|
405
|
+
start/build scripts, deployment adapter and existing server hooks. In a workspace, inspect each
|
|
406
|
+
deployed package separately. Trace the production entry to pages, APIs, loaders/actions, RPC and
|
|
407
|
+
server functions; include separately deployed functions and additional listeners. UI dependencies
|
|
408
|
+
and Vite alone do not establish a server or a static-only deployment. For a static export, establish
|
|
409
|
+
that no request handler is deployed before reporting runtime protection as not applicable.
|
|
410
|
+
2. **Choose the shared request entry.** Prefer the framework's server hook or the deployed server's
|
|
411
|
+
outer handler over individual routes. Read the generated guard's exports and calling convention.
|
|
412
|
+
`protectFetch` wraps a handler receiving a Web `Request` and returning a `Response`; it is not a
|
|
413
|
+
wrapper for arbitrary framework contexts or response-writing callbacks. Preserve handler arguments,
|
|
414
|
+
runtime bindings and any required receiver. Do not pass Connect middleware directly to Koa, Hapi,
|
|
415
|
+
Adonis or other incompatible middleware APIs. If the conversion cannot be established, leave that
|
|
416
|
+
entry unwired and name the missing integration rather than inventing an API.
|
|
417
|
+
3. **Compose a minimal edit.** Preserve authentication, redirects, rewrites, cookies, headers, errors
|
|
418
|
+
and existing matchers. Inspect matcher exclusions for skipped application routes. Keep the guard
|
|
419
|
+
server-only and return its blocking response before calling application code; call the original
|
|
420
|
+
handler once for an allowed request. The Express adapter's guard uses parsed `req.body` and belongs
|
|
421
|
+
**after** the body parser, before routes. The generic Node guard reads the stream and belongs
|
|
422
|
+
**before** body parsers. Preserve raw-body webhook verification and test uploads and streams before
|
|
423
|
+
claiming those paths work. Do not edit generated build output, replace an existing hook wholesale,
|
|
424
|
+
add a server to a static app, or rerun scaffolding over a manually adapted guard without reviewing
|
|
425
|
+
what it will write. Check the diff for duplicate registrations and unrelated changes.
|
|
426
|
+
4. **Verify the integration and its limits.** Run `protect --check`, then the app's existing typecheck,
|
|
427
|
+
build and relevant request tests. In a local test environment, check an allowed request and a
|
|
428
|
+
controlled blocking case for each independent entry and representative page/API/action path;
|
|
429
|
+
verify the blocked request does not reach the handler. Include existing authentication, redirects,
|
|
430
|
+
body handling and error behavior. The opt-in `--runtime` check described above probes the guard
|
|
431
|
+
seam; it does not prove route coverage or rule effectiveness. Record an unsupported probe or an
|
|
432
|
+
unrecognized custom seam as unverified. Never add marker comments, dummy imports or unused calls
|
|
433
|
+
merely to make the source check pass.
|
|
434
|
+
|
|
435
|
+
### Framework entry points to inspect
|
|
436
|
+
|
|
437
|
+
These are navigation hints for agent-assisted integration, not additional automatic adapters or a
|
|
438
|
+
compatibility guarantee. Confirm the installed version and deployment mode before choosing a hook.
|
|
439
|
+
The linked framework documentation describes its lifecycle; the generated Connect guard determines
|
|
440
|
+
which integration API is available.
|
|
441
|
+
|
|
442
|
+
| Framework or UI layer | Server entry and coverage question |
|
|
443
|
+
| --- | --- |
|
|
444
|
+
| [React](https://react.dev/learn/creating-a-react-app) | Find the hosting framework or custom server. A browser component or client router is not a request guard. |
|
|
445
|
+
| [Vue](https://vuejs.org/guide/scaling-up/ssr.html) | Inspect the SSR host or separate API; component setup and router navigation guards do not guard server requests. |
|
|
446
|
+
| [Angular](https://angular.dev/guide/ssr) | Distinguish browser/prerender output from the deployed SSR server; inspect `server.ts` and its HTTP adapter. |
|
|
447
|
+
| [Svelte](https://svelte.dev/docs/svelte/overview) | Determine whether this is a browser bundle, SvelteKit or a custom SSR host before choosing a server hook. |
|
|
448
|
+
| [Preact](https://preactjs.com/guide/v10/server-side-rendering/) | Guard the host invoking SSR or APIs; a render function alone is not the shared HTTP entry. |
|
|
449
|
+
| [Solid](https://docs.solidjs.com/quick-start) | Separate the UI library from SolidStart or a custom server; keep protection out of client components. |
|
|
450
|
+
| [Qwik](https://qwik.dev/docs/qwikcity/) | Inspect Qwik City and the deployment adapter; component resumability does not identify the request entry. |
|
|
451
|
+
| [Ember](https://guides.emberjs.com/release/getting-started/quick-start/) | Inspect the deployed backend or SSR host separately; browser routes and the development server are not production coverage. |
|
|
452
|
+
| [Next.js](https://nextjs.org/docs/app/api-reference/file-conventions/proxy) | Inspect root or `src/` middleware/proxy, matchers, APIs and Server Actions. Next 16 renamed middleware to proxy; Connect scaffolds `middleware.ts`. Do not leave competing files or assume its source check validates `proxy.ts`. |
|
|
453
|
+
| [Nuxt](https://nuxt.com/docs/4.x/directory-structure/server) | Inspect the configured server directory and Nitro server middleware, not client navigation middleware. Distinguish a server deployment from generated static output. |
|
|
454
|
+
| [SvelteKit](https://svelte.dev/docs/kit/hooks) | Compose the existing server `handle` hook; check endpoints, actions, prerendering and the deployed adapter. |
|
|
455
|
+
| [Astro](https://docs.astro.build/en/guides/middleware/) | Compose `onRequest` in server middleware; distinguish execution during prerendering from on-demand routes behind an adapter. |
|
|
456
|
+
| [Remix](https://v2.remix.run/docs/discussion/runtimes/) | Inspect the adapter around `createRequestHandler`; cover document requests, loaders, actions and resource routes, not only `entry.server` rendering. |
|
|
457
|
+
| [React Router](https://reactrouter.com/how-to/middleware) | Determine library versus framework/SSR mode. Inspect the server adapter and version-specific server middleware; client middleware cannot guard loaders/actions on the server. |
|
|
458
|
+
| [TanStack Start](https://tanstack.com/start/latest/docs/framework/react/guide/middleware) | Inspect the server entry and global request middleware, including server functions. The automatic TanStack/Supabase adapter matches a particular project layout, not every Start app. |
|
|
459
|
+
| [SolidStart](https://docs.solidjs.com/solid-start/v1/advanced/middleware) | Inspect configured server middleware and adapter; verify API and server action paths separately rather than assuming rendering middleware covers them. |
|
|
460
|
+
| [Qwik City](https://qwik.dev/docs/middleware/) | Inspect deployment entry and request middleware, including endpoints, loaders and actions. Confirm route/layout scope and static output. |
|
|
461
|
+
| [Gatsby](https://www.gatsbyjs.com/docs/reference/functions/) | Static pages need no request guard, but `src/api` functions and SSR deployments need their own server entry review. |
|
|
462
|
+
| [Docusaurus](https://docusaurus.io/docs/deployment) | Confirm static output; inspect any separately deployed API or custom server without adding a guard to browser code. |
|
|
463
|
+
| [Eleventy](https://www.11ty.dev/docs/) | Confirm static output; review accompanying functions or a custom server separately from build-time templates. |
|
|
464
|
+
| [Express](https://expressjs.com/en/guide/using-middleware/) | Register the generated Express guard after its body parser and before routers, for every app that actually serves traffic. |
|
|
465
|
+
| [NestJS](https://docs.nestjs.com/middleware) | Inspect `NestFactory.create` and the selected HTTP adapter. Express-style middleware is not proof of Fastify compatibility or microservice/WebSocket coverage. |
|
|
466
|
+
| [Fastify](https://fastify.dev/docs/latest/Reference/Hooks/) | Register the generated plugin in the root scope before route plugins; inspect encapsulation and every server instance. |
|
|
467
|
+
| [Hono](https://hono.dev/docs/api/hono) | Inspect the deployed Fetch entry and preserve environment/context arguments; check mounted apps and any other exported handlers. |
|
|
468
|
+
| [Koa](https://koajs.com/) | Inspect the Node server around `app.callback()` or use a verified Koa integration; `(ctx, next)` is not Connect's `(req, res, next)`. |
|
|
469
|
+
| [Elysia](https://elysiajs.com/integrations/cheat-sheet) | Inspect the actual Bun/Node/Fetch deployment entry and plugin scope; a local hook need not cover sibling routes. |
|
|
470
|
+
| [AdonisJS](https://docs.adonisjs.com/guides/basics/middleware) | Inspect the server middleware stack in `start/kernel.ts`; named route middleware alone leaves other routes outside its scope. |
|
|
471
|
+
| [Hapi](https://hapi.dev/api/21.x.x) | Inspect server lifecycle extensions and payload timing; adapt request/response semantics instead of passing Express middleware to `server.ext`. |
|
|
472
|
+
| [Nitro](https://nitro.build/docs/migration) | Inspect installed major, configured server directories, middleware and deployment preset; directory scanning conventions differ across versions. |
|
|
473
|
+
| [Strapi](https://docs.strapi.io/cms/backend-customization/middlewares) | Inspect configured global Koa middleware; route middleware and Document Service middleware do not establish whole-server HTTP coverage. |
|
|
474
|
+
|
|
475
|
+
### When wiring cannot be completed
|
|
476
|
+
|
|
477
|
+
Return a short handoff in the current conversation for the person or their coding agent: framework
|
|
478
|
+
and version, deployment mode, project-relative server entries, files changed, exact failing check,
|
|
479
|
+
the remaining edit, and the routes or services whose coverage is unknown. Include commands actually
|
|
480
|
+
run and their results; omit credentials and environment values. If no server entry can be established,
|
|
481
|
+
say so and identify the missing deployment information. Do not invent one to clear a checklist.
|
|
482
|
+
|
|
483
|
+
Keep automatic scaffolding, source verification, local request verification and deployed coverage
|
|
484
|
+
separate in the report. HTTP middleware does not establish coverage of jobs, queues, WebSocket messages
|
|
485
|
+
or services deployed elsewhere. Connect prints a local plan; it does not automatically contact another
|
|
486
|
+
AI model. A framework or hosting upgrade requires this review again.
|
|
487
|
+
|
|
380
488
|
## Rules
|
|
381
489
|
|
|
382
490
|
- Never invent or guess a UUID — the scan provisions it, the widget silently no-ops on a fake one.
|
package/README.md
CHANGED
|
@@ -200,6 +200,36 @@ Options (for demo and demo-guide):
|
|
|
200
200
|
(default: http://localhost:3000/api/tasks)
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
+
### Next.js request and response protection
|
|
204
|
+
|
|
205
|
+
`protect` composes straightforward existing middleware instead of replacing its authentication or
|
|
206
|
+
redirect logic. The request guard gets a catch-all matcher; the application's middleware still runs
|
|
207
|
+
only within its original scope. Automatic composition accepts directly exported handlers and literal
|
|
208
|
+
path matchers (including a terminal `/:path*`). Complex matchers, re-exports and custom URL routing
|
|
209
|
+
are left untouched with an integration message. Source-aware edits use the application's installed
|
|
210
|
+
`typescript` parser; configuration files are never executed.
|
|
211
|
+
|
|
212
|
+
Apps with an existing `proxy.ts`/`proxy.js` (including under `src/`) require manual integration.
|
|
213
|
+
The installer leaves them unchanged and `protect --check` reports the gap. It never adds middleware
|
|
214
|
+
alongside a proxy, since Next.js does not allow both.
|
|
215
|
+
|
|
216
|
+
For App Router `app/**/route.ts` or `route.js` files, it also adds request checks and screens each
|
|
217
|
+
returned response. The shared server-only `patchstack.next` helper initializes one policy per module
|
|
218
|
+
instance, retries a failed initialization, and imports the same fallback rules file as middleware.
|
|
219
|
+
Supported handlers are directly exported async functions or block-bodied async arrows without an
|
|
220
|
+
explicit return type. Re-run `protect` after adding routes. Unsupported handlers remain unchanged;
|
|
221
|
+
`protect --check` lists the gaps instead of reporting complete route coverage.
|
|
222
|
+
|
|
223
|
+
Middleware cannot inspect the body that a downstream page returns. This setup does **not** establish
|
|
224
|
+
response filtering for rendered pages, Server Actions or Pages Router API handlers. Those need their
|
|
225
|
+
own server-side response boundary. It also cannot repair a vulnerability that bypasses Next.js
|
|
226
|
+
middleware itself: keep Next.js patched or filter such requests before they reach Next.js.
|
|
227
|
+
|
|
228
|
+
Set `PATCHSTACK_API_KEY` in the server environment for live rule delivery. Edge middleware cannot read
|
|
229
|
+
the local credential file. Never expose it as a `NEXT_PUBLIC_*` variable. A passing source check is not
|
|
230
|
+
proof of live protection: the running guard's `ruleSource`, `mode`, and coverage determine what it
|
|
231
|
+
actually received and screened.
|
|
232
|
+
|
|
203
233
|
### Verifying the guard at runtime (opt-in)
|
|
204
234
|
|
|
205
235
|
`protect --check` reads the app's source. That establishes the guard is imported and called on a
|