@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/mcp.js
CHANGED
|
@@ -152,7 +152,7 @@ function refuse(res, code, message, id) {
|
|
|
152
152
|
function _readBearer(req) {
|
|
153
153
|
var h = req.headers && req.headers.authorization;
|
|
154
154
|
if (typeof h !== "string") return null;
|
|
155
|
-
if (h
|
|
155
|
+
if (safeBuffer.byteLengthOf(h) > C.BYTES.kib(8)) return null;
|
|
156
156
|
var m = /^Bearer\s+([A-Za-z0-9._~+/=-]+)$/.exec(h.trim());
|
|
157
157
|
return m ? m[1] : null;
|
|
158
158
|
}
|
|
@@ -636,10 +636,12 @@ function _validateValueAgainstSchema(value, schema, path) {
|
|
|
636
636
|
}
|
|
637
637
|
if (typeof schema.pattern === "string") {
|
|
638
638
|
// Schema-supplied pattern — operator-controlled at registration
|
|
639
|
-
// time, not request-controlled. Cap
|
|
640
|
-
// codebase-patterns regex-bound rule so a
|
|
641
|
-
//
|
|
642
|
-
|
|
639
|
+
// time, not request-controlled. Cap the input LENGTH first per the
|
|
640
|
+
// codebase-patterns regex-bound rule so a huge string can't ReDoS the
|
|
641
|
+
// validator. This is a CHARACTER cap, not a byte cap: regex matching
|
|
642
|
+
// cost scales with the number of code units the engine scans, so 4096
|
|
643
|
+
// chars is the correct ReDoS bound regardless of UTF-8 byte size.
|
|
644
|
+
if (value.length > 4096) return path + ": value exceeds 4096-char cap before regex test"; // ReDoS char cap (not bytes)
|
|
643
645
|
try {
|
|
644
646
|
var pat = new RegExp(schema.pattern); // allow:dynamic-regex — schema.pattern from registered tool author, not request input; bounded above
|
|
645
647
|
if (!pat.test(value)) return path + ": does not match pattern";
|
package/lib/mdoc.js
CHANGED
|
@@ -53,6 +53,8 @@ var C = require("./constants");
|
|
|
53
53
|
var cbor = require("./cbor");
|
|
54
54
|
var cose = require("./cose");
|
|
55
55
|
var bCrypto = require("./crypto");
|
|
56
|
+
var x509Chain = require("./x509-chain");
|
|
57
|
+
var safeBuffer = require("./safe-buffer");
|
|
56
58
|
var validateOpts = require("./validate-opts");
|
|
57
59
|
var { defineClass } = require("./framework-error");
|
|
58
60
|
|
|
@@ -65,11 +67,14 @@ var TAG_ENCODED_CBOR = 24; // RFC 8949 §3.4.5.1 emb
|
|
|
65
67
|
var ALLOWED_TAGS = [0, 1, TAG_ENCODED_CBOR, 1004];
|
|
66
68
|
var DIGEST_ALGS = { "SHA-256": "sha256", "SHA-384": "sha384", "SHA-512": "sha512" };
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
// mdoc inputs are CBOR byte strings, never text (allowString:false).
|
|
71
|
+
var _bytes = safeBuffer.makeByteCoercer({
|
|
72
|
+
errorClass: MdocError,
|
|
73
|
+
typeCode: "mdoc/bad-input",
|
|
74
|
+
messagePrefix: "mdoc: ",
|
|
75
|
+
messageSuffix: " must be a Buffer / Uint8Array of CBOR",
|
|
76
|
+
allowString: false,
|
|
77
|
+
});
|
|
73
78
|
|
|
74
79
|
// validityInfo dates are tdate (Tag 0, an RFC 3339 string) or epoch
|
|
75
80
|
// (Tag 1). Returns epoch-ms; fails closed on a malformed value.
|
|
@@ -124,17 +129,28 @@ function _mapGet(m, k) { return m instanceof Map ? m.get(k) : (m ? m[k] : undefi
|
|
|
124
129
|
*/
|
|
125
130
|
async function verifyIssuerSigned(issuerSigned, opts) {
|
|
126
131
|
validateOpts.requireObject(opts, "mdoc.verifyIssuerSigned", MdocError);
|
|
127
|
-
validateOpts(opts, ["algorithms", "trustAnchorsPem", "expectedDocType", "at", "maxBytes", "maxDepth"], "mdoc.verifyIssuerSigned");
|
|
132
|
+
validateOpts(opts, ["algorithms", "trustAnchorsPem", "allowUntrustedIssuer", "expectedDocType", "at", "maxBytes", "maxDepth"], "mdoc.verifyIssuerSigned");
|
|
128
133
|
if (!Array.isArray(opts.algorithms) || opts.algorithms.length === 0) {
|
|
129
134
|
throw new MdocError("mdoc/algorithms-required", "mdoc.verifyIssuerSigned: opts.algorithms is required");
|
|
130
135
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
// Issuer authentication is the WHOLE point of verifyIssuerSigned. The signer
|
|
137
|
+
// cert rides in the attacker-supplied x5chain, so verifying the COSE_Sign1
|
|
138
|
+
// against that embedded key only proves the MSO is self-consistent — a forged
|
|
139
|
+
// mDL (attacker keypair, self-signed leaf, self-signed MSO) passes unless the
|
|
140
|
+
// chain is anchored to a configured trust root. Require trustAnchorsPem by
|
|
141
|
+
// default; an operator who genuinely wants trust-anchor-free verification must
|
|
142
|
+
// opt in EXPLICITLY (and gets issuerTrusted:false on the result so the
|
|
143
|
+
// unauthenticated posture is visible). Fail closed — never silently accept an
|
|
144
|
+
// unanchored issuer.
|
|
145
|
+
var hasAnchors = opts.trustAnchorsPem !== undefined && opts.trustAnchorsPem !== null;
|
|
146
|
+
if (!hasAnchors && opts.allowUntrustedIssuer !== true) {
|
|
147
|
+
throw new MdocError("mdoc/trust-anchors-required",
|
|
148
|
+
"mdoc.verifyIssuerSigned: opts.trustAnchorsPem is required to authenticate the issuer " +
|
|
149
|
+
"(the x5chain signer cert is attacker-controlled); pass trustAnchorsPem, or set " +
|
|
150
|
+
"allowUntrustedIssuer:true to explicitly accept an unauthenticated issuer");
|
|
137
151
|
}
|
|
152
|
+
validateOpts.optionalDate(opts.at, "mdoc.verifyIssuerSigned: opts.at", MdocError, "mdoc/bad-at");
|
|
153
|
+
var at = (opts.at !== undefined && opts.at !== null) ? opts.at : new Date();
|
|
138
154
|
var decodeOpts = { allowedTags: ALLOWED_TAGS, maxBytes: opts.maxBytes, maxDepth: opts.maxDepth };
|
|
139
155
|
|
|
140
156
|
var top = cbor.decode(_bytes(issuerSigned, "issuerSigned"), decodeOpts);
|
|
@@ -265,6 +281,10 @@ async function verifyIssuerSigned(issuerSigned, opts) {
|
|
|
265
281
|
deviceKey: deviceKey,
|
|
266
282
|
signerCert: signerCert.toString(),
|
|
267
283
|
alg: verified.alg,
|
|
284
|
+
// true only when the signer chain was validated against a configured trust
|
|
285
|
+
// anchor; false on the explicit allowUntrustedIssuer opt-out so a caller
|
|
286
|
+
// can tell an authenticated issuer from a merely self-consistent MSO.
|
|
287
|
+
issuerTrusted: hasAnchors,
|
|
268
288
|
};
|
|
269
289
|
}
|
|
270
290
|
|
|
@@ -410,8 +430,10 @@ function _verifyChain(chainDer, anchorsPem, at) {
|
|
|
410
430
|
throw new MdocError("mdoc/chain-loop", "mdoc.verifyIssuerSigned: certificate chain did not terminate");
|
|
411
431
|
}
|
|
412
432
|
function _issued(issuer, subject) {
|
|
413
|
-
|
|
414
|
-
|
|
433
|
+
// Enforces basicConstraints cA:TRUE on the issuer alongside the
|
|
434
|
+
// checkIssued + signature linkage — an IACA/DS chain cannot accept a
|
|
435
|
+
// non-CA cert as an issuer (basicConstraints bypass, CVE-2002-0862 class).
|
|
436
|
+
return x509Chain.issuerValidlyIssued(issuer, subject);
|
|
415
437
|
}
|
|
416
438
|
function _assertValidAt(cert, atMs) {
|
|
417
439
|
if (atMs < cert.validFromDate.getTime() || atMs > cert.validToDate.getTime()) {
|
package/lib/metrics.js
CHANGED
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
|
|
40
40
|
var C = require("./constants");
|
|
41
41
|
var canonicalJson = require("./canonical-json");
|
|
42
|
-
var nodeFs = require("node:fs");
|
|
43
42
|
var atomicFile = require("./atomic-file");
|
|
44
43
|
var safeJson = require("./safe-json");
|
|
45
44
|
var { defineClass } = require("./framework-error");
|
|
@@ -48,6 +47,7 @@ var numericBounds = require("./numeric-bounds");
|
|
|
48
47
|
var requestHelpers = require("./request-helpers");
|
|
49
48
|
var { resolveRoute, captureResponseStatus, HTTP_STATUS } = requestHelpers;
|
|
50
49
|
var validateOpts = require("./validate-opts");
|
|
50
|
+
var boundedMap = require("./bounded-map");
|
|
51
51
|
|
|
52
52
|
var MetricsError = defineClass("MetricsError", { alwaysPermanent: true });
|
|
53
53
|
var log = boot("metrics");
|
|
@@ -353,10 +353,10 @@ function create(opts) {
|
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
function _registerMetric(metric) {
|
|
356
|
-
|
|
356
|
+
boundedMap.requireAbsent(metrics, metric.name, function () {
|
|
357
357
|
throw new MetricsError("metrics/duplicate",
|
|
358
358
|
"metric '" + metric.name + "' already registered");
|
|
359
|
-
}
|
|
359
|
+
});
|
|
360
360
|
metrics.set(metric.name, metric);
|
|
361
361
|
return metric;
|
|
362
362
|
}
|
|
@@ -388,19 +388,20 @@ function create(opts) {
|
|
|
388
388
|
}
|
|
389
389
|
var resolved = _resolveLabels(defaultLabels, labelNames, arg.labels);
|
|
390
390
|
var key = _labelsKey(resolved);
|
|
391
|
-
var entry =
|
|
392
|
-
|
|
393
|
-
|
|
391
|
+
var entry = boundedMap.getOrInsert(values, key, function () {
|
|
392
|
+
return { labels: resolved, value: 0 };
|
|
393
|
+
}, {
|
|
394
|
+
maxSize: cardinalityCap,
|
|
395
|
+
onFull: function () {
|
|
394
396
|
if (!capWarned) {
|
|
395
397
|
log("metric '" + fullName + "' hit labelCardinalityCap (" + cardinalityCap +
|
|
396
398
|
") — dropping new label combinations. Reduce label cardinality or raise the cap.");
|
|
397
399
|
capWarned = true;
|
|
398
400
|
}
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
401
|
+
return null;
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
if (!entry) return;
|
|
404
405
|
entry.value += arg.value;
|
|
405
406
|
},
|
|
406
407
|
reset: function () { values.clear(); capWarned = false; },
|
|
@@ -428,19 +429,18 @@ function create(opts) {
|
|
|
428
429
|
function _ensure(callLabels) {
|
|
429
430
|
var resolved = _resolveLabels(defaultLabels, labelNames, callLabels);
|
|
430
431
|
var key = _labelsKey(resolved);
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
432
|
+
return boundedMap.getOrInsert(values, key, function () {
|
|
433
|
+
return { labels: resolved, value: 0 };
|
|
434
|
+
}, {
|
|
435
|
+
maxSize: cardinalityCap,
|
|
436
|
+
onFull: function () {
|
|
434
437
|
if (!capWarned) {
|
|
435
438
|
log("metric '" + fullName + "' hit labelCardinalityCap (" + cardinalityCap + ")");
|
|
436
439
|
capWarned = true;
|
|
437
440
|
}
|
|
438
441
|
return null;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
values.set(key, entry);
|
|
442
|
-
}
|
|
443
|
-
return entry;
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
444
|
}
|
|
445
445
|
|
|
446
446
|
var instance = {
|
|
@@ -524,25 +524,26 @@ function create(opts) {
|
|
|
524
524
|
}
|
|
525
525
|
var resolved = _resolveLabels(defaultLabels, labelNames, arg.labels);
|
|
526
526
|
var key = _labelsKey(resolved);
|
|
527
|
-
var entry =
|
|
528
|
-
if (!entry) {
|
|
529
|
-
if (values.size >= cardinalityCap) {
|
|
530
|
-
if (!capWarned) {
|
|
531
|
-
log("metric '" + fullName + "' hit labelCardinalityCap (" + cardinalityCap + ")");
|
|
532
|
-
capWarned = true;
|
|
533
|
-
}
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
527
|
+
var entry = boundedMap.getOrInsert(values, key, function () {
|
|
536
528
|
// counts[i] is the count for the [<=buckets[i]] bucket; counts[buckets.length] is +Inf.
|
|
537
|
-
|
|
529
|
+
return {
|
|
538
530
|
labels: resolved,
|
|
539
531
|
counts: new Array(buckets.length + 1).fill(0),
|
|
540
532
|
sum: 0,
|
|
541
533
|
count: 0,
|
|
542
534
|
exemplars: new Array(buckets.length + 1).fill(null),
|
|
543
535
|
};
|
|
544
|
-
|
|
545
|
-
|
|
536
|
+
}, {
|
|
537
|
+
maxSize: cardinalityCap,
|
|
538
|
+
onFull: function () {
|
|
539
|
+
if (!capWarned) {
|
|
540
|
+
log("metric '" + fullName + "' hit labelCardinalityCap (" + cardinalityCap + ")");
|
|
541
|
+
capWarned = true;
|
|
542
|
+
}
|
|
543
|
+
return null;
|
|
544
|
+
},
|
|
545
|
+
});
|
|
546
|
+
if (!entry) return;
|
|
546
547
|
for (var i = 0; i < buckets.length; i++) {
|
|
547
548
|
if (arg.value <= buckets[i]) {
|
|
548
549
|
entry.counts[i]++;
|
|
@@ -1065,8 +1066,20 @@ function snapshotRead(p) {
|
|
|
1065
1066
|
MetricsError, "metrics-snapshot/bad-path");
|
|
1066
1067
|
var raw;
|
|
1067
1068
|
try {
|
|
1068
|
-
|
|
1069
|
+
// Capped fd-bound read: the snapshot is parsed AFTER read, so the cap must
|
|
1070
|
+
// precede the alloc — a hostile multi-GB file at the snapshot path would
|
|
1071
|
+
// otherwise OOM the reader before safeJson's 4 MiB parse cap is consulted.
|
|
1072
|
+
raw = atomicFile.fdSafeReadSync(p, {
|
|
1073
|
+
maxBytes: C.BYTES.mib(4), encoding: "utf8", refuseSymlink: true,
|
|
1074
|
+
errorFor: function (kind, detail) {
|
|
1075
|
+
if (kind === "enoent") return new MetricsError("metrics-snapshot/not-found", "metrics.snapshot.read: " + p + " — not found");
|
|
1076
|
+
if (kind === "too-large") return new MetricsError("metrics-snapshot/too-large", "metrics.snapshot.read: " + p + " exceeds " + detail.max + " bytes");
|
|
1077
|
+
if (kind === "symlink") return new MetricsError("metrics-snapshot/symlink-refused", "metrics.snapshot.read: " + p + " is a symlink (refused)");
|
|
1078
|
+
return undefined;
|
|
1079
|
+
},
|
|
1080
|
+
});
|
|
1069
1081
|
} catch (e) {
|
|
1082
|
+
if (e instanceof MetricsError) throw e;
|
|
1070
1083
|
throw new MetricsError("metrics-snapshot/not-found",
|
|
1071
1084
|
"metrics.snapshot.read: " + p + " — " + (e && e.message ? e.message : String(e)));
|
|
1072
1085
|
}
|
|
@@ -1327,7 +1340,7 @@ function shadowRegistry(opts) {
|
|
|
1327
1340
|
var gaugeSet = _shadowSetOf(opts.gauges, "gauges");
|
|
1328
1341
|
var infoSet = _shadowSetOf(opts.info, "info");
|
|
1329
1342
|
var cap = opts.cardinalityCap === undefined ? SHADOW_DEFAULT_CARDINALITY : opts.cardinalityCap;
|
|
1330
|
-
if (
|
|
1343
|
+
if (!numericBounds.isPositiveFiniteInt(cap)) {
|
|
1331
1344
|
throw new MetricsError("metrics-shadow/bad-cap",
|
|
1332
1345
|
"shadowRegistry: cardinalityCap must be a positive integer");
|
|
1333
1346
|
}
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
var defineClass = require("../framework-error").defineClass;
|
|
39
39
|
var lazyRequire = require("../lazy-require");
|
|
40
40
|
var validateOpts = require("../validate-opts");
|
|
41
|
+
var requestHelpers = require("../request-helpers");
|
|
41
42
|
var denyResponse = require("./deny-response").denyResponse;
|
|
42
43
|
|
|
43
44
|
var audit = lazyRequire(function () { return require("../audit"); });
|
|
@@ -106,7 +107,6 @@ function create(opts) {
|
|
|
106
107
|
? opts.consentRequired : null;
|
|
107
108
|
var hasParentalConsent = typeof opts.hasParentalConsent === "function" ? opts.hasParentalConsent : null;
|
|
108
109
|
var skipPaths = Array.isArray(opts.skipPaths) ? opts.skipPaths.slice() : [];
|
|
109
|
-
var auditOn = opts.audit !== false;
|
|
110
110
|
var errorMessage = typeof opts.errorMessage === "string" && opts.errorMessage.length > 0
|
|
111
111
|
? opts.errorMessage : "service unavailable without parental consent";
|
|
112
112
|
var onDeny = typeof opts.onDeny === "function" ? opts.onDeny : null;
|
|
@@ -123,29 +123,9 @@ function create(opts) {
|
|
|
123
123
|
privacyPostureHeader = "X-Privacy-Posture";
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
if (skipPaths.length === 0) return false;
|
|
128
|
-
var p = req.url || "";
|
|
129
|
-
var qpos = p.indexOf("?");
|
|
130
|
-
if (qpos !== -1) p = p.slice(0, qpos);
|
|
131
|
-
for (var i = 0; i < skipPaths.length; i++) {
|
|
132
|
-
var s = skipPaths[i];
|
|
133
|
-
if (typeof s === "string" && (p === s || p.indexOf(s + "/") === 0)) return true;
|
|
134
|
-
if (s instanceof RegExp && s.test(p)) return true;
|
|
135
|
-
}
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
126
|
+
var _shouldSkip = requestHelpers.makeSkipMatcher({ skipPaths: skipPaths }, "middleware.ageGate");
|
|
138
127
|
|
|
139
|
-
|
|
140
|
-
if (!auditOn) return;
|
|
141
|
-
try {
|
|
142
|
-
audit().safeEmit({
|
|
143
|
-
action: "middleware.age_gate." + action,
|
|
144
|
-
outcome: outcome,
|
|
145
|
-
metadata: metadata || {},
|
|
146
|
-
});
|
|
147
|
-
} catch (_e) { /* drop-silent */ }
|
|
148
|
-
}
|
|
128
|
+
var _emitAudit = audit().namespaced("middleware.age_gate", opts.audit);
|
|
149
129
|
|
|
150
130
|
return function ageGateMiddleware(req, res, next) {
|
|
151
131
|
if (_shouldSkip(req)) return next();
|
|
@@ -98,8 +98,9 @@
|
|
|
98
98
|
|
|
99
99
|
var bCrypto = require("../crypto");
|
|
100
100
|
var C = require("../constants");
|
|
101
|
+
var numericBounds = require("../numeric-bounds");
|
|
101
102
|
var lazyRequire = require("../lazy-require");
|
|
102
|
-
var
|
|
103
|
+
var nonceStore = require("../nonce-store");
|
|
103
104
|
var requestHelpers = require("../request-helpers");
|
|
104
105
|
var safeJson = require("../safe-json");
|
|
105
106
|
var validateOpts = require("../validate-opts");
|
|
@@ -313,7 +314,7 @@ function create(opts) {
|
|
|
313
314
|
"apiEncrypt: pruneIntervalMs", ApiEncryptError, "BAD_OPT");
|
|
314
315
|
var pruneIntervalMs = opts.pruneIntervalMs != null
|
|
315
316
|
? opts.pruneIntervalMs : Math.max(C.TIME.seconds(30), Math.floor(replayWindowMs / 2));
|
|
316
|
-
var
|
|
317
|
+
var store = opts.nonceStore || nonceStore.create({ backend: "memory" });
|
|
317
318
|
var exemptPaths = Array.isArray(opts.exemptPaths) ? opts.exemptPaths.slice() : [];
|
|
318
319
|
// contentTypes scoping — middleware only operates on requests whose
|
|
319
320
|
// Content-Type is in this list. Default JSON; operators with more
|
|
@@ -344,12 +345,9 @@ function create(opts) {
|
|
|
344
345
|
"apiEncrypt: sessionTtlMs must be a positive finite number (ms), got " +
|
|
345
346
|
JSON.stringify(opts.sessionTtlMs), 500);
|
|
346
347
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
"apiEncrypt: sessionMaxResponses must be a positive finite integer, got " +
|
|
351
|
-
JSON.stringify(opts.sessionMaxResponses), 500);
|
|
352
|
-
}
|
|
348
|
+
numericBounds.requirePositiveFiniteInt(sessionMaxResponses,
|
|
349
|
+
"apiEncrypt: sessionMaxResponses", ApiEncryptError, "BAD_OPT", null,
|
|
350
|
+
{ permanent: true, statusCode: 500 });
|
|
353
351
|
// sessionStore — duck-typed handle exposing { get, set, delete }. The
|
|
354
352
|
// helper optionalObjectWithMethod only checks one method; here we need
|
|
355
353
|
// three. Inline shape kept; not a generic enough pattern to warrant a
|
|
@@ -393,16 +391,7 @@ function create(opts) {
|
|
|
393
391
|
} catch (_e) { /* audit best-effort */ }
|
|
394
392
|
}
|
|
395
393
|
|
|
396
|
-
|
|
397
|
-
var p = req.pathname || (req.url || "/").split("?")[0];
|
|
398
|
-
for (var i = 0; i < exemptPaths.length; i++) {
|
|
399
|
-
var rule = exemptPaths[i];
|
|
400
|
-
if (typeof rule === "string" ? p === rule || p.indexOf(rule + "/") === 0 : rule.test(p)) {
|
|
401
|
-
return true;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
return false;
|
|
405
|
-
}
|
|
394
|
+
var _isExempt = requestHelpers.makeSkipMatcher({ skipPaths: exemptPaths }, "middleware.apiEncrypt");
|
|
406
395
|
|
|
407
396
|
function _matchesContentType(req) {
|
|
408
397
|
if (!contentTypes) return true; // filtering disabled
|
|
@@ -443,7 +432,7 @@ function create(opts) {
|
|
|
443
432
|
var now = Date.now();
|
|
444
433
|
if (now - lastPruneAt < pruneIntervalMs) return;
|
|
445
434
|
lastPruneAt = now;
|
|
446
|
-
|
|
435
|
+
store.purgeExpired().catch(function (e) {
|
|
447
436
|
try {
|
|
448
437
|
logger().warn("nonce-store prune failed: " + ((e && e.message) || String(e)));
|
|
449
438
|
} catch (_e) { /* logger best-effort */ }
|
|
@@ -548,7 +537,7 @@ function create(opts) {
|
|
|
548
537
|
var nonceHash = bCrypto.sha3Hash(nonce, "hex");
|
|
549
538
|
var expireAt = now + replayWindowMs;
|
|
550
539
|
var freshNonce;
|
|
551
|
-
try { freshNonce = await
|
|
540
|
+
try { freshNonce = await store.checkAndInsert(nonceHash, expireAt); }
|
|
552
541
|
catch (_e) {
|
|
553
542
|
_emitFailure(req, "nonce-store-error");
|
|
554
543
|
return _writeRejection(res, HTTP_STATUS.INTERNAL_SERVER_ERROR, { error: "nonce-store-unavailable" });
|
|
@@ -567,7 +556,7 @@ function create(opts) {
|
|
|
567
556
|
_emitFailure(req, "shape");
|
|
568
557
|
return _writeRejection(res, HTTP_STATUS.BAD_REQUEST, { error: "encrypted-payload-required" });
|
|
569
558
|
}
|
|
570
|
-
if (
|
|
559
|
+
if (!numericBounds.isNonNegativeFiniteInt(ctr)) {
|
|
571
560
|
_emitFailure(req, "shape");
|
|
572
561
|
return _writeRejection(res, HTTP_STATUS.BAD_REQUEST, { error: "encrypted-payload-required" });
|
|
573
562
|
}
|
|
@@ -607,7 +596,7 @@ function create(opts) {
|
|
|
607
596
|
_emitFailure(req, "shape");
|
|
608
597
|
return _writeRejection(res, HTTP_STATUS.BAD_REQUEST, { error: "encrypted-payload-required" });
|
|
609
598
|
}
|
|
610
|
-
if (!
|
|
599
|
+
if (!numericBounds.isNonNegativeFiniteInt(ctr)) {
|
|
611
600
|
_emitFailure(req, "shape");
|
|
612
601
|
return _writeRejection(res, HTTP_STATUS.BAD_REQUEST, { error: "encrypted-payload-required" });
|
|
613
602
|
}
|
|
@@ -682,7 +671,7 @@ function create(opts) {
|
|
|
682
671
|
// capacity.
|
|
683
672
|
var ctrKey = "ctr:" + sid + ":" + ctr;
|
|
684
673
|
var ctrFresh;
|
|
685
|
-
try { ctrFresh = await
|
|
674
|
+
try { ctrFresh = await store.checkAndInsert(ctrKey, session.expiresAt); }
|
|
686
675
|
catch (_e) {
|
|
687
676
|
_emitFailure(req, "nonce-store-error");
|
|
688
677
|
return _writeRejection(res, HTTP_STATUS.INTERNAL_SERVER_ERROR, { error: "nonce-store-unavailable" });
|
|
@@ -779,7 +768,7 @@ function create(opts) {
|
|
|
779
768
|
|
|
780
769
|
middleware.publishPublicKey = publishPublicKey;
|
|
781
770
|
middleware.close = function () {
|
|
782
|
-
if (typeof
|
|
771
|
+
if (typeof store.close === "function") store.close();
|
|
783
772
|
if (sessionStore && typeof sessionStore.close === "function") sessionStore.close();
|
|
784
773
|
};
|
|
785
774
|
// Expose for tests / operator dashboards. Counts are 0 in per-request mode.
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
var lazyRequire = require("../lazy-require");
|
|
29
29
|
var safeJson = require("../safe-json");
|
|
30
30
|
var validateOpts = require("../validate-opts");
|
|
31
|
+
var denyResponse = require("./deny-response");
|
|
31
32
|
var { defineClass } = require("../framework-error");
|
|
32
33
|
|
|
33
34
|
var AssetlinksError = defineClass("AssetlinksError", { alwaysPermanent: true });
|
|
@@ -108,13 +109,7 @@ function create(opts) {
|
|
|
108
109
|
var path = qIdx === -1 ? url : url.slice(0, qIdx);
|
|
109
110
|
if (path !== "/.well-known/assetlinks.json") return next();
|
|
110
111
|
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
111
|
-
|
|
112
|
-
res.writeHead(405, { // HTTP 405 status
|
|
113
|
-
"Allow": "GET, HEAD",
|
|
114
|
-
"Content-Type": "text/plain; charset=utf-8",
|
|
115
|
-
"Content-Length": Buffer.byteLength(bodyMsg),
|
|
116
|
-
});
|
|
117
|
-
res.end(bodyMsg);
|
|
112
|
+
denyResponse.methodNotAllowed(res, "GET, HEAD");
|
|
118
113
|
return;
|
|
119
114
|
}
|
|
120
115
|
res.writeHead(200, { // HTTP 200 status
|
|
@@ -38,6 +38,7 @@ var lazyRequire = require("../lazy-require");
|
|
|
38
38
|
var requestHelpers = require("../request-helpers");
|
|
39
39
|
var validateOpts = require("../validate-opts");
|
|
40
40
|
var denyResponse = require("./deny-response").denyResponse;
|
|
41
|
+
var codepointClass = require("../codepoint-class");
|
|
41
42
|
var { AuthError } = require("../framework-error");
|
|
42
43
|
|
|
43
44
|
var audit = lazyRequire(function () { return require("../audit"); });
|
|
@@ -164,7 +165,7 @@ function create(opts) {
|
|
|
164
165
|
}
|
|
165
166
|
for (var ri = 0; ri < realm.length; ri += 1) {
|
|
166
167
|
var rcode = realm.charCodeAt(ri);
|
|
167
|
-
if (rcode
|
|
168
|
+
if (codepointClass.isForbiddenControlChar(rcode, { forbidTab: true })) { // ASCII control codepoints
|
|
168
169
|
throw new AuthError("auth-bearer/bad-realm",
|
|
169
170
|
"realm contains control character at index " + ri);
|
|
170
171
|
}
|