@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/guard-json.js
CHANGED
|
@@ -72,10 +72,11 @@
|
|
|
72
72
|
|
|
73
73
|
var codepointClass = require("./codepoint-class");
|
|
74
74
|
var lazyRequire = require("./lazy-require");
|
|
75
|
+
var pick = require("./pick");
|
|
75
76
|
var gateContract = require("./gate-contract");
|
|
76
77
|
var C = require("./constants");
|
|
77
|
-
var numericBounds = require("./numeric-bounds");
|
|
78
78
|
var safeJson = require("./safe-json");
|
|
79
|
+
var safeBuffer = require("./safe-buffer");
|
|
79
80
|
var { GuardJsonError } = require("./framework-error");
|
|
80
81
|
|
|
81
82
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
@@ -87,14 +88,9 @@ var _err = GuardJsonError.factory;
|
|
|
87
88
|
|
|
88
89
|
var BIDI_RE = codepointClass.BIDI_RE;
|
|
89
90
|
var C0_CTRL_RE = codepointClass.C0_CTRL_RE;
|
|
90
|
-
var ZW_RE = codepointClass.ZERO_WIDTH_RE;
|
|
91
91
|
var NULL_BYTE = codepointClass.NULL_BYTE;
|
|
92
92
|
var BOM_CHAR = codepointClass.BOM_CHAR;
|
|
93
93
|
|
|
94
|
-
// Prototype-pollution key denylist. Operator-supplied JSON containing
|
|
95
|
-
// any of these keys at any depth is refused under strict.
|
|
96
|
-
var POLLUTION_KEYS = Object.freeze(["__proto__", "constructor", "prototype"]);
|
|
97
|
-
|
|
98
94
|
// Comment / NaN / Infinity / hex / single-quote markers — pre-parse
|
|
99
95
|
// scan on the raw source. JSON.parse rejects most of these, but JSON5
|
|
100
96
|
// / JSONC parsers accept and silently coerce; we surface the source
|
|
@@ -132,10 +128,7 @@ var PROFILES = Object.freeze({
|
|
|
132
128
|
trailingCommaPolicy: "reject",
|
|
133
129
|
json5SyntaxPolicy: "reject", // single-quoted / hex / unquoted-key
|
|
134
130
|
bomPolicy: "reject",
|
|
135
|
-
|
|
136
|
-
controlPolicy: "reject",
|
|
137
|
-
nullBytePolicy: "reject",
|
|
138
|
-
zeroWidthPolicy: "reject",
|
|
131
|
+
...gateContract.CHAR_THREATS_REJECT_ALL,
|
|
139
132
|
numericPrecisionPolicy: "reject",
|
|
140
133
|
requireTopLevelKeyAllowlist: false, // operator opts in via topLevelKeyAllowlist
|
|
141
134
|
topLevelKeyAllowlist: null,
|
|
@@ -192,26 +185,12 @@ var PROFILES = Object.freeze({
|
|
|
192
185
|
},
|
|
193
186
|
});
|
|
194
187
|
|
|
195
|
-
var DEFAULTS =
|
|
196
|
-
mode: "enforce",
|
|
188
|
+
var DEFAULTS = gateContract.strictDefaults(PROFILES, {
|
|
197
189
|
maxRuntimeMs: C.TIME.seconds(10),
|
|
198
|
-
}));
|
|
199
|
-
|
|
200
|
-
var COMPLIANCE_POSTURES = Object.freeze({
|
|
201
|
-
"hipaa": Object.assign({}, PROFILES["strict"], {
|
|
202
|
-
forensicSnippetBytes: C.BYTES.bytes(256),
|
|
203
|
-
}),
|
|
204
|
-
"pci-dss": Object.assign({}, PROFILES["strict"], {
|
|
205
|
-
forensicSnippetBytes: C.BYTES.bytes(256),
|
|
206
|
-
}),
|
|
207
|
-
"gdpr": Object.assign({}, PROFILES["balanced"], {
|
|
208
|
-
forensicSnippetBytes: C.BYTES.bytes(128),
|
|
209
|
-
}),
|
|
210
|
-
"soc2": Object.assign({}, PROFILES["strict"], {
|
|
211
|
-
forensicSnippetBytes: C.BYTES.bytes(512),
|
|
212
|
-
}),
|
|
213
190
|
});
|
|
214
191
|
|
|
192
|
+
var COMPLIANCE_POSTURES = gateContract.compliancePostures(PROFILES, { base: 256 });
|
|
193
|
+
|
|
215
194
|
// ---- Helpers ----
|
|
216
195
|
|
|
217
196
|
function _resolveOpts(opts) {
|
|
@@ -225,7 +204,10 @@ function _resolveOpts(opts) {
|
|
|
225
204
|
}
|
|
226
205
|
|
|
227
206
|
function _isPollutionKey(key) {
|
|
228
|
-
|
|
207
|
+
// The framework's single prototype-pollution predicate (core JS vectors
|
|
208
|
+
// plus any operator-registered defense-in-depth extensions) — strict JSON
|
|
209
|
+
// refuses / strips every key it names, at any depth.
|
|
210
|
+
return pick.isPoisonedKey(key);
|
|
229
211
|
}
|
|
230
212
|
|
|
231
213
|
// _scanPollutionKeys — walks parsed JSON tree counting prototype-
|
|
@@ -248,12 +230,19 @@ function _scanTree(value, opts, ctx) {
|
|
|
248
230
|
return ctx;
|
|
249
231
|
}
|
|
250
232
|
if (value === null || typeof value !== "object") {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
233
|
+
// maxStringLength is a BYTE cap (profiles set it via C.BYTES.*). Measure
|
|
234
|
+
// UTF-8 byte length, not value.length (UTF-16 code units) — otherwise a
|
|
235
|
+
// multibyte string (emoji / CJK / accented) under-enforces the cap by up
|
|
236
|
+
// to ~3x and the snippet mislabels the count.
|
|
237
|
+
if (typeof value === "string") {
|
|
238
|
+
var strBytes = safeBuffer.byteLengthOf(value);
|
|
239
|
+
if (strBytes > opts.maxStringLength) {
|
|
240
|
+
ctx.stringTooLongHits.push({
|
|
241
|
+
kind: "string-too-long",
|
|
242
|
+
snippet: "string byte length " + strBytes +
|
|
243
|
+
" exceeds maxStringLength " + opts.maxStringLength + " bytes",
|
|
244
|
+
});
|
|
245
|
+
}
|
|
257
246
|
}
|
|
258
247
|
return ctx;
|
|
259
248
|
}
|
|
@@ -382,31 +371,19 @@ function _scanRawSource(text, opts) {
|
|
|
382
371
|
});
|
|
383
372
|
}
|
|
384
373
|
}
|
|
385
|
-
// Bidi / null / control via shared codepoint class.
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
issues.push({
|
|
390
|
-
kind: "zero-width", severity: "warn", ruleId: "json.zero-width",
|
|
391
|
-
snippet: "zero-width / invisible-formatting char in JSON source",
|
|
392
|
-
});
|
|
393
|
-
}
|
|
374
|
+
// Bidi / null / control / zero-width via the shared codepoint class. JSON
|
|
375
|
+
// source treats an invisible-formatting char as a `warn` (cosmetic, not a
|
|
376
|
+
// structural threat) — passed as the zero-width severity.
|
|
377
|
+
issues.push.apply(issues, codepointClass.detectCharThreats(text, opts, "json", "warn"));
|
|
394
378
|
return issues;
|
|
395
379
|
}
|
|
396
380
|
|
|
397
381
|
// _detectIssues — full validate path: raw-source pre-scan + parse +
|
|
398
382
|
// tree walk.
|
|
399
383
|
function _detectIssues(input, opts) {
|
|
400
|
-
var
|
|
401
|
-
if (
|
|
402
|
-
|
|
403
|
-
snippet: "input is not a string" }];
|
|
404
|
-
}
|
|
405
|
-
if (input.length > opts.maxBytes) {
|
|
406
|
-
return [{ kind: "too-large", severity: "high", ruleId: "json.too-large",
|
|
407
|
-
snippet: "input " + input.length +
|
|
408
|
-
" bytes exceeds maxBytes " + opts.maxBytes }];
|
|
409
|
-
}
|
|
384
|
+
var pre = gateContract.detectStringInput(input, opts, { name: "json", noun: "input", emptyMode: "skip", scanCodepoints: false, cap: { bytes: opts.maxBytes, kind: "too-large", snippet: function (byteLen, max) { return "input " + byteLen + " bytes exceeds maxBytes " + max; } } });
|
|
385
|
+
if (pre.done) return pre.issues;
|
|
386
|
+
var issues = pre.issues;
|
|
410
387
|
|
|
411
388
|
// Raw-source pre-scan.
|
|
412
389
|
issues = issues.concat(_scanRawSource(input, opts));
|
|
@@ -616,21 +593,13 @@ function _stripPollutionTree(value, opts, depth) {
|
|
|
616
593
|
* rv.ok; // → false
|
|
617
594
|
* rv.issues.some(function (i) { return i.kind === "prototype-pollution-key"; }); // → true
|
|
618
595
|
*/
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
return {
|
|
627
|
-
ok: false,
|
|
628
|
-
issues: [{ kind: "bad-input", severity: "high",
|
|
629
|
-
snippet: "input is not a string" }],
|
|
630
|
-
};
|
|
631
|
-
}
|
|
632
|
-
return gateContract.aggregateIssues(_detectIssues(input, opts));
|
|
633
|
-
}
|
|
596
|
+
// validate is assembled by gateContract.defineGuard from `detect`
|
|
597
|
+
// (_detectIssues) below — `validate(input, opts) = aggregateIssues(detect(
|
|
598
|
+
// input, resolveOpts(opts)))`, with the maxBytes/maxDepth/maxKeysPerObject/
|
|
599
|
+
// maxArrayLength/maxStringLength/maxTotalNodes caps declared via `intOpts`.
|
|
600
|
+
// Non-string input reduces to the same single `bad-input` issue _detectIssues
|
|
601
|
+
// already emits, so the prior explicit early-return is subsumed. The
|
|
602
|
+
// @primitive block above documents the resulting ABI.
|
|
634
603
|
|
|
635
604
|
/**
|
|
636
605
|
* @primitive b.guardJson.parse
|
|
@@ -779,45 +748,65 @@ function _policyKeyForRuleId(ruleId) {
|
|
|
779
748
|
* var verdict = await jsonGate.check({ bytes: hostile });
|
|
780
749
|
* verdict.action; // → "refuse"
|
|
781
750
|
*/
|
|
751
|
+
// Disposition of each json finding = what the operator's policy for that class
|
|
752
|
+
// selected. The RFC-deviation findings (comments / trailing commas / NaN /
|
|
753
|
+
// JSON5 syntax / BOM / prototype-pollution / duplicate keys) sanitize by
|
|
754
|
+
// re-parsing under a mitigation policy and refuse under `reject`; the bidi /
|
|
755
|
+
// null / control char threats follow their shared policies; structural caps,
|
|
756
|
+
// a parse failure, and an allowlist miss always refuse; the big-integer
|
|
757
|
+
// precision and zero-width notes are audit-only. Exhaustive over every kind
|
|
758
|
+
// _detectIssues emits (the gate-disposition coverage test enforces it).
|
|
759
|
+
function _gateDispositionFor(issue, opts) {
|
|
760
|
+
var shared = gateContract.charThreatDisposition(issue, opts);
|
|
761
|
+
if (shared) return shared;
|
|
762
|
+
switch (issue.kind) {
|
|
763
|
+
case "bom-leading":
|
|
764
|
+
case "bom-mid-stream": return gateContract.policyDisposition(opts.bomPolicy);
|
|
765
|
+
case "comment-block":
|
|
766
|
+
case "comment-line": return gateContract.policyDisposition(opts.commentPolicy);
|
|
767
|
+
case "nan-infinity": return gateContract.policyDisposition(opts.nanInfinityPolicy);
|
|
768
|
+
case "trailing-comma": return gateContract.policyDisposition(opts.trailingCommaPolicy);
|
|
769
|
+
case "single-quoted-key":
|
|
770
|
+
case "hex-literal": return gateContract.policyDisposition(opts.json5SyntaxPolicy);
|
|
771
|
+
case "prototype-pollution-key": return gateContract.policyDisposition(opts.pollutionPolicy);
|
|
772
|
+
case "duplicate-key": return gateContract.policyDisposition(opts.duplicateKeyPolicy);
|
|
773
|
+
// zero-width is classified by charThreatDisposition above (its
|
|
774
|
+
// zeroWidthPolicy). numeric-precision-loss follows its own policy like every
|
|
775
|
+
// other RFC-deviation finding — under numericPrecisionPolicy:reject it
|
|
776
|
+
// refuses, not audits.
|
|
777
|
+
case "numeric-precision-loss": return gateContract.policyDisposition(opts.numericPrecisionPolicy);
|
|
778
|
+
case "node-count-cap":
|
|
779
|
+
case "depth-cap":
|
|
780
|
+
case "string-too-long":
|
|
781
|
+
case "array-length-cap":
|
|
782
|
+
case "key-count-cap":
|
|
783
|
+
case "bad-input":
|
|
784
|
+
case "too-large":
|
|
785
|
+
case "parse-failed":
|
|
786
|
+
case "missing-allowlist":
|
|
787
|
+
case "top-level-key-not-allowlisted": return "refuse";
|
|
788
|
+
default: return null;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
782
792
|
function gate(opts) {
|
|
783
793
|
opts = _resolveOpts(opts);
|
|
784
|
-
return gateContract.
|
|
785
|
-
opts.name || "guardJson:" + (opts.profile || "default"),
|
|
786
|
-
opts,
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
opts.nanInfinityPolicy !== "reject" &&
|
|
801
|
-
opts.commentPolicy !== "reject" &&
|
|
802
|
-
opts.trailingCommaPolicy !== "reject" &&
|
|
803
|
-
opts.json5SyntaxPolicy !== "reject" &&
|
|
804
|
-
opts.bomPolicy !== "reject" &&
|
|
805
|
-
opts.bidiPolicy !== "reject" &&
|
|
806
|
-
opts.controlPolicy !== "reject" &&
|
|
807
|
-
opts.nullBytePolicy !== "reject";
|
|
808
|
-
if (canSanitize) {
|
|
809
|
-
try {
|
|
810
|
-
var clean = parse(text, opts);
|
|
811
|
-
var emitted = JSON.stringify(clean);
|
|
812
|
-
return {
|
|
813
|
-
ok: true, action: "sanitize",
|
|
814
|
-
sanitized: Buffer.from(emitted, "utf8"),
|
|
815
|
-
issues: rv.issues,
|
|
816
|
-
};
|
|
817
|
-
} catch (_e) { /* fall through */ }
|
|
818
|
-
}
|
|
819
|
-
return { ok: false, action: "refuse", issues: rv.issues };
|
|
820
|
-
});
|
|
794
|
+
return gateContract.buildContentGate({
|
|
795
|
+
name: opts.name || "guardJson:" + (opts.profile || "default"),
|
|
796
|
+
opts: opts,
|
|
797
|
+
validate: module.exports.validate,
|
|
798
|
+
dispositionFor: _gateDispositionFor,
|
|
799
|
+
// A sanitize-disposition finding (a class set to a mitigation) is repaired in
|
|
800
|
+
// two passes: first the char-strip policies remove bidi / control / null /
|
|
801
|
+
// zero-width per policy (so a strip-policy char threat is excised even when
|
|
802
|
+
// it sits inside a string value), then a parse + re-serialize drops
|
|
803
|
+
// __proto__ / comments / NaN / trailing commas per the active policy. Under a
|
|
804
|
+
// reject policy the finding is already refuse-disposition, so this is not
|
|
805
|
+
// reached for that class.
|
|
806
|
+
produceSanitized: function (text, o) {
|
|
807
|
+
return JSON.stringify(parse(codepointClass.applyCharStripPolicies(text, o), o));
|
|
808
|
+
},
|
|
809
|
+
});
|
|
821
810
|
}
|
|
822
811
|
|
|
823
812
|
// buildProfile / compliancePosture / loadRulePack are assembled by
|
|
@@ -840,8 +829,8 @@ var INTEGRATION_FIXTURES = Object.freeze({
|
|
|
840
829
|
// exports (NAME / KIND / MIME_TYPES / EXTENSIONS / INTEGRATION_FIXTURES),
|
|
841
830
|
// buildProfile / compliancePosture / loadRulePack wiring, plus the
|
|
842
831
|
// per-guard inspection surface (validate / parse) and JSON extras
|
|
843
|
-
// (POLLUTION_KEYS
|
|
844
|
-
// JSON's sanitize-reparse-reserialize chain unchanged.
|
|
832
|
+
// (POLLUTION_KEYS, surfaced from the framework's canonical pick.POISONED_KEYS).
|
|
833
|
+
// The bespoke `gate` carries JSON's sanitize-reparse-reserialize chain unchanged.
|
|
845
834
|
module.exports = gateContract.defineGuard({
|
|
846
835
|
name: "json",
|
|
847
836
|
kind: "content",
|
|
@@ -852,11 +841,14 @@ module.exports = gateContract.defineGuard({
|
|
|
852
841
|
mimeTypes: ["application/json", "application/ld+json", "application/vnd.api+json"],
|
|
853
842
|
extensions: [".json", ".jsonld"],
|
|
854
843
|
integrationFixtures: INTEGRATION_FIXTURES,
|
|
855
|
-
|
|
844
|
+
detect: _detectIssues,
|
|
845
|
+
intOpts: ["maxBytes", "maxDepth", "maxKeysPerObject", "maxArrayLength",
|
|
846
|
+
"maxStringLength", "maxTotalNodes"],
|
|
856
847
|
gate: gate,
|
|
857
848
|
extra: {
|
|
849
|
+
_gateDispositionForTest: _gateDispositionFor,
|
|
858
850
|
parse: parse,
|
|
859
|
-
POLLUTION_KEYS:
|
|
851
|
+
POLLUTION_KEYS: pick.POISONED_KEYS,
|
|
860
852
|
},
|
|
861
853
|
});
|
|
862
854
|
|
package/lib/guard-jsonpath.js
CHANGED
|
@@ -50,11 +50,9 @@
|
|
|
50
50
|
* JSONPath content-safety guard — refuses user-supplied JSONPath query strings that exhibit dynamic-code-execution shapes BEFORE they reach a JSONPath evaluator.
|
|
51
51
|
*/
|
|
52
52
|
|
|
53
|
-
var codepointClass = require("./codepoint-class");
|
|
54
53
|
var lazyRequire = require("./lazy-require");
|
|
55
54
|
var gateContract = require("./gate-contract");
|
|
56
55
|
var C = require("./constants");
|
|
57
|
-
var numericBounds = require("./numeric-bounds");
|
|
58
56
|
var { GuardJsonpathError } = require("./framework-error");
|
|
59
57
|
|
|
60
58
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
@@ -81,10 +79,7 @@ var RECURSIVE_DESCENT_RE = /\.\.\[?\*\]?/g;
|
|
|
81
79
|
|
|
82
80
|
var PROFILES = Object.freeze({
|
|
83
81
|
"strict": {
|
|
84
|
-
|
|
85
|
-
controlPolicy: "reject",
|
|
86
|
-
nullBytePolicy: "reject",
|
|
87
|
-
zeroWidthPolicy: "reject",
|
|
82
|
+
...gateContract.CHAR_THREATS_REJECT_ALL,
|
|
88
83
|
filterExprPolicy: "reject",
|
|
89
84
|
scriptExprPolicy: "reject",
|
|
90
85
|
dynamicHintPolicy: "reject",
|
|
@@ -96,10 +91,7 @@ var PROFILES = Object.freeze({
|
|
|
96
91
|
maxRuntimeMs: C.TIME.seconds(2),
|
|
97
92
|
},
|
|
98
93
|
"balanced": {
|
|
99
|
-
|
|
100
|
-
controlPolicy: "reject",
|
|
101
|
-
nullBytePolicy: "reject",
|
|
102
|
-
zeroWidthPolicy: "reject",
|
|
94
|
+
...gateContract.CHAR_THREATS_REJECT_ALL,
|
|
103
95
|
filterExprPolicy: "reject", // RCE class — refused at every profile
|
|
104
96
|
scriptExprPolicy: "reject", // RCE class — refused at every profile
|
|
105
97
|
dynamicHintPolicy: "reject", // RCE class — refused at every profile
|
|
@@ -111,10 +103,7 @@ var PROFILES = Object.freeze({
|
|
|
111
103
|
maxRuntimeMs: C.TIME.seconds(2),
|
|
112
104
|
},
|
|
113
105
|
"permissive": {
|
|
114
|
-
|
|
115
|
-
controlPolicy: "reject", // controls refused at every profile
|
|
116
|
-
nullBytePolicy: "reject", // null refused at every profile
|
|
117
|
-
zeroWidthPolicy: "reject", // zero-width refused at every profile
|
|
106
|
+
...gateContract.CHAR_THREATS_REJECT_ALL,
|
|
118
107
|
filterExprPolicy: "reject", // RCE class refused at every profile
|
|
119
108
|
scriptExprPolicy: "reject", // RCE class refused at every profile
|
|
120
109
|
dynamicHintPolicy: "reject", // RCE class refused at every profile
|
|
@@ -127,35 +116,6 @@ var PROFILES = Object.freeze({
|
|
|
127
116
|
},
|
|
128
117
|
});
|
|
129
118
|
|
|
130
|
-
var DEFAULTS = Object.freeze(Object.assign({}, PROFILES["strict"], {
|
|
131
|
-
mode: "enforce",
|
|
132
|
-
}));
|
|
133
|
-
|
|
134
|
-
var COMPLIANCE_POSTURES = Object.freeze({
|
|
135
|
-
"hipaa": Object.assign({}, PROFILES["strict"], {
|
|
136
|
-
forensicSnippetBytes: C.BYTES.bytes(256),
|
|
137
|
-
}),
|
|
138
|
-
"pci-dss": Object.assign({}, PROFILES["strict"], {
|
|
139
|
-
forensicSnippetBytes: C.BYTES.bytes(256),
|
|
140
|
-
}),
|
|
141
|
-
"gdpr": Object.assign({}, PROFILES["balanced"], {
|
|
142
|
-
forensicSnippetBytes: C.BYTES.bytes(128),
|
|
143
|
-
}),
|
|
144
|
-
"soc2": Object.assign({}, PROFILES["strict"], {
|
|
145
|
-
forensicSnippetBytes: C.BYTES.bytes(512),
|
|
146
|
-
}),
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
function _resolveOpts(opts) {
|
|
150
|
-
return gateContract.resolveProfileAndPosture(opts, {
|
|
151
|
-
profiles: PROFILES,
|
|
152
|
-
compliancePostures: COMPLIANCE_POSTURES,
|
|
153
|
-
defaults: DEFAULTS,
|
|
154
|
-
errorClass: GuardJsonpathError,
|
|
155
|
-
errCodePrefix: "jsonpath",
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
119
|
function _hasDynamicHint(input) {
|
|
160
120
|
for (var i = 0; i < DYNAMIC_HINTS.length; i += 1) {
|
|
161
121
|
if (input.indexOf(DYNAMIC_HINTS[i]) !== -1) return DYNAMIC_HINTS[i];
|
|
@@ -164,26 +124,9 @@ function _hasDynamicHint(input) {
|
|
|
164
124
|
}
|
|
165
125
|
|
|
166
126
|
function _detectIssues(input, opts) {
|
|
167
|
-
var
|
|
168
|
-
if (
|
|
169
|
-
|
|
170
|
-
ruleId: "jsonpath.bad-input",
|
|
171
|
-
snippet: "jsonpath is not a string" }];
|
|
172
|
-
}
|
|
173
|
-
if (input.length === 0) {
|
|
174
|
-
return [{ kind: "empty", severity: "high",
|
|
175
|
-
ruleId: "jsonpath.empty",
|
|
176
|
-
snippet: "jsonpath is empty" }];
|
|
177
|
-
}
|
|
178
|
-
if (Buffer.byteLength(input, "utf8") > opts.maxPatternBytes) {
|
|
179
|
-
return [{ kind: "pattern-cap", severity: "high",
|
|
180
|
-
ruleId: "jsonpath.pattern-cap",
|
|
181
|
-
snippet: "jsonpath exceeds maxPatternBytes " +
|
|
182
|
-
opts.maxPatternBytes }];
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
var charThreats = codepointClass.detectCharThreats(input, opts, "jsonpath");
|
|
186
|
-
for (var ci = 0; ci < charThreats.length; ci += 1) issues.push(charThreats[ci]);
|
|
127
|
+
var pre = gateContract.detectStringInput(input, opts, { name: "jsonpath", cap: { bytes: opts.maxPatternBytes, kind: "pattern-cap", snippet: "jsonpath exceeds maxPatternBytes " + opts.maxPatternBytes } });
|
|
128
|
+
if (pre.done) return pre.issues;
|
|
129
|
+
var issues = pre.issues;
|
|
187
130
|
|
|
188
131
|
if (opts.filterExprPolicy !== "allow" && FILTER_EXPR_RE.test(input)) { // allow:regex-no-length-cap — input bounded by maxPatternBytes
|
|
189
132
|
issues.push({
|
|
@@ -280,13 +223,10 @@ function _detectIssues(input, opts) {
|
|
|
280
223
|
* hostile.ok; // → false
|
|
281
224
|
* hostile.issues.some(function (i) { return i.kind === "filter-expression"; }); // → true
|
|
282
225
|
*/
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
"guardJsonpath.validate", GuardJsonpathError, "jsonpath.bad-opt");
|
|
288
|
-
return gateContract.aggregateIssues(_detectIssues(input, opts));
|
|
289
|
-
}
|
|
226
|
+
// validate is assembled by gateContract.defineGuard from `detect`
|
|
227
|
+
// (_detectIssues) below — `validate(input, opts) = aggregateIssues(detect(
|
|
228
|
+
// input, resolveOpts(opts)))`, with the numeric opts validated via `intOpts`.
|
|
229
|
+
// The @primitive block above documents the resulting public ABI.
|
|
290
230
|
|
|
291
231
|
/**
|
|
292
232
|
* @primitive b.guardJsonpath.sanitize
|
|
@@ -327,13 +267,11 @@ function validate(input, opts) {
|
|
|
327
267
|
* e.code; // → "jsonpath.filter-expression"
|
|
328
268
|
* }
|
|
329
269
|
*/
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
var issues = _detectIssues(input, opts);
|
|
336
|
-
gateContract.throwOnRefusalSeverity(issues, { errorClass: GuardJsonpathError, codePrefix: "jsonpath" });
|
|
270
|
+
// _sanitizeTransform — the guard-specific normalize applied by defineGuard's
|
|
271
|
+
// generated sanitize AFTER resolve → detect → throw-on-refusal. JSONPath
|
|
272
|
+
// expressions cannot be safely repaired, so the transform is pure pass-through:
|
|
273
|
+
// input that survives the refusal gate is returned unchanged.
|
|
274
|
+
function _sanitizeTransform(input) {
|
|
337
275
|
return input;
|
|
338
276
|
}
|
|
339
277
|
|
|
@@ -351,13 +289,7 @@ function sanitize(input, opts) {
|
|
|
351
289
|
// Their wiki sections render from the single-sourced @abiTemplate blocks
|
|
352
290
|
// in gate-contract.js, instantiated per guard by the page generator.
|
|
353
291
|
|
|
354
|
-
var INTEGRATION_FIXTURES =
|
|
355
|
-
kind: "identifier",
|
|
356
|
-
benignBytes: Buffer.from("$.users[*].name", "utf8"),
|
|
357
|
-
hostileBytes: Buffer.from("$..[?(@.x)]", "utf8"),
|
|
358
|
-
benignIdentifier: "$.users[*].name",
|
|
359
|
-
hostileIdentifier: "$..[?(@.x)]",
|
|
360
|
-
});
|
|
292
|
+
var INTEGRATION_FIXTURES = gateContract.identifierFixtures("$.users[*].name", "$..[?(@.x)]");
|
|
361
293
|
|
|
362
294
|
// Assembled from the gate-contract guard factory: error class, registry
|
|
363
295
|
// exports (NAME / KIND / INTEGRATION_FIXTURES), buildProfile /
|
|
@@ -370,10 +302,10 @@ module.exports = gateContract.defineGuard({
|
|
|
370
302
|
kind: "identifier",
|
|
371
303
|
errorClass: GuardJsonpathError,
|
|
372
304
|
profiles: PROFILES,
|
|
373
|
-
|
|
374
|
-
postures: COMPLIANCE_POSTURES,
|
|
305
|
+
base: 256,
|
|
375
306
|
integrationFixtures: INTEGRATION_FIXTURES,
|
|
376
|
-
|
|
377
|
-
|
|
307
|
+
detect: _detectIssues,
|
|
308
|
+
sanitizeTransform: _sanitizeTransform,
|
|
309
|
+
intOpts: ["maxBytes", "maxPatternBytes", "maxRecursiveDescents"],
|
|
378
310
|
ctxFields: ["identifier", "jsonpath"],
|
|
379
311
|
});
|