@blamejs/core 0.8.43 → 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.
- package/CHANGELOG.md +92 -0
- package/README.md +10 -10
- package/index.js +52 -0
- package/lib/a2a.js +159 -34
- package/lib/acme.js +762 -0
- package/lib/ai-pref.js +166 -43
- package/lib/api-key.js +108 -47
- package/lib/api-snapshot.js +157 -40
- package/lib/app-shutdown.js +113 -77
- package/lib/archive.js +337 -40
- package/lib/arg-parser.js +697 -0
- package/lib/asyncapi.js +99 -55
- package/lib/atomic-file.js +465 -104
- package/lib/audit-chain.js +123 -34
- package/lib/audit-daily-review.js +389 -0
- package/lib/audit-sign.js +302 -56
- package/lib/audit-tools.js +412 -63
- package/lib/audit.js +656 -35
- package/lib/auth/jwt-external.js +17 -0
- package/lib/auth/oauth.js +7 -0
- package/lib/auth-bot-challenge.js +505 -0
- package/lib/auth-header.js +92 -25
- package/lib/backup/bundle.js +26 -0
- package/lib/backup/index.js +512 -89
- package/lib/backup/manifest.js +168 -7
- package/lib/break-glass.js +415 -39
- package/lib/budr.js +103 -30
- package/lib/bundler.js +86 -66
- package/lib/cache.js +192 -72
- package/lib/chain-writer.js +65 -40
- package/lib/circuit-breaker.js +56 -33
- package/lib/cli-helpers.js +106 -75
- package/lib/cli.js +6 -30
- package/lib/cloud-events.js +99 -32
- package/lib/cluster-storage.js +162 -37
- package/lib/cluster.js +340 -49
- package/lib/codepoint-class.js +66 -0
- package/lib/compliance.js +424 -24
- package/lib/config-drift.js +111 -46
- package/lib/config.js +94 -40
- package/lib/consent.js +165 -18
- package/lib/constants.js +1 -0
- package/lib/content-credentials.js +153 -48
- package/lib/cookies.js +154 -62
- package/lib/credential-hash.js +133 -61
- package/lib/crypto-field.js +702 -18
- package/lib/crypto-hpke.js +256 -0
- package/lib/crypto.js +744 -22
- package/lib/csv.js +178 -35
- package/lib/daemon.js +456 -0
- package/lib/dark-patterns.js +186 -55
- package/lib/db-query.js +79 -2
- package/lib/db.js +1431 -60
- package/lib/ddl-change-control.js +523 -0
- package/lib/deprecate.js +195 -40
- package/lib/dev.js +82 -39
- package/lib/dora.js +67 -48
- package/lib/dr-runbook.js +368 -0
- package/lib/dsr.js +142 -11
- package/lib/dual-control.js +91 -56
- package/lib/events.js +120 -41
- package/lib/external-db-migrate.js +192 -2
- package/lib/external-db.js +795 -50
- package/lib/fapi2.js +122 -1
- package/lib/fda-21cfr11.js +395 -0
- package/lib/fdx.js +132 -2
- package/lib/file-type.js +87 -0
- package/lib/file-upload.js +93 -0
- package/lib/flag.js +82 -20
- package/lib/forms.js +132 -29
- package/lib/framework-error.js +169 -0
- package/lib/framework-schema.js +163 -35
- package/lib/gate-contract.js +849 -175
- package/lib/graphql-federation.js +68 -7
- package/lib/guard-all.js +172 -55
- package/lib/guard-archive.js +286 -124
- package/lib/guard-auth.js +194 -21
- package/lib/guard-cidr.js +190 -28
- package/lib/guard-csv.js +397 -51
- package/lib/guard-domain.js +213 -91
- package/lib/guard-email.js +236 -29
- package/lib/guard-filename.js +307 -75
- package/lib/guard-graphql.js +263 -30
- package/lib/guard-html.js +310 -116
- package/lib/guard-image.js +243 -30
- package/lib/guard-json.js +260 -54
- package/lib/guard-jsonpath.js +235 -23
- package/lib/guard-jwt.js +284 -30
- package/lib/guard-markdown.js +204 -22
- package/lib/guard-mime.js +190 -26
- package/lib/guard-oauth.js +277 -28
- package/lib/guard-pdf.js +251 -27
- package/lib/guard-regex.js +226 -18
- package/lib/guard-shell.js +229 -26
- package/lib/guard-svg.js +177 -10
- package/lib/guard-template.js +232 -21
- package/lib/guard-time.js +195 -29
- package/lib/guard-uuid.js +189 -30
- package/lib/guard-xml.js +259 -36
- package/lib/guard-yaml.js +241 -44
- package/lib/honeytoken.js +63 -27
- package/lib/html-balance.js +83 -0
- package/lib/http-client.js +486 -59
- package/lib/http-message-signature.js +582 -0
- package/lib/i18n.js +102 -49
- package/lib/iab-mspa.js +112 -32
- package/lib/iab-tcf.js +107 -2
- package/lib/inbox.js +90 -52
- package/lib/keychain.js +865 -0
- package/lib/legal-hold.js +374 -0
- package/lib/local-db-thin.js +320 -0
- package/lib/log-stream.js +281 -51
- package/lib/log.js +184 -86
- package/lib/mail-bounce.js +107 -62
- package/lib/mail.js +295 -58
- package/lib/mcp.js +108 -27
- package/lib/metrics.js +98 -89
- package/lib/middleware/age-gate.js +36 -0
- package/lib/middleware/ai-act-disclosure.js +37 -0
- package/lib/middleware/api-encrypt.js +45 -0
- package/lib/middleware/assetlinks.js +40 -0
- package/lib/middleware/asyncapi-serve.js +35 -0
- package/lib/middleware/attach-user.js +40 -0
- package/lib/middleware/bearer-auth.js +40 -0
- package/lib/middleware/body-parser.js +230 -0
- package/lib/middleware/bot-disclose.js +34 -0
- package/lib/middleware/bot-guard.js +39 -0
- package/lib/middleware/compression.js +37 -0
- package/lib/middleware/cookies.js +32 -0
- package/lib/middleware/cors.js +40 -0
- package/lib/middleware/csp-nonce.js +40 -0
- package/lib/middleware/csp-report.js +34 -0
- package/lib/middleware/csrf-protect.js +43 -0
- package/lib/middleware/daily-byte-quota.js +53 -85
- package/lib/middleware/db-role-for.js +40 -0
- package/lib/middleware/dpop.js +40 -0
- package/lib/middleware/error-handler.js +37 -14
- package/lib/middleware/fetch-metadata.js +39 -0
- package/lib/middleware/flag-context.js +34 -0
- package/lib/middleware/gpc.js +33 -0
- package/lib/middleware/headers.js +35 -0
- package/lib/middleware/health.js +46 -0
- package/lib/middleware/host-allowlist.js +30 -0
- package/lib/middleware/network-allowlist.js +38 -0
- package/lib/middleware/openapi-serve.js +34 -0
- package/lib/middleware/rate-limit.js +160 -18
- package/lib/middleware/request-id.js +36 -18
- package/lib/middleware/request-log.js +37 -0
- package/lib/middleware/require-aal.js +29 -0
- package/lib/middleware/require-auth.js +32 -0
- package/lib/middleware/require-bound-key.js +41 -0
- package/lib/middleware/require-content-type.js +32 -0
- package/lib/middleware/require-methods.js +27 -0
- package/lib/middleware/require-mtls.js +33 -0
- package/lib/middleware/require-step-up.js +37 -0
- package/lib/middleware/security-headers.js +44 -0
- package/lib/middleware/security-txt.js +38 -0
- package/lib/middleware/span-http-server.js +37 -0
- package/lib/middleware/sse.js +36 -0
- package/lib/middleware/trace-log-correlation.js +33 -0
- package/lib/middleware/trace-propagate.js +32 -0
- package/lib/middleware/tus-upload.js +90 -0
- package/lib/middleware/web-app-manifest.js +53 -0
- package/lib/mtls-ca.js +100 -70
- package/lib/network-byte-quota.js +308 -0
- package/lib/network-heartbeat.js +135 -0
- package/lib/network-tls.js +534 -4
- package/lib/network.js +103 -0
- package/lib/notify.js +114 -43
- package/lib/ntp-check.js +192 -51
- package/lib/observability.js +145 -47
- package/lib/openapi.js +90 -44
- package/lib/outbox.js +99 -1
- package/lib/pagination.js +168 -86
- package/lib/parsers/index.js +16 -5
- package/lib/permissions.js +93 -40
- package/lib/pqc-agent.js +84 -8
- package/lib/pqc-software.js +94 -60
- package/lib/process-spawn.js +95 -21
- package/lib/pubsub.js +96 -66
- package/lib/queue.js +375 -54
- package/lib/redact.js +793 -21
- package/lib/render.js +139 -47
- package/lib/request-helpers.js +485 -121
- package/lib/restore-bundle.js +142 -39
- package/lib/restore-rollback.js +136 -45
- package/lib/retention.js +178 -50
- package/lib/retry.js +116 -33
- package/lib/router.js +475 -23
- package/lib/safe-async.js +543 -94
- package/lib/safe-buffer.js +337 -41
- package/lib/safe-json.js +467 -62
- package/lib/safe-jsonpath.js +285 -0
- package/lib/safe-schema.js +631 -87
- package/lib/safe-sql.js +221 -59
- package/lib/safe-url.js +278 -46
- package/lib/sandbox-worker.js +135 -0
- package/lib/sandbox.js +358 -0
- package/lib/scheduler.js +135 -70
- package/lib/self-update.js +647 -0
- package/lib/session-device-binding.js +431 -0
- package/lib/session.js +259 -49
- package/lib/slug.js +138 -26
- package/lib/ssrf-guard.js +316 -56
- package/lib/storage.js +433 -70
- package/lib/subject.js +405 -23
- package/lib/template.js +148 -8
- package/lib/tenant-quota.js +545 -0
- package/lib/testing.js +440 -53
- package/lib/time.js +291 -23
- package/lib/tls-exporter.js +239 -0
- package/lib/tracing.js +90 -74
- package/lib/uuid.js +97 -22
- package/lib/vault/index.js +284 -22
- package/lib/vault/seal-pem-file.js +66 -0
- package/lib/watcher.js +368 -0
- package/lib/webhook.js +196 -63
- package/lib/websocket.js +393 -68
- package/lib/wiki-concepts.js +338 -0
- package/lib/worker-pool.js +464 -0
- package/package.json +3 -3
- package/sbom.cyclonedx.json +7 -7
package/lib/safe-buffer.js
CHANGED
|
@@ -1,40 +1,56 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|