@blamejs/core 0.15.12 → 0.15.14
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 +45 -29
- package/lib/acme.js +6 -5
- package/lib/agent-event-bus.js +18 -4
- package/lib/agent-idempotency.js +7 -6
- package/lib/agent-orchestrator.js +2 -5
- package/lib/agent-saga.js +3 -5
- package/lib/agent-snapshot.js +32 -2
- package/lib/agent-tenant.js +2 -5
- package/lib/ai-adverse-decision.js +2 -15
- package/lib/ai-aedt-bias-audit.js +2 -1
- package/lib/ai-capability.js +1 -6
- package/lib/ai-content-detect.js +1 -3
- package/lib/ai-dp.js +1 -5
- package/lib/ai-frontier-protocol.js +1 -1
- package/lib/ai-input.js +2 -2
- package/lib/ai-model-manifest.js +1 -1
- package/lib/ai-output.js +16 -7
- package/lib/ai-pref.js +3 -8
- package/lib/ai-quota.js +3 -14
- package/lib/api-key.js +37 -28
- package/lib/api-snapshot.js +4 -1
- package/lib/app-shutdown.js +7 -1
- package/lib/archive-adapters.js +2 -4
- package/lib/archive-entry-policy.js +32 -0
- package/lib/archive-gz.js +9 -0
- package/lib/archive-read.js +14 -24
- package/lib/archive-tar-read.js +56 -24
- package/lib/archive.js +6 -12
- package/lib/arg-parser.js +7 -6
- package/lib/asn1-der.js +70 -22
- package/lib/asyncapi-traits.js +2 -6
- package/lib/atomic-file.js +312 -33
- package/lib/audit-chain.js +183 -54
- package/lib/audit-daily-review.js +36 -16
- package/lib/audit-emit.js +82 -0
- package/lib/audit-sign.js +258 -0
- package/lib/audit-tools.js +108 -23
- package/lib/audit.js +91 -2
- package/lib/auth/access-lock.js +7 -29
- package/lib/auth/bot-challenge.js +1 -1
- package/lib/auth/ciba.js +43 -4
- package/lib/auth/dpop.js +29 -36
- package/lib/auth/fido-mds3.js +14 -16
- package/lib/auth/jwt-external.js +26 -8
- package/lib/auth/jwt.js +15 -17
- package/lib/auth/lockout.js +24 -27
- 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 +4 -4
- package/lib/auth/password.js +8 -2
- package/lib/auth/saml.js +70 -40
- 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 +25 -5
- package/lib/auth/status-list.js +21 -9
- package/lib/auth/step-up.js +15 -13
- package/lib/auth-bot-challenge.js +27 -39
- package/lib/backup/bundle.js +18 -20
- package/lib/backup/crypto.js +23 -8
- package/lib/backup/index.js +55 -67
- package/lib/backup/manifest.js +7 -1
- package/lib/bounded-map.js +112 -1
- package/lib/breach-deadline.js +1 -11
- package/lib/break-glass.js +41 -22
- package/lib/cache.js +7 -18
- package/lib/cbor.js +36 -12
- package/lib/cdn-cache-control.js +15 -12
- package/lib/cert.js +8 -13
- package/lib/chain-writer.js +162 -47
- package/lib/cli.js +10 -2
- package/lib/client-hints.js +7 -9
- package/lib/cloud-events.js +43 -33
- package/lib/cluster-storage.js +9 -41
- package/lib/cms-codec.js +2 -1
- package/lib/codepoint-class.js +84 -1
- package/lib/compliance-ai-act-logging.js +1 -6
- package/lib/compliance-ai-act-transparency.js +2 -4
- package/lib/compliance-eaa.js +2 -12
- package/lib/compliance-sanctions-fetcher.js +21 -28
- package/lib/compliance-sanctions.js +11 -21
- package/lib/compliance.js +3 -10
- package/lib/config-drift.js +24 -20
- package/lib/content-credentials.js +11 -8
- package/lib/content-digest.js +21 -12
- package/lib/cookies.js +15 -13
- package/lib/cose.js +39 -11
- package/lib/cra-report.js +1 -11
- package/lib/crdt.js +2 -1
- package/lib/crypto-field.js +34 -33
- package/lib/crypto.js +138 -7
- package/lib/csp.js +239 -3
- package/lib/daemon.js +46 -42
- package/lib/data-act.js +46 -13
- package/lib/db-file-lifecycle.js +14 -6
- package/lib/db-query.js +75 -49
- package/lib/db.js +66 -27
- 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/dora.js +43 -13
- package/lib/dr-runbook.js +1 -1
- package/lib/dsa.js +2 -1
- package/lib/dsr.js +30 -20
- package/lib/early-hints.js +19 -0
- package/lib/eat.js +5 -1
- package/lib/external-db-migrate.js +21 -22
- package/lib/external-db.js +74 -30
- package/lib/fda-21cfr11.js +42 -13
- package/lib/fdx.js +22 -18
- package/lib/file-upload.js +80 -66
- package/lib/flag-evaluation-context.js +5 -8
- package/lib/flag-providers.js +6 -2
- package/lib/forms.js +1 -1
- 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 +911 -39
- package/lib/gdpr-ropa.js +22 -22
- package/lib/graphql-federation.js +18 -5
- package/lib/guard-agent-registry.js +2 -1
- package/lib/guard-all.js +3 -2
- 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 +17 -14
- package/lib/guard-email.js +46 -53
- package/lib/guard-envelope.js +2 -2
- package/lib/guard-event-bus-topic.js +2 -1
- package/lib/guard-filename.js +12 -60
- package/lib/guard-graphql.js +28 -75
- package/lib/guard-html-wcag.js +15 -2
- package/lib/guard-html.js +74 -128
- package/lib/guard-idempotency-key.js +2 -1
- package/lib/guard-image.js +280 -77
- package/lib/guard-imap-command.js +9 -10
- package/lib/guard-jmap.js +1 -1
- package/lib/guard-json.js +101 -109
- 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 +3 -2
- package/lib/guard-mail-query.js +5 -3
- package/lib/guard-mail-sieve.js +6 -7
- package/lib/guard-managesieve-command.js +6 -5
- 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 +13 -14
- 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 +9 -12
- package/lib/guard-sql.js +15 -13
- package/lib/guard-stream-args.js +2 -1
- package/lib/guard-svg.js +103 -149
- 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/html-balance.js +7 -3
- package/lib/http-client-cache.js +5 -12
- package/lib/http-client-cookie-jar.js +35 -16
- package/lib/http-client.js +233 -74
- package/lib/http-message-signature.js +3 -2
- package/lib/i18n-messageformat.js +5 -7
- package/lib/i18n.js +83 -26
- package/lib/iab-tcf.js +3 -2
- package/lib/importmap-integrity.js +41 -1
- package/lib/inbox.js +8 -8
- package/lib/incident-report.js +12 -27
- package/lib/ip-utils.js +49 -6
- package/lib/jobs.js +3 -2
- package/lib/json-patch.js +1 -1
- package/lib/json-path.js +24 -3
- package/lib/jtd.js +2 -2
- package/lib/keychain.js +6 -18
- package/lib/legal-hold.js +30 -23
- package/lib/log-stream-cloudwatch.js +44 -112
- package/lib/log-stream-otlp-grpc.js +17 -14
- package/lib/log-stream-otlp.js +16 -80
- package/lib/log-stream-webhook.js +20 -92
- package/lib/log.js +2 -2
- package/lib/mail-agent.js +2 -2
- package/lib/mail-arc-sign.js +8 -3
- package/lib/mail-arf.js +4 -3
- package/lib/mail-auth.js +43 -69
- package/lib/mail-bimi.js +35 -55
- package/lib/mail-bounce.js +3 -3
- package/lib/mail-crypto-pgp.js +3 -2
- package/lib/mail-crypto-smime.js +71 -6
- package/lib/mail-dav.js +8 -35
- package/lib/mail-deploy.js +15 -14
- package/lib/mail-dkim.js +19 -38
- package/lib/mail-greylist.js +17 -30
- package/lib/mail-helo.js +4 -7
- package/lib/mail-journal.js +13 -9
- package/lib/mail-mdn.js +12 -7
- package/lib/mail-rbl.js +7 -12
- package/lib/mail-scan.js +5 -7
- package/lib/mail-send-deliver.js +33 -20
- package/lib/mail-server-imap.js +32 -87
- package/lib/mail-server-jmap.js +39 -52
- 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-registry.js +1 -1
- package/lib/mail-server-submission.js +34 -73
- package/lib/mail-server-tls.js +98 -2
- package/lib/mail-spam-score.js +9 -12
- package/lib/mail-store-fts.js +1 -1
- package/lib/mail-store.js +3 -2
- package/lib/mail.js +28 -13
- package/lib/markup-escape.js +31 -0
- package/lib/markup-tokenizer.js +78 -0
- package/lib/mcp-tool-registry.js +26 -23
- package/lib/mcp.js +7 -5
- package/lib/mdoc.js +36 -14
- package/lib/metrics.js +46 -33
- package/lib/middleware/age-gate.js +3 -23
- package/lib/middleware/api-encrypt.js +13 -24
- package/lib/middleware/assetlinks.js +2 -7
- package/lib/middleware/bearer-auth.js +2 -1
- package/lib/middleware/body-parser.js +41 -52
- package/lib/middleware/bot-disclose.js +7 -10
- package/lib/middleware/bot-guard.js +28 -28
- package/lib/middleware/clear-site-data.js +5 -1
- package/lib/middleware/compression.js +9 -0
- package/lib/middleware/cors.js +32 -23
- package/lib/middleware/csrf-protect.js +73 -23
- package/lib/middleware/daily-byte-quota.js +12 -30
- package/lib/middleware/deny-response.js +19 -1
- package/lib/middleware/fetch-metadata.js +35 -4
- package/lib/middleware/idempotency-key.js +4 -20
- package/lib/middleware/network-allowlist.js +61 -30
- package/lib/middleware/rate-limit.js +45 -44
- package/lib/middleware/scim-server.js +5 -3
- package/lib/middleware/security-headers.js +33 -7
- package/lib/middleware/security-txt.js +2 -6
- package/lib/middleware/speculation-rules.js +6 -3
- package/lib/middleware/tus-upload.js +14 -29
- 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 +125 -41
- package/lib/network-byte-quota.js +4 -18
- package/lib/network-dane.js +8 -6
- package/lib/network-dns-resolver.js +98 -7
- package/lib/network-dns.js +9 -2
- package/lib/network-dnssec.js +18 -17
- package/lib/network-heartbeat.js +3 -2
- package/lib/network-smtp-policy.js +52 -46
- package/lib/network-tls.js +67 -47
- package/lib/network-tsig.js +2 -2
- package/lib/nis2-report.js +2 -12
- package/lib/nist-crosswalk.js +1 -1
- package/lib/nonce-store.js +81 -3
- package/lib/ntp-check.js +28 -0
- package/lib/numeric-bounds.js +31 -8
- package/lib/object-store/azure-blob-bucket-ops.js +2 -6
- package/lib/object-store/azure-blob.js +6 -47
- package/lib/object-store/gcs-bucket-ops.js +4 -2
- package/lib/object-store/gcs.js +14 -75
- package/lib/object-store/http-put.js +2 -7
- package/lib/object-store/http-request.js +157 -0
- package/lib/object-store/local.js +37 -17
- package/lib/object-store/sigv4-bucket-ops.js +2 -1
- package/lib/object-store/sigv4.js +10 -79
- package/lib/observability-otlp-exporter.js +29 -29
- 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 +54 -41
- 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 +3 -3
- package/lib/parsers/safe-yaml.js +28 -9
- 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/queue-local.js +10 -3
- package/lib/redact.js +9 -4
- package/lib/render.js +4 -2
- package/lib/request-helpers.js +334 -29
- package/lib/resource-access-lock.js +3 -3
- package/lib/restore-bundle.js +46 -18
- package/lib/restore-rollback.js +10 -4
- package/lib/restore.js +24 -22
- package/lib/retention.js +23 -9
- package/lib/router.js +17 -4
- 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 +14 -28
- package/lib/safe-icap.js +1 -1
- package/lib/safe-json.js +51 -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-sieve.js +1 -1
- package/lib/safe-sql.js +126 -0
- package/lib/safe-vcard.js +13 -27
- package/lib/sandbox-worker.js +15 -2
- package/lib/sandbox.js +4 -3
- package/lib/scheduler.js +22 -13
- package/lib/sec-cyber.js +82 -37
- package/lib/seeders.js +4 -11
- package/lib/self-update-standalone-verifier.js +16 -0
- package/lib/self-update.js +92 -69
- package/lib/session-device-binding.js +112 -66
- package/lib/session.js +219 -7
- package/lib/sql.js +228 -81
- package/lib/sse.js +6 -10
- package/lib/static.js +201 -111
- package/lib/storage.js +4 -2
- package/lib/structured-fields.js +275 -16
- package/lib/template.js +7 -5
- package/lib/tenant-quota.js +53 -38
- package/lib/tsa.js +16 -17
- package/lib/validate-opts.js +154 -8
- package/lib/vault/index.js +5 -0
- package/lib/vault/passphrase-ops.js +22 -26
- package/lib/vault/passphrase-source.js +8 -3
- package/lib/vault/rotate.js +13 -18
- package/lib/vault/seal-pem-file.js +34 -49
- package/lib/vault-aad.js +3 -2
- package/lib/vc.js +3 -8
- package/lib/vendor/MANIFEST.json +10 -10
- package/lib/vendor/public-suffix-list.dat +23 -10
- package/lib/vendor/public-suffix-list.data.js +5498 -5494
- 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 +59 -19
- package/lib/websocket-channels.js +9 -7
- package/lib/websocket.js +8 -4
- package/lib/worm.js +3 -4
- package/lib/ws-client.js +65 -56
- package/lib/x509-chain.js +44 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/safe-vcard.js
CHANGED
|
@@ -62,8 +62,11 @@
|
|
|
62
62
|
*/
|
|
63
63
|
|
|
64
64
|
var C = require("./constants");
|
|
65
|
+
var codepointClass = require("./codepoint-class");
|
|
66
|
+
var structuredFields = require("./structured-fields");
|
|
65
67
|
var { defineClass } = require("./framework-error");
|
|
66
68
|
var gateContract = require("./gate-contract");
|
|
69
|
+
var pick = require("./pick");
|
|
67
70
|
|
|
68
71
|
var SafeVcardError = defineClass("SafeVcardError", { alwaysPermanent: true });
|
|
69
72
|
|
|
@@ -280,13 +283,11 @@ function _parseContentLine(line) {
|
|
|
280
283
|
var head = line.slice(0, colonIdx);
|
|
281
284
|
var value = line.slice(colonIdx + 1);
|
|
282
285
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
" in property value (header-injection defense)");
|
|
289
|
-
}
|
|
286
|
+
var ctrlAt = codepointClass.firstControlCharOffset(value); // C0 (except TAB) + DEL refusal
|
|
287
|
+
if (ctrlAt !== -1) {
|
|
288
|
+
throw new SafeVcardError("safe-vcard/control-char-in-value",
|
|
289
|
+
"safeVcard.parse: control char 0x" + value.charCodeAt(ctrlAt).toString(16) +
|
|
290
|
+
" in property value (header-injection defense)");
|
|
290
291
|
}
|
|
291
292
|
|
|
292
293
|
// RFC 6350 §3.3 — property name may be prefixed by an optional
|
|
@@ -310,7 +311,7 @@ function _parseContentLine(line) {
|
|
|
310
311
|
}
|
|
311
312
|
var pname = seg.slice(0, eq).toUpperCase();
|
|
312
313
|
var pvalue = seg.slice(eq + 1);
|
|
313
|
-
if (pname
|
|
314
|
+
if (pick.isPoisonedKey(pname)) continue;
|
|
314
315
|
if (params[pname]) {
|
|
315
316
|
params[pname].push(_stripDoubleQuotes(pvalue));
|
|
316
317
|
} else {
|
|
@@ -331,26 +332,11 @@ function _findUnquotedColon(line) {
|
|
|
331
332
|
}
|
|
332
333
|
|
|
333
334
|
function _splitUnquoted(s, sep) {
|
|
334
|
-
|
|
335
|
-
var inQ = false;
|
|
336
|
-
var start = 0;
|
|
337
|
-
for (var i = 0; i < s.length; i++) {
|
|
338
|
-
var c = s.charAt(i);
|
|
339
|
-
if (c === '"') { inQ = !inQ; continue; }
|
|
340
|
-
if (c === sep && !inQ) {
|
|
341
|
-
out.push(s.slice(start, i));
|
|
342
|
-
start = i + 1;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
out.push(s.slice(start));
|
|
346
|
-
return out;
|
|
335
|
+
return structuredFields.splitUnquoted(s, sep);
|
|
347
336
|
}
|
|
348
337
|
|
|
349
338
|
function _stripDoubleQuotes(s) {
|
|
350
|
-
|
|
351
|
-
return s.slice(1, -1);
|
|
352
|
-
}
|
|
353
|
-
return s;
|
|
339
|
+
return structuredFields.stripDoubleQuotes(s);
|
|
354
340
|
}
|
|
355
341
|
|
|
356
342
|
function _parseVcard(lines, startIdx, caps, extraProps) {
|
|
@@ -375,7 +361,7 @@ function _parseVcard(lines, startIdx, caps, extraProps) {
|
|
|
375
361
|
"safeVcard.parse: nested BEGIN inside VCARD (vCard does not support sub-components)");
|
|
376
362
|
}
|
|
377
363
|
var pn = ln.name;
|
|
378
|
-
if (!KNOWN_PROPERTIES
|
|
364
|
+
if (!Object.prototype.hasOwnProperty.call(KNOWN_PROPERTIES, pn) && !extraProps[pn] && pn.indexOf("X-") !== 0) {
|
|
379
365
|
throw new SafeVcardError("safe-vcard/unknown-property",
|
|
380
366
|
"safeVcard.parse: unknown property '" + pn +
|
|
381
367
|
"' (extend via opts.extraProperties or use X- prefix)");
|
|
@@ -399,7 +385,7 @@ function _parseVcard(lines, startIdx, caps, extraProps) {
|
|
|
399
385
|
"safeVcard.parse: property count exceeds maxPropertiesPerCard=" +
|
|
400
386
|
caps.maxPropertiesPerCard);
|
|
401
387
|
}
|
|
402
|
-
if (pn
|
|
388
|
+
if (pick.isPoisonedKey(pn)) {
|
|
403
389
|
i += 1;
|
|
404
390
|
continue;
|
|
405
391
|
}
|
package/lib/sandbox-worker.js
CHANGED
|
@@ -40,6 +40,13 @@ var workerThreads = require("node:worker_threads");
|
|
|
40
40
|
var allowed = Array.isArray(data.allowedGlobals) ? data.allowedGlobals : [];
|
|
41
41
|
var maxResultBytes = (typeof data.maxResultBytes === "number") ? data.maxResultBytes : null;
|
|
42
42
|
|
|
43
|
+
// Capture the real UTF-8 byte counter BEFORE the global strip below deletes
|
|
44
|
+
// `Buffer`. The result cap is a BYTE budget — measuring the serialized
|
|
45
|
+
// result with `String.length` (UTF-16 code units) under-enforces it on
|
|
46
|
+
// multibyte output. A detached `Buffer.byteLength` keeps working after
|
|
47
|
+
// `delete globalThis.Buffer`.
|
|
48
|
+
var byteLength = Buffer.byteLength;
|
|
49
|
+
|
|
43
50
|
var ALWAYS_AVAILABLE = [
|
|
44
51
|
"Object", "Array", "String", "Number", "Boolean", "Symbol",
|
|
45
52
|
"Promise", "Error", "TypeError", "RangeError", "RegExp",
|
|
@@ -56,6 +63,12 @@ var workerThreads = require("node:worker_threads");
|
|
|
56
63
|
"setInterval", "clearInterval",
|
|
57
64
|
"queueMicrotask",
|
|
58
65
|
"global",
|
|
66
|
+
// WebAssembly linear memory is allocated OUTSIDE the V8 heap, so the
|
|
67
|
+
// worker's maxOldGenerationSizeMb cap (derived from opts.maxBytes) does
|
|
68
|
+
// NOT bound `new WebAssembly.Memory(...).grow(N)` — operator source could
|
|
69
|
+
// commit multi-GiB of off-heap RAM under a tiny maxBytes (CWE-770). It is
|
|
70
|
+
// not in KNOWN_SAFE_BUILTINS so no `allowed` opt can re-expose it.
|
|
71
|
+
"WebAssembly",
|
|
59
72
|
];
|
|
60
73
|
for (var k = 0; k < NODE_BUILTINS.length; k += 1) {
|
|
61
74
|
var nm = NODE_BUILTINS[k];
|
|
@@ -113,10 +126,10 @@ var workerThreads = require("node:worker_threads");
|
|
|
113
126
|
});
|
|
114
127
|
return;
|
|
115
128
|
}
|
|
116
|
-
if (maxResultBytes !== null && serialized && serialized
|
|
129
|
+
if (maxResultBytes !== null && serialized && byteLength(serialized, "utf8") > maxResultBytes) {
|
|
117
130
|
workerThreads.parentPort.postMessage({
|
|
118
131
|
ok: false, code: "sandbox/oversized-result",
|
|
119
|
-
message: "sandbox result exceeded maxResultBytes (" + serialized
|
|
132
|
+
message: "sandbox result exceeded maxResultBytes (" + byteLength(serialized, "utf8") + " > " + maxResultBytes + ")",
|
|
120
133
|
runtimeMs: runtimeMs, peakBytes: peakBytes,
|
|
121
134
|
});
|
|
122
135
|
return;
|
package/lib/sandbox.js
CHANGED
|
@@ -82,6 +82,7 @@ var nodePath = require("node:path");
|
|
|
82
82
|
var lazyRequire = require("./lazy-require");
|
|
83
83
|
var validateOpts = require("./validate-opts");
|
|
84
84
|
var numericBounds = require("./numeric-bounds");
|
|
85
|
+
var safeBuffer = require("./safe-buffer");
|
|
85
86
|
var C = require("./constants");
|
|
86
87
|
var { SandboxError } = require("./framework-error");
|
|
87
88
|
|
|
@@ -132,7 +133,7 @@ function _validateAllowed(allowed) {
|
|
|
132
133
|
throw new SandboxError("sandbox/bad-allowed",
|
|
133
134
|
"sandbox.run: opts.allowed[" + i + "] must be a non-empty identifier string");
|
|
134
135
|
}
|
|
135
|
-
if (!KNOWN_SAFE_BUILTINS
|
|
136
|
+
if (!Object.prototype.hasOwnProperty.call(KNOWN_SAFE_BUILTINS, name)) {
|
|
136
137
|
throw new SandboxError("sandbox/bad-allowed",
|
|
137
138
|
"sandbox.run: opts.allowed[" + i + "] = " + JSON.stringify(name) +
|
|
138
139
|
" is not in the sandbox built-in allowlist " +
|
|
@@ -201,9 +202,9 @@ function run(opts) {
|
|
|
201
202
|
return Promise.reject(new SandboxError("sandbox/bad-input",
|
|
202
203
|
"sandbox.run: opts.input is not JSON-serializable: " + (eSer && eSer.message)));
|
|
203
204
|
}
|
|
204
|
-
if (inputJson !== null && inputJson
|
|
205
|
+
if (inputJson !== null && safeBuffer.byteLengthOf(inputJson) > maxBytes) {
|
|
205
206
|
return Promise.reject(new SandboxError("sandbox/input-too-large",
|
|
206
|
-
"sandbox.run: opts.input serialized to " + inputJson
|
|
207
|
+
"sandbox.run: opts.input serialized to " + safeBuffer.byteLengthOf(inputJson) + " bytes (>" + maxBytes + ")"));
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
var workerThreads;
|
package/lib/scheduler.js
CHANGED
|
@@ -40,11 +40,12 @@
|
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
42
|
var lazyRequire = require("./lazy-require");
|
|
43
|
-
var
|
|
43
|
+
var auditEmit = require("./audit-emit");
|
|
44
44
|
var log = lazyRequire(function () { return require("./log").boot("scheduler"); });
|
|
45
45
|
var clusterStorage = require("./cluster-storage");
|
|
46
46
|
var sql = require("./sql");
|
|
47
47
|
var validateOpts = require("./validate-opts");
|
|
48
|
+
var boundedMap = require("./bounded-map");
|
|
48
49
|
var C = require("./constants");
|
|
49
50
|
var { SchedulerError } = require("./framework-error");
|
|
50
51
|
|
|
@@ -176,7 +177,7 @@ function parseCron(expr) {
|
|
|
176
177
|
"cron expression must be a non-empty string", true);
|
|
177
178
|
}
|
|
178
179
|
var trimmed = expr.trim();
|
|
179
|
-
if (CRON_SHORTHANDS
|
|
180
|
+
if (Object.prototype.hasOwnProperty.call(CRON_SHORTHANDS, trimmed.toLowerCase())) {
|
|
180
181
|
trimmed = CRON_SHORTHANDS[trimmed.toLowerCase()];
|
|
181
182
|
}
|
|
182
183
|
var fields = trimmed.split(/\s+/);
|
|
@@ -417,15 +418,7 @@ function create(opts) {
|
|
|
417
418
|
|
|
418
419
|
var _err = SchedulerError.factory;
|
|
419
420
|
|
|
420
|
-
|
|
421
|
-
if (!auditOn) return;
|
|
422
|
-
audit().safeEmit({
|
|
423
|
-
action: action,
|
|
424
|
-
outcome: outcome,
|
|
425
|
-
metadata: info || {},
|
|
426
|
-
reason: info && info.reason ? info.reason : null,
|
|
427
|
-
});
|
|
428
|
-
}
|
|
421
|
+
var _emit = auditEmit.gatedReasonEmitter({ audit: auditOn });
|
|
429
422
|
|
|
430
423
|
function _isLeaderHere() {
|
|
431
424
|
if (!clusterInstance) return true;
|
|
@@ -447,10 +440,10 @@ function create(opts) {
|
|
|
447
440
|
if (typeof spec.name !== "string" || spec.name.length === 0) {
|
|
448
441
|
throw _err("INVALID_NAME", "scheduler.schedule: spec.name is required", true);
|
|
449
442
|
}
|
|
450
|
-
|
|
443
|
+
boundedMap.requireAbsent(tasks, spec.name, function () {
|
|
451
444
|
throw _err("DUPLICATE_NAME",
|
|
452
445
|
"scheduler.schedule: '" + spec.name + "' is already scheduled", true);
|
|
453
|
-
}
|
|
446
|
+
});
|
|
454
447
|
|
|
455
448
|
var hasCron = typeof spec.cron === "string";
|
|
456
449
|
var hasEvery = typeof spec.every === "number";
|
|
@@ -718,8 +711,24 @@ function create(opts) {
|
|
|
718
711
|
});
|
|
719
712
|
}
|
|
720
713
|
|
|
714
|
+
// Node coerces a setTimeout delay greater than the 32-bit signed max
|
|
715
|
+
// (2147483647 ms ≈ 24.8 days) to 1 ms — so a far-future task would fire
|
|
716
|
+
// immediately, and an interval longer than the max would tight-loop. Wait
|
|
717
|
+
// the max chunk, then re-arm for the remainder WITHOUT firing.
|
|
718
|
+
var TIMEOUT_MAX_MS = 2147483647;
|
|
719
|
+
|
|
721
720
|
function _arm(task) {
|
|
722
721
|
var delay = Math.max(0, task.nextRun - Date.now());
|
|
722
|
+
if (delay > TIMEOUT_MAX_MS) {
|
|
723
|
+
var tc = setTimeout(function () {
|
|
724
|
+
timers.delete(tc);
|
|
725
|
+
if (!started) return;
|
|
726
|
+
_arm(task); // re-arm for the remaining delay; do NOT fire yet
|
|
727
|
+
}, TIMEOUT_MAX_MS);
|
|
728
|
+
if (typeof tc.unref === "function") tc.unref();
|
|
729
|
+
timers.add(tc);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
723
732
|
var t = setTimeout(function () {
|
|
724
733
|
timers.delete(t);
|
|
725
734
|
if (!started) return;
|
package/lib/sec-cyber.js
CHANGED
|
@@ -94,45 +94,90 @@ function eightKArtifact(opts) {
|
|
|
94
94
|
throw SecCyberError.factory("sec-cyber/bad-opts",
|
|
95
95
|
"secCyber.eightKArtifact: opts required");
|
|
96
96
|
}
|
|
97
|
-
validateOpts.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
97
|
+
validateOpts.shape(opts, {
|
|
98
|
+
incidentId: "required-string",
|
|
99
|
+
registrant: function (registrant) {
|
|
100
|
+
if (!registrant || typeof registrant !== "object") {
|
|
101
|
+
throw SecCyberError.factory("sec-cyber/bad-registrant",
|
|
102
|
+
"secCyber.eightKArtifact: registrant object required");
|
|
103
|
+
}
|
|
104
|
+
validateOpts.requireNonEmptyString(registrant.name,
|
|
105
|
+
"secCyber.eightKArtifact: registrant.name", SecCyberError, "BAD_REGISTRANT_NAME");
|
|
106
|
+
validateOpts.requireNonEmptyString(registrant.cik,
|
|
107
|
+
"secCyber.eightKArtifact: registrant.cik", SecCyberError, "BAD_CIK");
|
|
108
|
+
},
|
|
109
|
+
detectedAt: function (detectedAt) {
|
|
110
|
+
numericBounds.requirePositiveFiniteIntIfPresent(detectedAt,
|
|
111
|
+
"secCyber.eightKArtifact: detectedAt", SecCyberError, "BAD_DETECTED_AT");
|
|
112
|
+
},
|
|
113
|
+
materialityDeterminedAt: function (materialityDeterminedAt) {
|
|
114
|
+
numericBounds.requirePositiveFiniteIntIfPresent(materialityDeterminedAt,
|
|
115
|
+
"secCyber.eightKArtifact: materialityDeterminedAt", SecCyberError, "BAD_MAT_AT");
|
|
116
|
+
},
|
|
117
|
+
// materialityFinding / materialityReasoning and the material-only fields
|
|
118
|
+
// (nature/scope/timing/impact) and the AG-delay fields each carry their own
|
|
119
|
+
// per-field code (BAD_FINDING / BAD_REASONING / BAD_NATURE / ... /
|
|
120
|
+
// BAD_AG_JUSTIFICATION). Cross-field requiredness (the material-only and
|
|
121
|
+
// AG-delay fields) lives in the function rule via the 5th `opts` arg, so the
|
|
122
|
+
// whole contract is enforced inside this one exhaustive shape.
|
|
123
|
+
materialityFinding: function (v) {
|
|
124
|
+
if (FINDINGS.indexOf(v) === -1) {
|
|
125
|
+
throw SecCyberError.factory("sec-cyber/bad-finding",
|
|
126
|
+
"secCyber.eightKArtifact: materialityFinding must be one of " + FINDINGS.join(", "));
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
materialityReasoning: function (v) {
|
|
130
|
+
validateOpts.requireNonEmptyString(v,
|
|
131
|
+
"secCyber.eightKArtifact: materialityReasoning", SecCyberError, "BAD_REASONING");
|
|
132
|
+
},
|
|
133
|
+
nature: function (v, label, e, c, o) {
|
|
134
|
+
if (o.materialityFinding === "material") {
|
|
135
|
+
validateOpts.requireNonEmptyString(v,
|
|
136
|
+
"secCyber.eightKArtifact: nature", SecCyberError, "BAD_NATURE");
|
|
137
|
+
} else {
|
|
138
|
+
validateOpts.optionalNonEmptyString(v, label, e, c);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
scope: function (v, label, e, c, o) {
|
|
142
|
+
if (o.materialityFinding === "material") {
|
|
143
|
+
validateOpts.requireNonEmptyString(v,
|
|
144
|
+
"secCyber.eightKArtifact: scope", SecCyberError, "BAD_SCOPE");
|
|
145
|
+
} else {
|
|
146
|
+
validateOpts.optionalNonEmptyString(v, label, e, c);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
timing: function (v, label, e, c, o) {
|
|
150
|
+
if (o.materialityFinding === "material") {
|
|
151
|
+
validateOpts.requireNonEmptyString(v,
|
|
152
|
+
"secCyber.eightKArtifact: timing", SecCyberError, "BAD_TIMING");
|
|
153
|
+
} else {
|
|
154
|
+
validateOpts.optionalNonEmptyString(v, label, e, c);
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
impact: function (v, label, e, c, o) {
|
|
158
|
+
if (o.materialityFinding === "material") {
|
|
159
|
+
validateOpts.requireNonEmptyString(v,
|
|
160
|
+
"secCyber.eightKArtifact: impact", SecCyberError, "BAD_IMPACT");
|
|
161
|
+
} else {
|
|
162
|
+
validateOpts.optionalNonEmptyString(v, label, e, c);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
agDelayRequested: "optional-boolean",
|
|
166
|
+
agDelayJustification: function (v, label, e, c, o) {
|
|
167
|
+
if (o.agDelayRequested === true) {
|
|
168
|
+
validateOpts.requireNonEmptyString(v,
|
|
169
|
+
"secCyber.eightKArtifact: agDelayJustification (required when agDelayRequested=true)",
|
|
170
|
+
SecCyberError, "BAD_AG_JUSTIFICATION");
|
|
171
|
+
} else {
|
|
172
|
+
validateOpts.optionalNonEmptyString(v, label, e, c);
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
// audit is a boolean toggle (`opts.audit !== false` suppresses emission);
|
|
176
|
+
// the emit uses the module-level b.audit sink, not this value.
|
|
177
|
+
audit: "optional-boolean",
|
|
178
|
+
}, "secCyber.eightKArtifact", SecCyberError, "BAD_INCIDENT_ID");
|
|
129
179
|
|
|
130
180
|
var agDelayRequested = opts.agDelayRequested === true;
|
|
131
|
-
if (agDelayRequested) {
|
|
132
|
-
validateOpts.requireNonEmptyString(opts.agDelayJustification,
|
|
133
|
-
"secCyber.eightKArtifact: agDelayJustification (required when agDelayRequested=true)",
|
|
134
|
-
SecCyberError, "BAD_AG_JUSTIFICATION");
|
|
135
|
-
}
|
|
136
181
|
|
|
137
182
|
var matAt = opts.materialityDeterminedAt || Date.now();
|
|
138
183
|
var deadline = agDelayRequested ? null : _addBusinessDays(matAt, 4);
|
package/lib/seeders.js
CHANGED
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
57
|
var nodePath = require("node:path");
|
|
58
|
+
var moduleLoader = require("./module-loader");
|
|
58
59
|
var atomicFile = require("./atomic-file");
|
|
59
60
|
var C = require("./constants");
|
|
60
61
|
var dbSchema = require("./db-schema");
|
|
@@ -190,18 +191,10 @@ function _listSeedFiles(rootDir, env) {
|
|
|
190
191
|
}
|
|
191
192
|
|
|
192
193
|
function _loadSeed(rootDir, env, file) {
|
|
193
|
-
var
|
|
194
|
-
|
|
195
|
-
// between calls picks it up. Production restarts the process anyway.
|
|
196
|
-
try { delete require.cache[require.resolve(fullPath)]; } catch (_e) { /* not yet cached */ }
|
|
197
|
-
var mod;
|
|
198
|
-
// Operator-supplied seed — dynamic by design, can't be bundle-traced.
|
|
199
|
-
// Host-CLI scope; deploying via SEA / pkg drops this surface.
|
|
200
|
-
try { mod = require(fullPath); } // allow:dynamic-require — operator-supplied seed
|
|
201
|
-
catch (e) {
|
|
202
|
-
throw _err("LOAD_FAILED",
|
|
194
|
+
var mod = moduleLoader.requireFresh(nodePath.join(_envDir(rootDir, env), file), function (e) {
|
|
195
|
+
return _err("LOAD_FAILED",
|
|
203
196
|
"seed '" + env + "/" + file + "' failed to load: " + ((e && e.message) || String(e)));
|
|
204
|
-
}
|
|
197
|
+
});
|
|
205
198
|
if (!mod || typeof mod.run !== "function") {
|
|
206
199
|
throw _err("BAD_SEED",
|
|
207
200
|
"seed '" + env + "/" + file + "' must export an async `run(db, ctx)` function");
|
|
@@ -181,6 +181,14 @@ function verify(assetPath, signaturePath, pubkeyPem) {
|
|
|
181
181
|
var signature;
|
|
182
182
|
try {
|
|
183
183
|
var sigStat = nodeFs.fstatSync(sigFd);
|
|
184
|
+
// Bound the alloc: the largest supported signature (ML-DSA-87) is ~4.6 KB;
|
|
185
|
+
// 64 KiB is far above any legitimate sig and stops Buffer.allocUnsafe(stat.size)
|
|
186
|
+
// from OOM-ing if signaturePath is pointed at a giant file. Zero-dep by
|
|
187
|
+
// contract — inline literal, cannot import C.BYTES.
|
|
188
|
+
if (sigStat.size > 64 * 1024) { // allow:raw-byte-literal — zero-dep module
|
|
189
|
+
throw new Error("standalone-verifier.verify: signature file implausibly large (" +
|
|
190
|
+
sigStat.size + " bytes)");
|
|
191
|
+
}
|
|
184
192
|
signature = Buffer.allocUnsafe(sigStat.size);
|
|
185
193
|
if (sigStat.size > 0) nodeFs.readSync(sigFd, signature, 0, sigStat.size, 0);
|
|
186
194
|
} finally {
|
|
@@ -219,6 +227,14 @@ function verify(assetPath, signaturePath, pubkeyPem) {
|
|
|
219
227
|
// to come from the same {0..assetStat.size} range fixed at open
|
|
220
228
|
// time.
|
|
221
229
|
var assetStat = nodeFs.fstatSync(assetFd);
|
|
230
|
+
// Bound the asset alloc before Buffer.allocUnsafe(assetStat.size): a self-update
|
|
231
|
+
// bundle (SEA) is intentionally large, so the ceiling is generous (2 GiB), but
|
|
232
|
+
// it stops a signaturePath/assetPath pointed at an unbounded file from OOM-ing
|
|
233
|
+
// the verifier before any byte is hashed. Zero-dep by contract — inline literal.
|
|
234
|
+
if (assetStat.size > 2 * 1024 * 1024 * 1024) { // allow:raw-byte-literal — zero-dep module, 2 GiB asset ceiling
|
|
235
|
+
throw new Error("standalone-verifier.verify: asset implausibly large (" +
|
|
236
|
+
assetStat.size + " bytes) — exceeds the 2 GiB self-update ceiling");
|
|
237
|
+
}
|
|
222
238
|
var sha256 = nodeCrypto.createHash("sha256");
|
|
223
239
|
var sha3 = nodeCrypto.createHash("sha3-512");
|
|
224
240
|
var verifier = (alg === "ecdsa-p384") ? nodeCrypto.createVerify("sha3-512") : null;
|
package/lib/self-update.js
CHANGED
|
@@ -57,13 +57,12 @@ var bCrypto = require("./crypto");
|
|
|
57
57
|
var httpClient = require("./http-client");
|
|
58
58
|
var safeJson = require("./safe-json");
|
|
59
59
|
var { URL: NodeUrl } = require("node:url");
|
|
60
|
-
var lazyRequire = require("./lazy-require");
|
|
61
60
|
var C = require("./constants");
|
|
62
61
|
var standaloneVerifier = require("./self-update-standalone-verifier");
|
|
63
62
|
var { boot } = require("./log");
|
|
64
63
|
var { defineClass } = require("./framework-error");
|
|
65
64
|
|
|
66
|
-
var
|
|
65
|
+
var auditEmit = require("./audit-emit");
|
|
67
66
|
|
|
68
67
|
var SelfUpdateError = defineClass("SelfUpdateError", { alwaysPermanent: true });
|
|
69
68
|
var log = boot("self-update");
|
|
@@ -76,13 +75,7 @@ var DEFAULT_HASH_ALG = "sha3-512";
|
|
|
76
75
|
var DEFAULT_RELEASES_BYTES = C.BYTES.mib(8); // GitHub releases JSON ~hundreds of KB; 8 MiB caps a malicious response
|
|
77
76
|
|
|
78
77
|
function _safeAuditEmit(action, outcome, metadata) {
|
|
79
|
-
|
|
80
|
-
audit().safeEmit({
|
|
81
|
-
action: action,
|
|
82
|
-
outcome: outcome || "success",
|
|
83
|
-
metadata: metadata || {},
|
|
84
|
-
});
|
|
85
|
-
} catch (_e) { /* drop-silent — by design */ }
|
|
78
|
+
auditEmit.emit(action, metadata, outcome);
|
|
86
79
|
}
|
|
87
80
|
|
|
88
81
|
// ---- semver-shaped comparison (tag_name like "v0.7.30" or "0.7.30") ----
|
|
@@ -218,44 +211,66 @@ function _compareTags(a, b) {
|
|
|
218
211
|
// ---- poll ----
|
|
219
212
|
|
|
220
213
|
function _validatePollOpts(opts) {
|
|
221
|
-
validateOpts.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
214
|
+
validateOpts.shape(opts, {
|
|
215
|
+
releasesUrl: function (value) {
|
|
216
|
+
validateOpts.requireNonEmptyString(value,
|
|
217
|
+
"selfUpdate.poll: opts.releasesUrl", SelfUpdateError, "selfupdate/bad-releases-url");
|
|
218
|
+
// Scheme enforcement at config-time so the bug surfaces here, not
|
|
219
|
+
// inside the request loop. Default policy: https only. Operators
|
|
220
|
+
// wiring against an internal mirror can pass allowedProtocols
|
|
221
|
+
// explicitly to opt in to http (e.g. a TLS-terminating proxy
|
|
222
|
+
// upstream of the framework process). The full SSRF / hostname /
|
|
223
|
+
// length policy still runs inside httpClient.request.
|
|
224
|
+
var parsedProto;
|
|
225
|
+
try { parsedProto = new NodeUrl(value).protocol; }
|
|
226
|
+
catch (_e) {
|
|
227
|
+
throw new SelfUpdateError("selfupdate/bad-releases-url",
|
|
228
|
+
"selfUpdate.poll: opts.releasesUrl is not parseable as a URL");
|
|
229
|
+
}
|
|
230
|
+
var allowedProtocols = Array.isArray(opts.allowedProtocols) && opts.allowedProtocols.length > 0
|
|
231
|
+
? opts.allowedProtocols.slice() : ["https:"];
|
|
232
|
+
if (allowedProtocols.indexOf(parsedProto) === -1) {
|
|
233
|
+
throw new SelfUpdateError("selfupdate/bad-releases-url",
|
|
234
|
+
"selfUpdate.poll: opts.releasesUrl protocol '" + parsedProto +
|
|
235
|
+
"' not in allowedProtocols [" + allowedProtocols.join(", ") + "]");
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
currentVersion: { rule: "required-string", code: "selfupdate/bad-current-version",
|
|
239
|
+
label: "selfUpdate.poll: opts.currentVersion" },
|
|
240
|
+
assetPattern: function (value) {
|
|
241
|
+
if (value !== undefined && !(value instanceof RegExp) && typeof value !== "string") {
|
|
242
|
+
throw new SelfUpdateError("selfupdate/bad-asset-pattern",
|
|
243
|
+
"selfUpdate.poll: opts.assetPattern must be a RegExp or string when present");
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
signaturePattern: function (value) {
|
|
247
|
+
if (value !== undefined && !(value instanceof RegExp) && typeof value !== "string") {
|
|
248
|
+
throw new SelfUpdateError("selfupdate/bad-sig-pattern",
|
|
249
|
+
"selfUpdate.poll: opts.signaturePattern must be a RegExp or string when present");
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
maxBytes: function (value) {
|
|
253
|
+
numericBounds.requirePositiveFiniteIntIfPresent(value,
|
|
254
|
+
"selfUpdate.poll: opts.maxBytes", SelfUpdateError, "selfupdate/bad-max-bytes");
|
|
255
|
+
},
|
|
256
|
+
timeoutMs: function (value) {
|
|
257
|
+
numericBounds.requirePositiveFiniteIntIfPresent(value,
|
|
258
|
+
"selfUpdate.poll: opts.timeoutMs", SelfUpdateError, "selfupdate/bad-timeout");
|
|
259
|
+
},
|
|
260
|
+
// allowedProtocols is consumed locally (the releasesUrl scheme gate
|
|
261
|
+
// above reads it) and also forwarded to httpClient.request.
|
|
262
|
+
allowedProtocols: { rule: "optional-string-array", code: "selfupdate/bad-allowed-protocols",
|
|
263
|
+
label: "selfUpdate.poll: opts.allowedProtocols" },
|
|
264
|
+
// headers is merged onto the outbound request headers locally.
|
|
265
|
+
headers: { rule: "optional-plain-object", code: "selfupdate/bad-headers",
|
|
266
|
+
label: "selfUpdate.poll: opts.headers" },
|
|
267
|
+
// etag is used locally for the If-None-Match request header.
|
|
268
|
+
etag: { rule: "optional-string", code: "selfupdate/bad-etag",
|
|
269
|
+
label: "selfUpdate.poll: opts.etag" },
|
|
270
|
+
// allowedHosts / allowInternal are forwarded verbatim to
|
|
271
|
+
// httpClient.request, which owns their SSRF-gate validation.
|
|
272
|
+
}, "selfUpdate.poll", SelfUpdateError, "selfupdate/bad-opts",
|
|
273
|
+
{ allow: ["allowedHosts", "allowInternal"] });
|
|
259
274
|
}
|
|
260
275
|
|
|
261
276
|
function _matchAsset(name, pattern, fallback) {
|
|
@@ -461,21 +476,25 @@ async function poll(opts) {
|
|
|
461
476
|
// ---- verify ----
|
|
462
477
|
|
|
463
478
|
function _validateVerifyOpts(opts) {
|
|
464
|
-
validateOpts.
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
+
validateOpts.shape(opts, {
|
|
480
|
+
assetPath: { rule: "required-string", code: "selfupdate/bad-asset-path",
|
|
481
|
+
label: "selfUpdate.verify: opts.assetPath" },
|
|
482
|
+
signaturePath: { rule: "required-string", code: "selfupdate/bad-signature-path",
|
|
483
|
+
label: "selfUpdate.verify: opts.signaturePath" },
|
|
484
|
+
pubkeyPem: { rule: "required-string", code: "selfupdate/bad-pubkey",
|
|
485
|
+
label: "selfUpdate.verify: opts.pubkeyPem (PEM-encoded public key)" },
|
|
486
|
+
hashAlgo: function (value) {
|
|
487
|
+
if (value !== undefined &&
|
|
488
|
+
(typeof value !== "string" || ALLOWED_HASH_ALGS.indexOf(value) === -1)) {
|
|
489
|
+
throw new SelfUpdateError("selfupdate/bad-hash-algo",
|
|
490
|
+
"selfUpdate.verify: opts.hashAlgo must be one of " + ALLOWED_HASH_ALGS.join(", "));
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
maxBytes: function (value) {
|
|
494
|
+
numericBounds.requirePositiveFiniteIntIfPresent(value,
|
|
495
|
+
"selfUpdate.verify: opts.maxBytes", SelfUpdateError, "selfupdate/bad-max-bytes");
|
|
496
|
+
},
|
|
497
|
+
}, "selfUpdate.verify", SelfUpdateError, "selfupdate/bad-opts");
|
|
479
498
|
}
|
|
480
499
|
|
|
481
500
|
/**
|
|
@@ -565,15 +584,19 @@ async function verify(opts) {
|
|
|
565
584
|
// ---- swap ----
|
|
566
585
|
|
|
567
586
|
function _validateSwapOpts(opts, label) {
|
|
568
|
-
|
|
587
|
+
// requireObject runs first (shape does it) so an opts-shape error keeps
|
|
588
|
+
// the selfupdate/bad-opts code. `from` is validated only for swap, and
|
|
589
|
+
// first to preserve the original field-evaluation order.
|
|
590
|
+
var schema = {};
|
|
569
591
|
if (label === "swap") {
|
|
570
|
-
|
|
571
|
-
|
|
592
|
+
schema.from = { rule: "required-string", code: "selfupdate/bad-from",
|
|
593
|
+
label: "selfUpdate.swap: opts.from" };
|
|
572
594
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
595
|
+
schema.to = { rule: "required-string", code: "selfupdate/bad-to",
|
|
596
|
+
label: "selfUpdate." + label + ": opts.to" };
|
|
597
|
+
schema.backupTo = { rule: "required-string", code: "selfupdate/bad-backup",
|
|
598
|
+
label: "selfUpdate." + label + ": opts.backupTo" };
|
|
599
|
+
validateOpts.shape(opts, schema, "selfUpdate." + label, SelfUpdateError, "selfupdate/bad-opts");
|
|
577
600
|
}
|
|
578
601
|
|
|
579
602
|
// _safeRollback — best-effort restore of `to` from `backupTo` during
|