@blamejs/core 0.8.42 → 0.8.49

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.
Files changed (222) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +10 -10
  3. package/index.js +52 -0
  4. package/lib/a2a.js +159 -34
  5. package/lib/acme.js +762 -0
  6. package/lib/ai-pref.js +166 -43
  7. package/lib/api-key.js +108 -47
  8. package/lib/api-snapshot.js +157 -40
  9. package/lib/app-shutdown.js +113 -77
  10. package/lib/archive.js +337 -40
  11. package/lib/arg-parser.js +697 -0
  12. package/lib/asyncapi.js +99 -55
  13. package/lib/atomic-file.js +465 -104
  14. package/lib/audit-chain.js +123 -34
  15. package/lib/audit-daily-review.js +389 -0
  16. package/lib/audit-sign.js +302 -56
  17. package/lib/audit-tools.js +412 -63
  18. package/lib/audit.js +656 -35
  19. package/lib/auth/jwt-external.js +17 -0
  20. package/lib/auth/oauth.js +7 -0
  21. package/lib/auth-bot-challenge.js +505 -0
  22. package/lib/auth-header.js +92 -25
  23. package/lib/backup/bundle.js +26 -0
  24. package/lib/backup/index.js +512 -89
  25. package/lib/backup/manifest.js +168 -7
  26. package/lib/break-glass.js +415 -39
  27. package/lib/budr.js +103 -30
  28. package/lib/bundler.js +86 -66
  29. package/lib/cache.js +192 -72
  30. package/lib/chain-writer.js +65 -40
  31. package/lib/circuit-breaker.js +56 -33
  32. package/lib/cli-helpers.js +106 -75
  33. package/lib/cli.js +6 -30
  34. package/lib/cloud-events.js +99 -32
  35. package/lib/cluster-storage.js +162 -37
  36. package/lib/cluster.js +340 -49
  37. package/lib/codepoint-class.js +66 -0
  38. package/lib/compliance.js +424 -24
  39. package/lib/config-drift.js +111 -46
  40. package/lib/config.js +94 -40
  41. package/lib/consent.js +165 -18
  42. package/lib/constants.js +1 -0
  43. package/lib/content-credentials.js +153 -48
  44. package/lib/cookies.js +154 -62
  45. package/lib/credential-hash.js +133 -61
  46. package/lib/crypto-field.js +702 -18
  47. package/lib/crypto-hpke.js +256 -0
  48. package/lib/crypto.js +744 -22
  49. package/lib/csv.js +178 -35
  50. package/lib/daemon.js +456 -0
  51. package/lib/dark-patterns.js +186 -55
  52. package/lib/db-query.js +79 -2
  53. package/lib/db.js +1431 -60
  54. package/lib/ddl-change-control.js +523 -0
  55. package/lib/deprecate.js +195 -40
  56. package/lib/dev.js +82 -39
  57. package/lib/dora.js +67 -48
  58. package/lib/dr-runbook.js +368 -0
  59. package/lib/dsr.js +142 -11
  60. package/lib/dual-control.js +91 -56
  61. package/lib/events.js +120 -41
  62. package/lib/external-db-migrate.js +192 -2
  63. package/lib/external-db.js +795 -50
  64. package/lib/fapi2.js +122 -1
  65. package/lib/fda-21cfr11.js +395 -0
  66. package/lib/fdx.js +132 -2
  67. package/lib/file-type.js +87 -0
  68. package/lib/file-upload.js +93 -0
  69. package/lib/flag.js +82 -20
  70. package/lib/forms.js +132 -29
  71. package/lib/framework-error.js +169 -0
  72. package/lib/framework-schema.js +163 -35
  73. package/lib/gate-contract.js +849 -175
  74. package/lib/graphql-federation.js +68 -7
  75. package/lib/guard-all.js +172 -55
  76. package/lib/guard-archive.js +286 -124
  77. package/lib/guard-auth.js +194 -21
  78. package/lib/guard-cidr.js +190 -28
  79. package/lib/guard-csv.js +397 -51
  80. package/lib/guard-domain.js +213 -91
  81. package/lib/guard-email.js +236 -29
  82. package/lib/guard-filename.js +307 -75
  83. package/lib/guard-graphql.js +263 -30
  84. package/lib/guard-html.js +310 -116
  85. package/lib/guard-image.js +243 -30
  86. package/lib/guard-json.js +260 -54
  87. package/lib/guard-jsonpath.js +235 -23
  88. package/lib/guard-jwt.js +284 -30
  89. package/lib/guard-markdown.js +204 -22
  90. package/lib/guard-mime.js +190 -26
  91. package/lib/guard-oauth.js +277 -28
  92. package/lib/guard-pdf.js +251 -27
  93. package/lib/guard-regex.js +226 -18
  94. package/lib/guard-shell.js +229 -26
  95. package/lib/guard-svg.js +177 -10
  96. package/lib/guard-template.js +232 -21
  97. package/lib/guard-time.js +195 -29
  98. package/lib/guard-uuid.js +189 -30
  99. package/lib/guard-xml.js +259 -36
  100. package/lib/guard-yaml.js +241 -44
  101. package/lib/honeytoken.js +63 -27
  102. package/lib/html-balance.js +83 -0
  103. package/lib/http-client.js +486 -59
  104. package/lib/http-message-signature.js +582 -0
  105. package/lib/i18n.js +102 -49
  106. package/lib/iab-mspa.js +112 -32
  107. package/lib/iab-tcf.js +107 -2
  108. package/lib/inbox.js +90 -52
  109. package/lib/keychain.js +865 -0
  110. package/lib/legal-hold.js +374 -0
  111. package/lib/local-db-thin.js +320 -0
  112. package/lib/log-stream.js +281 -51
  113. package/lib/log.js +184 -86
  114. package/lib/mail-bounce.js +107 -62
  115. package/lib/mail.js +295 -58
  116. package/lib/mcp.js +108 -27
  117. package/lib/metrics.js +98 -89
  118. package/lib/middleware/age-gate.js +36 -0
  119. package/lib/middleware/ai-act-disclosure.js +37 -0
  120. package/lib/middleware/api-encrypt.js +45 -0
  121. package/lib/middleware/assetlinks.js +40 -0
  122. package/lib/middleware/asyncapi-serve.js +35 -0
  123. package/lib/middleware/attach-user.js +40 -0
  124. package/lib/middleware/bearer-auth.js +40 -0
  125. package/lib/middleware/body-parser.js +230 -0
  126. package/lib/middleware/bot-disclose.js +34 -0
  127. package/lib/middleware/bot-guard.js +39 -0
  128. package/lib/middleware/compression.js +37 -0
  129. package/lib/middleware/cookies.js +32 -0
  130. package/lib/middleware/cors.js +40 -0
  131. package/lib/middleware/csp-nonce.js +40 -0
  132. package/lib/middleware/csp-report.js +34 -0
  133. package/lib/middleware/csrf-protect.js +43 -0
  134. package/lib/middleware/daily-byte-quota.js +53 -85
  135. package/lib/middleware/db-role-for.js +40 -0
  136. package/lib/middleware/dpop.js +40 -0
  137. package/lib/middleware/error-handler.js +37 -14
  138. package/lib/middleware/fetch-metadata.js +39 -0
  139. package/lib/middleware/flag-context.js +34 -0
  140. package/lib/middleware/gpc.js +33 -0
  141. package/lib/middleware/headers.js +35 -0
  142. package/lib/middleware/health.js +46 -0
  143. package/lib/middleware/host-allowlist.js +30 -0
  144. package/lib/middleware/network-allowlist.js +38 -0
  145. package/lib/middleware/openapi-serve.js +34 -0
  146. package/lib/middleware/rate-limit.js +160 -18
  147. package/lib/middleware/request-id.js +36 -18
  148. package/lib/middleware/request-log.js +37 -0
  149. package/lib/middleware/require-aal.js +29 -0
  150. package/lib/middleware/require-auth.js +32 -0
  151. package/lib/middleware/require-bound-key.js +41 -0
  152. package/lib/middleware/require-content-type.js +32 -0
  153. package/lib/middleware/require-methods.js +27 -0
  154. package/lib/middleware/require-mtls.js +33 -0
  155. package/lib/middleware/require-step-up.js +37 -0
  156. package/lib/middleware/security-headers.js +44 -0
  157. package/lib/middleware/security-txt.js +38 -0
  158. package/lib/middleware/span-http-server.js +37 -0
  159. package/lib/middleware/sse.js +36 -0
  160. package/lib/middleware/trace-log-correlation.js +33 -0
  161. package/lib/middleware/trace-propagate.js +32 -0
  162. package/lib/middleware/tus-upload.js +90 -0
  163. package/lib/middleware/web-app-manifest.js +53 -0
  164. package/lib/mtls-ca.js +100 -70
  165. package/lib/network-byte-quota.js +308 -0
  166. package/lib/network-heartbeat.js +135 -0
  167. package/lib/network-tls.js +534 -4
  168. package/lib/network.js +103 -0
  169. package/lib/notify.js +114 -43
  170. package/lib/ntp-check.js +192 -51
  171. package/lib/observability.js +145 -47
  172. package/lib/openapi.js +90 -44
  173. package/lib/outbox.js +99 -1
  174. package/lib/pagination.js +168 -86
  175. package/lib/parsers/index.js +16 -5
  176. package/lib/permissions.js +93 -40
  177. package/lib/pqc-agent.js +84 -8
  178. package/lib/pqc-software.js +94 -60
  179. package/lib/process-spawn.js +95 -21
  180. package/lib/pubsub.js +96 -66
  181. package/lib/queue.js +375 -54
  182. package/lib/redact.js +793 -21
  183. package/lib/render.js +139 -47
  184. package/lib/request-helpers.js +485 -121
  185. package/lib/restore-bundle.js +142 -39
  186. package/lib/restore-rollback.js +136 -45
  187. package/lib/retention.js +178 -50
  188. package/lib/retry.js +116 -33
  189. package/lib/router.js +475 -23
  190. package/lib/safe-async.js +543 -94
  191. package/lib/safe-buffer.js +337 -41
  192. package/lib/safe-json.js +467 -62
  193. package/lib/safe-jsonpath.js +285 -0
  194. package/lib/safe-schema.js +631 -87
  195. package/lib/safe-sql.js +221 -59
  196. package/lib/safe-url.js +278 -46
  197. package/lib/sandbox-worker.js +135 -0
  198. package/lib/sandbox.js +358 -0
  199. package/lib/scheduler.js +135 -70
  200. package/lib/self-update.js +647 -0
  201. package/lib/session-device-binding.js +431 -0
  202. package/lib/session.js +259 -49
  203. package/lib/slug.js +138 -26
  204. package/lib/ssrf-guard.js +316 -56
  205. package/lib/storage.js +433 -70
  206. package/lib/subject.js +405 -23
  207. package/lib/template.js +148 -8
  208. package/lib/tenant-quota.js +545 -0
  209. package/lib/testing.js +440 -53
  210. package/lib/time.js +291 -23
  211. package/lib/tls-exporter.js +239 -0
  212. package/lib/tracing.js +90 -74
  213. package/lib/uuid.js +97 -22
  214. package/lib/vault/index.js +284 -22
  215. package/lib/vault/seal-pem-file.js +66 -0
  216. package/lib/watcher.js +368 -0
  217. package/lib/webhook.js +196 -63
  218. package/lib/websocket.js +393 -68
  219. package/lib/wiki-concepts.js +338 -0
  220. package/lib/worker-pool.js +464 -0
  221. package/package.json +3 -3
  222. package/sbom.cyclonedx.json +7 -7
@@ -1,34 +1,44 @@
1
1
  "use strict";
2
2
  /**
3
- * guard-shell — Shell metacharacter identifier-safety primitive
4
- * (b.guardShell).
3
+ * @module b.guardShell
4
+ * @nav Guards
5
+ * @title Guard Shell
5
6
  *
6
- * Validates user-input strings BEFORE they're handed to a
7
- * child-process spawn (regardless of operator's `shell:` opt). The
8
- * canonical defense is "use array args + shell:false", but operators
9
- * still receive operator-untrusted strings that flow through path-
10
- * arg or arg-list shapes guardShell refuses obvious shell-injection
11
- * shapes before the spawn call. KIND="identifier" — consumes
12
- * ctx.identifier (or ctx.arg).
7
+ * @intro
8
+ * Shell-argument content-safety guard refuses user-supplied
9
+ * strings that carry shell-injection shapes BEFORE they reach a
10
+ * child-process spawn. The canonical defense is "command + literal
11
+ * argv array, never `shell: true`" (route through `b.processSpawn`,
12
+ * which holds that contract); guardShell layers the metacharacter
13
+ * catalog on top so even operator-untrusted strings flowing through
14
+ * the argv slots are screened. KIND=`identifier`; the gate consumes
15
+ * `ctx.identifier` (or `ctx.arg`) and refuses on hostile shapes.
13
16
  *
14
- * Threat catalog:
15
- * - POSIX shell metacharacters `;`, `&`, `|`, `<`, `>`, `(`, `)`,
16
- * `{`, `}`, `[`, `]`, `*`, `?`, `~`, `!`, `#`, `\`, single + double
17
- * quotes.
18
- * - Backtick command substitution.
19
- * - `$(...)` command substitution and `${VAR}` parameter expansion.
20
- * - Process substitution `<(...)` / `>(...)`.
21
- * - cmd.exe metacharacters `&`, `|`, `<`, `>`, `^`, `%`, `"`, `'`,
22
- * `(`, `)`, `,`, `;`, `=`, ` `, tabs, newlines.
23
- * - Newline / NUL injection (line splitting in scripts).
24
- * - Variable expansion `$VAR`.
25
- * - Operator may opt-in to `argHyphenPolicy` to refuse leading `-`
26
- * arguments (defense against `-rf` / `--exec` / etc.).
27
- * - BIDI / zero-width / control / null-byte universal refuse.
17
+ * Threat catalog: POSIX shell metacharacters
18
+ * (`;` `&` `|` `<` `>` `(` `)` `{` `}` `[` `]` `*` `?` `~` `!` `#`
19
+ * `\` and single/double quotes); backtick command substitution;
20
+ * `$(...)` command substitution and `${VAR}` parameter expansion;
21
+ * process substitution `<(...)` / `>(...)`; cmd.exe metacharacters
22
+ * (`&` `|` `<` `>` `^` `%` `"` `'` `(` `)` `,` `;` `=` plus
23
+ * whitespace + newlines); CR / LF / NUL line-splitting; bare
24
+ * `$VAR` parameter expansion; leading `-` arguments (`-rf` /
25
+ * `--exec` flag-injection class) gated by `argHyphenPolicy`; BIDI
26
+ * override / zero-width / C0 control / null-byte refuse at every
27
+ * profile.
28
28
  *
29
- * var rv = b.guardShell.validate("file with spaces.txt",
30
- * { profile: "strict" });
31
- * var g = b.guardShell.gate({ profile: "strict" });
29
+ * Profiles: `strict` / `balanced` / `permissive`. Compliance
30
+ * postures: `hipaa` / `pci-dss` / `gdpr` / `soc2`. Operators select
31
+ * via `{ profile: "strict" }` or `{ compliance: "hipaa" }`;
32
+ * postures overlay on top of the profile baseline.
33
+ *
34
+ * Shell args cannot be repaired safely — `sanitize` either passes
35
+ * through clean input or throws `GuardShellError`; the gate returns
36
+ * `serve` / `audit-only` / `refuse` (no `sanitize` action). Pair
37
+ * with `b.processSpawn` so the eventual `child_process.spawn` call
38
+ * uses `shell: false` and the screened argv values.
39
+ *
40
+ * @card
41
+ * Shell-argument content-safety guard — refuses user-supplied strings that carry shell-injection shapes BEFORE they reach a child-process spawn.
32
42
  */
33
43
 
34
44
  var codepointClass = require("./codepoint-class");
@@ -235,6 +245,46 @@ function _detectIssues(input, opts) {
235
245
  return issues;
236
246
  }
237
247
 
248
+ /**
249
+ * @primitive b.guardShell.validate
250
+ * @signature b.guardShell.validate(input, opts)
251
+ * @since 0.7.13
252
+ * @status stable
253
+ * @compliance hipaa, pci-dss, gdpr, soc2
254
+ * @related b.guardShell.gate, b.guardShell.sanitize, b.processSpawn
255
+ *
256
+ * Inspect a single shell-argument string and return an aggregated
257
+ * issue list. Pure inspection — never throws on hostile input;
258
+ * caller decides what to do with the issues. The `ok` flag is
259
+ * `true` only when zero `critical` / `high` issues fire. Throws
260
+ * `GuardShellError("shell.bad-opt")` when a numeric opt is
261
+ * non-finite / negative (config-time mistake by the operator).
262
+ *
263
+ * @opts
264
+ * profile: "strict"|"balanced"|"permissive",
265
+ * compliance: "hipaa"|"pci-dss"|"gdpr"|"soc2",
266
+ * bidiPolicy: "reject"|"audit"|"allow",
267
+ * controlPolicy: "reject"|"audit"|"allow",
268
+ * nullBytePolicy: "reject"|"audit"|"allow",
269
+ * zeroWidthPolicy: "reject"|"strip"|"audit"|"allow",
270
+ * posixMetaPolicy: "reject"|"audit"|"allow",
271
+ * cmdMetaPolicy: "reject"|"audit"|"allow",
272
+ * dollarSubstPolicy: "reject"|"audit"|"allow",
273
+ * processSubstPolicy:"reject"|"audit"|"allow",
274
+ * backtickPolicy: "reject"|"audit"|"allow",
275
+ * newlinePolicy: "reject"|"audit"|"allow",
276
+ * argHyphenPolicy: "reject"|"audit"|"allow",
277
+ * maxBytes: number,
278
+ * maxRuntimeMs: number,
279
+ *
280
+ * @example
281
+ * var clean = b.guardShell.validate("safe-arg-value", { profile: "strict" });
282
+ * clean.ok; // → true
283
+ *
284
+ * var hostile = b.guardShell.validate("safe; rm -rf /", { profile: "strict" });
285
+ * hostile.ok; // → false
286
+ * hostile.issues.some(function (i) { return i.kind === "posix-metachar"; }); // → true
287
+ */
238
288
  function validate(input, opts) {
239
289
  opts = _resolveOpts(opts);
240
290
  numericBounds.requireAllPositiveFiniteIntIfPresent(opts,
@@ -243,6 +293,47 @@ function validate(input, opts) {
243
293
  return gateContract.aggregateIssues(_detectIssues(input, opts));
244
294
  }
245
295
 
296
+ /**
297
+ * @primitive b.guardShell.sanitize
298
+ * @signature b.guardShell.sanitize(input, opts)
299
+ * @since 0.7.13
300
+ * @status stable
301
+ * @compliance hipaa, pci-dss, gdpr, soc2
302
+ * @related b.guardShell.validate, b.guardShell.gate
303
+ *
304
+ * Pass-through-or-throw. Shell arguments cannot be safely repaired
305
+ * (stripping a `;` inside an arg fundamentally changes operator
306
+ * intent); this primitive returns the input unchanged when no
307
+ * `critical` or `high` issue fires, otherwise throws
308
+ * `GuardShellError` with the offending rule id (e.g.
309
+ * `shell.posix-metachar`, `shell.dollar-substitution`,
310
+ * `shell.backtick`, `shell.newline`). Operators that need a
311
+ * "best-effort cleanup" semantic should use a different argv shape
312
+ * (path + literal arg array) rather than trying to disarm a hostile
313
+ * string.
314
+ *
315
+ * @opts
316
+ * profile: "strict"|"balanced"|"permissive",
317
+ * compliance: "hipaa"|"pci-dss"|"gdpr"|"soc2",
318
+ * posixMetaPolicy: "reject"|"audit"|"allow",
319
+ * cmdMetaPolicy: "reject"|"audit"|"allow",
320
+ * dollarSubstPolicy: "reject"|"audit"|"allow",
321
+ * processSubstPolicy:"reject"|"audit"|"allow",
322
+ * backtickPolicy: "reject"|"audit"|"allow",
323
+ * newlinePolicy: "reject"|"audit"|"allow",
324
+ * argHyphenPolicy: "reject"|"audit"|"allow",
325
+ * maxBytes: number,
326
+ *
327
+ * @example
328
+ * var arg = b.guardShell.sanitize("safe-arg-value", { profile: "strict" });
329
+ * arg; // → "safe-arg-value"
330
+ *
331
+ * try {
332
+ * b.guardShell.sanitize("safe; rm -rf /", { profile: "strict" });
333
+ * } catch (e) {
334
+ * e.code; // → "shell.posix-metachar"
335
+ * }
336
+ */
246
337
  function sanitize(input, opts) {
247
338
  opts = _resolveOpts(opts);
248
339
  if (typeof input !== "string") {
@@ -260,6 +351,49 @@ function sanitize(input, opts) {
260
351
  return input;
261
352
  }
262
353
 
354
+ /**
355
+ * @primitive b.guardShell.gate
356
+ * @signature b.guardShell.gate(opts)
357
+ * @since 0.7.13
358
+ * @status stable
359
+ * @compliance hipaa, pci-dss, gdpr, soc2
360
+ * @related b.guardShell.validate, b.guardShell.sanitize, b.processSpawn
361
+ *
362
+ * Build a `b.gateContract` gate that screens `ctx.identifier` (or
363
+ * `ctx.arg`) before each spawn. Action chain: `serve` (no issues)
364
+ * → `audit-only` (warn-only) → `refuse` (any `critical` or `high`).
365
+ * No `sanitize` action — shell args cannot be repaired. Compose
366
+ * with `b.processSpawn` so each argv slot is gated before reaching
367
+ * the OS (the spawn primitive itself enforces `shell: false`; the
368
+ * gate enforces metacharacter cleanliness).
369
+ *
370
+ * @opts
371
+ * profile: "strict"|"balanced"|"permissive",
372
+ * compliance: "hipaa"|"pci-dss"|"gdpr"|"soc2",
373
+ * name: string, // override gate name in audit emissions
374
+ * posixMetaPolicy: "reject"|"audit"|"allow",
375
+ * cmdMetaPolicy: "reject"|"audit"|"allow",
376
+ * dollarSubstPolicy: "reject"|"audit"|"allow",
377
+ * processSubstPolicy:"reject"|"audit"|"allow",
378
+ * backtickPolicy: "reject"|"audit"|"allow",
379
+ * newlinePolicy: "reject"|"audit"|"allow",
380
+ * argHyphenPolicy: "reject"|"audit"|"allow",
381
+ * maxBytes: number,
382
+ *
383
+ * @example
384
+ * var gate = b.guardShell.gate({ profile: "strict" });
385
+ *
386
+ * // Hostile arg — gate refuses before spawn.
387
+ * gate({ identifier: "safe; rm -rf /" }).then(function (rv) {
388
+ * rv.ok; // → false
389
+ * rv.action; // → "refuse"
390
+ * });
391
+ *
392
+ * // Benign arg — gate serves.
393
+ * gate({ identifier: "safe-arg-value" }).then(function (rv) {
394
+ * rv.action; // → "serve"
395
+ * });
396
+ */
263
397
  function gate(opts) {
264
398
  opts = _resolveOpts(opts);
265
399
  return gateContract.buildGuardGate(
@@ -283,14 +417,83 @@ function gate(opts) {
283
417
  });
284
418
  }
285
419
 
420
+ /**
421
+ * @primitive b.guardShell.buildProfile
422
+ * @signature b.guardShell.buildProfile(opts)
423
+ * @since 0.7.13
424
+ * @status stable
425
+ * @related b.guardShell.gate, b.guardShell.compliancePosture
426
+ *
427
+ * Compose a derived guardShell profile from one or more named bases
428
+ * plus inline overrides. `opts.extends` is a profile name
429
+ * (`"strict"` / `"balanced"` / `"permissive"`) or an array of
430
+ * names; later entries shadow earlier ones. Inline `opts` keys win
431
+ * last. Used to keep operator-defined profiles traceable to a
432
+ * baseline rather than re-typing every key.
433
+ *
434
+ * @opts
435
+ * extends: string|string[], // base profile name(s) to compose
436
+ * ...: any guardShell key, // inline override of resolved keys
437
+ *
438
+ * @example
439
+ * var custom = b.guardShell.buildProfile({
440
+ * extends: "balanced",
441
+ * argHyphenPolicy: "reject",
442
+ * });
443
+ * custom.argHyphenPolicy; // → "reject"
444
+ * custom.dollarSubstPolicy; // → "reject"
445
+ */
286
446
  var buildProfile = gateContract.makeProfileBuilder(PROFILES);
287
447
 
448
+ /**
449
+ * @primitive b.guardShell.compliancePosture
450
+ * @signature b.guardShell.compliancePosture(name)
451
+ * @since 0.7.13
452
+ * @status stable
453
+ * @compliance hipaa, pci-dss, gdpr, soc2
454
+ * @related b.guardShell.gate, b.guardShell.buildProfile
455
+ *
456
+ * Look up a compliance-posture overlay by name (`"hipaa"` /
457
+ * `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of
458
+ * the posture object — the caller may mutate freely. Throws
459
+ * `GuardShellError("shell.bad-posture")` on unknown name.
460
+ *
461
+ * @example
462
+ * var posture = b.guardShell.compliancePosture("hipaa");
463
+ * posture.posixMetaPolicy; // → "reject"
464
+ * posture.forensicSnippetBytes; // → 256
465
+ */
288
466
  function compliancePosture(name) {
289
467
  return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES,
290
468
  _err, "shell");
291
469
  }
292
470
 
293
471
  var _shellRulePacks = gateContract.makeRulePackLoader(GuardShellError, "shell");
472
+ /**
473
+ * @primitive b.guardShell.loadRulePack
474
+ * @signature b.guardShell.loadRulePack(pack)
475
+ * @since 0.7.13
476
+ * @status stable
477
+ * @related b.guardShell.gate
478
+ *
479
+ * Register an operator-supplied rule pack with the guardShell
480
+ * registry. The pack is identified by `pack.id` (non-empty string)
481
+ * and stored for later inspection / dispatch by gates that opt in
482
+ * via `opts.rulePackId`. Returns the pack object unchanged on
483
+ * success; throws `GuardShellError("shell.bad-opt")` when `pack`
484
+ * is missing or `pack.id` is not a non-empty string.
485
+ *
486
+ * @example
487
+ * var pack = b.guardShell.loadRulePack({
488
+ * id: "no-leading-dash",
489
+ * rules: [
490
+ * { id: "leading-dash", severity: "high",
491
+ * detect: function (arg) { return arg.charAt(0) === "-"; },
492
+ * reason: "argument starts with `-` flag prefix" },
493
+ * ],
494
+ * });
495
+ * pack.id; // → "no-leading-dash"
496
+ */
294
497
  var loadRulePack = _shellRulePacks.load;
295
498
 
296
499
  module.exports = {
package/lib/guard-svg.js CHANGED
@@ -1,17 +1,30 @@
1
1
  "use strict";
2
2
  /**
3
- * guard-svg — SVG content-safety primitive (b.guardSvg).
3
+ * @module b.guardSvg
4
+ * @nav Guards
5
+ * @title Guard Svg
4
6
  *
5
- * Threat catalog grounded in current SVG attack-surface research
6
- * (Fortinet anatomy of SVG attack surface; Angular GHSA-jrmj-c5cx-3cw6
7
- * + GHSA-v4hv-rgfq-gp49 SVG animation/href XSS; SVGO CVE-2026-29074
8
- * billion laughs DoS; siyuan-note GHSA-5hc8-qmg8-pw27 animate-element
9
- * sanitizer bypass; cure53/DOMPurify issue #233 xlink:href filtering;
10
- * insertScript SVG fun-time series; svg2raster-cheatsheet SSRF guide).
7
+ * @intro
8
+ * SVG content-safety primitive defends against XXE / billion-laughs
9
+ * entity expansion, SSRF via `xlink:href`, animation-href injection
10
+ * (the `<animate attributeName="href" ...>` retroactive-poisoning
11
+ * class), embedded `<script>` / `<foreignObject>` namespace-shift
12
+ * escape hatches, dangerous URL schemes, CSS injection in style
13
+ * attributes, SVGZ compressed payloads, and Trojan-Source bidi /
14
+ * zero-width / null-byte threats.
11
15
  *
12
- * var rv = b.guardSvg.validate(input, { profile: "strict" });
13
- * var safe = b.guardSvg.sanitize(input, { profile: "balanced" });
14
- * var g = b.guardSvg.gate({ profile: "strict" });
16
+ * Element + attribute allowlist with strict default (text + shape
17
+ * primitives only). Profiles `strict` / `balanced` / `permissive`
18
+ * compose with compliance postures `hipaa` / `pci-dss` / `gdpr` /
19
+ * `soc2`. Integrates with `b.fileUpload` and `b.staticServe`'s
20
+ * contentSafety hook by default.
21
+ *
22
+ * Source-of-truth references: Fortinet anatomy of SVG attack
23
+ * surface; Angular GHSA-jrmj-c5cx-3cw6 + GHSA-v4hv-rgfq-gp49 SVG
24
+ * animation/href XSS; SVGO CVE-2026-29074 billion-laughs DoS;
25
+ * siyuan-note GHSA-5hc8-qmg8-pw27 animate-element sanitizer bypass;
26
+ * cure53/DOMPurify issue #233 xlink:href filtering; insertScript
27
+ * SVG fun-time series; svg2raster-cheatsheet SSRF guide.
15
28
  *
16
29
  * Threat catalog covered:
17
30
  *
@@ -88,6 +101,9 @@
88
101
  * Threat-detection regex literals are composed PROGRAMMATICALLY from
89
102
  * numeric codepoint range tables. Source file never embeds attack
90
103
  * characters themselves.
104
+ *
105
+ * @card
106
+ * SVG content-safety primitive — defends against XXE / billion-laughs entity expansion, SSRF via `xlink:href`, animation-href injection (the `<animate attributeName="href" ...>` retroactive-poisoning class), embedded `<script>` / `<foreignObject>` namespace-shift escape hatches,...
91
107
  */
92
108
 
93
109
  var codepointClass = require("./codepoint-class");
@@ -890,6 +906,51 @@ function _sanitize(input, opts) {
890
906
 
891
907
  // ---- Public surface ----
892
908
 
909
+ /**
910
+ * @primitive b.guardSvg.validate
911
+ * @signature b.guardSvg.validate(input, opts)
912
+ * @since 0.7.7
913
+ * @status stable
914
+ * @related b.guardSvg.sanitize, b.guardSvg.gate
915
+ *
916
+ * Inspect an SVG payload (string or Buffer) and return
917
+ * `{ ok, issues }` describing every threat the parser found. Never
918
+ * throws on hostile input — callers see the full issue list and
919
+ * decide whether to refuse, sanitize, or audit.
920
+ *
921
+ * Issues carry `kind` / `severity` / `ruleId` / `location` /
922
+ * `snippet`. Severities `critical` and `high` are the gate's
923
+ * refuse / sanitize signal; `warn` is audit-only.
924
+ *
925
+ * @opts
926
+ * profile: "strict" | "balanced" | "permissive",
927
+ * compliancePosture: "hipaa" | "pci-dss" | "gdpr" | "soc2",
928
+ * allowedTags: Array<string>,
929
+ * allowedAttrs: Array<string>,
930
+ * urlSchemes: Array<string>,
931
+ * allowImageData: boolean,
932
+ * allowExternalRefs: boolean,
933
+ * allowAnimation: boolean,
934
+ * maxBytes: number,
935
+ * maxAttrValueBytes: number,
936
+ * maxElementCount: number,
937
+ * maxUseDepth: number,
938
+ * maxAttrsPerTag: number,
939
+ *
940
+ * @example
941
+ * var rv = b.guardSvg.validate(
942
+ * '<svg><script>alert(1)</script></svg>',
943
+ * { profile: "strict" });
944
+ * rv.ok; // → false
945
+ * rv.issues[0].kind; // → "dangerous-tag"
946
+ * rv.issues[0].severity; // → "critical"
947
+ *
948
+ * var clean = b.guardSvg.validate(
949
+ * '<svg><circle r="10"/></svg>',
950
+ * { profile: "strict" });
951
+ * clean.ok; // → true
952
+ * clean.issues.length; // → 0
953
+ */
893
954
  function validate(input, opts) {
894
955
  opts = _resolveOpts(opts);
895
956
  numericBounds.requireAllPositiveFiniteIntIfPresent(opts,
@@ -901,6 +962,46 @@ function validate(input, opts) {
901
962
  return gateContract.aggregateIssues(_detectIssues(input, opts));
902
963
  }
903
964
 
965
+ /**
966
+ * @primitive b.guardSvg.sanitize
967
+ * @signature b.guardSvg.sanitize(input, opts)
968
+ * @since 0.7.7
969
+ * @status stable
970
+ * @related b.guardSvg.validate, b.guardSvg.gate
971
+ *
972
+ * Best-effort sanitizer. Strips dangerous tags (`<script>`,
973
+ * `<foreignObject>`, plugin embeds, animation elements when the
974
+ * profile forbids them), event-handler attributes (every
975
+ * `/^on[a-z]/`), URL attributes carrying `javascript:` /
976
+ * `vbscript:` / non-allowlisted schemes, CSS injection inside
977
+ * `style="..."`, DOCTYPE / `<!ENTITY>` / processing instructions /
978
+ * CDATA, bidi / control / null-byte / zero-width threats per the
979
+ * profile's char policies. Throws `GuardSvgError` (`svg.svgz`) on
980
+ * SVGZ input — operators must ungzip first then re-sanitize.
981
+ *
982
+ * @opts
983
+ * profile: "strict" | "balanced" | "permissive",
984
+ * compliancePosture: "hipaa" | "pci-dss" | "gdpr" | "soc2",
985
+ * allowedTags: Array<string>,
986
+ * urlSchemes: Array<string>,
987
+ * allowImageData: boolean,
988
+ * allowExternalRefs: boolean,
989
+ * allowAnimation: boolean,
990
+ * maxBytes: number,
991
+ *
992
+ * @example
993
+ * var safe = b.guardSvg.sanitize(
994
+ * '<svg><script>alert(1)</script><circle r="10"/></svg>',
995
+ * { profile: "balanced" });
996
+ * safe;
997
+ * // → '<svg><circle r="10"></circle></svg>'
998
+ *
999
+ * // Event-handler attributes are stripped:
1000
+ * var clean = b.guardSvg.sanitize(
1001
+ * '<svg onload="x()"><rect width="10" height="10"/></svg>',
1002
+ * { profile: "strict" });
1003
+ * /onload/.test(clean); // → false
1004
+ */
904
1005
  function sanitize(input, opts) {
905
1006
  opts = _resolveOpts(opts);
906
1007
  if (typeof input !== "string" && !Buffer.isBuffer(input)) {
@@ -909,6 +1010,51 @@ function sanitize(input, opts) {
909
1010
  return _sanitize(input, opts);
910
1011
  }
911
1012
 
1013
+ /**
1014
+ * @primitive b.guardSvg.gate
1015
+ * @signature b.guardSvg.gate(opts)
1016
+ * @since 0.7.7
1017
+ * @status stable
1018
+ * @related b.guardSvg.validate, b.guardSvg.sanitize, b.fileUpload, b.staticServe
1019
+ *
1020
+ * Build a uniform gate over guard-* family contract. Returns an
1021
+ * async function whose verdict is `{ ok, action, issues?,
1022
+ * sanitized? }` where `action` is `serve` / `audit-only` /
1023
+ * `sanitize` / `refuse`. SVGZ inputs always refuse — operators
1024
+ * ungzip and re-gate the inner SVG. External `xlink:href` on
1025
+ * `<use>` / `<feImage>` refuses under `strict` (SSRF + XSS chain).
1026
+ * Sanitize path is taken when no policy is set to `reject` and the
1027
+ * issue set is repairable.
1028
+ *
1029
+ * @opts
1030
+ * profile: "strict" | "balanced" | "permissive",
1031
+ * compliancePosture: "hipaa" | "pci-dss" | "gdpr" | "soc2",
1032
+ * mode: "enforce" | "audit-only",
1033
+ * audit: AuditEmitter,
1034
+ * observability: ObservabilityEmitter,
1035
+ * forensicEvidenceStore: ForensicStore,
1036
+ * allowedTags: Array<string>,
1037
+ * urlSchemes: Array<string>,
1038
+ * allowExternalRefs: boolean,
1039
+ * allowAnimation: boolean,
1040
+ * maxBytes: number,
1041
+ * maxRuntimeMs: number,
1042
+ *
1043
+ * @example
1044
+ * var g = b.guardSvg.gate({ profile: "strict" });
1045
+ * var verdict = await g({
1046
+ * bytes: Buffer.from('<svg><circle r="10"/></svg>', "utf8"),
1047
+ * });
1048
+ * verdict.action; // → "serve"
1049
+ *
1050
+ * // Refuses external xlink:href under strict:
1051
+ * var refuse = await g({
1052
+ * bytes: Buffer.from(
1053
+ * '<svg><use xlink:href="https://evil.example/x.svg#a"/></svg>',
1054
+ * "utf8"),
1055
+ * });
1056
+ * refuse.action; // → "refuse"
1057
+ */
912
1058
  function gate(opts) {
913
1059
  opts = _resolveOpts(opts);
914
1060
  return gateContract.buildGuardGate(
@@ -949,6 +1095,27 @@ function gate(opts) {
949
1095
 
950
1096
  var buildProfile = gateContract.makeProfileBuilder(PROFILES);
951
1097
 
1098
+ /**
1099
+ * @primitive b.guardSvg.compliancePosture
1100
+ * @signature b.guardSvg.compliancePosture(name)
1101
+ * @since 0.7.7
1102
+ * @status stable
1103
+ * @compliance hipaa, pci-dss, gdpr, soc2
1104
+ * @related b.guardSvg.gate, b.compliance.set
1105
+ *
1106
+ * Look up a regulatory-posture override. Returns a shallow clone
1107
+ * of the named posture's option overlay; throws
1108
+ * `GuardSvgError` (`svg.bad-posture`) on unknown names. Operators
1109
+ * pass it through `gate({ compliancePosture: "hipaa" })` rather
1110
+ * than calling this directly — exposed for introspection and
1111
+ * audit-evidence collection.
1112
+ *
1113
+ * @example
1114
+ * var hipaa = b.guardSvg.compliancePosture("hipaa");
1115
+ * hipaa.bidiPolicy; // → "reject"
1116
+ * hipaa.allowExternalRefs; // → false
1117
+ * hipaa.allowAnimation; // → false
1118
+ */
952
1119
  function compliancePosture(name) {
953
1120
  return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES, _err, "svg");
954
1121
  }