@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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// markup-tokenizer — neutral lexing helpers shared by the markup sanitizers
|
|
4
|
+
// (b.guardHtml, b.guardSvg) and the BIMI SVG Tiny PS validator (b.mail.bimi).
|
|
5
|
+
// Each of those keeps its OWN tokenizer loop because their security postures
|
|
6
|
+
// genuinely diverge — guard-html/guard-svg recover leniently from a truncated
|
|
7
|
+
// tag (emit what was scanned, keep going), while the BIMI validator fails
|
|
8
|
+
// closed (throws on any malformation), and each recognizes a different set of
|
|
9
|
+
// declaration forms (`<?`, `<!ENTITY>`, balanced `<!DOCTYPE [...]>`). What they
|
|
10
|
+
// share verbatim is the one quote-aware step below; centralizing it keeps the
|
|
11
|
+
// attribute-quote handling — the part a bypass hides in — identical everywhere.
|
|
12
|
+
|
|
13
|
+
// scanToTagEnd(s, from, len) — advance from `from` (the index just past the
|
|
14
|
+
// opening "<") to the tag's closing ">", treating a ">" that appears inside a
|
|
15
|
+
// single- or double-quoted attribute value as a literal, not a terminator
|
|
16
|
+
// (e.g. `<a title="a>b">` ends at the SECOND ">"). Returns the index of the
|
|
17
|
+
// terminating ">", or `len` if the tag is unterminated (the caller decides
|
|
18
|
+
// whether that is lenient end-of-input or a hard error).
|
|
19
|
+
function scanToTagEnd(s, from, len) {
|
|
20
|
+
var p = from;
|
|
21
|
+
var inQuote = "";
|
|
22
|
+
while (p < len) {
|
|
23
|
+
var ch = s.charAt(p);
|
|
24
|
+
if (inQuote) {
|
|
25
|
+
if (ch === inQuote) inQuote = "";
|
|
26
|
+
} else {
|
|
27
|
+
if (ch === '"' || ch === "'") inQuote = ch;
|
|
28
|
+
else if (ch === ">") break;
|
|
29
|
+
}
|
|
30
|
+
p += 1;
|
|
31
|
+
}
|
|
32
|
+
return p;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// splitTagNameAttrs(inner, tagNameRe) — given a start tag's inner text (the
|
|
36
|
+
// bytes between "<" and ">", with any trailing self-closing "/" already
|
|
37
|
+
// stripped by the caller), split it into the lower-cased `tagName` and the raw
|
|
38
|
+
// `attrSrc` remainder. `tagNameRe` is the caller's tag-name grammar (it must
|
|
39
|
+
// capture the name in group 1) — HTML allows `[A-Za-z][A-Za-z0-9:-]*`, while
|
|
40
|
+
// the XML-family grammars (SVG, BIMI SVG Tiny PS) also allow `_`. A tag whose
|
|
41
|
+
// start does not match the grammar yields an empty name + empty attrSrc (the
|
|
42
|
+
// caller treats it as a bogus tag).
|
|
43
|
+
function splitTagNameAttrs(inner, tagNameRe) {
|
|
44
|
+
var nameMatch = inner.match(tagNameRe);
|
|
45
|
+
return {
|
|
46
|
+
tagName: nameMatch ? nameMatch[1].toLowerCase() : "",
|
|
47
|
+
attrSrc: nameMatch ? inner.slice(nameMatch[0].length) : "",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
scanToTagEnd: scanToTagEnd,
|
|
53
|
+
splitTagNameAttrs: splitTagNameAttrs,
|
|
54
|
+
};
|
package/lib/mcp-tool-registry.js
CHANGED
|
@@ -65,8 +65,8 @@ var validateOpts = require("./validate-opts");
|
|
|
65
65
|
var { McpError } = require("./framework-error");
|
|
66
66
|
|
|
67
67
|
var bCrypto = lazyRequire(function () { return require("./crypto"); });
|
|
68
|
-
var
|
|
69
|
-
var C
|
|
68
|
+
var auditEmit = require("./audit-emit");
|
|
69
|
+
var C = require("./constants");
|
|
70
70
|
|
|
71
71
|
// Shared name-shape regex across mcp.js / mcp-tool-registry.js / a2a-tasks.js
|
|
72
72
|
// — every agent-protocol identifier follows the same RFC-3986-unreserved
|
|
@@ -80,15 +80,7 @@ var ALLOWED_ALGS = Object.freeze([
|
|
|
80
80
|
"slh-dsa-shake-256f",
|
|
81
81
|
]);
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
audit().safeEmit({
|
|
86
|
-
action: action,
|
|
87
|
-
outcome: outcome || "success",
|
|
88
|
-
metadata: metadata,
|
|
89
|
-
});
|
|
90
|
-
} catch (_e) { /* best-effort */ }
|
|
91
|
-
}
|
|
83
|
+
var _emitAudit = auditEmit.emit;
|
|
92
84
|
|
|
93
85
|
function _validateAlg(alg, label) {
|
|
94
86
|
if (alg === undefined || alg === null) return "ml-dsa-87";
|
|
@@ -175,20 +167,31 @@ function create(opts) {
|
|
|
175
167
|
throw new McpError("mcp/bad-registry-opts",
|
|
176
168
|
"toolRegistry.create: opts required (tools + signingKey)", true);
|
|
177
169
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
170
|
+
validateOpts.shape(opts, {
|
|
171
|
+
tools: function (value) {
|
|
172
|
+
if (!Array.isArray(value)) {
|
|
173
|
+
throw new McpError("mcp/bad-registry-opts",
|
|
174
|
+
"toolRegistry.create: opts.tools must be an array", true);
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
signingKey: { rule: "required-string", code: "mcp/bad-signing-key", label: "toolRegistry.create.signingKey" },
|
|
178
|
+
verifyingKey: { rule: "optional-string", code: "mcp/bad-verifying-key", label: "toolRegistry.create.verifyingKey" },
|
|
179
|
+
alg: function (value) {
|
|
180
|
+
// undefined → default applied below; any present value is registry-checked.
|
|
181
|
+
_validateAlg(value, "toolRegistry.create.alg");
|
|
182
|
+
},
|
|
183
|
+
ttlMs: function (value) {
|
|
184
|
+
// undefined → registry default applied below; any present value must be a
|
|
185
|
+
// finite millisecond count at or above the 1-second floor.
|
|
186
|
+
if (value === undefined) return;
|
|
187
|
+
if (typeof value !== "number" || !isFinite(value) || value < 1000) { // minimum-ttl threshold (1 second), not bytes
|
|
188
|
+
throw new McpError("mcp/bad-ttl",
|
|
189
|
+
"toolRegistry.create.ttlMs must be >= 1000 ms", true);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
}, "toolRegistry.create", McpError, "mcp/bad-registry-opts");
|
|
186
193
|
var alg = _validateAlg(opts.alg, "toolRegistry.create.alg");
|
|
187
194
|
var defaultTtlMs = opts.ttlMs !== undefined ? opts.ttlMs : C.TIME.minutes(5);
|
|
188
|
-
if (typeof defaultTtlMs !== "number" || !isFinite(defaultTtlMs) || defaultTtlMs < 1000) { // minimum-ttl threshold (1 second), not bytes
|
|
189
|
-
throw new McpError("mcp/bad-ttl",
|
|
190
|
-
"toolRegistry.create.ttlMs must be >= 1000 ms", true);
|
|
191
|
-
}
|
|
192
195
|
|
|
193
196
|
var signingKey = opts.signingKey;
|
|
194
197
|
var verifyingKey = opts.verifyingKey || null;
|
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
|
}
|
package/lib/mdoc.js
CHANGED
|
@@ -53,6 +53,7 @@ var C = require("./constants");
|
|
|
53
53
|
var cbor = require("./cbor");
|
|
54
54
|
var cose = require("./cose");
|
|
55
55
|
var bCrypto = require("./crypto");
|
|
56
|
+
var safeBuffer = require("./safe-buffer");
|
|
56
57
|
var validateOpts = require("./validate-opts");
|
|
57
58
|
var { defineClass } = require("./framework-error");
|
|
58
59
|
|
|
@@ -65,11 +66,14 @@ var TAG_ENCODED_CBOR = 24; // RFC 8949 §3.4.5.1 emb
|
|
|
65
66
|
var ALLOWED_TAGS = [0, 1, TAG_ENCODED_CBOR, 1004];
|
|
66
67
|
var DIGEST_ALGS = { "SHA-256": "sha256", "SHA-384": "sha384", "SHA-512": "sha512" };
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
// mdoc inputs are CBOR byte strings, never text (allowString:false).
|
|
70
|
+
var _bytes = safeBuffer.makeByteCoercer({
|
|
71
|
+
errorClass: MdocError,
|
|
72
|
+
typeCode: "mdoc/bad-input",
|
|
73
|
+
messagePrefix: "mdoc: ",
|
|
74
|
+
messageSuffix: " must be a Buffer / Uint8Array of CBOR",
|
|
75
|
+
allowString: false,
|
|
76
|
+
});
|
|
73
77
|
|
|
74
78
|
// validityInfo dates are tdate (Tag 0, an RFC 3339 string) or epoch
|
|
75
79
|
// (Tag 1). Returns epoch-ms; fails closed on a malformed value.
|
|
@@ -128,13 +132,8 @@ async function verifyIssuerSigned(issuerSigned, opts) {
|
|
|
128
132
|
if (!Array.isArray(opts.algorithms) || opts.algorithms.length === 0) {
|
|
129
133
|
throw new MdocError("mdoc/algorithms-required", "mdoc.verifyIssuerSigned: opts.algorithms is required");
|
|
130
134
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (!(opts.at instanceof Date) || !isFinite(opts.at.getTime())) {
|
|
134
|
-
throw new MdocError("mdoc/bad-at", "mdoc.verifyIssuerSigned: opts.at must be a valid Date");
|
|
135
|
-
}
|
|
136
|
-
at = opts.at;
|
|
137
|
-
}
|
|
135
|
+
validateOpts.optionalDate(opts.at, "mdoc.verifyIssuerSigned: opts.at", MdocError, "mdoc/bad-at");
|
|
136
|
+
var at = (opts.at !== undefined && opts.at !== null) ? opts.at : new Date();
|
|
138
137
|
var decodeOpts = { allowedTags: ALLOWED_TAGS, maxBytes: opts.maxBytes, maxDepth: opts.maxDepth };
|
|
139
138
|
|
|
140
139
|
var top = cbor.decode(_bytes(issuerSigned, "issuerSigned"), decodeOpts);
|
package/lib/metrics.js
CHANGED
|
@@ -48,6 +48,7 @@ var numericBounds = require("./numeric-bounds");
|
|
|
48
48
|
var requestHelpers = require("./request-helpers");
|
|
49
49
|
var { resolveRoute, captureResponseStatus, HTTP_STATUS } = requestHelpers;
|
|
50
50
|
var validateOpts = require("./validate-opts");
|
|
51
|
+
var boundedMap = require("./bounded-map");
|
|
51
52
|
|
|
52
53
|
var MetricsError = defineClass("MetricsError", { alwaysPermanent: true });
|
|
53
54
|
var log = boot("metrics");
|
|
@@ -353,10 +354,10 @@ function create(opts) {
|
|
|
353
354
|
}
|
|
354
355
|
|
|
355
356
|
function _registerMetric(metric) {
|
|
356
|
-
|
|
357
|
+
boundedMap.requireAbsent(metrics, metric.name, function () {
|
|
357
358
|
throw new MetricsError("metrics/duplicate",
|
|
358
359
|
"metric '" + metric.name + "' already registered");
|
|
359
|
-
}
|
|
360
|
+
});
|
|
360
361
|
metrics.set(metric.name, metric);
|
|
361
362
|
return metric;
|
|
362
363
|
}
|
|
@@ -388,19 +389,20 @@ function create(opts) {
|
|
|
388
389
|
}
|
|
389
390
|
var resolved = _resolveLabels(defaultLabels, labelNames, arg.labels);
|
|
390
391
|
var key = _labelsKey(resolved);
|
|
391
|
-
var entry =
|
|
392
|
-
|
|
393
|
-
|
|
392
|
+
var entry = boundedMap.getOrInsert(values, key, function () {
|
|
393
|
+
return { labels: resolved, value: 0 };
|
|
394
|
+
}, {
|
|
395
|
+
maxSize: cardinalityCap,
|
|
396
|
+
onFull: function () {
|
|
394
397
|
if (!capWarned) {
|
|
395
398
|
log("metric '" + fullName + "' hit labelCardinalityCap (" + cardinalityCap +
|
|
396
399
|
") — dropping new label combinations. Reduce label cardinality or raise the cap.");
|
|
397
400
|
capWarned = true;
|
|
398
401
|
}
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
402
|
+
return null;
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
if (!entry) return;
|
|
404
406
|
entry.value += arg.value;
|
|
405
407
|
},
|
|
406
408
|
reset: function () { values.clear(); capWarned = false; },
|
|
@@ -428,19 +430,18 @@ function create(opts) {
|
|
|
428
430
|
function _ensure(callLabels) {
|
|
429
431
|
var resolved = _resolveLabels(defaultLabels, labelNames, callLabels);
|
|
430
432
|
var key = _labelsKey(resolved);
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
433
|
+
return boundedMap.getOrInsert(values, key, function () {
|
|
434
|
+
return { labels: resolved, value: 0 };
|
|
435
|
+
}, {
|
|
436
|
+
maxSize: cardinalityCap,
|
|
437
|
+
onFull: function () {
|
|
434
438
|
if (!capWarned) {
|
|
435
439
|
log("metric '" + fullName + "' hit labelCardinalityCap (" + cardinalityCap + ")");
|
|
436
440
|
capWarned = true;
|
|
437
441
|
}
|
|
438
442
|
return null;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
values.set(key, entry);
|
|
442
|
-
}
|
|
443
|
-
return entry;
|
|
443
|
+
},
|
|
444
|
+
});
|
|
444
445
|
}
|
|
445
446
|
|
|
446
447
|
var instance = {
|
|
@@ -524,25 +525,26 @@ function create(opts) {
|
|
|
524
525
|
}
|
|
525
526
|
var resolved = _resolveLabels(defaultLabels, labelNames, arg.labels);
|
|
526
527
|
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
|
-
}
|
|
528
|
+
var entry = boundedMap.getOrInsert(values, key, function () {
|
|
536
529
|
// counts[i] is the count for the [<=buckets[i]] bucket; counts[buckets.length] is +Inf.
|
|
537
|
-
|
|
530
|
+
return {
|
|
538
531
|
labels: resolved,
|
|
539
532
|
counts: new Array(buckets.length + 1).fill(0),
|
|
540
533
|
sum: 0,
|
|
541
534
|
count: 0,
|
|
542
535
|
exemplars: new Array(buckets.length + 1).fill(null),
|
|
543
536
|
};
|
|
544
|
-
|
|
545
|
-
|
|
537
|
+
}, {
|
|
538
|
+
maxSize: cardinalityCap,
|
|
539
|
+
onFull: function () {
|
|
540
|
+
if (!capWarned) {
|
|
541
|
+
log("metric '" + fullName + "' hit labelCardinalityCap (" + cardinalityCap + ")");
|
|
542
|
+
capWarned = true;
|
|
543
|
+
}
|
|
544
|
+
return null;
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
if (!entry) return;
|
|
546
548
|
for (var i = 0; i < buckets.length; i++) {
|
|
547
549
|
if (arg.value <= buckets[i]) {
|
|
548
550
|
entry.counts[i]++;
|
|
@@ -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" });
|
|
@@ -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
|
}
|