@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,40 +1,56 @@
1
1
  "use strict";
2
2
  /**
3
- * Buffer-safety primitives — centralizes the input-normalize, capped
4
- * chunk collection, and secure-zero patterns that were scattered across
5
- * lib/parsers/*, lib/atomic-file.js, lib/object-store-*.js, and
6
- * lib/log-stream-*.js.
7
- *
8
- * Public API:
9
- * safeBuffer.normalizeText(input, { maxBytes, stripBom, errorClass })
10
- * Accept string | Buffer | Uint8Array → returns string. Strips a
11
- * leading UTF-8 BOM (U+FEFF) by default. Throws errorClass(message,
12
- * code) if input is the wrong type or exceeds maxBytes.
13
- *
14
- * safeBuffer.toBuffer(data, { maxBytes, errorClass })
15
- * Accept Buffer | Uint8Array | string returns Buffer. Throws
16
- * errorClass on type mismatch or oversize.
17
- *
18
- * safeBuffer.boundedChunkCollector({ maxBytes, errorClass })
19
- * Returns { push(chunk), result(), bytesCollected() }. Each push()
20
- * enforces the cap on every chunk — the OOM defense for unbounded
21
- * HTTP response bodies replacing the previous `chunks.push(c)` +
22
- * `Buffer.concat(chunks)` pattern that accumulated arbitrary bytes
23
- * before checking size.
24
- *
25
- * safeBuffer.secureZero(buf)
26
- * Best-effort zero of buf contents (`buf.fill(0)`). JavaScript can't
27
- * truly zero memory V8 may have copies — but `fill(0)` removes the
28
- * in-Buffer reference so a heap-dump won't show the secret in this
29
- * particular Buffer. No-op on non-Buffers.
30
- *
31
- * Why a default error class:
32
- * Each caller (xml-safe, json-safe, atomic-file, ...) wants to throw
33
- * its own format-specific error class with a particular `code`. The
34
- * helpers accept `{ errorClass }` so the byte-handling lives here but
35
- * the error type stays format-aware (existing tests check
36
- * e.code === "xml/too-large" etc.). A default SafeBufferError is used
37
- * if the caller doesn't pass one.
3
+ * @module b.safeBuffer
4
+ * @nav Validation
5
+ * @title Safe Buffer
6
+ *
7
+ * @intro
8
+ * Buffer-safety primitives that centralize the input-normalize,
9
+ * capped-collection, and secure-zero patterns previously scattered
10
+ * across parsers, atomic-file, object-store, and log-stream
11
+ * modules.
12
+ *
13
+ * The safety guarantees the family enforces:
14
+ *
15
+ * 1. Type-discriminated input every helper accepts the exact set
16
+ * of byte-shaped inputs it documents (Buffer / Uint8Array /
17
+ * string) and throws on anything else, instead of silently
18
+ * coercing `undefined` to `"undefined"` or letting an Object
19
+ * slip through `Buffer.from`.
20
+ *
21
+ * 2. Caller-supplied byte cap enforced BEFORE allocation. Numeric
22
+ * `maxBytes` opts are validated as positive finite integers —
23
+ * `Infinity`, `NaN`, and negative values throw at config time
24
+ * rather than disabling the cap. The bounded-chunk collector
25
+ * checks the running total on every push so a hostile 10-GB
26
+ * upstream rejects on the chunk that overflows, not after
27
+ * accumulating the full payload in memory.
28
+ *
29
+ * 3. UTF-8 BOM (U+FEFF) stripped by default in normalizeText so
30
+ * Windows-authored config files don't break downstream parsers
31
+ * that don't expect a leading BOM.
32
+ *
33
+ * 4. Best-effort secret hygiene via secureZero `buf.fill(0)`
34
+ * clears the visible Buffer so a heap-dump won't show the
35
+ * secret in that allocation. JavaScript can't guarantee zeroing
36
+ * across V8 copies, but the in-buffer reference is gone.
37
+ *
38
+ * 5. Format-aware error classes. Each call site (xml-safe,
39
+ * json-safe, atomic-file, …) passes its own `errorClass` so the
40
+ * byte-handling lives here but the thrown error matches the
41
+ * caller's contract (`e.code === "xml/too-large"` etc.).
42
+ * A default SafeBufferError is used when no class is supplied.
43
+ *
44
+ * The byte-shape predicates (HEX_RE, BASE64URL_RE, TRACE_ID_HEX_RE,
45
+ * SPAN_ID_HEX_RE, RFC7230_TCHAR_RE, CRLF_RE, TRAILING_HSPACE_RE)
46
+ * plus their helper functions (isHex, hasCrlf, stripCrlf,
47
+ * stripTrailingHspace) live alongside the buffer helpers because
48
+ * every caller that bounds bytes also tends to validate the textual
49
+ * shape of those bytes (header tokens, hex digests, JOSE compact
50
+ * serialisations, DKIM canonicalization).
51
+ *
52
+ * @card
53
+ * Buffer-safety primitives that centralize the input-normalize, capped-collection, and secure-zero patterns previously scattered across parsers, atomic-file, object-store, and log-stream modules.
38
54
  */
39
55
 
40
56
  var numericBounds = require("./numeric-bounds");
@@ -54,8 +70,53 @@ function _throw(errorClass, message, code) {
54
70
  throw new Cls(message, code);
55
71
  }
56
72
 
57
- // ---- normalizeText ----
58
-
73
+ /**
74
+ * @primitive b.safeBuffer.normalizeText
75
+ * @signature b.safeBuffer.normalizeText(input, opts?)
76
+ * @since 0.4.9
77
+ * @related b.safeBuffer.toBuffer, b.safeBuffer.boundedChunkCollector
78
+ *
79
+ * Normalize a byte-shaped input (string / Buffer / Uint8Array) to a
80
+ * UTF-8 string with the byte cap enforced BEFORE the result is handed
81
+ * back. Anything outside the documented input set throws — `null`,
82
+ * `undefined`, plain objects, numbers all reject instead of being
83
+ * coerced via `Buffer.from`. The leading UTF-8 BOM (U+FEFF) is
84
+ * stripped by default so Windows-authored config files don't break
85
+ * downstream parsers.
86
+ *
87
+ * Numeric `maxBytes` is validated as a positive finite integer at
88
+ * call-time — `Infinity` / `NaN` / negative throw rather than
89
+ * silently disabling the cap.
90
+ *
91
+ * @opts
92
+ * maxBytes: number, // optional positive finite int; UTF-8 byte cap
93
+ * stripBom: boolean, // default true; remove leading U+FEFF
94
+ * errorClass: Function, // caller-supplied Error subclass for thrown errors
95
+ * typeCode: string, // default "buffer/wrong-input-type"
96
+ * sizeCode: string, // default "buffer/too-large"
97
+ * typeMessage: string, // override the wrong-input-type message
98
+ * sizeMessage: string, // override the too-large message
99
+ *
100
+ * @example
101
+ * var b = require("blamejs");
102
+ * var s = b.safeBuffer.normalizeText(Buffer.from("hello"));
103
+ * // → "hello"
104
+ *
105
+ * // BOM stripped by default.
106
+ * var bom = Buffer.from([0xEF, 0xBB, 0xBF, 0x68, 0x69]);
107
+ * b.safeBuffer.normalizeText(bom);
108
+ * // → "hi"
109
+ *
110
+ * // Non-byte input throws instead of coercing to "undefined".
111
+ * try { b.safeBuffer.normalizeText(undefined); }
112
+ * catch (e) { e.code; }
113
+ * // → "buffer/wrong-input-type"
114
+ *
115
+ * // maxBytes enforced; Infinity rejected at config time.
116
+ * try { b.safeBuffer.normalizeText("xxx", { maxBytes: Infinity }); }
117
+ * catch (e) { e.code; }
118
+ * // → "buffer/bad-arg"
119
+ */
59
120
  function normalizeText(input, opts) {
60
121
  opts = opts || {};
61
122
  // maxBytes optional; positive finite int when set — Infinity / NaN
@@ -91,8 +152,48 @@ function normalizeText(input, opts) {
91
152
  return text;
92
153
  }
93
154
 
94
- // ---- toBuffer ----
95
-
155
+ /**
156
+ * @primitive b.safeBuffer.toBuffer
157
+ * @signature b.safeBuffer.toBuffer(data, opts?)
158
+ * @since 0.4.9
159
+ * @related b.safeBuffer.normalizeText, b.safeBuffer.boundedChunkCollector
160
+ *
161
+ * Coerce a byte-shaped input (Buffer / Uint8Array / string) to a
162
+ * Buffer with the byte cap enforced before return. Unlike raw
163
+ * `Buffer.from`, an Object / number / `undefined` does NOT slip
164
+ * through — every non-byte input throws with a documented code.
165
+ * `Buffer.isBuffer(data)` returns the input unchanged (zero copy);
166
+ * Uint8Array is wrapped, string is encoded as UTF-8.
167
+ *
168
+ * @opts
169
+ * maxBytes: number, // optional positive finite int; byte cap
170
+ * errorClass: Function, // caller-supplied Error subclass
171
+ * typeCode: string, // default "buffer/wrong-input-type"
172
+ * sizeCode: string, // default "buffer/too-large"
173
+ * typeMessage: string, // override the wrong-input-type message
174
+ * sizeMessage: string, // override the too-large message
175
+ *
176
+ * @example
177
+ * var b = require("blamejs");
178
+ * var buf = b.safeBuffer.toBuffer("hello");
179
+ * buf.length;
180
+ * // → 5
181
+ *
182
+ * // Buffer passes through unchanged (zero copy).
183
+ * var input = Buffer.from([1, 2, 3]);
184
+ * b.safeBuffer.toBuffer(input) === input;
185
+ * // → true
186
+ *
187
+ * // Object input throws instead of coercing.
188
+ * try { b.safeBuffer.toBuffer({ not: "bytes" }); }
189
+ * catch (e) { e.code; }
190
+ * // → "buffer/wrong-input-type"
191
+ *
192
+ * // maxBytes cap.
193
+ * try { b.safeBuffer.toBuffer("abcdef", { maxBytes: 3 }); }
194
+ * catch (e) { e.code; }
195
+ * // → "buffer/too-large"
196
+ */
96
197
  function toBuffer(data, opts) {
97
198
  opts = opts || {};
98
199
  // maxBytes optional; positive finite int when provided.
@@ -132,6 +233,53 @@ function toBuffer(data, opts) {
132
233
  // chunk that overflows — without first accumulating the whole 10 GB in
133
234
  // the chunks array.
134
235
 
236
+ /**
237
+ * @primitive b.safeBuffer.boundedChunkCollector
238
+ * @signature b.safeBuffer.boundedChunkCollector(opts)
239
+ * @since 0.4.9
240
+ * @related b.safeBuffer.toBuffer, b.safeBuffer.normalizeText
241
+ *
242
+ * Streaming-body collector that enforces `maxBytes` at every `push()`
243
+ * — never after. A hostile upstream sending a 10-GB response rejects
244
+ * on the chunk that overflows the cap, instead of accumulating the
245
+ * full 10 GB in memory before the framework discovers the problem.
246
+ *
247
+ * `maxBytes` is REQUIRED (positive finite integer). `Infinity` is
248
+ * rejected at construction because it defeats the entire purpose of
249
+ * the bounded collector. Each `push()` accepts Buffer / Uint8Array /
250
+ * string; non-byte chunks throw.
251
+ *
252
+ * Returns `{ push, result, bytesCollected }`. Call `result()` when the
253
+ * stream ends to get the concatenated Buffer.
254
+ *
255
+ * @opts
256
+ * maxBytes: number, // REQUIRED positive finite int; total byte cap
257
+ * errorClass: Function, // caller-supplied Error subclass
258
+ * sizeCode: string, // default "buffer/too-large"
259
+ * sizeMessage: string, // override the too-large message
260
+ *
261
+ * @example
262
+ * var b = require("blamejs");
263
+ * var c = b.safeBuffer.boundedChunkCollector({ maxBytes: 1024 });
264
+ * c.push(Buffer.from("hello "));
265
+ * c.push(Buffer.from("world"));
266
+ * c.bytesCollected();
267
+ * // → 11
268
+ * c.result().toString("utf8");
269
+ * // → "hello world"
270
+ *
271
+ * // Cap enforced at push, not at result().
272
+ * var c2 = b.safeBuffer.boundedChunkCollector({ maxBytes: 4 });
273
+ * c2.push(Buffer.from("abc"));
274
+ * try { c2.push(Buffer.from("defgh")); }
275
+ * catch (e) { e.code; }
276
+ * // → "buffer/too-large"
277
+ *
278
+ * // Infinity rejected at construction.
279
+ * try { b.safeBuffer.boundedChunkCollector({ maxBytes: Infinity }); }
280
+ * catch (e) { e.code; }
281
+ * // → "buffer/bad-arg"
282
+ */
135
283
  function boundedChunkCollector(opts) {
136
284
  opts = opts || {};
137
285
  // maxBytes required, positive finite integer. Accepting Infinity
@@ -173,8 +321,36 @@ function boundedChunkCollector(opts) {
173
321
  };
174
322
  }
175
323
 
176
- // ---- secureZero ----
177
-
324
+ /**
325
+ * @primitive b.safeBuffer.secureZero
326
+ * @signature b.safeBuffer.secureZero(buf)
327
+ * @since 0.4.9
328
+ * @related b.safeBuffer.toBuffer, b.crypto.generateBytes
329
+ *
330
+ * Best-effort secret hygiene. `buf.fill(0)` clears the visible Buffer
331
+ * / Uint8Array so a heap-dump won't show the secret in that
332
+ * allocation. JavaScript can't guarantee zeroing across V8 internal
333
+ * copies (string interning, JIT-spilled registers), but the in-buffer
334
+ * reference is gone and that's the only handle the framework can
335
+ * reliably wipe.
336
+ *
337
+ * Silently no-ops on non-byte inputs and on locked / shared buffers
338
+ * that throw on `.fill` — the caller's contract is "I'm done with
339
+ * this", not "guarantee zeroing succeeded." Pair with `Buffer`
340
+ * allocations whose lifetime is short and well-scoped.
341
+ *
342
+ * @example
343
+ * var b = require("blamejs");
344
+ * var key = Buffer.from("super-secret-key");
345
+ * // ... use key ...
346
+ * b.safeBuffer.secureZero(key);
347
+ * key[0];
348
+ * // → 0
349
+ *
350
+ * // No-op on non-byte input.
351
+ * b.safeBuffer.secureZero("a string");
352
+ * // → undefined
353
+ */
178
354
  function secureZero(buf) {
179
355
  if (Buffer.isBuffer(buf) || buf instanceof Uint8Array) {
180
356
  try { buf.fill(0); } catch (_e) { /* best effort — locked memory etc. */ }
@@ -219,21 +395,141 @@ var CRLF_RE_GLOBAL = /[\r\n]/g; // for `.replace` strip use
219
395
  // site that needs the "rstrip" semantic. Spaces and tabs only;
220
396
  // callers that want CR/LF stripped use stripCrlf.
221
397
  var TRAILING_HSPACE_RE = /[ \t]+$/;
398
+ /**
399
+ * @primitive b.safeBuffer.stripTrailingHspace
400
+ * @signature b.safeBuffer.stripTrailingHspace(s)
401
+ * @since 0.7.0
402
+ * @related b.safeBuffer.stripCrlf, b.safeBuffer.hasCrlf
403
+ *
404
+ * Strip trailing horizontal whitespace (spaces and tabs only) from a
405
+ * string — the "rstrip" semantic used by DKIM canonicalization
406
+ * (RFC 6376 §3.4.4 relaxed body), `.env` parsers, and YAML scalar
407
+ * readers. Does NOT touch CR / LF — pair with `stripCrlf` when you
408
+ * need full whitespace stripping. Non-string input passes through
409
+ * unchanged so the helper is safe in mixed pipelines.
410
+ *
411
+ * @example
412
+ * var b = require("blamejs");
413
+ * b.safeBuffer.stripTrailingHspace("hello ");
414
+ * // → "hello"
415
+ *
416
+ * // Tabs stripped too; internal whitespace preserved.
417
+ * b.safeBuffer.stripTrailingHspace("a b\t\t");
418
+ * // → "a b"
419
+ *
420
+ * // CR / LF intentionally preserved.
421
+ * b.safeBuffer.stripTrailingHspace("hello \n");
422
+ * // → "hello \n"
423
+ *
424
+ * // Non-string passthrough.
425
+ * b.safeBuffer.stripTrailingHspace(42);
426
+ * // → 42
427
+ */
222
428
  function stripTrailingHspace(s) {
223
429
  if (typeof s !== "string") return s;
224
430
  return s.replace(TRAILING_HSPACE_RE, "");
225
431
  }
226
432
 
433
+ /**
434
+ * @primitive b.safeBuffer.isHex
435
+ * @signature b.safeBuffer.isHex(s, expectedLength?)
436
+ * @since 0.7.0
437
+ * @related b.safeBuffer.hasCrlf, b.crypto.timingSafeEqual
438
+ *
439
+ * Predicate for non-empty all-hex strings (case-insensitive). Pass
440
+ * `expectedLength` to bound the protocol-fixed digests — SHA3-512 is
441
+ * 128 hex chars, SHA-256 is 64, etc. Without `expectedLength` the
442
+ * predicate is length-agnostic and the caller is responsible for
443
+ * bounding length per protocol (X.509 serial, DKIM hash, audit-chain
444
+ * digest).
445
+ *
446
+ * Non-string input returns `false` so the helper is safe in defensive
447
+ * request-shape readers.
448
+ *
449
+ * @example
450
+ * var b = require("blamejs");
451
+ * b.safeBuffer.isHex("deadbeef");
452
+ * // → true
453
+ *
454
+ * // Length-bounded check (SHA-256 = 64 hex chars).
455
+ * b.safeBuffer.isHex("deadbeef", 64);
456
+ * // → false
457
+ *
458
+ * // Mixed case accepted.
459
+ * b.safeBuffer.isHex("DeadBeef");
460
+ * // → true
461
+ *
462
+ * // Non-string returns false.
463
+ * b.safeBuffer.isHex(null);
464
+ * // → false
465
+ */
227
466
  function isHex(s, expectedLength) {
228
467
  if (typeof s !== "string") return false;
229
468
  if (typeof expectedLength === "number" && s.length !== expectedLength) return false;
230
469
  return HEX_RE.test(s);
231
470
  }
232
471
 
472
+ /**
473
+ * @primitive b.safeBuffer.hasCrlf
474
+ * @signature b.safeBuffer.hasCrlf(s)
475
+ * @since 0.7.0
476
+ * @related b.safeBuffer.stripCrlf, b.safeBuffer.stripTrailingHspace
477
+ *
478
+ * Detect CR or LF in a string — the canonical injection vector for
479
+ * HTTP-header / SMTP-envelope smuggling. Header values containing CR
480
+ * or LF must be rejected before serialization or stripped via
481
+ * `stripCrlf`. Non-string input returns `false` so callers can chain
482
+ * the predicate without pre-typechecking.
483
+ *
484
+ * @example
485
+ * var b = require("blamejs");
486
+ * b.safeBuffer.hasCrlf("X-Custom-Header: ok");
487
+ * // → false
488
+ *
489
+ * // Injection attempt.
490
+ * b.safeBuffer.hasCrlf("ok\r\nX-Injected: bad");
491
+ * // → true
492
+ *
493
+ * // Bare LF also detected.
494
+ * b.safeBuffer.hasCrlf("ok\nbad");
495
+ * // → true
496
+ *
497
+ * // Non-string returns false.
498
+ * b.safeBuffer.hasCrlf(undefined);
499
+ * // → false
500
+ */
233
501
  function hasCrlf(s) {
234
502
  return typeof s === "string" && CRLF_RE.test(s);
235
503
  }
236
504
 
505
+ /**
506
+ * @primitive b.safeBuffer.stripCrlf
507
+ * @signature b.safeBuffer.stripCrlf(s, replacement?)
508
+ * @since 0.7.0
509
+ * @related b.safeBuffer.hasCrlf, b.safeBuffer.stripTrailingHspace
510
+ *
511
+ * Remove every CR and LF from a string, replacing each with the
512
+ * `replacement` argument (default `""`). Use this when the framework
513
+ * must serialize an operator-supplied string into a CRLF-delimited
514
+ * protocol (HTTP header value, SMTP envelope field) and prefers
515
+ * silent stripping over rejecting the request — most security-
516
+ * critical sites should use `hasCrlf` + reject instead.
517
+ *
518
+ * Non-string input passes through unchanged.
519
+ *
520
+ * @example
521
+ * var b = require("blamejs");
522
+ * b.safeBuffer.stripCrlf("ok\r\nbad");
523
+ * // → "okbad"
524
+ *
525
+ * // Custom replacement (e.g. space).
526
+ * b.safeBuffer.stripCrlf("a\nb\nc", " ");
527
+ * // → "a b c"
528
+ *
529
+ * // Non-string passthrough.
530
+ * b.safeBuffer.stripCrlf(42);
531
+ * // → 42
532
+ */
237
533
  function stripCrlf(s, replacement) {
238
534
  if (typeof s !== "string") return s;
239
535
  return s.replace(CRLF_RE_GLOBAL, replacement === undefined ? "" : replacement);