@patchstack/connect 0.3.30 → 0.3.32

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 CHANGED
@@ -21,12 +21,15 @@ Every command at a glance — what it does, whether it reads your source, what i
21
21
  | `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 |
22
22
  | `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 |
23
23
 
24
- Only `map` reads your source, and only `map --upload` sends anything derived from it. `scan` transmits nothing but package names + versions — never source code, env var values, file paths, or git history.
24
+ Only `map` reads your source, and only `map --upload` sends anything derived from it. `scan` transmits nothing but package names + versions — never source code, env var values, file paths, or git history. `scan --install-paths` additionally sends where each package sits in the dependency tree; it is off unless you pass it.
25
25
 
26
26
  ## Package and command behavior
27
27
 
28
28
  - Package: [`@patchstack/connect`](https://www.npmjs.com/package/@patchstack/connect), MIT-licensed, source at https://github.com/patchstack/connect. `npm view @patchstack/connect` shows the live registry metadata.
29
- - **What is sent to Patchstack is the dependency list only** — read from the lockfile (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`) or, on bun projects (`bun.lock`/`bun.lockb`), by enumerating the installed packages under `node_modules/` — package names + versions, for vulnerability matching. No source code, no env var values, no file paths, no git history is ever transmitted. (`mark-build` additionally stamps built HTML with a coarse stack descriptor that may include hosting-related env variable *names* — e.g. `VERCEL`, `CF_PAGES` — never their values.)
29
+ - **What is sent to Patchstack is the dependency list only** — read from the lockfile (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`) or, on bun projects (`bun.lock`/`bun.lockb`), by enumerating the installed packages under `node_modules/` — package names + versions, for vulnerability matching. No source code, no env var values, no file paths, no git history is ever transmitted.
30
+ - **`scan --install-paths` is the one exception, and it is opt-in.** It adds where each package sits in the dependency tree — repo-relative paths made of `node_modules` segments, plus a workspace directory name when a workspace pins its own copy. They are read from the lockfile's own keys or from the `node_modules` walk, **never from your source tree**: no path to a file you wrote is sent by either form of `scan`.
31
+ - Why it exists: the same package is routinely installed twice at different versions, and without the locations an advisory affecting only one of them cannot be matched to the copy your code actually loads. Node resolves an import by walking up from the importing file, so the location is what distinguishes "you are running the vulnerable copy" from "the vulnerable copy is installed but nothing reaches it". Absent them, every installed version has to be treated as if the app used it — warnings about code you never call, and protection rules pinned to routes that run the safe copy.
32
+ - Why it is off by default: it widens what leaves the machine, so it is your explicit choice and not a consequence of upgrading the package. (`mark-build` additionally stamps built HTML with a coarse stack descriptor that may include hosting-related env variable *names* — e.g. `VERCEL`, `CF_PAGES` — never their values.)
30
33
  - **One command reads source files:** `map` (see below) 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. No other command reads source (`protect` writes guard files but does not analyze your code).
31
34
  - **`scan` makes up to two source edits, both in the project's root shell:** the disclosure widget's `<script>` tag, and the production marker. Neither runs on `--dry-run`, both are idempotent, both leave a pre-existing manual install untouched, and `"widget": false` in `.patchstackrc.json` disables both.
32
35
  - 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.
@@ -141,18 +144,112 @@ would have stopped while it is still in dry-run. Two separate paths, with differ
141
144
  token; the `apiKey` itself is not sent to the log endpoint. Disable with `PATCHSTACK_TELEMETRY=off`, or
142
145
  `reportFirewallLog: false` in `createProtection`.
143
146
  - **Every rule that matched** goes to `monitor/pulse/detections/<your site uuid>` — including matches that
144
- blocked, which are reported on both paths. This is **off unless you pass `reportDetections: true`** to
145
- `createProtection`; the scaffolded guard does not pass it. It also requires a provisioned site UUID, a
146
- resolvable credential, and is disabled by `PATCHSTACK_TELEMETRY=off`. It exists because a rule carrying
147
- `dry-run` blocks nothing, so without it nothing distinguishes a rule that is protecting from one that is
148
- quietly wrong.
149
-
150
- What a detection report contains, per matched rule: the rule id, the request path **with any query string
151
- removed**, the parameter names that rule reads (from the rule's own definition), which phase matched,
152
- whether it was enforced, the identifier of the rule bundle in use, the revision of the rule itself when the
153
- bundle carried one, and a timestamp. Each batch also
154
- carries a count of reports dropped when traffic outran the flush, so a partial sample is not read as a
155
- complete one.
147
+ blocked, which are reported on both paths. It exists because a rule carrying `dry-run` blocks nothing, so
148
+ without it nothing distinguishes a rule that is protecting from one that is quietly wrong.
149
+
150
+ **This is on by default for a site enrolled with Patchstack that is running Patchstack-delivered rules**,
151
+ and off otherwise. Specifically, it requires all of: a provisioned site UUID, rules that came from
152
+ Patchstack rather than from a local bundle, and a resolvable credential. A local install, or a guard
153
+ running its own `rules`, sends nothing.
154
+
155
+ Switch it off with **`PATCHSTACK_REPORT_DETECTIONS=0`**, or `reportDetections: false` in
156
+ `createProtection`, or `PATCHSTACK_TELEMETRY=off` which covers all telemetry. `reportDetections` is an
157
+ opt-out only — passing `true` cannot switch reporting on for a site that is not enrolled.
158
+
159
+ `protection.detectionReporting` names the current state locally, so a guard that is not reporting says
160
+ which reason applies: `on`, `disabled-by-config`, `disabled-by-telemetry-opt-out`, `not-enrolled`,
161
+ `no-managed-rules`, or `unavailable-no-credential`.
162
+
163
+ **How the state reaches Patchstack, and what that costs on the network.** The state travels as a header
164
+ on the rules request the guard already makes — no extra request for it. Two of the six never travel:
165
+ `not-enrolled` makes no site-addressed request at all, and `unavailable-no-credential` cannot produce an
166
+ authenticated one, and the header is withheld from unauthenticated requests. Those two are local
167
+ diagnostics only.
168
+
169
+ There is one case that does add a request. The header is set before the rules request finishes, so a
170
+ guard booting with no cached rules declares `no-managed-rules` and then receives managed rules on that
171
+ same response. When that happens it sends **one immediate POST to the detections endpoint containing the
172
+ corrected state and no detections at all** — an empty `detections` array plus `reporting_state`. It is
173
+ sent once per process, only when the state changed, and never when the guard already had cached rules.
174
+ Without it, a guard with rule refreshing switched off would leave Patchstack holding the pre-resolution
175
+ answer for the life of the process.
176
+
177
+ What a detection report contains, per matched rule — on every phase, whatever fired it: the rule id, the revision of the rule when the
178
+ bundle carried one, the identifier of the rule bundle in use, which phase matched,
179
+ whether it was enforced, the request path **with the query string's values removed**,
180
+ that query's parameter names, the method, and a timestamp. Each batch also carries a count of reports dropped when traffic outran the flush,
181
+ so a partial sample is not read as a complete one.
182
+
183
+ Two fields depend on the phase, because one kind of detection has a client and the other does not. A
184
+ **request or response** detection also carries the user agent,
185
+ and the client address together with where that address came from.
186
+ An **egress** detection — a rule that fired on a call your application made outbound — carries neither:
187
+ the call was your application's own, so there is no visitor to attribute it to, and those fields read
188
+ `null` and `unavailable` rather than being guessed at. "What values a report can contain" below says the
189
+ same thing about captured evidence.
190
+
191
+ Every field is bounded in size, and an event that had to be shortened says so.
192
+ `truncated` lists **which fields were shortened**.
193
+ `parameters_total` records **how many parameters the rule reads**, when a rule reads more than the event
194
+ names.
195
+ `query_keys_total` records **how many query parameters the request carried**, counted as DISTINCT names,
196
+ when it carried more than the event lists — a parameter repeated three times is one name to look up. The
197
+ names themselves are the ones the guard addresses a parameter by, so `?first+name=x` is reported as
198
+ `first name`. Both appear only when something really was shortened, so their absence is not a claim of its own —
199
+ and a shortened route or rule id is marked rather than passed off as complete, because a reader must not
200
+ use one as a key believing it names the whole thing.
201
+
202
+ Delivery is retried, up to four attempts per batch, with exponential backoff and jitter, honouring a
203
+ `Retry-After` header when the endpoint sets one. Only failures worth retrying are retried — unreachable,
204
+ rate-limited, or a server error; a batch that was refused on its merits is not sent again. Every attempt
205
+ of one batch carries the same `Idempotency-Key` header, and a different batch carries a different one, so
206
+ a redelivery is identifiable as the same batch rather than a new one — an acknowledgement can be lost
207
+ after the server has already taken a batch. One request is in flight at a time, so a slow endpoint slows
208
+ the queue rather than opening more sockets; each attempt is abandoned after 10 seconds, so a request that
209
+ never settles cannot hold that slot; and a batch that exhausts its attempts is dropped and counted rather
210
+ than retried forever. Stopping a guard makes one last attempt at whatever is outstanding and counts
211
+ anything it could not send. `stop()` returns a promise that settles once the reporters have finished or
212
+ been given up on, so a shutdown handler can `await protection.stop()` instead of racing the last batch
213
+ against process exit. Each reporter has its own budget, and when it runs out that reporter is ended: its
214
+ requests are aborted, it starts nothing further, and it discards what it was holding. An abort is a
215
+ request to stop, not a guarantee — a transport that ignores it is detached rather than completed, so
216
+ "resolved" means the reporter is finished with it, and a runtime that kills the process still wins
217
+ regardless. Every detection event ends up delivered, refused or dropped and is reported in the health
218
+ counts; block-log records have no counters, so one lost to a failed send or an expired shutdown is
219
+ reported nowhere.
220
+
221
+ The client address is reported with its **provenance**, because an address is only as trustworthy as
222
+ whatever supplied it. `client_ip_source` is one of `runtime` (the address the transport observed),
223
+ `trusted-proxy` (read from a forwarded header, through peers you declared via `trustedProxy`), or
224
+ `unavailable`. When it is `unavailable` the `client_ip` field is **omitted entirely** rather than sent
225
+ empty, so a missing address cannot read as a failed lookup of a real one. A forwarded header is never
226
+ trusted implicitly: with no `trustedProxy` policy the address is whatever the transport observed, and in a
227
+ runtime that exposes no transport peer there is no address to report at all.
228
+
229
+ ### Behaviour change: how the client address is determined
230
+
231
+ The guard resolves the client address itself, once per request, and shares that one answer with rule
232
+ matching, block logging and detection reports — so those cannot disagree about who a request came from.
233
+ Two consequences if you are upgrading:
234
+
235
+ - **Express and Node: forwarded headers are no longer read implicitly.** Earlier versions took the
236
+ address from `X-Forwarded-For`, `CF-Connecting-IP` or `X-Real-IP` (the Node guard), or from `req.ip`
237
+ (the Express guard, where it reflects Express's own `trust proxy` setting). Neither source can be
238
+ verified by the guard, and any client can send those headers, so both guards now read the transport
239
+ peer. **If your app runs behind a proxy or load balancer, addresses will now show as the proxy's**
240
+ until you declare your proxies with `trustedProxy` (below) — which affects attribution in reports and
241
+ any rule matching on `server.ip` or `REMOTE_ADDR`.
242
+ - **Fetch runtimes report no address at all.** A WHATWG `Request` exposes no transport peer, so a Fetch
243
+ guard (Workers, Deno, Bun, edge) has nothing to observe, and no forwarded header is accepted in its
244
+ place under any `trustedProxy` policy: `client_ip_source` is `unavailable` and no address is sent.
245
+ Earlier versions reported the forwarded header here, so an address-scoped rule that appeared to work on
246
+ such a runtime was matching a client-supplied value.
247
+
248
+ `trustedProxy` is the only way to make a forwarded header count. It takes the proxies you actually run —
249
+ `{ peers: ['10.0.0.0/8'] }`, or `{ hops: 1 }` to trust that many hops in from the peer, plus optional
250
+ `header` and `isTrusted` — and the chain is then read from your application inward, stopping at the first
251
+ hop you have not declared. There are no built-in provider presets: a header a provider sets is
252
+ indistinguishable from one a client sent unless you say which peers may set it.
156
253
 
157
254
  The parameter names are **identifiers, and they name the request region they refer to** — `post.title`,
158
255
  `get.redirect_to`, `cookie.session`, `server.HTTP_AUTHORIZATION`. So a rule that inspects a cookie or an
@@ -160,16 +257,79 @@ The parameter names are **identifiers, and they name the request region they ref
160
257
  definition, not from your traffic, so they describe what is being screened rather than what any request
161
258
  contained.
162
259
 
163
- What it does not contain: **no values of any kind.** Not the value that matched, not the request body,
164
- and not the value of any header, cookie or query-string parameter — including those of the parameters
165
- named above. Reports are batched, capped in memory, and dropped rather than retried if Patchstack cannot
260
+ ### What values a report can contain
261
+
262
+ **A request or response detection** carries the request's method and path,
263
+ the query string's parameter **names**,
264
+ the user agent, the client address with its provenance, and a timestamp.
265
+
266
+ **An egress detection** — a rule that fired on a request your application made outbound — carries the
267
+ outbound method and path, the query's parameter names, and a timestamp. It carries **no user agent and no
268
+ client address**: the call was your application's own, so there is no visitor to attribute it to, and
269
+ those fields read `null` and `unavailable` rather than being guessed at.
270
+
271
+ Beyond that baseline, either can include the **values of the parameters a rule names** — and nothing
272
+ else. Counting that a
273
+ rule fired is not enough to act on it: whoever triages a detection still has to decide whether the request
274
+ was really an attack, and for that they need to see what the rule saw.
275
+
276
+ **A rule earns each permission by naming what it reads.** What may be captured is derived from the rule
277
+ itself, never configured per site:
278
+
279
+ - a rule naming a parameter (`post.title`, `cookie.session`, `server.HTTP_AUTHORIZATION`) permits **that
280
+ parameter's value**, because the rule was written to inspect it;
281
+ - a prefix (`post.field_*`) permits the values of keys that match, and no others;
282
+ - a rule reading `raw` or `all` — the whole request — permits **nothing at all**, so the broadest rules
283
+ grant the narrowest capture;
284
+ - **response** values are never captured: the phase that reads them exists to redact secrets, and
285
+ capturing them would collect the very values that redaction stops leaving;
286
+ - **raw request bytes** need an explicit, reviewed opt-in on the individual rule, and are then limited to
287
+ a short prefix of the body.
288
+
289
+ **Everything is bounded, and the bounds report themselves.**
290
+ At most 10 values per detection, at most 512 characters each, and at most 5 values from any one prefix. A value shortened to fit is marked; values a bound
291
+ left out are counted; a value refused because it was not a plain string, number or boolean is counted
292
+ separately; and a read that failed is counted as a failure rather than as absence — so a short list is
293
+ never mistaken for a complete one.
294
+
295
+ **A `capture.plan` identifies the permissions, not the rule.** Each report carries a `capture.plan` reference derived
296
+ from the permissions themselves — which parameters, which prefixes, which bounds — so what a given report
297
+ was permitted to include can be established after the fact, without the rule in front of you. It
298
+ identifies the PERMISSIONS, not the rule: two different rules that read the same parameters share one
299
+ reference, and the rule document is identified by `rule_id` with `rule_revision`.
300
+
301
+ **Capture is the union of everything the rule reads, not only the condition that matched.** The engine
302
+ reports which rule fired, not which of its conditions did, so a rule reading `post.title` and
303
+ `cookie.session` permits both values whichever one triggered the detection. A rule scoped to one parameter
304
+ captures one; a broad rule captures what it is broad about.
305
+
306
+ **One header value always travels, whatever the rule names: the User-Agent** — on a request or response
307
+ detection, where there is a client to attribute. It is part of the baseline above, because attribution is
308
+ what this channel is for and a detection without it cannot be told from another client's. It is the only
309
+ exception to the rule-scoped policy, and the only header value sent without a rule naming it.
310
+
311
+ **What a report never contains:** the value of any parameter the matched rule does not name — the
312
+ User-Agent above excepted; any response body, header or status value;
313
+ the request body, other than the reviewed raw prefix above;
314
+ and anything at all from a rule that reads the whole request without that opt-in.
315
+
316
+ **One qualification on the query string.** The exclusion above is about baseline URL metadata: `route` and
317
+ `query_keys` describe a URL without disclosing what was in it. It is not a promise about captured
318
+ evidence. A rule that names `egress.url` reads the outbound URL, so its capture carries that URL as the
319
+ rule read it — query values included. That is the rule-scoped policy working as described, not an
320
+ exception to it: the rule named the parameter, so the parameter's value travels. The value recorded is the request as the
321
+ engine resolved it — URL- and entity-decoded — and not the result of a rule's own further mutations.
322
+
323
+ Reports are batched, capped in memory, retried a bounded number of times, and dropped if Patchstack cannot
166
324
  be reached — a reporting failure never delays or fails a request.
167
325
 
168
- The endpoint needs a credential, so `reportDetections: true` with none resolved starts nothing: the guard
169
- warns once at boot and `protection.detectionReporting` reads `unavailable-no-credential` instead of `on`.
326
+ The endpoint needs a credential, so an enrolled site with none resolved starts nothing: the guard warns
327
+ once at boot and `protection.detectionReporting` reads `unavailable-no-credential` instead of `on`.
170
328
  When reporting is on, `protection.detectionHealth()` returns local counts — detections attempted,
171
329
  acknowledged, refused or unreachable, dropped for queue pressure — and the time of the last
172
- acknowledgement. Those counts stay in your process; nothing extra is sent to report them.
330
+ acknowledgement. Counts for the state POST described above are kept separately under `capability`, since it
331
+ carries no detections and would otherwise read as one. Those counts stay in your process; nothing extra is
332
+ sent to report them.
173
333
 
174
334
  `protection.stop()` stops everything the guard has running in the background — the rule-refresh loop, the
175
335
  block-log reporter, the detection reporter — and flushes what is buffered. `protection.stopRefresh()` is
package/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  Connect a JavaScript / Node.js application to [Patchstack](https://patchstack.com) for continuous vulnerability monitoring. Scans your `package-lock.json` and reports installed packages so Patchstack can match them against its vulnerability database and notify you when something needs patching.
4
4
 
5
- For how this repo fits with the wider Patchstack ecosystem (`saas`, `hub`, `patchstack-website`, `patchstack-connect`), see [`patchstack/saas` → `docs/ecosystem.md`](https://github.com/patchstack/saas/blob/main/docs/ecosystem.md).
6
-
7
5
  ## Agent-assisted setup
8
6
 
9
7
  Copy this request into a coding assistant, or run the same command yourself:
@@ -89,6 +87,7 @@ patchstack-connect demo-guide node-serialize Read-only, state-aware instru
89
87
  local demo, including the next exact command,
90
88
  expected proof, and cleanup.
91
89
  patchstack-connect help Print help
90
+ patchstack-connect --version Print the installed version
92
91
 
93
92
  Options (for scan, setup, and status):
94
93
  --site-uuid <uuid> Override the configured site UUID
@@ -216,6 +215,7 @@ Lower-level pieces are also exported: `scanLockfile`, `buildWirePayload`, `postM
216
215
  ```json
217
216
  {
218
217
  "ecosystem": "npm",
218
+ "installPathsComplete": false,
219
219
  "packages": [
220
220
  { "name": "axios", "version": "1.6.0" },
221
221
  { "name": "lodash", "version": "4.17.15" },
@@ -224,7 +224,21 @@ Lower-level pieces are also exported: `scanLockfile`, `buildWirePayload`, `postM
224
224
  }
225
225
  ```
226
226
 
227
- That's the entire payload. No source code, no environment variable values, no file paths — just the package names and versions from your lockfile. (The `map` command reads source files locally to report your attack surface; it transmits nothing unless you pass `--upload`, which sends that structural description — route paths, parameter names, the dependency behind each sink, and file/line locations, never file contents — to your own site's endpoint so rules can be pinned to your real parameter names.) Duplicate names with different versions are preserved so transitive vulnerabilities aren't missed. (`mark-build` separately stamps built HTML with a stack descriptor that may include hosting-related env variable *names* — e.g. `VERCEL` — never their values.)
227
+ That's the entire payload. No source code, no environment variable values, no file paths — just the package names and versions from your lockfile.
228
+
229
+ ### `scan --install-paths` (opt-in)
230
+
231
+ Pass it and each entry also carries where that version is installed:
232
+
233
+ ```json
234
+ { "name": "lodash", "version": "4.17.15", "paths": ["apps/api/node_modules/lodash"] }
235
+ ```
236
+
237
+ Why you might want it: the two `lodash` entries above are not a contrived example — the same package is routinely installed twice at different versions. An advisory affecting only `4.17.15` cannot otherwise be matched to the copy your code actually loads. Node resolves an import by walking up from the importing file, so the location is what separates "you are running the vulnerable copy" from "the vulnerable copy is installed but nothing reaches it". Without it every installed version has to be treated as if the app used it — warnings about code you never call, and protection rules pinned to routes that run the safe copy.
238
+
239
+ These are repo-relative locations built from `node_modules` segments, plus a workspace directory name when a workspace pins its own copy. They come from the lockfile's own keys or from the `node_modules` walk — **never from your source tree**. No path to a file you wrote is sent by either form of `scan`.
240
+
241
+ `installPathsComplete` says whether the set is total. It is `false` without the flag, and `false` with it whenever the source cannot supply locations — a `yarn.lock` is flat because hoisting is decided at install time, and a v1 `package-lock.json` records the dependency graph rather than the installed tree. Whenever it is `false`, a missing `paths` means **"not recorded"**, never "not installed there". (The `map` command reads source files locally to report your attack surface; it transmits nothing unless you pass `--upload`, which sends that structural description — route paths, parameter names, the dependency behind each sink, and file/line locations, never file contents — to your own site's endpoint so rules can be pinned to your real parameter names.) Duplicate names with different versions are preserved so transitive vulnerabilities aren't missed. (`mark-build` separately stamps built HTML with a stack descriptor that may include hosting-related env variable *names* — e.g. `VERCEL` — never their values.)
228
242
 
229
243
  ## Supported lockfiles
230
244
 
@@ -84,4 +84,4 @@ export {
84
84
  pulseAuthHeader,
85
85
  pulseFetch
86
86
  };
87
- //# sourceMappingURL=chunk-LLKP5EJS.js.map
87
+ //# sourceMappingURL=chunk-3G2I6QL6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pulse-token.ts"],"mappings":"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","names":[]}