@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/network-tls.js
CHANGED
|
@@ -5,6 +5,8 @@ var nodeFs = require("node:fs");
|
|
|
5
5
|
var nodePath = require("node:path");
|
|
6
6
|
var net = require("node:net");
|
|
7
7
|
var nodeCrypto = require("node:crypto");
|
|
8
|
+
var numericBounds = require("./numeric-bounds");
|
|
9
|
+
var atomicFile = require("./atomic-file");
|
|
8
10
|
|
|
9
11
|
var bCrypto = require("./crypto");
|
|
10
12
|
var C = require("./constants");
|
|
@@ -23,6 +25,29 @@ var networkDns = lazyRequire(function () { return require("./network-dns"); });
|
|
|
23
25
|
var httpClient = lazyRequire(function () { return require("./http-client"); });
|
|
24
26
|
var asn1 = require("./asn1-der");
|
|
25
27
|
|
|
28
|
+
// Audit + observability emit for an outbound TLS connection that runs with
|
|
29
|
+
// peer-certificate validation DISABLED (an explicit operator opt-in —
|
|
30
|
+
// rejectUnauthorized:false / allowInsecure — never a framework default).
|
|
31
|
+
// Emitted at the point the disable is HONORED so the degraded posture is
|
|
32
|
+
// observable (compliance evidence + incident response), parallel to the
|
|
33
|
+
// tls.classical_downgrade audit. Drop-silent best-effort (§8 hot-path sink) —
|
|
34
|
+
// an audit-sink failure must never break the TLS connect itself.
|
|
35
|
+
function auditInsecureTls(meta) {
|
|
36
|
+
meta = meta || {};
|
|
37
|
+
try {
|
|
38
|
+
observability().safeEvent("tls.insecure_skip_verify", 1, {
|
|
39
|
+
host: meta.host || null, port: meta.port || null, source: meta.source || null,
|
|
40
|
+
});
|
|
41
|
+
} catch (_e) { /* drop-silent */ }
|
|
42
|
+
try {
|
|
43
|
+
audit().safeEmit({
|
|
44
|
+
action: "tls.insecure_skip_verify",
|
|
45
|
+
outcome: "success",
|
|
46
|
+
metadata: { host: meta.host || null, port: meta.port || null, source: meta.source || null },
|
|
47
|
+
});
|
|
48
|
+
} catch (_e) { /* drop-silent — audit best-effort, never break TLS */ }
|
|
49
|
+
}
|
|
50
|
+
|
|
26
51
|
// STATE.tlsKeyShares is initialized to the default PQC group list at
|
|
27
52
|
// module load — operator setKeyShares() overrides; resetKeyShares()
|
|
28
53
|
// restores the default. Empty array means "fall back to Node's TLS
|
|
@@ -80,7 +105,7 @@ function _certMetadata(pem) {
|
|
|
80
105
|
|
|
81
106
|
function _isPathLike(s) {
|
|
82
107
|
if (s.indexOf("-----BEGIN") !== -1) return false;
|
|
83
|
-
if (s
|
|
108
|
+
if (safeBuffer.byteLengthOf(s) > C.BYTES.kib(1)) return false;
|
|
84
109
|
if (safeBuffer.hasCrlf(s)) return false;
|
|
85
110
|
return true;
|
|
86
111
|
}
|
|
@@ -92,20 +117,13 @@ function _isPathLike(s) {
|
|
|
92
117
|
// shape, where an attacker who could swap the file in-between could
|
|
93
118
|
// short-circuit the PEM marker check downstream.
|
|
94
119
|
function _readPathFile(p) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (n === 0) break;
|
|
103
|
-
read += n;
|
|
104
|
-
}
|
|
105
|
-
return buf.slice(0, read).toString("utf8");
|
|
106
|
-
} finally {
|
|
107
|
-
try { nodeFs.closeSync(fd); } catch (_c) { /* close best-effort */ }
|
|
108
|
-
}
|
|
120
|
+
// TOCTOU-safe read via atomic-file: utf8 string, slice on a short read,
|
|
121
|
+
// raw ENOENT preserved (errorFor returns undefined → rethrow).
|
|
122
|
+
return atomicFile.fdSafeReadSync(p, {
|
|
123
|
+
encoding: "utf8",
|
|
124
|
+
allowShortRead: true,
|
|
125
|
+
errorFor: function () { return undefined; },
|
|
126
|
+
});
|
|
109
127
|
}
|
|
110
128
|
|
|
111
129
|
function _readPath(p) {
|
|
@@ -144,7 +162,7 @@ function addCa(pemOrPath, opts) {
|
|
|
144
162
|
added.push(meta);
|
|
145
163
|
}
|
|
146
164
|
_emitAuditAdd(added, opts);
|
|
147
|
-
|
|
165
|
+
observability().safeEvent("network.tls.ca.added", 1, { count: added.length });
|
|
148
166
|
return added;
|
|
149
167
|
}
|
|
150
168
|
|
|
@@ -154,7 +172,7 @@ function addCaBundle(p, opts) {
|
|
|
154
172
|
|
|
155
173
|
function useSystemTrust(enable) {
|
|
156
174
|
STATE.systemTrust = enable !== false;
|
|
157
|
-
|
|
175
|
+
observability().safeEvent("network.tls.system_trust.set", 1, { enabled: STATE.systemTrust });
|
|
158
176
|
}
|
|
159
177
|
|
|
160
178
|
function isSystemTrustEnabled() { return !!STATE.systemTrust; }
|
|
@@ -175,6 +193,21 @@ function getTrustStore() {
|
|
|
175
193
|
});
|
|
176
194
|
}
|
|
177
195
|
|
|
196
|
+
// _certAuditMetadata(m) — the shared cert-identity fields stamped on every
|
|
197
|
+
// network.tls.ca.* audit event (the add / remove emitters overlay their
|
|
198
|
+
// own label / reason on top via Object.assign). Distinct from
|
|
199
|
+
// _certMetadata(pem), which PARSES a PEM into the meta object `m`.
|
|
200
|
+
function _certAuditMetadata(m) {
|
|
201
|
+
return {
|
|
202
|
+
subject: m.subject,
|
|
203
|
+
issuer: m.issuer,
|
|
204
|
+
fingerprint256: m.fingerprint256,
|
|
205
|
+
validFrom: m.validFrom,
|
|
206
|
+
validTo: m.validTo,
|
|
207
|
+
isSelfSigned: m.isSelfSigned,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
178
211
|
function _emitAuditRemove(metaList, reason) {
|
|
179
212
|
var sink;
|
|
180
213
|
try { sink = audit(); } catch (_e) { sink = null; }
|
|
@@ -185,16 +218,10 @@ function _emitAuditRemove(metaList, reason) {
|
|
|
185
218
|
sink.safeEmit({
|
|
186
219
|
action: "network.tls.ca.removed",
|
|
187
220
|
outcome: "success",
|
|
188
|
-
metadata: {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
validFrom: m.validFrom,
|
|
193
|
-
validTo: m.validTo,
|
|
194
|
-
isSelfSigned: m.isSelfSigned,
|
|
195
|
-
label: m.label,
|
|
196
|
-
reason: reason || "operator",
|
|
197
|
-
},
|
|
221
|
+
metadata: Object.assign(_certAuditMetadata(m), {
|
|
222
|
+
label: m.label,
|
|
223
|
+
reason: reason || "operator",
|
|
224
|
+
}),
|
|
198
225
|
});
|
|
199
226
|
} catch (_e) { /* audit best-effort — never break the caller */ }
|
|
200
227
|
}
|
|
@@ -216,7 +243,7 @@ function removeCa(fingerprint256, opts) {
|
|
|
216
243
|
});
|
|
217
244
|
if (removed.length === 0) return 0;
|
|
218
245
|
if (!opts || opts.audit !== false) _emitAuditRemove(removed, "operator-remove");
|
|
219
|
-
|
|
246
|
+
observability().safeEvent("network.tls.ca.removed", 1, { count: removed.length, reason: "operator" });
|
|
220
247
|
return removed.length;
|
|
221
248
|
}
|
|
222
249
|
|
|
@@ -234,7 +261,7 @@ function removeCaByLabel(label, opts) {
|
|
|
234
261
|
});
|
|
235
262
|
if (removed.length === 0) return 0;
|
|
236
263
|
if (!opts || opts.audit !== false) _emitAuditRemove(removed, "operator-remove-by-label");
|
|
237
|
-
|
|
264
|
+
observability().safeEvent("network.tls.ca.removed", 1, { count: removed.length, reason: "label" });
|
|
238
265
|
return removed.length;
|
|
239
266
|
}
|
|
240
267
|
|
|
@@ -243,7 +270,7 @@ function clearAll(opts) {
|
|
|
243
270
|
var removed = STATE.cas.map(function (e) { return Object.assign({ label: e.label }, e.meta); });
|
|
244
271
|
STATE.cas = [];
|
|
245
272
|
if (!opts || opts.audit !== false) _emitAuditRemove(removed, "operator-clear-all");
|
|
246
|
-
|
|
273
|
+
observability().safeEvent("network.tls.ca.cleared", 1, { count: removed.length });
|
|
247
274
|
return removed.length;
|
|
248
275
|
}
|
|
249
276
|
|
|
@@ -260,7 +287,7 @@ function purgeExpired(opts) {
|
|
|
260
287
|
});
|
|
261
288
|
if (removed.length === 0) return 0;
|
|
262
289
|
if (!opts || opts.audit !== false) _emitAuditRemove(removed, "expired");
|
|
263
|
-
|
|
290
|
+
observability().safeEvent("network.tls.ca.purged_expired", 1, { count: removed.length });
|
|
264
291
|
return removed.length;
|
|
265
292
|
}
|
|
266
293
|
|
|
@@ -492,7 +519,7 @@ var DEFAULT_PQC_KEY_SHARES = Object.freeze([
|
|
|
492
519
|
]);
|
|
493
520
|
|
|
494
521
|
function _validateKeyShare(name) {
|
|
495
|
-
if (typeof name !== "string" || name.length === 0 || name
|
|
522
|
+
if (typeof name !== "string" || name.length === 0 || safeBuffer.byteLengthOf(name) > C.BYTES.bytes(64)) { // bound
|
|
496
523
|
throw new TlsTrustError("tls/bad-key-share",
|
|
497
524
|
"tls.pqc.setKeyShares: each entry must be a non-empty string up to 64 chars");
|
|
498
525
|
}
|
|
@@ -691,24 +718,12 @@ function _emitAuditAdd(metaList, opts) {
|
|
|
691
718
|
sink.safeEmit({
|
|
692
719
|
action: "network.tls.ca.added",
|
|
693
720
|
outcome: "success",
|
|
694
|
-
metadata: {
|
|
695
|
-
subject: m.subject,
|
|
696
|
-
issuer: m.issuer,
|
|
697
|
-
fingerprint256: m.fingerprint256,
|
|
698
|
-
validFrom: m.validFrom,
|
|
699
|
-
validTo: m.validTo,
|
|
700
|
-
isSelfSigned: m.isSelfSigned,
|
|
701
|
-
label: opts.label || null,
|
|
702
|
-
},
|
|
721
|
+
metadata: Object.assign(_certAuditMetadata(m), { label: opts.label || null }),
|
|
703
722
|
});
|
|
704
723
|
} catch (_e) { /* audit best-effort — never break the caller */ }
|
|
705
724
|
}
|
|
706
725
|
}
|
|
707
726
|
|
|
708
|
-
function _emitObs(name, fields) {
|
|
709
|
-
try { observability().emit(name, fields || {}); } catch (_e) { /* obs best-effort */ }
|
|
710
|
-
}
|
|
711
|
-
|
|
712
727
|
function _resetForTest() {
|
|
713
728
|
STATE.cas = [];
|
|
714
729
|
STATE.systemTrust = false;
|
|
@@ -2682,11 +2697,8 @@ function connectWithEch(opts) {
|
|
|
2682
2697
|
validateOpts.requireNonEmptyString(opts.host, "connectWithEch: host",
|
|
2683
2698
|
NetworkTlsError, "tls/ech-bad-opts");
|
|
2684
2699
|
var port = opts.port === undefined ? 443 : opts.port; // HTTPS default port
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
throw new NetworkTlsError("tls/ech-bad-opts",
|
|
2688
|
-
"connectWithEch: port must be an integer in 1..65535");
|
|
2689
|
-
}
|
|
2700
|
+
numericBounds.requirePositiveFiniteInt(port,
|
|
2701
|
+
"connectWithEch: port", NetworkTlsError, "tls/ech-bad-opts", { max: 65535 });
|
|
2690
2702
|
if (opts.alpn !== undefined && !Array.isArray(opts.alpn)) {
|
|
2691
2703
|
throw new NetworkTlsError("tls/ech-bad-opts",
|
|
2692
2704
|
"connectWithEch: alpn must be an array of strings");
|
|
@@ -2723,8 +2735,14 @@ function connectWithEch(opts) {
|
|
|
2723
2735
|
if (typeof opts.checkServerIdentity === "function") {
|
|
2724
2736
|
connectOpts.checkServerIdentity = opts.checkServerIdentity;
|
|
2725
2737
|
}
|
|
2726
|
-
|
|
2727
|
-
|
|
2738
|
+
// rejectUnauthorized defaults to true (full validation). An operator may
|
|
2739
|
+
// explicitly opt out — audited, never a framework default. Derived from
|
|
2740
|
+
// the operator's own value (operator-governed shape, mirroring mail.js /
|
|
2741
|
+
// log-stream-syslog.js), not a hardcoded literal.
|
|
2742
|
+
var rejectUnauthorized = opts.rejectUnauthorized !== false;
|
|
2743
|
+
connectOpts.rejectUnauthorized = rejectUnauthorized;
|
|
2744
|
+
if (!rejectUnauthorized) {
|
|
2745
|
+
auditInsecureTls({ host: opts.host, port: port, source: "network.tls.connectWithEch" });
|
|
2728
2746
|
}
|
|
2729
2747
|
var echAttached = false;
|
|
2730
2748
|
if (echConfigBuf && nodeSupportsEch) {
|
|
@@ -2735,7 +2753,7 @@ function connectWithEch(opts) {
|
|
|
2735
2753
|
// gracefully with a one-shot warn so operators know they're
|
|
2736
2754
|
// sending an outer-only ClientHello.
|
|
2737
2755
|
try {
|
|
2738
|
-
observability().
|
|
2756
|
+
observability().safeEvent("network.tls.ech.unsupported", 1, {
|
|
2739
2757
|
host: opts.host, source: sourceLabel,
|
|
2740
2758
|
});
|
|
2741
2759
|
} catch (_e) { /* drop-silent */ }
|
|
@@ -2772,7 +2790,7 @@ function connectWithEch(opts) {
|
|
|
2772
2790
|
settled = true;
|
|
2773
2791
|
if (to) clearTimeout(to);
|
|
2774
2792
|
try {
|
|
2775
|
-
observability().
|
|
2793
|
+
observability().safeEvent("network.tls.ech.connected", 1, {
|
|
2776
2794
|
host: opts.host, echAttached: echAttached, source: sourceLabel,
|
|
2777
2795
|
});
|
|
2778
2796
|
} catch (_e) { /* drop-silent */ }
|
|
@@ -2832,7 +2850,7 @@ function connectWithEch(opts) {
|
|
|
2832
2850
|
// operator still gets a working TLS session. Emit obs so the
|
|
2833
2851
|
// operator sees the degradation.
|
|
2834
2852
|
try {
|
|
2835
|
-
observability().
|
|
2853
|
+
observability().safeEvent("network.tls.ech.dns_failed", 1, {
|
|
2836
2854
|
host: opts.host, error: (e && e.message) || String(e),
|
|
2837
2855
|
});
|
|
2838
2856
|
} catch (_e) { /* drop-silent */ }
|
|
@@ -3174,6 +3192,7 @@ function wrapSNICallback(operatorCb) {
|
|
|
3174
3192
|
}
|
|
3175
3193
|
|
|
3176
3194
|
module.exports = {
|
|
3195
|
+
auditInsecureTls: auditInsecureTls,
|
|
3177
3196
|
addCa: addCa,
|
|
3178
3197
|
addCaBundle: addCaBundle,
|
|
3179
3198
|
removeCa: removeCa,
|
package/lib/network.js
CHANGED
|
@@ -151,7 +151,7 @@ var ntpFacade = {
|
|
|
151
151
|
throw new NetworkError("ntp/bad-servers", "ntp.setServers: expected non-empty array");
|
|
152
152
|
}
|
|
153
153
|
ntpFacade._defaultServers = list.slice();
|
|
154
|
-
|
|
154
|
+
observability().safeEvent("network.ntp.servers.set", 1, { count: list.length });
|
|
155
155
|
},
|
|
156
156
|
getServers: function () {
|
|
157
157
|
return (ntpFacade._defaultServers || ntpCheck.DEFAULT_SERVERS).slice();
|
|
@@ -262,7 +262,7 @@ function bootFromEnv(opts) {
|
|
|
262
262
|
} catch (_e) { /* audit best-effort — never break boot */ }
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
|
-
|
|
265
|
+
observability().safeEvent("network.boot.from_env", 1, { source: "env" });
|
|
266
266
|
return applied;
|
|
267
267
|
}
|
|
268
268
|
|
|
@@ -301,10 +301,6 @@ function snapshot() {
|
|
|
301
301
|
};
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
-
function _emitObs(name, fields) {
|
|
305
|
-
try { observability().emit(name, fields || {}); } catch (_e) { /* obs best-effort */ }
|
|
306
|
-
}
|
|
307
|
-
|
|
308
304
|
function _resetForTest() {
|
|
309
305
|
ntpFacade._defaultServers = null;
|
|
310
306
|
ntpFacade._defaultTimeoutMs = null;
|
package/lib/nis2-report.js
CHANGED
|
@@ -66,7 +66,6 @@ function create(opts) {
|
|
|
66
66
|
var sectorAnnex = opts.sectorAnnex;
|
|
67
67
|
var csirtEndpoint = opts.csirtEndpoint || null;
|
|
68
68
|
var httpClient = opts.httpClient || null;
|
|
69
|
-
var auditOn = opts.audit !== false;
|
|
70
69
|
|
|
71
70
|
var ir = incidentReport().create({
|
|
72
71
|
audit: opts.audit,
|
|
@@ -79,16 +78,7 @@ function create(opts) {
|
|
|
79
78
|
},
|
|
80
79
|
});
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
if (!auditOn) return;
|
|
84
|
-
try {
|
|
85
|
-
audit().safeEmit({
|
|
86
|
-
action: "nis2.report." + action,
|
|
87
|
-
outcome: outcome,
|
|
88
|
-
metadata: metadata || {},
|
|
89
|
-
});
|
|
90
|
-
} catch (_e) { /* drop-silent */ }
|
|
91
|
-
}
|
|
81
|
+
var _emitAudit = audit().namespaced("nis2.report", opts.audit);
|
|
92
82
|
|
|
93
83
|
async function _submitToCsirt(payload) {
|
|
94
84
|
if (!csirtEndpoint || !httpClient) {
|
package/lib/nonce-store.js
CHANGED
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
* inserting second would race on concurrent requests carrying
|
|
14
14
|
* the same nonce.
|
|
15
15
|
*
|
|
16
|
+
* await store.release(nonce) → boolean
|
|
17
|
+
* Un-claims a single nonce so a later checkAndInsert of the same value
|
|
18
|
+
* succeeds again — the rollback half of reserve -> commit -> rollback.
|
|
19
|
+
* A handler that claims an event id the moment a signature verifies (to
|
|
20
|
+
* win the race against a concurrent duplicate) MUST release it when
|
|
21
|
+
* downstream processing fails, otherwise the provider's at-least-once
|
|
22
|
+
* redelivery is reported as a replay and the event is dropped. Returns
|
|
23
|
+
* whether a live claim existed. Pair with checkAndInsert; without it an
|
|
24
|
+
* eager claim inverts the at-least-once contract on any failure.
|
|
25
|
+
*
|
|
16
26
|
* await store.purgeExpired() → number
|
|
17
27
|
* Removes entries past their expireAt. Called periodically by the
|
|
18
28
|
* middleware that uses the store. Returns the count purged.
|
|
@@ -32,8 +42,11 @@
|
|
|
32
42
|
* table _blamejs_api_encrypt_nonces. The PRIMARY KEY race is what
|
|
33
43
|
* makes the check + insert atomic across nodes.
|
|
34
44
|
*
|
|
35
|
-
* { checkAndInsert, purgeExpired
|
|
36
|
-
* custom backend (Redis SETNX, Memcached add, etc.).
|
|
45
|
+
* { checkAndInsert, release?, purgeExpired?, close? } — operator-supplied
|
|
46
|
+
* custom backend (Redis SETNX, Memcached add, etc.). release() is
|
|
47
|
+
* optional but required to use reserve -> rollback; a backend omitting
|
|
48
|
+
* it throws NONCE_BACKEND_NO_RELEASE the first time release() is called
|
|
49
|
+
* (a loud gap, never a silent dropped rollback). Use this
|
|
37
50
|
* for cross-process accuracy without a per-request SQL hop.
|
|
38
51
|
*
|
|
39
52
|
* Sweep cadence: memory backend sweeps every opts.sweepIntervalMs
|
|
@@ -132,6 +145,21 @@ function _memoryBackend(opts) {
|
|
|
132
145
|
return Promise.resolve(true);
|
|
133
146
|
}
|
|
134
147
|
|
|
148
|
+
// Un-claim a nonce so a future checkAndInsert of the same value succeeds
|
|
149
|
+
// again — the rollback half of reserve -> commit -> rollback. A handler
|
|
150
|
+
// that claims an event id the moment a signature verifies (to win the race
|
|
151
|
+
// against a concurrent duplicate) MUST release it when downstream processing
|
|
152
|
+
// fails, otherwise the provider's at-least-once redelivery is reported as a
|
|
153
|
+
// replay and the event is dropped. Returns whether a live claim existed.
|
|
154
|
+
function release(nonce) {
|
|
155
|
+
if (typeof nonce !== "string" || nonce.length === 0) {
|
|
156
|
+
return Promise.reject(_err("INVALID_NONCE", "nonce must be a non-empty string"));
|
|
157
|
+
}
|
|
158
|
+
var existed = seen.get(nonce) !== undefined;
|
|
159
|
+
if (existed) seen.delete(nonce);
|
|
160
|
+
return Promise.resolve(existed);
|
|
161
|
+
}
|
|
162
|
+
|
|
135
163
|
function purgeExpired() {
|
|
136
164
|
return Promise.resolve(_purgeExpiredSync());
|
|
137
165
|
}
|
|
@@ -144,6 +172,7 @@ function _memoryBackend(opts) {
|
|
|
144
172
|
return {
|
|
145
173
|
name: "memory",
|
|
146
174
|
checkAndInsert: checkAndInsert,
|
|
175
|
+
release: release,
|
|
147
176
|
purgeExpired: purgeExpired,
|
|
148
177
|
close: close,
|
|
149
178
|
// Test hooks — underlying entry count + count of capacity fail-closed
|
|
@@ -178,6 +207,19 @@ function _clusterBackend(_opts) {
|
|
|
178
207
|
return (result && result.rowCount > 0);
|
|
179
208
|
}
|
|
180
209
|
|
|
210
|
+
async function release(nonce) {
|
|
211
|
+
if (typeof nonce !== "string" || nonce.length === 0) {
|
|
212
|
+
throw _err("INVALID_NONCE", "nonce must be a non-empty string");
|
|
213
|
+
}
|
|
214
|
+
// Un-claim a single nonce (the reserve -> rollback half). The middleware
|
|
215
|
+
// hashes the raw nonce before it reaches here, so we delete by hash.
|
|
216
|
+
var built = sql.delete(frameworkSchema.tableName(NONCE_TABLE), _nonceSqlOpts())
|
|
217
|
+
.where("nonceHash", "=", nonce)
|
|
218
|
+
.toSql();
|
|
219
|
+
var result = await clusterStorage.execute(built.sql, built.params);
|
|
220
|
+
return (result && result.rowCount > 0);
|
|
221
|
+
}
|
|
222
|
+
|
|
181
223
|
async function purgeExpired() {
|
|
182
224
|
var built = sql.delete(frameworkSchema.tableName(NONCE_TABLE), _nonceSqlOpts())
|
|
183
225
|
.where("expireAt", "<=", Date.now())
|
|
@@ -191,6 +233,7 @@ function _clusterBackend(_opts) {
|
|
|
191
233
|
return {
|
|
192
234
|
name: "cluster",
|
|
193
235
|
checkAndInsert: checkAndInsert,
|
|
236
|
+
release: release,
|
|
194
237
|
purgeExpired: purgeExpired,
|
|
195
238
|
close: close,
|
|
196
239
|
};
|
|
@@ -208,6 +251,15 @@ function create(opts) {
|
|
|
208
251
|
return Object.assign({
|
|
209
252
|
name: "custom",
|
|
210
253
|
purgeExpired: function () { return Promise.resolve(0); },
|
|
254
|
+
// A custom backend that omits release() cannot participate in
|
|
255
|
+
// reserve -> rollback. Fail LOUDLY (not a silent no-op) so the gap
|
|
256
|
+
// surfaces the first time a handler tries to un-claim, rather than
|
|
257
|
+
// silently dropping the rollback and inverting the at-least-once contract.
|
|
258
|
+
release: function () {
|
|
259
|
+
return Promise.reject(_err("BACKEND_NO_RELEASE",
|
|
260
|
+
"this custom nonce backend does not implement release(nonce); " +
|
|
261
|
+
"the reserve -> commit -> rollback pattern requires it"));
|
|
262
|
+
},
|
|
211
263
|
close: function () {},
|
|
212
264
|
}, backend);
|
|
213
265
|
}
|
|
@@ -215,11 +267,37 @@ function create(opts) {
|
|
|
215
267
|
if (!backend || backend === "memory") return _memoryBackend(opts);
|
|
216
268
|
throw _err("UNKNOWN_BACKEND",
|
|
217
269
|
"nonce-store: unknown backend '" + backend +
|
|
218
|
-
"' (must be 'memory', 'cluster', or { checkAndInsert, purgeExpired
|
|
270
|
+
"' (must be 'memory', 'cluster', or { checkAndInsert, release?, purgeExpired?, close? })");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// enforceReplay(store, jti, expireAtMs, opts) — single-use enforcement
|
|
274
|
+
// against a replay store: `await store.checkAndInsert(jti, expireAtMs)`,
|
|
275
|
+
// raising opts.errorClass(opts.storeFailedCode, …) if the store itself
|
|
276
|
+
// fails (a store outage must NOT be mistaken for a clean token) and
|
|
277
|
+
// opts.errorClass(opts.replayCode, "<opts.tokenLabel> jti='<jti>' has
|
|
278
|
+
// been seen before — replay refused") if the jti was already seen. The
|
|
279
|
+
// fail-closed anti-replay control flow every JWT / DPoP verifier repeated
|
|
280
|
+
// identically — centralised here so the contract lives in one place.
|
|
281
|
+
// Verifiers with a divergent message or store contract (e.g. an atomic
|
|
282
|
+
// back-channel-logout-token store) keep their own inline check.
|
|
283
|
+
async function enforceReplay(store, jti, expireAtMs, opts) {
|
|
284
|
+
opts = opts || {};
|
|
285
|
+
var inserted;
|
|
286
|
+
try {
|
|
287
|
+
inserted = await store.checkAndInsert(jti, expireAtMs);
|
|
288
|
+
} catch (e) {
|
|
289
|
+
throw new opts.errorClass(opts.storeFailedCode,
|
|
290
|
+
"replayStore.checkAndInsert threw: " + ((e && e.message) || String(e)));
|
|
291
|
+
}
|
|
292
|
+
if (inserted === false) {
|
|
293
|
+
throw new opts.errorClass(opts.replayCode,
|
|
294
|
+
opts.tokenLabel + " jti='" + jti + "' has been seen before — replay refused");
|
|
295
|
+
}
|
|
219
296
|
}
|
|
220
297
|
|
|
221
298
|
module.exports = {
|
|
222
299
|
create: create,
|
|
300
|
+
enforceReplay: enforceReplay,
|
|
223
301
|
NonceStoreError: NonceStoreError,
|
|
224
302
|
_memoryBackend: _memoryBackend,
|
|
225
303
|
_clusterBackend: _clusterBackend,
|
package/lib/notify.js
CHANGED
|
@@ -408,11 +408,6 @@ function create(opts) {
|
|
|
408
408
|
channels[n] = registry;
|
|
409
409
|
}
|
|
410
410
|
|
|
411
|
-
function _emitObs(name, labels) {
|
|
412
|
-
try { observability().event(name, 1, labels || {}); }
|
|
413
|
-
catch (_e) { /* drop-silent — observability sink must not crash send() */ }
|
|
414
|
-
}
|
|
415
|
-
|
|
416
411
|
var _emitAudit = validateOpts.makeAuditEmitter(audit);
|
|
417
412
|
|
|
418
413
|
function _actor(callerOpts) {
|
|
@@ -465,7 +460,7 @@ function create(opts) {
|
|
|
465
460
|
// mechanism — no double-retry inside the breaker or transport).
|
|
466
461
|
async function _oneAttempt(attemptIdx) {
|
|
467
462
|
attemptCount = attemptIdx;
|
|
468
|
-
|
|
463
|
+
observability().safeEvent("notify.send.attempt", 1, { channel: channel, attempt: attemptIdx });
|
|
469
464
|
var sendPromise = entry.transport.send(message, input.sendOpts || null);
|
|
470
465
|
// withTimeout from b.safeAsync — never re-implement timer races.
|
|
471
466
|
var timed = (perCallTimeoutMs > 0)
|
|
@@ -478,7 +473,7 @@ function create(opts) {
|
|
|
478
473
|
// classifies it correctly (operators can still opt OUT by setting
|
|
479
474
|
// err.permanent in their transport).
|
|
480
475
|
if (e && e.code === "async/timeout") {
|
|
481
|
-
|
|
476
|
+
observability().safeEvent("notify.send.timeout", 1, { channel: channel });
|
|
482
477
|
var te = _err("TIMEOUT",
|
|
483
478
|
"notify.send: '" + channel + "' transport timed out after " + perCallTimeoutMs + "ms");
|
|
484
479
|
// Mark transient via a NETWORK-style code so b.retry.isRetryable
|
|
@@ -497,7 +492,7 @@ function create(opts) {
|
|
|
497
492
|
return await entry.breaker.wrap(function () { return _oneAttempt(attemptIdx); });
|
|
498
493
|
} catch (e) {
|
|
499
494
|
if (e && e.code === "CIRCUIT_OPEN") {
|
|
500
|
-
|
|
495
|
+
observability().safeEvent("notify.send.breaker.open", 1, { channel: channel });
|
|
501
496
|
}
|
|
502
497
|
throw e;
|
|
503
498
|
}
|
|
@@ -524,7 +519,7 @@ function create(opts) {
|
|
|
524
519
|
}, perCallRetry);
|
|
525
520
|
|
|
526
521
|
var durationMs = clock() - startedAt;
|
|
527
|
-
|
|
522
|
+
observability().safeEvent("notify.send.success", 1, { channel: channel, durationMs: durationMs });
|
|
528
523
|
if (auditSuccess) {
|
|
529
524
|
_emitAudit("notify.send.success", {
|
|
530
525
|
actor: _actor(input),
|
|
@@ -543,7 +538,7 @@ function create(opts) {
|
|
|
543
538
|
durationMs: durationMs,
|
|
544
539
|
});
|
|
545
540
|
} catch (e) {
|
|
546
|
-
|
|
541
|
+
observability().safeEvent("notify.send.failure", 1, {
|
|
547
542
|
channel: channel,
|
|
548
543
|
reason: (e && e.code) || "unknown",
|
|
549
544
|
});
|
|
@@ -594,7 +589,7 @@ function create(opts) {
|
|
|
594
589
|
if (results[i] && results[i].isNotifyError) failed++;
|
|
595
590
|
else ok++;
|
|
596
591
|
}
|
|
597
|
-
|
|
592
|
+
observability().safeEvent("notify.batch", 1, { size: inputs.length, ok: ok, failed: failed });
|
|
598
593
|
return results;
|
|
599
594
|
}
|
|
600
595
|
|
|
@@ -626,7 +621,7 @@ function create(opts) {
|
|
|
626
621
|
} catch (_e) { /* operator may register their own handler */ }
|
|
627
622
|
}
|
|
628
623
|
var jobId = await q.enqueue(queueName, input);
|
|
629
|
-
|
|
624
|
+
observability().safeEvent("notify.queue.enqueued", 1, { channel: input.channel, queueName: queueName });
|
|
630
625
|
return { jobId: jobId };
|
|
631
626
|
}
|
|
632
627
|
|
package/lib/numeric-bounds.js
CHANGED
|
@@ -42,6 +42,20 @@ function shape(value) {
|
|
|
42
42
|
return (typeof value) + " " + String(value);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// _throwInt — construct + throw a code-first defineClass error. When no
|
|
46
|
+
// errorOpts is supplied the call is the historical bare `new ErrorClass(
|
|
47
|
+
// code, message)` (byte-identical for every existing caller); pass
|
|
48
|
+
// errorOpts { permanent, statusCode } to forward the framework error's
|
|
49
|
+
// non-retryable flag / HTTP status (a config opt that's wrong on retry
|
|
50
|
+
// stays wrong; a request-shaped opt carries its 4xx). Message-FIRST
|
|
51
|
+
// classes (AtomicFileError / SafeBufferError / SafeUrlError) must NOT use
|
|
52
|
+
// the require* throwers — they use the isPositiveFiniteInt predicate + their
|
|
53
|
+
// own throw (see the module header) so code/message don't swap.
|
|
54
|
+
function _throwInt(errorClass, code, message, errorOpts) {
|
|
55
|
+
if (errorOpts) throw new errorClass(code, message, errorOpts.permanent, errorOpts.statusCode);
|
|
56
|
+
throw new errorClass(code, message);
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
function isPositiveFiniteInt(value) {
|
|
46
60
|
return typeof value === "number" && Number.isFinite(value) &&
|
|
47
61
|
Number.isInteger(value) && value > 0;
|
|
@@ -64,11 +78,11 @@ function isNonNegativeFiniteInt(value) {
|
|
|
64
78
|
// nb.requirePositiveFiniteIntIfPresent(opts.graceMs,
|
|
65
79
|
// "app-shutdown.create: opts.graceMs", AppShutdownError,
|
|
66
80
|
// "app-shutdown/bad-grace-ms");
|
|
67
|
-
function requirePositiveFiniteIntIfPresent(value, label, errorClass, code) {
|
|
81
|
+
function requirePositiveFiniteIntIfPresent(value, label, errorClass, code, errorOpts) {
|
|
68
82
|
if (value === undefined) return value;
|
|
69
83
|
if (!isPositiveFiniteInt(value)) {
|
|
70
|
-
|
|
71
|
-
(label || "value") + " must be a positive finite integer; got " + shape(value));
|
|
84
|
+
_throwInt(errorClass, code,
|
|
85
|
+
(label || "value") + " must be a positive finite integer; got " + shape(value), errorOpts);
|
|
72
86
|
}
|
|
73
87
|
return value;
|
|
74
88
|
}
|
|
@@ -102,13 +116,13 @@ function _rangeSuffix(range) {
|
|
|
102
116
|
if (range.min != null) return " >= " + range.min;
|
|
103
117
|
return "";
|
|
104
118
|
}
|
|
105
|
-
function requirePositiveFiniteInt(value, label, errorClass, code, range) {
|
|
119
|
+
function requirePositiveFiniteInt(value, label, errorClass, code, range, errorOpts) {
|
|
106
120
|
var inRange = !range ||
|
|
107
121
|
((range.min == null || value >= range.min) && (range.max == null || value <= range.max));
|
|
108
122
|
if (!isPositiveFiniteInt(value) || !inRange) {
|
|
109
|
-
|
|
123
|
+
_throwInt(errorClass, code,
|
|
110
124
|
(label || "value") + " must be a positive finite integer" +
|
|
111
|
-
_rangeSuffix(range) + "; got " + shape(value));
|
|
125
|
+
_rangeSuffix(range) + "; got " + shape(value), errorOpts);
|
|
112
126
|
}
|
|
113
127
|
return value;
|
|
114
128
|
}
|
|
@@ -123,12 +137,12 @@ function requirePositiveFiniteInt(value, label, errorClass, code, range) {
|
|
|
123
137
|
// nb.requireAllPositiveFiniteIntIfPresent(opts,
|
|
124
138
|
// ["maxBytes", "maxAttrValueBytes", "maxTagDepth", "maxAttrsPerTag"],
|
|
125
139
|
// "guardHtml.validate", GuardHtmlError, "html.bad-opt");
|
|
126
|
-
function requireAllPositiveFiniteIntIfPresent(opts, names, labelPrefix, errorClass, code) {
|
|
140
|
+
function requireAllPositiveFiniteIntIfPresent(opts, names, labelPrefix, errorClass, code, errorOpts) {
|
|
127
141
|
if (!opts || !Array.isArray(names)) return;
|
|
128
142
|
for (var i = 0; i < names.length; i += 1) {
|
|
129
143
|
var n = names[i];
|
|
130
144
|
requirePositiveFiniteIntIfPresent(opts[n],
|
|
131
|
-
(labelPrefix || "") + ": " + n, errorClass, code);
|
|
145
|
+
(labelPrefix || "") + ": " + n, errorClass, code, errorOpts);
|
|
132
146
|
}
|
|
133
147
|
}
|
|
134
148
|
|
|
@@ -33,6 +33,7 @@ var C = require("../constants");
|
|
|
33
33
|
var httpClient = require("../http-client");
|
|
34
34
|
var requestHelpers = require("../request-helpers");
|
|
35
35
|
var safeUrl = require("../safe-url");
|
|
36
|
+
var markupEscape = require("../markup-escape").markupEscape;
|
|
36
37
|
var { ObjectStoreError } = require("../framework-error");
|
|
37
38
|
|
|
38
39
|
var _err = ObjectStoreError.factory;
|
|
@@ -122,12 +123,7 @@ function _validateCorsRule(rule, idx) {
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
function _xmlEscape(s) {
|
|
125
|
-
return
|
|
126
|
-
.replace(/&/g, "&")
|
|
127
|
-
.replace(/</g, "<")
|
|
128
|
-
.replace(/>/g, ">")
|
|
129
|
-
.replace(/"/g, """)
|
|
130
|
-
.replace(/'/g, "'");
|
|
126
|
+
return markupEscape(s, { apos: "'" });
|
|
131
127
|
}
|
|
132
128
|
|
|
133
129
|
function _buildCorsXml(rules) {
|