@blamejs/core 0.15.11 → 0.15.13
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 +4 -0
- package/index.js +2 -0
- package/lib/a2a-tasks.js +7 -23
- package/lib/acme.js +13 -16
- package/lib/agent-event-bus.js +5 -4
- package/lib/agent-idempotency.js +2 -5
- package/lib/agent-orchestrator.js +2 -5
- package/lib/agent-saga.js +3 -5
- package/lib/agent-tenant.js +2 -5
- package/lib/ai-adverse-decision.js +2 -15
- package/lib/ai-capability.js +1 -6
- package/lib/ai-dp.js +1 -5
- package/lib/ai-input.js +2 -2
- package/lib/ai-pref.js +3 -8
- package/lib/ai-quota.js +3 -14
- package/lib/api-key.js +37 -28
- package/lib/archive-adapters.js +2 -4
- package/lib/archive-entry-policy.js +32 -0
- package/lib/archive-read.js +5 -17
- package/lib/archive-tar-read.js +5 -16
- package/lib/archive.js +2 -10
- package/lib/arg-parser.js +7 -6
- package/lib/asyncapi-traits.js +2 -6
- package/lib/atomic-file.js +108 -31
- package/lib/audit-chain.js +133 -53
- package/lib/audit-daily-review.js +24 -14
- package/lib/audit-emit.js +82 -0
- package/lib/audit-sign.js +257 -0
- package/lib/audit.js +84 -0
- package/lib/auth/access-lock.js +5 -27
- package/lib/auth/dpop.js +23 -35
- package/lib/auth/fido-mds3.js +9 -15
- package/lib/auth/jwt-external.js +26 -8
- package/lib/auth/jwt.js +13 -15
- package/lib/auth/lockout.js +6 -25
- package/lib/auth/oauth.js +67 -45
- package/lib/auth/oid4vci.js +55 -32
- package/lib/auth/oid4vp.js +3 -2
- package/lib/auth/openid-federation.js +6 -6
- package/lib/auth/passkey.js +3 -3
- package/lib/auth/password.js +7 -1
- package/lib/auth/saml.js +37 -27
- package/lib/auth/sd-jwt-vc-holder.js +2 -14
- package/lib/auth/sd-jwt-vc-issuer.js +2 -14
- package/lib/auth/sd-jwt-vc.js +1 -1
- package/lib/auth/status-list.js +7 -7
- package/lib/auth/step-up.js +6 -12
- package/lib/auth-bot-challenge.js +6 -37
- package/lib/backup/bundle.js +11 -18
- package/lib/backup/index.js +14 -47
- package/lib/bounded-map.js +112 -1
- package/lib/breach-deadline.js +1 -11
- package/lib/cache.js +7 -18
- package/lib/cbor.js +2 -1
- package/lib/cdn-cache-control.js +8 -9
- package/lib/cert.js +3 -10
- package/lib/chain-writer.js +162 -47
- package/lib/cli.js +5 -1
- package/lib/client-hints.js +10 -10
- package/lib/cloud-events.js +40 -31
- package/lib/cluster-storage.js +2 -38
- package/lib/cluster.js +4 -2
- package/lib/cms-codec.js +2 -1
- package/lib/codepoint-class.js +67 -1
- package/lib/compliance-ai-act-logging.js +1 -6
- package/lib/compliance-ai-act-transparency.js +2 -4
- package/lib/compliance-eaa.js +1 -11
- package/lib/compliance-sanctions-fetcher.js +21 -28
- package/lib/compliance-sanctions.js +2 -14
- package/lib/compliance.js +2 -9
- package/lib/config-drift.js +2 -12
- package/lib/content-digest.js +10 -8
- package/lib/cookies.js +5 -11
- package/lib/cose.js +19 -11
- package/lib/cra-report.js +1 -11
- package/lib/crypto-field.js +5 -10
- package/lib/crypto.js +120 -3
- package/lib/csp.js +235 -3
- package/lib/daemon.js +42 -41
- package/lib/data-act.js +19 -9
- package/lib/db-query.js +6 -40
- package/lib/db.js +34 -12
- package/lib/dbsc.js +5 -6
- package/lib/ddl-change-control.js +18 -14
- package/lib/deprecate.js +4 -5
- package/lib/did.js +6 -2
- package/lib/dsr.js +8 -12
- package/lib/external-db-migrate.js +21 -22
- package/lib/external-db.js +14 -26
- package/lib/fda-21cfr11.js +12 -8
- package/lib/fdx.js +22 -18
- package/lib/file-upload.js +80 -66
- package/lib/flag-evaluation-context.js +5 -8
- package/lib/framework-error.js +12 -0
- package/lib/framework-schema.js +19 -0
- package/lib/fsm.js +7 -12
- package/lib/gate-contract.js +869 -38
- package/lib/gdpr-ropa.js +4 -13
- package/lib/graphql-federation.js +1 -1
- package/lib/guard-agent-registry.js +2 -1
- package/lib/guard-all.js +1 -0
- package/lib/guard-archive.js +9 -30
- package/lib/guard-auth.js +23 -80
- package/lib/guard-cidr.js +20 -96
- package/lib/guard-csv.js +135 -196
- package/lib/guard-domain.js +23 -106
- package/lib/guard-dsn.js +16 -13
- package/lib/guard-email.js +46 -53
- package/lib/guard-envelope.js +1 -1
- package/lib/guard-event-bus-topic.js +2 -1
- package/lib/guard-filename.js +18 -62
- package/lib/guard-graphql.js +28 -75
- package/lib/guard-html-wcag.js +15 -2
- package/lib/guard-html.js +65 -117
- package/lib/guard-idempotency-key.js +2 -1
- package/lib/guard-image.js +280 -77
- package/lib/guard-imap-command.js +8 -9
- package/lib/guard-json.js +87 -103
- package/lib/guard-jsonpath.js +20 -88
- package/lib/guard-jwt.js +32 -114
- package/lib/guard-list-id.js +2 -7
- package/lib/guard-list-unsubscribe.js +2 -7
- package/lib/guard-mail-compose.js +5 -6
- package/lib/guard-mail-move.js +2 -1
- package/lib/guard-mail-query.js +5 -3
- package/lib/guard-mail-sieve.js +6 -7
- package/lib/guard-managesieve-command.js +5 -4
- package/lib/guard-markdown.js +76 -110
- package/lib/guard-message-id.js +5 -6
- package/lib/guard-mime.js +20 -99
- package/lib/guard-oauth.js +19 -73
- package/lib/guard-pdf.js +65 -72
- package/lib/guard-pop3-command.js +12 -13
- package/lib/guard-posture-chain.js +2 -1
- package/lib/guard-regex.js +24 -99
- package/lib/guard-saga-config.js +2 -1
- package/lib/guard-shell.js +22 -87
- package/lib/guard-smtp-command.js +8 -11
- package/lib/guard-sql.js +15 -13
- package/lib/guard-stream-args.js +2 -1
- package/lib/guard-svg.js +95 -140
- package/lib/guard-template.js +23 -80
- package/lib/guard-tenant-id.js +2 -1
- package/lib/guard-text.js +592 -0
- package/lib/guard-time.js +27 -95
- package/lib/guard-uuid.js +21 -93
- package/lib/guard-xml.js +76 -106
- package/lib/guard-yaml.js +24 -60
- package/lib/http-client-cache.js +8 -13
- package/lib/http-client-cookie-jar.js +2 -4
- package/lib/http-client.js +8 -21
- package/lib/http-message-signature.js +26 -8
- package/lib/i18n-messageformat.js +5 -7
- package/lib/i18n.js +83 -26
- package/lib/inbox.js +8 -8
- package/lib/incident-report.js +3 -21
- package/lib/ip-utils.js +49 -6
- package/lib/jobs.js +3 -2
- package/lib/keychain.js +6 -18
- package/lib/legal-hold.js +6 -15
- package/lib/log-stream-cloudwatch.js +44 -112
- package/lib/log-stream-otlp-grpc.js +28 -14
- package/lib/log-stream-otlp.js +16 -80
- package/lib/log-stream-syslog.js +6 -0
- package/lib/log-stream-webhook.js +20 -92
- package/lib/log.js +24 -2
- package/lib/mail-arc-sign.js +8 -3
- package/lib/mail-arf.js +3 -2
- package/lib/mail-auth.js +40 -66
- package/lib/mail-bimi.js +19 -39
- package/lib/mail-crypto-pgp.js +3 -2
- package/lib/mail-dav.js +8 -35
- package/lib/mail-deploy.js +6 -9
- package/lib/mail-dkim.js +19 -38
- package/lib/mail-greylist.js +15 -26
- package/lib/mail-helo.js +2 -3
- package/lib/mail-journal.js +2 -1
- package/lib/mail-mdn.js +4 -3
- package/lib/mail-rbl.js +5 -8
- package/lib/mail-scan.js +2 -2
- package/lib/mail-send-deliver.js +33 -20
- package/lib/mail-server-imap.js +32 -87
- package/lib/mail-server-jmap.js +35 -51
- package/lib/mail-server-managesieve.js +29 -83
- package/lib/mail-server-mx.js +33 -74
- package/lib/mail-server-net.js +177 -0
- package/lib/mail-server-pop3.js +30 -83
- package/lib/mail-server-rate-limit.js +30 -6
- package/lib/mail-server-submission.js +34 -73
- package/lib/mail-server-tls.js +89 -0
- package/lib/mail-spam-score.js +7 -8
- package/lib/mail-store.js +3 -2
- package/lib/mail.js +11 -11
- package/lib/markup-escape.js +31 -0
- package/lib/markup-tokenizer.js +54 -0
- package/lib/mcp-tool-registry.js +26 -23
- package/lib/mcp.js +1 -1
- package/lib/mdoc.js +11 -12
- package/lib/metrics.js +32 -30
- package/lib/middleware/age-gate.js +3 -23
- package/lib/middleware/api-encrypt.js +11 -22
- package/lib/middleware/assetlinks.js +2 -7
- package/lib/middleware/bearer-auth.js +2 -1
- package/lib/middleware/body-parser.js +79 -54
- package/lib/middleware/bot-disclose.js +7 -10
- package/lib/middleware/bot-guard.js +2 -10
- package/lib/middleware/csrf-protect.js +13 -2
- package/lib/middleware/daily-byte-quota.js +6 -26
- package/lib/middleware/deny-response.js +19 -1
- package/lib/middleware/fetch-metadata.js +7 -0
- package/lib/middleware/idempotency-key.js +4 -20
- package/lib/middleware/rate-limit.js +20 -28
- package/lib/middleware/scim-server.js +3 -2
- package/lib/middleware/security-headers.js +9 -1
- package/lib/middleware/security-txt.js +2 -6
- package/lib/middleware/tus-upload.js +12 -27
- package/lib/middleware/web-app-manifest.js +2 -7
- package/lib/migrations.js +4 -13
- package/lib/mime-parse.js +34 -17
- package/lib/module-loader.js +44 -0
- package/lib/money.js +105 -0
- package/lib/mtls-ca.js +116 -36
- package/lib/network-byte-quota.js +4 -18
- package/lib/network-dane.js +8 -6
- package/lib/network-dns-resolver.js +97 -6
- package/lib/network-dns.js +22 -26
- package/lib/network-dnssec.js +16 -16
- package/lib/network-heartbeat.js +6 -5
- package/lib/network-proxy.js +3 -7
- package/lib/network-smtp-policy.js +29 -41
- package/lib/network-tls.js +74 -55
- package/lib/network.js +2 -6
- package/lib/nis2-report.js +1 -11
- package/lib/nonce-store.js +81 -3
- package/lib/notify.js +7 -12
- package/lib/numeric-bounds.js +22 -8
- package/lib/object-store/azure-blob-bucket-ops.js +2 -6
- package/lib/object-store/azure-blob.js +5 -45
- package/lib/object-store/gcs.js +8 -71
- package/lib/object-store/http-put.js +1 -5
- package/lib/object-store/http-request.js +128 -0
- package/lib/object-store/sigv4-bucket-ops.js +2 -1
- package/lib/object-store/sigv4.js +9 -77
- package/lib/observability-otlp-exporter.js +9 -25
- package/lib/observability.js +95 -0
- package/lib/openapi-paths-builder.js +7 -3
- package/lib/otel-export.js +7 -10
- package/lib/outbox.js +43 -37
- package/lib/pagination.js +11 -15
- package/lib/parsers/safe-env.js +5 -4
- package/lib/parsers/safe-ini.js +10 -4
- package/lib/parsers/safe-toml.js +11 -9
- package/lib/parsers/safe-xml.js +2 -2
- package/lib/parsers/safe-yaml.js +7 -6
- package/lib/pick.js +63 -5
- package/lib/pipl-cn.js +69 -59
- package/lib/privacy-pass.js +8 -6
- package/lib/problem-details.js +2 -5
- package/lib/pubsub.js +4 -8
- package/lib/redact.js +2 -1
- package/lib/render.js +4 -2
- package/lib/request-helpers.js +133 -6
- package/lib/restore.js +5 -22
- package/lib/retention.js +3 -5
- package/lib/safe-async.js +457 -0
- package/lib/safe-buffer.js +137 -6
- package/lib/safe-decompress.js +2 -1
- package/lib/safe-dns.js +2 -1
- package/lib/safe-ical.js +12 -26
- package/lib/safe-json.js +7 -8
- package/lib/safe-jsonpath.js +6 -5
- package/lib/safe-mime.js +25 -29
- package/lib/safe-redirect.js +2 -5
- package/lib/safe-schema.js +7 -6
- package/lib/safe-sql.js +126 -0
- package/lib/safe-vcard.js +12 -26
- package/lib/sandbox-worker.js +9 -2
- package/lib/sandbox.js +3 -2
- package/lib/scheduler.js +5 -12
- package/lib/sec-cyber.js +82 -37
- package/lib/seeders.js +9 -21
- package/lib/self-update.js +92 -69
- package/lib/session-device-binding.js +112 -66
- package/lib/session.js +194 -6
- package/lib/sql.js +225 -78
- package/lib/sse.js +6 -10
- package/lib/static.js +136 -98
- package/lib/storage.js +4 -2
- package/lib/structured-fields.js +313 -17
- package/lib/tenant-quota.js +2 -20
- package/lib/tsa.js +11 -15
- package/lib/validate-opts.js +154 -8
- package/lib/vault/seal-pem-file.js +30 -48
- package/lib/vault-aad.js +3 -2
- package/lib/vc.js +2 -7
- package/lib/vex.js +35 -13
- package/lib/web-push-vapid.js +23 -20
- package/lib/webhook-dispatcher.js +706 -0
- package/lib/webhook.js +43 -18
- package/lib/websocket-channels.js +9 -7
- package/lib/websocket.js +7 -3
- package/lib/worm.js +2 -3
- package/lib/ws-client.js +8 -10
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/storage.js
CHANGED
|
@@ -38,10 +38,12 @@
|
|
|
38
38
|
* Filesystem-and-cloud-backed object storage with sealed per-file encryption keys, classification routing, and residency enforcement.
|
|
39
39
|
*/
|
|
40
40
|
var C = require("./constants");
|
|
41
|
+
var safeBuffer = require("./safe-buffer");
|
|
41
42
|
var { generateBytes, encryptPacked, decryptPacked } = require("./crypto");
|
|
42
43
|
var objectStore = require("./object-store");
|
|
43
44
|
var lazyRequire = require("./lazy-require");
|
|
44
45
|
var numericBounds = require("./numeric-bounds");
|
|
46
|
+
var codepointClass = require("./codepoint-class");
|
|
45
47
|
var canonicalJson = require("./canonical-json");
|
|
46
48
|
var { StorageError } = require("./framework-error");
|
|
47
49
|
|
|
@@ -950,7 +952,7 @@ function _validateAssemblyId(id) {
|
|
|
950
952
|
for (var i = 0; i < id.length; i += 1) {
|
|
951
953
|
var c = id.charCodeAt(i);
|
|
952
954
|
// Refuse: C0 (0x00-0x1F), DEL (0x7F), slash, backslash, dot-prefix
|
|
953
|
-
if (c
|
|
955
|
+
if (codepointClass.isForbiddenControlChar(c, { forbidTab: true }) || c === 0x2F || c === 0x5C) {
|
|
954
956
|
throw _err("INVALID_ARGUMENT",
|
|
955
957
|
"chunkScratch: assemblyId carries forbidden character at byte " + i, true);
|
|
956
958
|
}
|
|
@@ -1083,7 +1085,7 @@ function chunkScratch(opts) {
|
|
|
1083
1085
|
if (!Buffer.isBuffer(args.data)) {
|
|
1084
1086
|
throw _err("INVALID_ARGUMENT", "chunkScratch.saveChunk: data must be a Buffer", true);
|
|
1085
1087
|
}
|
|
1086
|
-
if (args.data
|
|
1088
|
+
if (safeBuffer.byteLengthOf(args.data) > maxChunkBytes) {
|
|
1087
1089
|
throw _err("INVALID_ARGUMENT",
|
|
1088
1090
|
"chunkScratch.saveChunk: chunk exceeds maxChunkBytes (" + args.data.length + " > " + maxChunkBytes + ")", true);
|
|
1089
1091
|
}
|
package/lib/structured-fields.js
CHANGED
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
// node:util is a builtin (no lib require cycle) — used for strict UTF-8
|
|
71
71
|
// validation of RFC 9651 Display Strings.
|
|
72
72
|
var TextDecoder = require("node:util").TextDecoder;
|
|
73
|
+
var codepointClass = require("./codepoint-class");
|
|
73
74
|
|
|
74
75
|
function splitTopLevel(s, sep) {
|
|
75
76
|
if (typeof s !== "string") return [];
|
|
@@ -103,6 +104,261 @@ function splitTopLevel(s, sep) {
|
|
|
103
104
|
return out;
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
/**
|
|
108
|
+
* @primitive b.structuredFields.splitUnquoted
|
|
109
|
+
* @signature b.structuredFields.splitUnquoted(s, sep)
|
|
110
|
+
* @since 0.15.13
|
|
111
|
+
* @status stable
|
|
112
|
+
* @related b.structuredFields.splitTopLevel
|
|
113
|
+
*
|
|
114
|
+
* Split `s` on every `sep` that falls OUTSIDE a `"..."` quoted run — the
|
|
115
|
+
* iCalendar / vCard variant of the quote-aware splitter. A `"` toggles the
|
|
116
|
+
* quoted state and there is NO backslash escaping of the quote (RFC 5545
|
|
117
|
+
* 3.1.1 / RFC 6350 3.3 QSAFE-CHAR excludes DQUOTE, so a `"` always opens or
|
|
118
|
+
* closes a run). This is deliberately simpler than `splitTopLevel`, which
|
|
119
|
+
* honours HTTP structured-field backslash-quote escapes; the two are NOT
|
|
120
|
+
* interchangeable. Accepts any single-character separator.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* b.structuredFields.splitUnquoted('a;b="x;y";c', ";");
|
|
124
|
+
* // returns ['a', 'b="x;y"', 'c']
|
|
125
|
+
*/
|
|
126
|
+
function splitUnquoted(s, sep) {
|
|
127
|
+
var out = [];
|
|
128
|
+
var inQ = false;
|
|
129
|
+
var start = 0;
|
|
130
|
+
for (var i = 0; i < s.length; i++) {
|
|
131
|
+
var c = s.charAt(i);
|
|
132
|
+
if (c === '"') { inQ = !inQ; continue; }
|
|
133
|
+
if (c === sep && !inQ) {
|
|
134
|
+
out.push(s.slice(start, i));
|
|
135
|
+
start = i + 1;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
out.push(s.slice(start));
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @primitive b.structuredFields.stripDoubleQuotes
|
|
144
|
+
* @signature b.structuredFields.stripDoubleQuotes(s)
|
|
145
|
+
* @since 0.15.13
|
|
146
|
+
* @status stable
|
|
147
|
+
* @related b.structuredFields.splitUnquoted, b.structuredFields.unquoteSfString
|
|
148
|
+
*
|
|
149
|
+
* Strip ONE layer of surrounding `"` from `s` when both ends are a double
|
|
150
|
+
* quote (length ≥ 2), otherwise return `s` unchanged. The plain DQUOTE
|
|
151
|
+
* unwrap used by the iCal / vCard parsers for a quoted parameter value —
|
|
152
|
+
* no backslash-escape processing (RFC 5545 3.1 / RFC 6350 5: a quoted
|
|
153
|
+
* param value is QSAFE-CHAR, which excludes DQUOTE, so there is nothing to
|
|
154
|
+
* unescape). Distinct from `unquoteSfString`, which decodes HTTP
|
|
155
|
+
* structured-field `\"` / `\\` escapes.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* b.structuredFields.stripDoubleQuotes('"a;b"');
|
|
159
|
+
* // returns 'a;b'
|
|
160
|
+
*
|
|
161
|
+
* b.structuredFields.stripDoubleQuotes('plain');
|
|
162
|
+
* // returns 'plain'
|
|
163
|
+
*/
|
|
164
|
+
function stripDoubleQuotes(s) {
|
|
165
|
+
if (s.length >= 2 && s.charAt(0) === '"' && s.charAt(s.length - 1) === '"') {
|
|
166
|
+
return s.slice(1, -1);
|
|
167
|
+
}
|
|
168
|
+
return s;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @primitive b.structuredFields.unfoldHeaderContinuations
|
|
173
|
+
* @signature b.structuredFields.unfoldHeaderContinuations(value)
|
|
174
|
+
* @since 0.15.13
|
|
175
|
+
* @status stable
|
|
176
|
+
* @related b.structuredFields.parseTagList
|
|
177
|
+
*
|
|
178
|
+
* Collapse RFC 5322 folding whitespace — a CRLF (or bare LF) followed by one
|
|
179
|
+
* or more spaces/tabs — back to a single space, reversing the line folding a
|
|
180
|
+
* header value may carry in transit. Used before parsing DKIM / ARC /
|
|
181
|
+
* Authentication-Results tag lists, where a folded `b=` / `bh=` value must be
|
|
182
|
+
* rejoined before its base64 is read.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* b.structuredFields.unfoldHeaderContinuations("v=DKIM1;\r\n k=rsa");
|
|
186
|
+
* // returns "v=DKIM1; k=rsa"
|
|
187
|
+
*/
|
|
188
|
+
function unfoldHeaderContinuations(value) {
|
|
189
|
+
return String(value).replace(/\r?\n[ \t]+/g, " ");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @primitive b.structuredFields.parseKeyValuePiece
|
|
194
|
+
* @signature b.structuredFields.parseKeyValuePiece(piece, kvSep?, lowerKey?)
|
|
195
|
+
* @since 0.15.13
|
|
196
|
+
* @status stable
|
|
197
|
+
* @related b.structuredFields.parseTagList, b.structuredFields.splitTopLevel
|
|
198
|
+
*
|
|
199
|
+
* Parse ONE already-split list piece into a `{ key, value }` pair: `key` is the
|
|
200
|
+
* text before the first `kvSep` (default "="), trimmed and — unless `lowerKey`
|
|
201
|
+
* is `false` — lower-cased; `value` is the raw remainder (the caller trims /
|
|
202
|
+
* unquotes / sf-string-parses it per its own grammar). A piece with NO `kvSep`
|
|
203
|
+
* is a "bare" item — `value` is null — which the caller handles per its grammar
|
|
204
|
+
* (skip it, or treat as a flag-style directive).
|
|
205
|
+
*
|
|
206
|
+
* The shared per-pair step behind every `key=value` list parser: the naive
|
|
207
|
+
* `parseTagList` loop AND the quote-aware parsers that first `splitTopLevel`
|
|
208
|
+
* then parse each piece (Cache-Control directives, Client-Hints brand params,
|
|
209
|
+
* Content-Type parameters). `lowerKey:false` serves the rare grammar whose key
|
|
210
|
+
* is case-sensitive (a verbatim tag-list passthrough).
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* b.structuredFields.parseKeyValuePiece("Max-Age=60"); // → { key: "max-age", value: "60" }
|
|
214
|
+
* b.structuredFields.parseKeyValuePiece("no-store"); // → { key: "no-store", value: null }
|
|
215
|
+
* b.structuredFields.parseKeyValuePiece("Key=a=b", "=", false); // → { key: "Key", value: "a=b" }
|
|
216
|
+
*/
|
|
217
|
+
function parseKeyValuePiece(piece, kvSep, lowerKey) {
|
|
218
|
+
var sep = kvSep || "=";
|
|
219
|
+
var at = piece.indexOf(sep);
|
|
220
|
+
var rawKey = (at === -1 ? piece : piece.slice(0, at)).trim();
|
|
221
|
+
var key = lowerKey === false ? rawKey : rawKey.toLowerCase();
|
|
222
|
+
return { key: key, value: at === -1 ? null : piece.slice(at + sep.length) };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* @primitive b.structuredFields.parseTagList
|
|
227
|
+
* @signature b.structuredFields.parseTagList(input, opts?)
|
|
228
|
+
* @since 0.15.13
|
|
229
|
+
* @status stable
|
|
230
|
+
* @related b.structuredFields.splitTopLevel
|
|
231
|
+
*
|
|
232
|
+
* Parse a NAIVE delimited `key<kvSep>value` tag list into an ordered
|
|
233
|
+
* array of `[key, value]` pairs. This is the non-quote-aware sibling
|
|
234
|
+
* of `splitTopLevel`: it is for grammars whose RFC forbids the DQUOTE
|
|
235
|
+
* structured-string form, so a bare `split` is correct and a
|
|
236
|
+
* quote-aware walk would be wrong — DKIM (RFC 6376 §3.2), DMARC
|
|
237
|
+
* (RFC 7489 §6.4), ARC (RFC 8617 §4), BIMI (RFC 9091 §4), and the
|
|
238
|
+
* MTA-STS policy grammar (RFC 8461, line/colon delimited).
|
|
239
|
+
*
|
|
240
|
+
* Pairs are returned (not a map) so a caller whose grammar permits a
|
|
241
|
+
* repeated key — MTA-STS `mx:` lines list one host each — keeps every
|
|
242
|
+
* occurrence; a caller wanting last-wins map semantics folds the
|
|
243
|
+
* pairs into a plain object itself. Order is preserved, so a caller
|
|
244
|
+
* that throws on a malformed value throws at the same point it would
|
|
245
|
+
* have mid-loop.
|
|
246
|
+
*
|
|
247
|
+
* Entries that are empty after trimming, or carry no `kvSep`, are
|
|
248
|
+
* skipped (matching every shipped parser's prior behavior).
|
|
249
|
+
*
|
|
250
|
+
* @opts
|
|
251
|
+
* sep: string|RegExp, // entry separator. default: ";" (MTA-STS uses /\r?\n/)
|
|
252
|
+
* kvSep: string, // key/value separator. default: "=" (MTA-STS uses ":")
|
|
253
|
+
* unfold: boolean, // collapse CRLF+WSP folds to a space first (DKIM FWS). default: false
|
|
254
|
+
* stripValueWs: boolean, // strip all whitespace inside each value (DKIM/ARC FWS). default: false
|
|
255
|
+
* lowerKey: boolean, // lower-case each key. default: true (set false to preserve case)
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* b.structuredFields.parseTagList("v=DKIM1; k=rsa; p=MIGf");
|
|
259
|
+
* // → [ ["v", "DKIM1"], ["k", "rsa"], ["p", "MIGf"] ]
|
|
260
|
+
*
|
|
261
|
+
* b.structuredFields.parseTagList("version:STSv1\nmx:a.example\nmx:b.example",
|
|
262
|
+
* { sep: /\r?\n/, kvSep: ":" });
|
|
263
|
+
* // → [ ["version","STSv1"], ["mx","a.example"], ["mx","b.example"] ]
|
|
264
|
+
*/
|
|
265
|
+
function parseTagList(input, opts) {
|
|
266
|
+
opts = opts || {};
|
|
267
|
+
var sep = opts.sep === undefined ? ";" : opts.sep;
|
|
268
|
+
var kvSep = opts.kvSep === undefined ? "=" : opts.kvSep;
|
|
269
|
+
var s = String(input);
|
|
270
|
+
if (opts.unfold) s = unfoldHeaderContinuations(s);
|
|
271
|
+
var kvps = parseKeyValuePieces(s.split(sep), 0, kvSep, opts.lowerKey);
|
|
272
|
+
var out = [];
|
|
273
|
+
forEachKeyValue(kvps, function (key, val) {
|
|
274
|
+
if (opts.stripValueWs) val = val.replace(/\s+/g, "");
|
|
275
|
+
out.push([key, val]);
|
|
276
|
+
});
|
|
277
|
+
return out;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* @primitive b.structuredFields.parseKeyValuePieces
|
|
282
|
+
* @signature b.structuredFields.parseKeyValuePieces(pieces, startIndex?, kvSep?, lowerKey?)
|
|
283
|
+
* @since 0.15.13
|
|
284
|
+
* @status stable
|
|
285
|
+
* @related b.structuredFields.parseKeyValuePiece, b.structuredFields.parseTagList
|
|
286
|
+
*
|
|
287
|
+
* Iterate an already-split list of structured-field pieces, trimming each,
|
|
288
|
+
* dropping empties, and parsing the survivors into `{ key, value }` records
|
|
289
|
+
* via `parseKeyValuePiece`. `startIndex` skips a leading non-pair token (the
|
|
290
|
+
* media type in a `Content-Type`, the brand in a `Sec-CH-UA` member); `kvSep`
|
|
291
|
+
* overrides the default `"="`; `lowerKey:false` preserves key case for a
|
|
292
|
+
* case-sensitive grammar.
|
|
293
|
+
*
|
|
294
|
+
* The split is left to the caller because the boundary discipline differs by
|
|
295
|
+
* grammar — `splitTopLevel` honours quotes/parens for RFC 8941 lists, while a
|
|
296
|
+
* plain `String(value).split(";")` is right where inner separators cannot be
|
|
297
|
+
* quoted. Per-piece dispatch (unquote, numeric coercion, poisoned-key drops)
|
|
298
|
+
* stays with the caller; this owns only the uniform iterate-trim-skip-parse
|
|
299
|
+
* spine the parsers shared verbatim.
|
|
300
|
+
*
|
|
301
|
+
* @opts
|
|
302
|
+
* startIndex: number, // default: 0 — first piece treated as a key/value pair
|
|
303
|
+
* kvSep: string, // default: "=" — key/value separator within a piece
|
|
304
|
+
* lowerKey: boolean, // default: true — lower-case each parsed key
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* var kvps = b.structuredFields.parseKeyValuePieces(
|
|
308
|
+
* b.structuredFields.splitTopLevel("max-age=600, immutable", ","));
|
|
309
|
+
* // → [ { key: "max-age", value: "600" }, { key: "immutable", value: null } ]
|
|
310
|
+
*/
|
|
311
|
+
function parseKeyValuePieces(pieces, startIndex, kvSep, lowerKey) {
|
|
312
|
+
var out = [];
|
|
313
|
+
for (var i = startIndex || 0; i < pieces.length; i += 1) {
|
|
314
|
+
var p = pieces[i].trim();
|
|
315
|
+
if (p.length === 0) continue;
|
|
316
|
+
out.push(parseKeyValuePiece(p, kvSep, lowerKey));
|
|
317
|
+
}
|
|
318
|
+
return out;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @primitive b.structuredFields.forEachKeyValue
|
|
323
|
+
* @signature b.structuredFields.forEachKeyValue(kvps, handler)
|
|
324
|
+
* @since 0.15.13
|
|
325
|
+
* @status stable
|
|
326
|
+
* @related b.structuredFields.parseKeyValuePieces, b.structuredFields.parseTagList
|
|
327
|
+
*
|
|
328
|
+
* Consume the `{ key, value }` records from `parseKeyValuePieces`: skip
|
|
329
|
+
* the bare entries (those with a `null` value — a key that carried no
|
|
330
|
+
* separator), trim each surviving value, and invoke
|
|
331
|
+
* `handler(key, trimmedValue, index)`. The mirror of
|
|
332
|
+
* `parseKeyValuePieces` on the consuming side — that primitive owns the
|
|
333
|
+
* parse spine, this owns the iterate-skip-bare-trim spine every header
|
|
334
|
+
* parser repeated verbatim before dispatching.
|
|
335
|
+
*
|
|
336
|
+
* Per-key dispatch (sf-string unquoting, numeric coercion, poisoned-key
|
|
337
|
+
* drops, building a typed result) stays in the handler — a handler that
|
|
338
|
+
* `return`s skips the current entry, exactly like a `continue`. Parsers
|
|
339
|
+
* that instead treat a bare key as meaningful (a value-less directive)
|
|
340
|
+
* iterate the records directly rather than calling this.
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* var b = require("blamejs");
|
|
344
|
+
*
|
|
345
|
+
* var out = {};
|
|
346
|
+
* var kvps = b.structuredFields.parseKeyValuePieces("a=1; b=2".split(";"));
|
|
347
|
+
* b.structuredFields.forEachKeyValue(kvps, function (key, value) {
|
|
348
|
+
* out[key] = value;
|
|
349
|
+
* });
|
|
350
|
+
* // out → { a: "1", b: "2" }
|
|
351
|
+
*/
|
|
352
|
+
function forEachKeyValue(kvps, handler) {
|
|
353
|
+
if (typeof handler !== "function") {
|
|
354
|
+
throw new TypeError("structuredFields.forEachKeyValue: handler must be a function");
|
|
355
|
+
}
|
|
356
|
+
for (var i = 0; i < kvps.length; i += 1) {
|
|
357
|
+
if (kvps[i].value === null) continue;
|
|
358
|
+
handler(kvps[i].key, kvps[i].value.trim(), i);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
106
362
|
/**
|
|
107
363
|
* @primitive b.structuredFields.refuseControlBytes
|
|
108
364
|
* @signature b.structuredFields.refuseControlBytes(value, opts)
|
|
@@ -155,22 +411,18 @@ function refuseControlBytes(value, opts) {
|
|
|
155
411
|
if (!opts.label || typeof opts.label !== "string") {
|
|
156
412
|
throw new TypeError("refuseControlBytes: opts.label (non-empty string) is required");
|
|
157
413
|
}
|
|
158
|
-
var allowHt = opts.allowHt !== false;
|
|
159
|
-
|
|
160
|
-
var
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// matches every framework-error class generated by `defineClass`.
|
|
169
|
-
if (opts.useNativeError === true) {
|
|
170
|
-
throw new opts.ErrorClass(msg);
|
|
171
|
-
}
|
|
172
|
-
throw new opts.ErrorClass(opts.code, msg);
|
|
414
|
+
var allowHt = opts.allowHt !== false; // ASCII HT (folding whitespace) permitted unless allowHt === false
|
|
415
|
+
if (codepointClass.firstControlCharOffset(value, { forbidTab: !allowHt }) !== -1) { // C0 + DEL codepoint range
|
|
416
|
+
var msg = opts.label + ": value contains control characters (C0 / DEL)";
|
|
417
|
+
// opts.useNativeError === true → call the ErrorClass with a
|
|
418
|
+
// single-arg `message` (matches native Error / TypeError /
|
|
419
|
+
// RangeError signatures used by defensive request-shape
|
|
420
|
+
// readers). Default false → call with (code, message) which
|
|
421
|
+
// matches every framework-error class generated by `defineClass`.
|
|
422
|
+
if (opts.useNativeError === true) {
|
|
423
|
+
throw new opts.ErrorClass(msg);
|
|
173
424
|
}
|
|
425
|
+
throw new opts.ErrorClass(opts.code, msg);
|
|
174
426
|
}
|
|
175
427
|
}
|
|
176
428
|
|
|
@@ -204,7 +456,43 @@ function unquoteSfString(s) {
|
|
|
204
456
|
if (t.length === 0) return "";
|
|
205
457
|
if (t.charAt(0) !== "\"") return t;
|
|
206
458
|
if (t.length < 2 || t.charAt(t.length - 1) !== "\"") return null;
|
|
207
|
-
return t.slice(1, -1)
|
|
459
|
+
return unescapeSfStringBody(t.slice(1, -1));
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* @primitive b.structuredFields.unescapeSfStringBody
|
|
464
|
+
* @signature b.structuredFields.unescapeSfStringBody(body)
|
|
465
|
+
* @since 0.15.12
|
|
466
|
+
* @related b.structuredFields.unquoteSfString
|
|
467
|
+
*
|
|
468
|
+
* Undo the RFC 8941 §3.3.3 quoted-string backslash-escapes from the BODY of
|
|
469
|
+
* an sf-string (the bytes BETWEEN the surrounding double quotes). Only `\\\\`
|
|
470
|
+
* and `\\"` are legal escapes; every other backslash is literal.
|
|
471
|
+
*
|
|
472
|
+
* This is a single left-to-right scan, NOT two chained `.replace()` passes.
|
|
473
|
+
* The two-pass form (`.replace(/\\\\/g,"\\").replace(/\\"/g,'"')`, in either
|
|
474
|
+
* order) is not equivalent to a single decode: whichever pass runs first can
|
|
475
|
+
* rewrite a backslash the other escape sequence legitimately owns, so a lone
|
|
476
|
+
* escaped backslash (`\\\\`) decodes to two backslashes instead of one. The
|
|
477
|
+
* single pass consumes each escape exactly once. Non-string input passes
|
|
478
|
+
* through unchanged.
|
|
479
|
+
*
|
|
480
|
+
* @example
|
|
481
|
+
* b.structuredFields.unescapeSfStringBody('a\\"b\\\\c');
|
|
482
|
+
* // → 'a"b\c'
|
|
483
|
+
*/
|
|
484
|
+
function unescapeSfStringBody(body) {
|
|
485
|
+
if (typeof body !== "string") return body;
|
|
486
|
+
var out = "";
|
|
487
|
+
for (var i = 0; i < body.length; i++) {
|
|
488
|
+
var c = body.charAt(i);
|
|
489
|
+
if (c === "\\" && i + 1 < body.length) {
|
|
490
|
+
var n = body.charAt(i + 1);
|
|
491
|
+
if (n === "\\" || n === "\"") { out += n; i += 1; continue; }
|
|
492
|
+
}
|
|
493
|
+
out += c;
|
|
494
|
+
}
|
|
495
|
+
return out;
|
|
208
496
|
}
|
|
209
497
|
|
|
210
498
|
/**
|
|
@@ -235,7 +523,7 @@ function containsControlBytes(value, opts) {
|
|
|
235
523
|
for (var i = 0; i < value.length; i += 1) {
|
|
236
524
|
var cc = value.charCodeAt(i);
|
|
237
525
|
if (allowHt && cc === 9) continue; // ASCII HT (folding whitespace)
|
|
238
|
-
if (cc
|
|
526
|
+
if (codepointClass.isForbiddenControlChar(cc, { forbidTab: true })) return true; // C0 + DEL codepoint range
|
|
239
527
|
}
|
|
240
528
|
return false;
|
|
241
529
|
}
|
|
@@ -671,9 +959,17 @@ function serialize(value, type, opts) {
|
|
|
671
959
|
|
|
672
960
|
module.exports = {
|
|
673
961
|
splitTopLevel: splitTopLevel,
|
|
962
|
+
splitUnquoted: splitUnquoted,
|
|
963
|
+
stripDoubleQuotes: stripDoubleQuotes,
|
|
964
|
+
parseTagList: parseTagList,
|
|
965
|
+
unfoldHeaderContinuations: unfoldHeaderContinuations,
|
|
966
|
+
parseKeyValuePiece: parseKeyValuePiece,
|
|
967
|
+
parseKeyValuePieces: parseKeyValuePieces,
|
|
968
|
+
forEachKeyValue: forEachKeyValue,
|
|
674
969
|
refuseControlBytes: refuseControlBytes,
|
|
675
970
|
containsControlBytes: containsControlBytes,
|
|
676
971
|
unquoteSfString: unquoteSfString,
|
|
972
|
+
unescapeSfStringBody: unescapeSfStringBody,
|
|
677
973
|
parse: parse,
|
|
678
974
|
serialize: serialize,
|
|
679
975
|
Token: SfToken,
|
package/lib/tenant-quota.js
CHANGED
|
@@ -183,16 +183,7 @@ function create(opts) {
|
|
|
183
183
|
return defaultBytesCap;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
if (!auditOn) return;
|
|
188
|
-
try {
|
|
189
|
-
audit().safeEmit({
|
|
190
|
-
action: action,
|
|
191
|
-
outcome: outcome,
|
|
192
|
-
metadata: metadata || {},
|
|
193
|
-
});
|
|
194
|
-
} catch (_e) { /* audit best-effort */ }
|
|
195
|
-
}
|
|
186
|
+
var _emitAudit = audit().namespaced(null, { audit: auditOn });
|
|
196
187
|
|
|
197
188
|
function _emitMetric(name, n) {
|
|
198
189
|
try { observability().safeEvent(name, n || 1, {}); }
|
|
@@ -375,16 +366,7 @@ function budget(opts) {
|
|
|
375
366
|
return c;
|
|
376
367
|
}
|
|
377
368
|
|
|
378
|
-
|
|
379
|
-
if (!auditOn) return;
|
|
380
|
-
try {
|
|
381
|
-
audit().safeEmit({
|
|
382
|
-
action: action,
|
|
383
|
-
outcome: outcome,
|
|
384
|
-
metadata: metadata || {},
|
|
385
|
-
});
|
|
386
|
-
} catch (_e) { /* audit best-effort */ }
|
|
387
|
-
}
|
|
369
|
+
var _emitAudit = audit().namespaced(null, { audit: auditOn });
|
|
388
370
|
|
|
389
371
|
function _emitMetric(name, n) {
|
|
390
372
|
try { observability().safeEvent(name, n || 1, {}); }
|
package/lib/tsa.js
CHANGED
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
|
|
54
54
|
var nodeCrypto = require("node:crypto");
|
|
55
55
|
var bCrypto = require("./crypto");
|
|
56
|
+
var safeBuffer = require("./safe-buffer");
|
|
56
57
|
var asn1 = require("./asn1-der");
|
|
57
58
|
var cms = require("./cms-codec");
|
|
58
59
|
var validateOpts = require("./validate-opts");
|
|
@@ -104,12 +105,12 @@ var DIGEST_OID_TO_NODE = {
|
|
|
104
105
|
"2.16.840.1.101.3.4.2.10": "sha3-512",
|
|
105
106
|
};
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
108
|
+
var _bytes = safeBuffer.makeByteCoercer({
|
|
109
|
+
errorClass: TsaError,
|
|
110
|
+
typeCode: "tsa/bad-bytes",
|
|
111
|
+
messagePrefix: "tsa: ",
|
|
112
|
+
messageSuffix: " must be Buffer / Uint8Array / string",
|
|
113
|
+
});
|
|
113
114
|
|
|
114
115
|
function _normHex(h) {
|
|
115
116
|
return String(h).replace(/[^0-9a-fA-F]/g, "").replace(/^0+(?=.)/, "").toUpperCase();
|
|
@@ -658,15 +659,10 @@ function verifyToken(token, opts) {
|
|
|
658
659
|
throw new TsaError("tsa/bad-trust-anchors",
|
|
659
660
|
"tsa.verifyToken: trustAnchorsPem must be a non-empty PEM string or array of PEM strings");
|
|
660
661
|
}
|
|
661
|
-
// A supplied opts.at must be a valid Date — an Invalid Date would
|
|
662
|
-
//
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
if (!(opts.at instanceof Date) || !isFinite(opts.at.getTime())) {
|
|
666
|
-
throw new TsaError("tsa/bad-at", "tsa.verifyToken: opts.at must be a valid Date");
|
|
667
|
-
}
|
|
668
|
-
at = opts.at;
|
|
669
|
-
}
|
|
662
|
+
// A supplied opts.at must be a valid Date — an Invalid Date would make
|
|
663
|
+
// every validity-window comparison NaN (silently disabling it).
|
|
664
|
+
validateOpts.optionalDate(opts.at, "tsa.verifyToken: opts.at", TsaError, "tsa/bad-at");
|
|
665
|
+
var at = (opts.at !== undefined && opts.at !== null) ? opts.at : tst.genTime;
|
|
670
666
|
_verifyChain(signerCertDer, sd.certificates, anchors, at);
|
|
671
667
|
}
|
|
672
668
|
|