@blamejs/blamejs-shop 0.4.55 → 0.4.57
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/lib/admin.js +385 -2
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +426 -366
- package/lib/vendor/blamejs/.github/codeql/codeql-config.yml +25 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +1 -1
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +1 -1
- package/lib/vendor/blamejs/.github/workflows/ci.yml +13 -13
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +8 -7
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +2 -2
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +4 -4
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +1 -1
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
- package/lib/vendor/blamejs/CHANGELOG.md +2 -0
- package/lib/vendor/blamejs/api-snapshot.json +1381 -4
- package/lib/vendor/blamejs/eslint.config.mjs +1 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +4 -1
- package/lib/vendor/blamejs/examples/wiki/lib/symbol-index.js +5 -2
- package/lib/vendor/blamejs/examples/wiki/routes/pages.js +4 -1
- package/lib/vendor/blamejs/fuzz/guard-text.fuzz.js +20 -0
- package/lib/vendor/blamejs/index.js +2 -0
- package/lib/vendor/blamejs/lib/a2a-tasks.js +7 -23
- package/lib/vendor/blamejs/lib/acme.js +6 -5
- package/lib/vendor/blamejs/lib/agent-event-bus.js +5 -4
- package/lib/vendor/blamejs/lib/agent-idempotency.js +2 -5
- package/lib/vendor/blamejs/lib/agent-orchestrator.js +2 -5
- package/lib/vendor/blamejs/lib/agent-saga.js +3 -5
- package/lib/vendor/blamejs/lib/agent-tenant.js +2 -5
- package/lib/vendor/blamejs/lib/ai-adverse-decision.js +2 -15
- package/lib/vendor/blamejs/lib/ai-capability.js +1 -6
- package/lib/vendor/blamejs/lib/ai-dp.js +1 -5
- package/lib/vendor/blamejs/lib/ai-input.js +2 -2
- package/lib/vendor/blamejs/lib/ai-pref.js +3 -8
- package/lib/vendor/blamejs/lib/ai-quota.js +3 -14
- package/lib/vendor/blamejs/lib/api-key.js +37 -28
- package/lib/vendor/blamejs/lib/archive-adapters.js +2 -4
- package/lib/vendor/blamejs/lib/archive-entry-policy.js +32 -0
- package/lib/vendor/blamejs/lib/archive-read.js +5 -17
- package/lib/vendor/blamejs/lib/archive-tar-read.js +5 -16
- package/lib/vendor/blamejs/lib/archive.js +2 -10
- package/lib/vendor/blamejs/lib/arg-parser.js +7 -6
- package/lib/vendor/blamejs/lib/asyncapi-traits.js +2 -6
- package/lib/vendor/blamejs/lib/atomic-file.js +108 -31
- package/lib/vendor/blamejs/lib/audit-chain.js +133 -53
- package/lib/vendor/blamejs/lib/audit-daily-review.js +24 -14
- package/lib/vendor/blamejs/lib/audit-emit.js +82 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +257 -0
- package/lib/vendor/blamejs/lib/audit.js +84 -0
- package/lib/vendor/blamejs/lib/auth/access-lock.js +5 -27
- package/lib/vendor/blamejs/lib/auth/dpop.js +23 -35
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +9 -15
- package/lib/vendor/blamejs/lib/auth/jwt-external.js +26 -8
- package/lib/vendor/blamejs/lib/auth/jwt.js +13 -15
- package/lib/vendor/blamejs/lib/auth/lockout.js +6 -25
- package/lib/vendor/blamejs/lib/auth/oauth.js +67 -45
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +55 -32
- package/lib/vendor/blamejs/lib/auth/oid4vp.js +3 -2
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +6 -6
- package/lib/vendor/blamejs/lib/auth/passkey.js +3 -3
- package/lib/vendor/blamejs/lib/auth/password.js +7 -1
- package/lib/vendor/blamejs/lib/auth/saml.js +37 -27
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-holder.js +2 -14
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-issuer.js +2 -14
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +1 -1
- package/lib/vendor/blamejs/lib/auth/status-list.js +7 -7
- package/lib/vendor/blamejs/lib/auth/step-up.js +6 -12
- package/lib/vendor/blamejs/lib/auth-bot-challenge.js +6 -37
- package/lib/vendor/blamejs/lib/backup/bundle.js +11 -18
- package/lib/vendor/blamejs/lib/backup/index.js +14 -47
- package/lib/vendor/blamejs/lib/bounded-map.js +112 -1
- package/lib/vendor/blamejs/lib/breach-deadline.js +1 -11
- package/lib/vendor/blamejs/lib/cache.js +7 -18
- package/lib/vendor/blamejs/lib/cbor.js +2 -1
- package/lib/vendor/blamejs/lib/cdn-cache-control.js +8 -9
- package/lib/vendor/blamejs/lib/cert.js +3 -10
- package/lib/vendor/blamejs/lib/chain-writer.js +162 -47
- package/lib/vendor/blamejs/lib/cli.js +5 -1
- package/lib/vendor/blamejs/lib/client-hints.js +7 -9
- package/lib/vendor/blamejs/lib/cloud-events.js +40 -31
- package/lib/vendor/blamejs/lib/cluster-storage.js +2 -38
- package/lib/vendor/blamejs/lib/cms-codec.js +2 -1
- package/lib/vendor/blamejs/lib/codepoint-class.js +67 -1
- package/lib/vendor/blamejs/lib/compliance-ai-act-logging.js +1 -6
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +2 -4
- package/lib/vendor/blamejs/lib/compliance-eaa.js +1 -11
- package/lib/vendor/blamejs/lib/compliance-sanctions-fetcher.js +21 -28
- package/lib/vendor/blamejs/lib/compliance-sanctions.js +2 -14
- package/lib/vendor/blamejs/lib/compliance.js +2 -9
- package/lib/vendor/blamejs/lib/config-drift.js +2 -12
- package/lib/vendor/blamejs/lib/content-digest.js +10 -8
- package/lib/vendor/blamejs/lib/cookies.js +5 -11
- package/lib/vendor/blamejs/lib/cose.js +19 -11
- package/lib/vendor/blamejs/lib/cra-report.js +1 -11
- package/lib/vendor/blamejs/lib/crypto-field.js +5 -10
- package/lib/vendor/blamejs/lib/crypto.js +120 -3
- package/lib/vendor/blamejs/lib/csp.js +235 -3
- package/lib/vendor/blamejs/lib/daemon.js +42 -41
- package/lib/vendor/blamejs/lib/data-act.js +19 -9
- package/lib/vendor/blamejs/lib/db-query.js +6 -40
- package/lib/vendor/blamejs/lib/db.js +34 -12
- package/lib/vendor/blamejs/lib/dbsc.js +5 -6
- package/lib/vendor/blamejs/lib/ddl-change-control.js +18 -14
- package/lib/vendor/blamejs/lib/deprecate.js +4 -5
- package/lib/vendor/blamejs/lib/did.js +6 -2
- package/lib/vendor/blamejs/lib/dsr.js +8 -12
- package/lib/vendor/blamejs/lib/external-db-migrate.js +21 -22
- package/lib/vendor/blamejs/lib/external-db.js +14 -26
- package/lib/vendor/blamejs/lib/fda-21cfr11.js +12 -8
- package/lib/vendor/blamejs/lib/fdx.js +22 -18
- package/lib/vendor/blamejs/lib/file-upload.js +80 -66
- package/lib/vendor/blamejs/lib/flag-evaluation-context.js +5 -8
- package/lib/vendor/blamejs/lib/framework-error.js +12 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +19 -0
- package/lib/vendor/blamejs/lib/fsm.js +7 -12
- package/lib/vendor/blamejs/lib/gate-contract.js +869 -38
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +4 -13
- package/lib/vendor/blamejs/lib/graphql-federation.js +1 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +2 -1
- package/lib/vendor/blamejs/lib/guard-all.js +1 -0
- package/lib/vendor/blamejs/lib/guard-archive.js +9 -30
- package/lib/vendor/blamejs/lib/guard-auth.js +23 -80
- package/lib/vendor/blamejs/lib/guard-cidr.js +20 -96
- package/lib/vendor/blamejs/lib/guard-csv.js +135 -196
- package/lib/vendor/blamejs/lib/guard-domain.js +23 -106
- package/lib/vendor/blamejs/lib/guard-dsn.js +16 -13
- package/lib/vendor/blamejs/lib/guard-email.js +46 -53
- package/lib/vendor/blamejs/lib/guard-envelope.js +1 -1
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +2 -1
- package/lib/vendor/blamejs/lib/guard-filename.js +12 -60
- package/lib/vendor/blamejs/lib/guard-graphql.js +28 -75
- package/lib/vendor/blamejs/lib/guard-html-wcag.js +15 -2
- package/lib/vendor/blamejs/lib/guard-html.js +65 -117
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +2 -1
- package/lib/vendor/blamejs/lib/guard-image.js +280 -77
- package/lib/vendor/blamejs/lib/guard-imap-command.js +8 -9
- package/lib/vendor/blamejs/lib/guard-json.js +87 -103
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +20 -88
- package/lib/vendor/blamejs/lib/guard-jwt.js +32 -114
- package/lib/vendor/blamejs/lib/guard-list-id.js +2 -7
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -7
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +5 -6
- package/lib/vendor/blamejs/lib/guard-mail-move.js +2 -1
- package/lib/vendor/blamejs/lib/guard-mail-query.js +5 -3
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +6 -7
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +5 -4
- package/lib/vendor/blamejs/lib/guard-markdown.js +76 -110
- package/lib/vendor/blamejs/lib/guard-message-id.js +5 -6
- package/lib/vendor/blamejs/lib/guard-mime.js +20 -99
- package/lib/vendor/blamejs/lib/guard-oauth.js +19 -73
- package/lib/vendor/blamejs/lib/guard-pdf.js +65 -72
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +12 -13
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +2 -1
- package/lib/vendor/blamejs/lib/guard-regex.js +24 -99
- package/lib/vendor/blamejs/lib/guard-saga-config.js +2 -1
- package/lib/vendor/blamejs/lib/guard-shell.js +22 -87
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +8 -11
- package/lib/vendor/blamejs/lib/guard-sql.js +15 -13
- package/lib/vendor/blamejs/lib/guard-stream-args.js +2 -1
- package/lib/vendor/blamejs/lib/guard-svg.js +95 -140
- package/lib/vendor/blamejs/lib/guard-template.js +23 -80
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +2 -1
- package/lib/vendor/blamejs/lib/guard-text.js +592 -0
- package/lib/vendor/blamejs/lib/guard-time.js +27 -95
- package/lib/vendor/blamejs/lib/guard-uuid.js +21 -93
- package/lib/vendor/blamejs/lib/guard-xml.js +76 -106
- package/lib/vendor/blamejs/lib/guard-yaml.js +24 -60
- package/lib/vendor/blamejs/lib/http-client-cache.js +5 -12
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +2 -4
- package/lib/vendor/blamejs/lib/http-client.js +8 -21
- package/lib/vendor/blamejs/lib/http-message-signature.js +3 -2
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +5 -7
- package/lib/vendor/blamejs/lib/i18n.js +83 -26
- package/lib/vendor/blamejs/lib/inbox.js +8 -8
- package/lib/vendor/blamejs/lib/incident-report.js +3 -21
- package/lib/vendor/blamejs/lib/ip-utils.js +49 -6
- package/lib/vendor/blamejs/lib/jobs.js +3 -2
- package/lib/vendor/blamejs/lib/keychain.js +6 -18
- package/lib/vendor/blamejs/lib/legal-hold.js +6 -15
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +44 -112
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +17 -14
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +16 -80
- package/lib/vendor/blamejs/lib/log-stream-webhook.js +20 -92
- package/lib/vendor/blamejs/lib/mail-arc-sign.js +8 -3
- package/lib/vendor/blamejs/lib/mail-arf.js +3 -2
- package/lib/vendor/blamejs/lib/mail-auth.js +40 -66
- package/lib/vendor/blamejs/lib/mail-bimi.js +19 -39
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +3 -2
- package/lib/vendor/blamejs/lib/mail-dav.js +8 -35
- package/lib/vendor/blamejs/lib/mail-deploy.js +6 -9
- package/lib/vendor/blamejs/lib/mail-dkim.js +19 -38
- package/lib/vendor/blamejs/lib/mail-greylist.js +15 -26
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -3
- package/lib/vendor/blamejs/lib/mail-journal.js +2 -1
- package/lib/vendor/blamejs/lib/mail-mdn.js +4 -3
- package/lib/vendor/blamejs/lib/mail-rbl.js +5 -8
- package/lib/vendor/blamejs/lib/mail-scan.js +2 -2
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +33 -20
- package/lib/vendor/blamejs/lib/mail-server-imap.js +32 -87
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +35 -51
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +29 -83
- package/lib/vendor/blamejs/lib/mail-server-mx.js +33 -74
- package/lib/vendor/blamejs/lib/mail-server-net.js +177 -0
- package/lib/vendor/blamejs/lib/mail-server-pop3.js +30 -83
- package/lib/vendor/blamejs/lib/mail-server-rate-limit.js +30 -6
- package/lib/vendor/blamejs/lib/mail-server-submission.js +34 -73
- package/lib/vendor/blamejs/lib/mail-server-tls.js +89 -0
- package/lib/vendor/blamejs/lib/mail-spam-score.js +7 -8
- package/lib/vendor/blamejs/lib/mail-store.js +3 -2
- package/lib/vendor/blamejs/lib/mail.js +6 -11
- package/lib/vendor/blamejs/lib/markup-escape.js +31 -0
- package/lib/vendor/blamejs/lib/markup-tokenizer.js +54 -0
- package/lib/vendor/blamejs/lib/mcp-tool-registry.js +26 -23
- package/lib/vendor/blamejs/lib/mcp.js +1 -1
- package/lib/vendor/blamejs/lib/mdoc.js +11 -12
- package/lib/vendor/blamejs/lib/metrics.js +32 -30
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +3 -23
- package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +11 -22
- package/lib/vendor/blamejs/lib/middleware/assetlinks.js +2 -7
- package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +2 -1
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +31 -48
- package/lib/vendor/blamejs/lib/middleware/bot-disclose.js +7 -10
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +2 -10
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +13 -2
- package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +6 -26
- package/lib/vendor/blamejs/lib/middleware/deny-response.js +19 -1
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +7 -0
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +4 -20
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +20 -28
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +3 -2
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +9 -1
- package/lib/vendor/blamejs/lib/middleware/security-txt.js +2 -6
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +12 -27
- package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +2 -7
- package/lib/vendor/blamejs/lib/migrations.js +4 -13
- package/lib/vendor/blamejs/lib/mime-parse.js +34 -17
- package/lib/vendor/blamejs/lib/module-loader.js +44 -0
- package/lib/vendor/blamejs/lib/money.js +105 -0
- package/lib/vendor/blamejs/lib/mtls-ca.js +116 -36
- package/lib/vendor/blamejs/lib/network-byte-quota.js +4 -18
- package/lib/vendor/blamejs/lib/network-dane.js +8 -6
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +97 -6
- package/lib/vendor/blamejs/lib/network-dnssec.js +16 -16
- package/lib/vendor/blamejs/lib/network-heartbeat.js +3 -2
- package/lib/vendor/blamejs/lib/network-smtp-policy.js +29 -41
- package/lib/vendor/blamejs/lib/network-tls.js +40 -42
- package/lib/vendor/blamejs/lib/nis2-report.js +1 -11
- package/lib/vendor/blamejs/lib/nonce-store.js +81 -3
- package/lib/vendor/blamejs/lib/numeric-bounds.js +22 -8
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +2 -6
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +5 -45
- package/lib/vendor/blamejs/lib/object-store/gcs.js +8 -71
- package/lib/vendor/blamejs/lib/object-store/http-put.js +1 -5
- package/lib/vendor/blamejs/lib/object-store/http-request.js +128 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +2 -1
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +9 -77
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -25
- package/lib/vendor/blamejs/lib/observability.js +95 -0
- package/lib/vendor/blamejs/lib/openapi-paths-builder.js +7 -3
- package/lib/vendor/blamejs/lib/otel-export.js +7 -10
- package/lib/vendor/blamejs/lib/outbox.js +43 -37
- package/lib/vendor/blamejs/lib/pagination.js +11 -15
- package/lib/vendor/blamejs/lib/parsers/safe-env.js +5 -4
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +10 -4
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +11 -9
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +2 -2
- package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +7 -6
- package/lib/vendor/blamejs/lib/pick.js +63 -5
- package/lib/vendor/blamejs/lib/pipl-cn.js +69 -59
- package/lib/vendor/blamejs/lib/privacy-pass.js +8 -6
- package/lib/vendor/blamejs/lib/problem-details.js +2 -5
- package/lib/vendor/blamejs/lib/pubsub.js +4 -8
- package/lib/vendor/blamejs/lib/redact.js +2 -1
- package/lib/vendor/blamejs/lib/render.js +4 -2
- package/lib/vendor/blamejs/lib/request-helpers.js +133 -6
- package/lib/vendor/blamejs/lib/restore.js +5 -22
- package/lib/vendor/blamejs/lib/retention.js +3 -5
- package/lib/vendor/blamejs/lib/safe-async.js +457 -0
- package/lib/vendor/blamejs/lib/safe-buffer.js +137 -6
- package/lib/vendor/blamejs/lib/safe-decompress.js +2 -1
- package/lib/vendor/blamejs/lib/safe-dns.js +2 -1
- package/lib/vendor/blamejs/lib/safe-ical.js +12 -26
- package/lib/vendor/blamejs/lib/safe-json.js +7 -8
- package/lib/vendor/blamejs/lib/safe-jsonpath.js +6 -5
- package/lib/vendor/blamejs/lib/safe-mime.js +25 -29
- package/lib/vendor/blamejs/lib/safe-redirect.js +2 -5
- package/lib/vendor/blamejs/lib/safe-schema.js +7 -6
- package/lib/vendor/blamejs/lib/safe-sql.js +126 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +12 -26
- package/lib/vendor/blamejs/lib/sandbox-worker.js +9 -2
- package/lib/vendor/blamejs/lib/sandbox.js +3 -2
- package/lib/vendor/blamejs/lib/scheduler.js +5 -12
- package/lib/vendor/blamejs/lib/sec-cyber.js +82 -37
- package/lib/vendor/blamejs/lib/seeders.js +4 -11
- package/lib/vendor/blamejs/lib/self-update.js +92 -69
- package/lib/vendor/blamejs/lib/session-device-binding.js +112 -66
- package/lib/vendor/blamejs/lib/session.js +194 -6
- package/lib/vendor/blamejs/lib/sql.js +225 -78
- package/lib/vendor/blamejs/lib/sse.js +6 -10
- package/lib/vendor/blamejs/lib/static.js +136 -98
- package/lib/vendor/blamejs/lib/storage.js +4 -2
- package/lib/vendor/blamejs/lib/structured-fields.js +275 -16
- package/lib/vendor/blamejs/lib/tenant-quota.js +2 -20
- package/lib/vendor/blamejs/lib/tsa.js +11 -15
- package/lib/vendor/blamejs/lib/validate-opts.js +154 -8
- package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +30 -48
- package/lib/vendor/blamejs/lib/vault-aad.js +3 -2
- package/lib/vendor/blamejs/lib/vc.js +2 -7
- package/lib/vendor/blamejs/lib/vex.js +35 -13
- package/lib/vendor/blamejs/lib/web-push-vapid.js +23 -20
- package/lib/vendor/blamejs/lib/webhook-dispatcher.js +706 -0
- package/lib/vendor/blamejs/lib/webhook.js +43 -18
- package/lib/vendor/blamejs/lib/websocket-channels.js +9 -7
- package/lib/vendor/blamejs/lib/websocket.js +7 -3
- package/lib/vendor/blamejs/lib/worm.js +2 -3
- package/lib/vendor/blamejs/lib/ws-client.js +8 -10
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.15.13.json +81 -0
- package/lib/vendor/blamejs/scripts/check-changelog-extract.js +1 -1
- package/lib/vendor/blamejs/test/00-primitives.js +136 -7
- package/lib/vendor/blamejs/test/10-state.js +9 -3
- package/lib/vendor/blamejs/test/30-chain.js +40 -0
- package/lib/vendor/blamejs/test/helpers/check.js +18 -0
- package/lib/vendor/blamejs/test/helpers/db.js +4 -0
- package/lib/vendor/blamejs/test/helpers/index.js +1 -0
- package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +2 -1
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +2 -1
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +8 -1
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +1 -1
- package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +2 -1
- package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +269 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-fd-read.test.js +149 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-framework-namespaces.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-use-store.test.js +55 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-object-store-adapter.test.js +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/bot-guard.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/bounded-map.test.js +129 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/chain-writer-multichain.test.js +107 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-api-key.test.js +12 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +2788 -3214
- package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +78 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-base64url.test.js +63 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +2 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-builder.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-nonce.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +2 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-resolve-opts.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +9 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract-content-gate.test.js +290 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-email.test.js +78 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +23 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-gate-disposition-coverage.test.js +93 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +60 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +184 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +54 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-text.test.js +365 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-xml.test.js +35 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-yaml.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +20 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-arf.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +39 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-greylist.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-tls.test.js +39 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mime-parse.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-revocation.test.js +98 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-release.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-memory-getorinsert.test.js +100 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-loops.test.js +278 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +37 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-jsonpath.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +2 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/session-valid-from.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +100 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/structured-fields.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/validate-opts-shape.test.js +235 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +374 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +75 -8
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +3 -3
- package/lib/winback-campaigns.js +202 -42
- package/package.json +1 -1
|
@@ -50,6 +50,8 @@ var bCrypto = require("./crypto");
|
|
|
50
50
|
var lazyRequire = require("./lazy-require");
|
|
51
51
|
var safeAsync = require("./safe-async");
|
|
52
52
|
var validateOpts = require("./validate-opts");
|
|
53
|
+
var numericBounds = require("./numeric-bounds");
|
|
54
|
+
var codepointClass = require("./codepoint-class");
|
|
53
55
|
var { GateContractError, defineClass } = require("./framework-error");
|
|
54
56
|
|
|
55
57
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
@@ -977,10 +979,8 @@ function canaryGate(gate, opts) {
|
|
|
977
979
|
function cachingGate(gate, opts) {
|
|
978
980
|
opts = opts || {};
|
|
979
981
|
var backend = opts.backend;
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
"cachingGate: opts.backend must expose { get, set } (b.cache shape)");
|
|
983
|
-
}
|
|
982
|
+
validateOpts.requireMethods(backend, ["get", "set"],
|
|
983
|
+
"cachingGate: opts.backend (b.cache shape)", GateContractError, "gate-contract/bad-opt");
|
|
984
984
|
// ttlMs is read inside check() via closure; keep DEFAULT_CACHE_TTL_MS
|
|
985
985
|
// referenced even if the host-side cache wrapping path doesn't need
|
|
986
986
|
// explicit TTL today (the gate's per-instance cache uses cacheTtlMs).
|
|
@@ -1166,6 +1166,44 @@ function makeProfileResolver(cfg) {
|
|
|
1166
1166
|
};
|
|
1167
1167
|
}
|
|
1168
1168
|
|
|
1169
|
+
/**
|
|
1170
|
+
* @primitive b.gateContract.resolveProfileName
|
|
1171
|
+
* @signature b.gateContract.resolveProfileName(opts, postures, defaultProfile)
|
|
1172
|
+
* @since 0.15.13
|
|
1173
|
+
* @status stable
|
|
1174
|
+
* @related b.gateContract.makeProfileResolver
|
|
1175
|
+
*
|
|
1176
|
+
* Resolve a profile NAME from create-time opts with PROFILE precedence: an
|
|
1177
|
+
* explicit `opts.profile` wins, else `opts.posture` mapped through the
|
|
1178
|
+
* compliance-posture table, else `defaultProfile`. Returns the name WITHOUT
|
|
1179
|
+
* validating it — the caller checks membership in its own `PROFILES` map and
|
|
1180
|
+
* throws its own typed, field-specific error. This is the resolution EXPRESSION
|
|
1181
|
+
* the mail-scanner / envelope factories (mail-greylist / mail-rbl / mail-scan /
|
|
1182
|
+
* mail-spam-score / mail-helo / guard-envelope) each hand-rolled identically.
|
|
1183
|
+
*
|
|
1184
|
+
* It differs from `makeProfileResolver` in two deliberate ways: it does not
|
|
1185
|
+
* throw (so the caller keeps its bespoke bad-profile message) and it gives
|
|
1186
|
+
* `profile` precedence rather than `posture` precedence. The two precedences
|
|
1187
|
+
* coexist in the framework today (the `defineParser`-shaped guards resolve
|
|
1188
|
+
* posture-first); unifying them is a policy decision, and routing every caller
|
|
1189
|
+
* through one of these two helpers is what makes that decision a single edit.
|
|
1190
|
+
*
|
|
1191
|
+
* @opts
|
|
1192
|
+
* profile: string, // explicit profile name — wins when present
|
|
1193
|
+
* posture: string, // compliance posture, mapped through `postures`
|
|
1194
|
+
*
|
|
1195
|
+
* @example
|
|
1196
|
+
* var name = b.gateContract.resolveProfileName(
|
|
1197
|
+
* { profile: "balanced" }, COMPLIANCE_POSTURES, "strict");
|
|
1198
|
+
* // → "balanced"
|
|
1199
|
+
*/
|
|
1200
|
+
function resolveProfileName(opts, postures, defaultProfile) {
|
|
1201
|
+
opts = opts || {};
|
|
1202
|
+
return opts.profile ||
|
|
1203
|
+
(opts.posture && postures && postures[opts.posture]) ||
|
|
1204
|
+
defaultProfile;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1169
1207
|
/**
|
|
1170
1208
|
* @primitive b.gateContract.throwOnRefusalSeverity
|
|
1171
1209
|
* @signature b.gateContract.throwOnRefusalSeverity(issues, cfg)
|
|
@@ -1249,6 +1287,244 @@ var ALL_STRICT_POSTURES = Object.freeze({
|
|
|
1249
1287
|
soc2: "strict",
|
|
1250
1288
|
});
|
|
1251
1289
|
|
|
1290
|
+
/**
|
|
1291
|
+
* @primitive b.gateContract.CHAR_THREATS_REJECT_ALL
|
|
1292
|
+
* @signature b.gateContract.CHAR_THREATS_REJECT_ALL
|
|
1293
|
+
* @since 0.15.13
|
|
1294
|
+
* @status stable
|
|
1295
|
+
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
1296
|
+
* @related b.gateContract.charThreatDisposition, b.gateContract.makeProfileBuilder
|
|
1297
|
+
*
|
|
1298
|
+
* The universal character-safety floor: the four invisible-character
|
|
1299
|
+
* threats — BIDI overrides, C0/C1 control bytes, embedded null bytes,
|
|
1300
|
+
* and zero-width characters — each set to `"reject"`. These four
|
|
1301
|
+
* classes are categorically unsafe in an identifier or structured
|
|
1302
|
+
* value (forgery, log injection, label-segmentation, parser
|
|
1303
|
+
* confusion), so every identifier/protocol guard refuses them in
|
|
1304
|
+
* every profile tier and the content guards refuse them in `strict`.
|
|
1305
|
+
*
|
|
1306
|
+
* Spread this frozen block into a profile tier instead of re-declaring
|
|
1307
|
+
* the four lines: `{ ...gateContract.CHAR_THREATS_REJECT_ALL, ... }`.
|
|
1308
|
+
* A tier that relaxes one class overrides after the spread (e.g.
|
|
1309
|
+
* `{ ...CHAR_THREATS_REJECT_ALL, zeroWidthPolicy: "strip" }`), keeping
|
|
1310
|
+
* the floor for the other three. Frozen and shared by reference; the
|
|
1311
|
+
* spread copies the values into each consumer's own tier object.
|
|
1312
|
+
*
|
|
1313
|
+
* @example
|
|
1314
|
+
* var PROFILES = Object.freeze({
|
|
1315
|
+
* strict: { ...b.gateContract.CHAR_THREATS_REJECT_ALL, maxBytes: 256 },
|
|
1316
|
+
* });
|
|
1317
|
+
* PROFILES.strict.bidiPolicy; // → "reject"
|
|
1318
|
+
* Object.isFrozen(b.gateContract.CHAR_THREATS_REJECT_ALL); // → true
|
|
1319
|
+
*/
|
|
1320
|
+
var CHAR_THREATS_REJECT_ALL = Object.freeze({
|
|
1321
|
+
bidiPolicy: "reject",
|
|
1322
|
+
controlPolicy: "reject",
|
|
1323
|
+
nullBytePolicy: "reject",
|
|
1324
|
+
zeroWidthPolicy: "reject",
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
/**
|
|
1328
|
+
* @primitive b.gateContract.DANGEROUS_URL_SCHEMES
|
|
1329
|
+
* @signature b.gateContract.DANGEROUS_URL_SCHEMES
|
|
1330
|
+
* @since 0.15.13
|
|
1331
|
+
* @status stable
|
|
1332
|
+
* @compliance soc2
|
|
1333
|
+
* @related b.gateContract.CHAR_THREATS_REJECT_ALL, b.guardHtml.validate, b.guardSvg.validate
|
|
1334
|
+
*
|
|
1335
|
+
* The frozen denylist of URL schemes that are categorically unsafe inside a
|
|
1336
|
+
* markup attribute value (`href` / `src` / `xlink:href`) — the markup XSS and
|
|
1337
|
+
* dangerous-resource vector set. `javascript` / `vbscript` / `livescript` /
|
|
1338
|
+
* `mocha` / `ecmascript` execute script; `data` / `view-source` / `mhtml` /
|
|
1339
|
+
* `feed` carry or expose renderable content; `file` / `jar` / `intent` reach
|
|
1340
|
+
* local resources or protocol handlers. A markup sanitizer rejects an
|
|
1341
|
+
* attribute whose scheme is in this list.
|
|
1342
|
+
*
|
|
1343
|
+
* Lower-cased, scheme-name only (no trailing colon) so callers compare against
|
|
1344
|
+
* a lower-cased parsed scheme via `indexOf(scheme) !== -1`. This is the
|
|
1345
|
+
* markup-attribute DENYLIST — distinct from `b.safeUrl`'s protocol ALLOWLIST,
|
|
1346
|
+
* which governs full-URL parsing where only an explicit set of protocols is
|
|
1347
|
+
* permitted.
|
|
1348
|
+
*
|
|
1349
|
+
* @example
|
|
1350
|
+
* b.gateContract.DANGEROUS_URL_SCHEMES.indexOf("javascript"); // → 0 (dangerous)
|
|
1351
|
+
* b.gateContract.DANGEROUS_URL_SCHEMES.indexOf("https"); // → -1 (allowed)
|
|
1352
|
+
* Object.isFrozen(b.gateContract.DANGEROUS_URL_SCHEMES); // → true
|
|
1353
|
+
*/
|
|
1354
|
+
var DANGEROUS_URL_SCHEMES = Object.freeze([
|
|
1355
|
+
"javascript", "vbscript", "livescript", "mocha", "ecmascript",
|
|
1356
|
+
"file", "mhtml", "jar", "intent", "view-source", "feed", "data",
|
|
1357
|
+
]);
|
|
1358
|
+
|
|
1359
|
+
/**
|
|
1360
|
+
* @primitive b.gateContract.SAFE_URL_SCHEMES
|
|
1361
|
+
* @signature b.gateContract.SAFE_URL_SCHEMES
|
|
1362
|
+
* @since 0.15.13
|
|
1363
|
+
* @status stable
|
|
1364
|
+
* @compliance soc2
|
|
1365
|
+
* @related b.gateContract.DANGEROUS_URL_SCHEMES
|
|
1366
|
+
*
|
|
1367
|
+
* The frozen base allowlist of URL schemes a markup sanitizer accepts in an
|
|
1368
|
+
* attribute value at the `strict` tier — `http` / `https` / `mailto` / `tel`.
|
|
1369
|
+
* A guard extends it for looser tiers (e.g. `SAFE_URL_SCHEMES.concat(["ftp"])`)
|
|
1370
|
+
* rather than re-declaring the base. Scheme names only, no trailing colon.
|
|
1371
|
+
*
|
|
1372
|
+
* @example
|
|
1373
|
+
* b.gateContract.SAFE_URL_SCHEMES; // → ["http","https","mailto","tel"]
|
|
1374
|
+
* Object.isFrozen(b.gateContract.SAFE_URL_SCHEMES); // → true
|
|
1375
|
+
*/
|
|
1376
|
+
var SAFE_URL_SCHEMES = Object.freeze(["http", "https", "mailto", "tel"]);
|
|
1377
|
+
|
|
1378
|
+
/**
|
|
1379
|
+
* @primitive b.gateContract.identifierFixtures
|
|
1380
|
+
* @signature b.gateContract.identifierFixtures(benign, hostile, encoding?)
|
|
1381
|
+
* @since 0.15.13
|
|
1382
|
+
* @status stable
|
|
1383
|
+
* @related b.gateContract.defineGuard
|
|
1384
|
+
*
|
|
1385
|
+
* Build an identifier guard's frozen `INTEGRATION_FIXTURES` from one benign
|
|
1386
|
+
* and one hostile sample string. The layer-5 host harness feeds the string
|
|
1387
|
+
* form to `gate.check({ identifier })` and the byte form to the upload /
|
|
1388
|
+
* digest paths, so the two are the same value in two representations. A guard
|
|
1389
|
+
* that hand-writes both forms repeats its sample literal twice (`benignBytes:
|
|
1390
|
+
* Buffer.from("x"), benignIdentifier: "x"`); declaring the string once and
|
|
1391
|
+
* deriving the buffer removes that per-guard duplication. `encoding` defaults
|
|
1392
|
+
* to `"utf8"` — pass `"ascii"` for line-protocol command samples whose bytes
|
|
1393
|
+
* must stay single-octet.
|
|
1394
|
+
*
|
|
1395
|
+
* @example
|
|
1396
|
+
* var INTEGRATION_FIXTURES =
|
|
1397
|
+
* b.gateContract.identifierFixtures("example.com", "192.168.1.1");
|
|
1398
|
+
* INTEGRATION_FIXTURES.benignIdentifier; // → "example.com"
|
|
1399
|
+
* INTEGRATION_FIXTURES.benignBytes; // → Buffer "example.com"
|
|
1400
|
+
* Object.isFrozen(INTEGRATION_FIXTURES); // → true
|
|
1401
|
+
*/
|
|
1402
|
+
function identifierFixtures(benign, hostile, encoding) {
|
|
1403
|
+
validateOpts.requireNonEmptyString(benign,
|
|
1404
|
+
"gateContract.identifierFixtures: benign", GateContractError, "gate-contract/bad-opt");
|
|
1405
|
+
validateOpts.requireNonEmptyString(hostile,
|
|
1406
|
+
"gateContract.identifierFixtures: hostile", GateContractError, "gate-contract/bad-opt");
|
|
1407
|
+
var enc = encoding || "utf8";
|
|
1408
|
+
if (typeof enc !== "string" || !Buffer.isEncoding(enc)) {
|
|
1409
|
+
throw _err("gate-contract/bad-opt",
|
|
1410
|
+
"gateContract.identifierFixtures: encoding must be a valid Buffer encoding, got " +
|
|
1411
|
+
JSON.stringify(encoding));
|
|
1412
|
+
}
|
|
1413
|
+
return Object.freeze({
|
|
1414
|
+
kind: "identifier",
|
|
1415
|
+
benignBytes: Buffer.from(benign, enc),
|
|
1416
|
+
hostileBytes: Buffer.from(hostile, enc),
|
|
1417
|
+
benignIdentifier: benign,
|
|
1418
|
+
hostileIdentifier: hostile,
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
/**
|
|
1423
|
+
* @primitive b.gateContract.compliancePostures
|
|
1424
|
+
* @signature b.gateContract.compliancePostures(profiles, spec)
|
|
1425
|
+
* @since 0.15.13
|
|
1426
|
+
* @status stable
|
|
1427
|
+
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
1428
|
+
* @related b.gateContract.ALL_STRICT_POSTURES, b.gateContract.resolveProfileAndPosture
|
|
1429
|
+
*
|
|
1430
|
+
* Build a content guard's four-posture `COMPLIANCE_POSTURES` map from its
|
|
1431
|
+
* profile set and a single forensic-snippet budget, encoding the framework's
|
|
1432
|
+
* regulation-disposition policy in one place instead of re-declaring it in
|
|
1433
|
+
* every guard. Each regulation maps to the profile tier whose disposition
|
|
1434
|
+
* matches its intent:
|
|
1435
|
+
*
|
|
1436
|
+
* - `hipaa` / `pci-dss` / `soc2` → the `strict` profile. These regimes
|
|
1437
|
+
* demand forensic integrity — the record must not be silently
|
|
1438
|
+
* altered — so every threat class is rejected, never sanitized.
|
|
1439
|
+
* - `gdpr` → the `balanced` profile. Data-minimization favors removing
|
|
1440
|
+
* the offending bytes over rejecting the whole value, so on free-text
|
|
1441
|
+
* content the balanced tier strips the sanitizable character classes (bidi
|
|
1442
|
+
* / control / zero-width) while still rejecting structural threats. For an
|
|
1443
|
+
* identifier guard the balanced tier rejects those classes too —
|
|
1444
|
+
* stripping bytes from an identifier would change its identity — so
|
|
1445
|
+
* the disposition follows the guard's own content kind automatically.
|
|
1446
|
+
*
|
|
1447
|
+
* The forensic snippet budget scales with each regime's retention posture:
|
|
1448
|
+
* `hipaa` / `pci-dss` keep `base` bytes, `gdpr` keeps `base / 2` (retain less
|
|
1449
|
+
* hostile data under data-minimization), `soc2` keeps `base * 2` (audit
|
|
1450
|
+
* retention). Pass `spec.overlays` to layer a deliberate per-posture delta on
|
|
1451
|
+
* top of the tier — e.g. a filename guard stripping bidi / control under
|
|
1452
|
+
* `gdpr` where its balanced profile would reject them. Each returned posture is
|
|
1453
|
+
* frozen and shared by reference.
|
|
1454
|
+
*
|
|
1455
|
+
* @opts
|
|
1456
|
+
* {
|
|
1457
|
+
* base: number, // required, positive even byte count: the hipaa/pci snippet budget
|
|
1458
|
+
* overlays: { // optional per-posture policy deltas, merged last
|
|
1459
|
+
* hipaa: object,
|
|
1460
|
+
* "pci-dss": object,
|
|
1461
|
+
* gdpr: object,
|
|
1462
|
+
* soc2: object,
|
|
1463
|
+
* },
|
|
1464
|
+
* }
|
|
1465
|
+
*
|
|
1466
|
+
* @example
|
|
1467
|
+
* var COMPLIANCE_POSTURES = b.gateContract.compliancePostures(PROFILES, {
|
|
1468
|
+
* base: 256,
|
|
1469
|
+
* });
|
|
1470
|
+
* COMPLIANCE_POSTURES.gdpr.forensicSnippetBytes; // → 128
|
|
1471
|
+
* Object.isFrozen(COMPLIANCE_POSTURES.hipaa); // → true
|
|
1472
|
+
*/
|
|
1473
|
+
function compliancePostures(profiles, spec) {
|
|
1474
|
+
validateOpts.requireObject(profiles, "gateContract.compliancePostures", GateContractError);
|
|
1475
|
+
if (!profiles.strict || !profiles.balanced) {
|
|
1476
|
+
throw _err("gate-contract/bad-profiles",
|
|
1477
|
+
"compliancePostures: profiles must include 'strict' and 'balanced'");
|
|
1478
|
+
}
|
|
1479
|
+
spec = spec || {};
|
|
1480
|
+
var base = spec.base;
|
|
1481
|
+
if (typeof base !== "number" || !isFinite(base) || base <= 0 || base % 2 !== 0) {
|
|
1482
|
+
throw _err("gate-contract/bad-base",
|
|
1483
|
+
"compliancePostures: spec.base must be a positive even byte count");
|
|
1484
|
+
}
|
|
1485
|
+
var overlays = spec.overlays || {};
|
|
1486
|
+
function build(tier, snippetBytes, overlay) {
|
|
1487
|
+
return Object.freeze(Object.assign({}, profiles[tier],
|
|
1488
|
+
{ forensicSnippetBytes: C.BYTES.bytes(snippetBytes) }, overlay || {}));
|
|
1489
|
+
}
|
|
1490
|
+
return Object.freeze({
|
|
1491
|
+
hipaa: build("strict", base, overlays.hipaa),
|
|
1492
|
+
"pci-dss": build("strict", base, overlays["pci-dss"]),
|
|
1493
|
+
gdpr: build("balanced", base / 2, overlays.gdpr),
|
|
1494
|
+
soc2: build("strict", base * 2, overlays.soc2),
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
/**
|
|
1499
|
+
* @primitive b.gateContract.strictDefaults
|
|
1500
|
+
* @signature b.gateContract.strictDefaults(profiles, overlay?)
|
|
1501
|
+
* @since 0.15.13
|
|
1502
|
+
* @status stable
|
|
1503
|
+
* @related b.gateContract.compliancePostures, b.gateContract.defineGuard
|
|
1504
|
+
*
|
|
1505
|
+
* Build a guard's frozen `DEFAULTS` opts: its `strict` profile, in `enforce`
|
|
1506
|
+
* mode, plus any per-guard overlay. Every guard's no-opts call path starts from
|
|
1507
|
+
* the strictest profile with enforcement on (security-on by default); the only
|
|
1508
|
+
* variation is a guard that adds a parse runtime cap (`maxRuntimeMs`) or another
|
|
1509
|
+
* default override. Replaces the hand-rolled `Object.freeze(Object.assign({},
|
|
1510
|
+
* PROFILES["strict"], { mode: "enforce", … }))` every guard repeated.
|
|
1511
|
+
*
|
|
1512
|
+
* @opts
|
|
1513
|
+
* overlay: object // optional per-guard default overrides merged last (e.g. { maxRuntimeMs: C.TIME.seconds(10) }); may override `mode`
|
|
1514
|
+
*
|
|
1515
|
+
* @example
|
|
1516
|
+
* var DEFAULTS = b.gateContract.strictDefaults(PROFILES); // strict + enforce
|
|
1517
|
+
* var DEFAULTS = b.gateContract.strictDefaults(PROFILES, { maxRuntimeMs: 10000 }); // + a parse runtime cap
|
|
1518
|
+
*/
|
|
1519
|
+
function strictDefaults(profiles, overlay) {
|
|
1520
|
+
validateOpts.requireObject(profiles, "gateContract.strictDefaults", GateContractError);
|
|
1521
|
+
if (!profiles.strict) {
|
|
1522
|
+
throw _err("gate-contract/bad-profiles",
|
|
1523
|
+
"strictDefaults: profiles must include 'strict'");
|
|
1524
|
+
}
|
|
1525
|
+
return Object.freeze(Object.assign({}, profiles.strict, { mode: "enforce" }, overlay || {}));
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1252
1528
|
/**
|
|
1253
1529
|
* @primitive b.gateContract.makeRulePackLoader
|
|
1254
1530
|
* @signature b.gateContract.makeRulePackLoader(errorClass, codePrefix)
|
|
@@ -1386,6 +1662,234 @@ function buildGuardGate(name, opts, check) {
|
|
|
1386
1662
|
});
|
|
1387
1663
|
}
|
|
1388
1664
|
|
|
1665
|
+
/**
|
|
1666
|
+
* @primitive b.gateContract.severityDisposition
|
|
1667
|
+
* @signature b.gateContract.severityDisposition(issues)
|
|
1668
|
+
* @since 0.15.13
|
|
1669
|
+
* @status stable
|
|
1670
|
+
* @related b.gateContract.buildGuardGate, b.gateContract.buildContentGate
|
|
1671
|
+
*
|
|
1672
|
+
* The non-sanitizing guard gate's severity action-chain in one place. A guard
|
|
1673
|
+
* that cannot repair its subject (an auth bundle, an OAuth flow, a GraphQL
|
|
1674
|
+
* request, image / PDF metadata, an archive entry list, an email body, a regex
|
|
1675
|
+
* pattern) ends its `gate` check with the identical disposition: `serve` when
|
|
1676
|
+
* there are no findings, `audit-only` when no finding reaches refusal severity,
|
|
1677
|
+
* else `refuse`. This is the sibling of `buildContentGate` (which adds the
|
|
1678
|
+
* sanitize attempt for content that CAN be repaired). Each guard keeps its own
|
|
1679
|
+
* subject extraction + validate call, then returns `severityDisposition(rv.issues)`.
|
|
1680
|
+
*
|
|
1681
|
+
* A finding of `critical` OR `high` severity refuses; anything lower is
|
|
1682
|
+
* `audit-only`. The returned shape matches a gate `check` result:
|
|
1683
|
+
* `{ ok, action }` (no `issues` on a clean serve) or `{ ok, action, issues }`.
|
|
1684
|
+
*
|
|
1685
|
+
* @example
|
|
1686
|
+
* var rv = module.exports.validate(bundle, opts);
|
|
1687
|
+
* return b.gateContract.severityDisposition(rv.issues);
|
|
1688
|
+
* // [] → { ok: true, action: "serve" }
|
|
1689
|
+
* // [{ severity: "low" }] → { ok: true, action: "audit-only", issues: [...] }
|
|
1690
|
+
* // [{ severity: "high" }] → { ok: false, action: "refuse", issues: [...] }
|
|
1691
|
+
*/
|
|
1692
|
+
function severityDisposition(issues) {
|
|
1693
|
+
if (issues.length === 0) return { ok: true, action: "serve" };
|
|
1694
|
+
var hasCritical = issues.some(function (i) { return i.severity === "critical"; });
|
|
1695
|
+
var hasHigh = issues.some(function (i) { return i.severity === "high"; });
|
|
1696
|
+
if (!hasCritical && !hasHigh) {
|
|
1697
|
+
return { ok: true, action: "audit-only", issues: issues };
|
|
1698
|
+
}
|
|
1699
|
+
return { ok: false, action: "refuse", issues: issues };
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
/**
|
|
1703
|
+
* @primitive b.gateContract.buildContentGate
|
|
1704
|
+
* @signature b.gateContract.buildContentGate(spec)
|
|
1705
|
+
* @since 0.15.13
|
|
1706
|
+
* @status stable
|
|
1707
|
+
* @related b.gateContract.buildGuardGate, b.gateContract.defineGuard
|
|
1708
|
+
*
|
|
1709
|
+
* The content-guard gate action-chain in one place. Every content guard's
|
|
1710
|
+
* `gate(opts)` ran the identical chain — extract the bytes, `serve` a clean
|
|
1711
|
+
* input, `audit-only` when no issue reaches the refusal severity, attempt
|
|
1712
|
+
* `sanitize` when the input is eligible, else `refuse` — differing only in
|
|
1713
|
+
* declarative axes. Passing those axes to one primitive replaces ~30 lines of
|
|
1714
|
+
* per-guard gate body and makes the chain impossible to drift between guards.
|
|
1715
|
+
*
|
|
1716
|
+
* A CRITICAL finding always `refuse`s — too severe to serve even sanitized (a
|
|
1717
|
+
* stripped script can still carry an mXSS / parser-differential vector). Only
|
|
1718
|
+
* HIGH findings are sanitize-eligible, and even then the action is PROVEN, not
|
|
1719
|
+
* guessed: the gate runs `produceSanitized` then RE-VALIDATES its output,
|
|
1720
|
+
* returning `sanitize` only when the result is verifiably clean — otherwise
|
|
1721
|
+
* `refuse`. An operator's `reject` choice lands as critical (→ refuse) and a
|
|
1722
|
+
* `strip` choice as high (→ sanitize-if-verified), so the severity carries the
|
|
1723
|
+
* disposition with no per-policy bookkeeping — replacing a global "is any
|
|
1724
|
+
* policy set to reject?" guess that wrongly froze sanitize for findings
|
|
1725
|
+
* unrelated to the rejected policy. The sanitizer's own policy-respecting
|
|
1726
|
+
* behaviour does the rest: it throws on / leaves a reject-class finding so the
|
|
1727
|
+
* re-validate still refuses, and refuses anything it cannot actually repair.
|
|
1728
|
+
* `sanitizeBlockingKinds` skips the attempt for inputs a text sanitizer must not
|
|
1729
|
+
* touch (gzipped SVGZ bytes); a thrown producer falls through to `refuse`.
|
|
1730
|
+
*
|
|
1731
|
+
* @opts
|
|
1732
|
+
* name: string, // gate label (audit/metric/cache identity)
|
|
1733
|
+
* opts: object, // already resolved profile/posture opts
|
|
1734
|
+
* validate: function, // (subject, opts) -> { ok, issues }
|
|
1735
|
+
* produceSanitized: function, // (subject, opts) -> Buffer|string
|
|
1736
|
+
* ctxField: "text"|"bytes", // default "text" (extractBytesAsText); "bytes" reads ctx.bytes raw
|
|
1737
|
+
* sanitizeBlockingKinds: string[], // issue kinds that skip the sanitize attempt (e.g. ["svgz-compressed"])
|
|
1738
|
+
*
|
|
1739
|
+
* @example
|
|
1740
|
+
* var g = b.gateContract.buildContentGate({
|
|
1741
|
+
* name: "guardXml:strict", opts: resolved, validate: validate,
|
|
1742
|
+
* produceSanitized: function (t, o) { return sanitize(t, o); },
|
|
1743
|
+
* });
|
|
1744
|
+
* (await g.check({ bytes: Buffer.from("<a>1</a>") })).action; // → "serve"
|
|
1745
|
+
*/
|
|
1746
|
+
function buildContentGate(spec) {
|
|
1747
|
+
var opts = spec.opts || {};
|
|
1748
|
+
var ctxField = spec.ctxField === "bytes" ? "bytes" : "text";
|
|
1749
|
+
var blockKind = Array.isArray(spec.sanitizeBlockingKinds) ? spec.sanitizeBlockingKinds : [];
|
|
1750
|
+
|
|
1751
|
+
// A finding's disposition is what the operator's POLICY for that finding
|
|
1752
|
+
// class chose, not the finding's impact severity. The guard declares the
|
|
1753
|
+
// binding via spec.dispositionFor(issue, opts); anything it doesn't classify
|
|
1754
|
+
// (an operator-injected rule, a structural hard-cap, an un-mapped kind) falls
|
|
1755
|
+
// back to severity — and that fallback is CONSERVATIVE (a refusal-severity
|
|
1756
|
+
// finding refuses, because the gate has no proof it can be repaired).
|
|
1757
|
+
function _disposition(issue, fromGuard) {
|
|
1758
|
+
if (fromGuard && typeof spec.dispositionFor === "function") {
|
|
1759
|
+
var d = spec.dispositionFor(issue, opts);
|
|
1760
|
+
if (d === "refuse" || d === "sanitize" || d === "audit") return d;
|
|
1761
|
+
}
|
|
1762
|
+
if (issue.disposition === "refuse" || issue.disposition === "sanitize" ||
|
|
1763
|
+
issue.disposition === "audit") return issue.disposition;
|
|
1764
|
+
return (issue.severity === "critical" || issue.severity === "high") ? "refuse" : "audit";
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
return buildGuardGate(spec.name, opts, async function (ctx) {
|
|
1768
|
+
var subject = ctxField === "bytes" ? (ctx && ctx.bytes) : extractBytesAsText(ctx);
|
|
1769
|
+
if (!subject) return { ok: true, action: "serve" };
|
|
1770
|
+
|
|
1771
|
+
var rv = spec.validate(subject, opts);
|
|
1772
|
+
var entries = rv.issues.map(function (i) { return { issue: i, disp: _disposition(i, true) }; });
|
|
1773
|
+
// Operator-injected detect-only findings (spec.extraIssues): the guard owns
|
|
1774
|
+
// no sanitizer for them, so a refusal-severity hit can only refuse — never
|
|
1775
|
+
// serve a "sanitized" output that still carries the operator's finding.
|
|
1776
|
+
if (typeof spec.extraIssues === "function") {
|
|
1777
|
+
var extra = spec.extraIssues(subject, opts, ctx) || [];
|
|
1778
|
+
for (var k = 0; k < extra.length; k++) {
|
|
1779
|
+
entries.push({ issue: extra[k], disp: _disposition(extra[k], false) });
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
if (entries.length === 0) return { ok: true, action: "serve" };
|
|
1783
|
+
|
|
1784
|
+
var issues = entries.map(function (e) { return e.issue; });
|
|
1785
|
+
// refuse wins over sanitize wins over audit. A finding whose policy is
|
|
1786
|
+
// `reject` (→ refuse), an always-dangerous denylist hit, or a structural
|
|
1787
|
+
// hard-cap refuses the whole input regardless of any sanitizable siblings.
|
|
1788
|
+
if (entries.some(function (e) { return e.disp === "refuse"; })) {
|
|
1789
|
+
return { ok: false, action: "refuse", issues: issues };
|
|
1790
|
+
}
|
|
1791
|
+
// Some finding's policy chose to mitigate (strip / prefix / redact / drop):
|
|
1792
|
+
// run the guard's own sanitizer, which performs exactly the policy-selected
|
|
1793
|
+
// transform, and serve its output. The operator picked sanitize-and-serve
|
|
1794
|
+
// over reject for these classes, so the result is trusted — no re-validation
|
|
1795
|
+
// (a mitigation like CSV's prefix-tab is in-place, not removal, so a second
|
|
1796
|
+
// detector pass would wrongly refuse it). sanitizeBlockingKinds skips the
|
|
1797
|
+
// attempt for inputs a text sanitizer must not touch (gzipped SVGZ bytes);
|
|
1798
|
+
// a sanitizer that throws (e.g. a reject-policy parse) falls through.
|
|
1799
|
+
if (entries.some(function (e) { return e.disp === "sanitize"; })) {
|
|
1800
|
+
var blocked = blockKind.some(function (kind) {
|
|
1801
|
+
return issues.some(function (it) { return it.kind === kind; });
|
|
1802
|
+
});
|
|
1803
|
+
if (!blocked && typeof spec.produceSanitized === "function") {
|
|
1804
|
+
try {
|
|
1805
|
+
var clean = spec.produceSanitized(subject, opts);
|
|
1806
|
+
var cleanBuf = Buffer.isBuffer(clean) ? clean : Buffer.from(String(clean), "utf8");
|
|
1807
|
+
return { ok: true, action: "sanitize", sanitized: cleanBuf, issues: issues };
|
|
1808
|
+
} catch (_e) { /* sanitizer could not repair → refuse */ }
|
|
1809
|
+
}
|
|
1810
|
+
return { ok: false, action: "refuse", issues: issues };
|
|
1811
|
+
}
|
|
1812
|
+
// Only audit-disposition (sub-refusal observational) findings remain.
|
|
1813
|
+
return { ok: true, action: "audit-only", issues: issues };
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
/**
|
|
1818
|
+
* @primitive b.gateContract.policyDisposition
|
|
1819
|
+
* @signature b.gateContract.policyDisposition(policy)
|
|
1820
|
+
* @since 0.15.13
|
|
1821
|
+
* @status stable
|
|
1822
|
+
* @related b.gateContract.buildContentGate
|
|
1823
|
+
*
|
|
1824
|
+
* Map an operator content-policy value to the gate disposition it selects.
|
|
1825
|
+
* A content guard emits a finding only when the governing policy is not
|
|
1826
|
+
* `allow`; this turns that policy into what the gate should DO with the
|
|
1827
|
+
* finding — independent of the finding's impact severity:
|
|
1828
|
+
*
|
|
1829
|
+
* - `reject` → `refuse` (operator chose to reject this class outright)
|
|
1830
|
+
* - `audit` / `audit-only` → `audit` (observe, do not block or alter)
|
|
1831
|
+
* - a known mitigation (`strip`, `prefix-tab`, `prefix-quote`,
|
|
1832
|
+
* `wrap-with-quotes-and-prefix`, `allowlist`, `redact`, `trim`) →
|
|
1833
|
+
* `sanitize` (the guard's sanitizer performs the chosen transform)
|
|
1834
|
+
*
|
|
1835
|
+
* Fails CLOSED: an unrecognized policy value (a typo such as `rejet`, or a
|
|
1836
|
+
* mitigation name not in the known set) maps to `refuse`, never `sanitize` —
|
|
1837
|
+
* a misconfiguration must not silently downgrade a finding to serve-after-
|
|
1838
|
+
* best-effort. Add a new mitigation to MITIGATION_POLICIES when one ships.
|
|
1839
|
+
*
|
|
1840
|
+
* @example
|
|
1841
|
+
* b.gateContract.policyDisposition("reject"); // → "refuse"
|
|
1842
|
+
* b.gateContract.policyDisposition("strip"); // → "sanitize"
|
|
1843
|
+
* b.gateContract.policyDisposition("audit-only"); // → "audit"
|
|
1844
|
+
* b.gateContract.policyDisposition("rejet"); // → "refuse" (fail closed)
|
|
1845
|
+
*/
|
|
1846
|
+
var MITIGATION_POLICIES = Object.freeze({
|
|
1847
|
+
strip: true, "prefix-tab": true, "prefix-quote": true,
|
|
1848
|
+
"wrap-with-quotes-and-prefix": true, allowlist: true, redact: true, trim: true,
|
|
1849
|
+
});
|
|
1850
|
+
function policyDisposition(policy) {
|
|
1851
|
+
if (policy === "reject") return "refuse";
|
|
1852
|
+
if (policy === "audit" || policy === "audit-only") return "audit";
|
|
1853
|
+
if (MITIGATION_POLICIES[policy] === true) return "sanitize";
|
|
1854
|
+
return "refuse";
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
/**
|
|
1858
|
+
* @primitive b.gateContract.charThreatDisposition
|
|
1859
|
+
* @signature b.gateContract.charThreatDisposition(issue, opts)
|
|
1860
|
+
* @since 0.15.13
|
|
1861
|
+
* @status stable
|
|
1862
|
+
* @related b.gateContract.policyDisposition, b.codepointClass.detectCharThreats
|
|
1863
|
+
*
|
|
1864
|
+
* Gate disposition for the shared character-threat findings every content
|
|
1865
|
+
* guard collects via `codepointClass.detectCharThreats` — `bidi-override`,
|
|
1866
|
+
* `null-byte`, `control-char` — resolved from the guard's per-class policy
|
|
1867
|
+
* (`bidiPolicy` / `nullBytePolicy` / `controlPolicy`). Returns `null` for any
|
|
1868
|
+
* other kind so a guard's own `dispositionFor` can fall through to it for the
|
|
1869
|
+
* shared kinds and handle its guard-specific findings itself.
|
|
1870
|
+
*
|
|
1871
|
+
* @opts
|
|
1872
|
+
* bidiPolicy: string, // governs the bidi-override finding
|
|
1873
|
+
* nullBytePolicy: string, // governs the null-byte finding
|
|
1874
|
+
* controlPolicy: string, // governs the control-char finding
|
|
1875
|
+
* zeroWidthPolicy: string, // governs the zero-width finding
|
|
1876
|
+
*
|
|
1877
|
+
* @example
|
|
1878
|
+
* function dispositionFor(issue, opts) {
|
|
1879
|
+
* return b.gateContract.charThreatDisposition(issue, opts) ||
|
|
1880
|
+
* mySpecificMapping(issue, opts);
|
|
1881
|
+
* }
|
|
1882
|
+
*/
|
|
1883
|
+
function charThreatDisposition(issue, opts) {
|
|
1884
|
+
switch (issue.kind) {
|
|
1885
|
+
case "bidi-override": return policyDisposition(opts.bidiPolicy);
|
|
1886
|
+
case "null-byte": return policyDisposition(opts.nullBytePolicy);
|
|
1887
|
+
case "control-char": return policyDisposition(opts.controlPolicy);
|
|
1888
|
+
case "zero-width": return policyDisposition(opts.zeroWidthPolicy);
|
|
1889
|
+
default: return null;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1389
1893
|
/**
|
|
1390
1894
|
* @primitive b.gateContract.aggregateIssues
|
|
1391
1895
|
* @signature b.gateContract.aggregateIssues(issues)
|
|
@@ -1439,34 +1943,159 @@ function aggregateIssues(issues) {
|
|
|
1439
1943
|
* bad.issues[0].kind; // → "bad-input"
|
|
1440
1944
|
*/
|
|
1441
1945
|
function badInputResultIfNotStringOrBuffer(input) {
|
|
1442
|
-
|
|
1946
|
+
// The "bytes" input contract IS this check — compose it so there is one
|
|
1947
|
+
// source of truth for "string or Buffer, else bad-input".
|
|
1948
|
+
var extracted = INPUT_CONTRACTS.bytes(input);
|
|
1949
|
+
if (!extracted.badInput) return null;
|
|
1443
1950
|
return {
|
|
1444
1951
|
ok: false,
|
|
1445
1952
|
issues: [{ kind: "bad-input", severity: "high",
|
|
1446
|
-
snippet:
|
|
1953
|
+
snippet: extracted.badInput }],
|
|
1447
1954
|
};
|
|
1448
1955
|
}
|
|
1449
1956
|
|
|
1957
|
+
/**
|
|
1958
|
+
* @primitive b.gateContract.detectStringInput
|
|
1959
|
+
* @signature b.gateContract.detectStringInput(input, opts, cfg)
|
|
1960
|
+
* @since 0.15.13
|
|
1961
|
+
* @status stable
|
|
1962
|
+
* @related b.gateContract.badInputResultIfNotStringOrBuffer, b.codepointClass.detectCharThreats
|
|
1963
|
+
*
|
|
1964
|
+
* The whole detector preamble every `raw`-contract string guard opens with:
|
|
1965
|
+
* reject a non-string input, then an empty one, then one over the byte cap,
|
|
1966
|
+
* else collect the codepoint-class threats (BIDI / control / null / zero-width)
|
|
1967
|
+
* the guard then appends its own findings to. A guard on the `raw` input
|
|
1968
|
+
* contract owns its own input check (see `INPUT_CONTRACTS`); this builds that
|
|
1969
|
+
* preamble once, guard-named, instead of re-spelling its four steps in every
|
|
1970
|
+
* `_detectIssues`. Returns `{ done, issues }`: when `done` the detector returns
|
|
1971
|
+
* `issues` verbatim (the `<name>.bad-input` / `<name>.empty` / cap issue, or
|
|
1972
|
+
* `[]` for a legal empty); when not `done`, `issues` is the codepoint-threat
|
|
1973
|
+
* list the detector continues from. The byte cap runs before the codepoint
|
|
1974
|
+
* scan so a huge input is rejected before the O(n) scan.
|
|
1975
|
+
*
|
|
1976
|
+
* The cap's divergence is data, not branching: `cap.bytes` is the limit (the
|
|
1977
|
+
* guard's resolved `maxBytes` / `maxPatternBytes` / `maxDomainOctets`),
|
|
1978
|
+
* `cap.kind` the issue kind (default `<name>-cap`), and `cap.snippet` the
|
|
1979
|
+
* message — a string, or a `function(byteLen, bytes)` when it embeds the
|
|
1980
|
+
* measured length. Omit `cap` for a guard with no byte cap.
|
|
1981
|
+
*
|
|
1982
|
+
* @opts
|
|
1983
|
+
* {
|
|
1984
|
+
* name: string, // required: the guard name — ruleId prefix + default noun
|
|
1985
|
+
* noun: string, // default name — the subject word in the bad-input/empty snippet
|
|
1986
|
+
* emptyMode: string, // "issue" (default) → <name>.empty issue · "ok" → [] (empty is legal) · "skip" → no empty check
|
|
1987
|
+
* cap: { // omit when the guard has no byte cap
|
|
1988
|
+
* bytes: number, // required: the byte limit
|
|
1989
|
+
* kind: string, // default "<name>-cap" — the cap issue kind (and ruleId suffix)
|
|
1990
|
+
* snippet: string|function, // default "<noun> input exceeds maxBytes <bytes>"; fn(byteLen, bytes) when it needs the measured length
|
|
1991
|
+
* },
|
|
1992
|
+
* scanCodepoints: boolean, // default true: the not-done result carries the codepoint-class scan. Pass false for a guard that scans codepoints later in its own detection (or parses them via its format, e.g. JSON) — the not-done result is then `[]`.
|
|
1993
|
+
* }
|
|
1994
|
+
*
|
|
1995
|
+
* @example
|
|
1996
|
+
* function _detectIssues(input, opts) {
|
|
1997
|
+
* var pre = b.gateContract.detectStringInput(input, opts, {
|
|
1998
|
+
* name: "cidr", cap: { bytes: opts.maxBytes },
|
|
1999
|
+
* });
|
|
2000
|
+
* if (pre.done) return pre.issues;
|
|
2001
|
+
* var issues = pre.issues; // codepoint-class threats so far
|
|
2002
|
+
* // … guard-specific detection appends to issues …
|
|
2003
|
+
* return issues;
|
|
2004
|
+
* }
|
|
2005
|
+
*/
|
|
2006
|
+
function detectStringInput(input, opts, cfg) {
|
|
2007
|
+
validateOpts.requireObject(cfg, "gateContract.detectStringInput", GateContractError);
|
|
2008
|
+
validateOpts.requireNonEmptyString(cfg.name,
|
|
2009
|
+
"gateContract.detectStringInput: cfg.name", GateContractError, "gate-contract/bad-opt");
|
|
2010
|
+
var noun = cfg.noun || cfg.name;
|
|
2011
|
+
if (typeof input !== "string") {
|
|
2012
|
+
return { done: true, issues: [{ kind: "bad-input", severity: "high",
|
|
2013
|
+
ruleId: cfg.name + ".bad-input", snippet: noun + " is not a string" }] };
|
|
2014
|
+
}
|
|
2015
|
+
var emptyMode = cfg.emptyMode || "issue";
|
|
2016
|
+
if (emptyMode !== "skip" && input.length === 0) {
|
|
2017
|
+
return { done: true, issues: emptyMode === "ok" ? [] :
|
|
2018
|
+
[{ kind: "empty", severity: "high", ruleId: cfg.name + ".empty",
|
|
2019
|
+
snippet: noun + " is empty" }] };
|
|
2020
|
+
}
|
|
2021
|
+
if (cfg.cap) {
|
|
2022
|
+
var byteLen = Buffer.byteLength(input, "utf8");
|
|
2023
|
+
if (byteLen > cfg.cap.bytes) {
|
|
2024
|
+
var capKind = cfg.cap.kind || (cfg.name + "-cap");
|
|
2025
|
+
var capSnippet = typeof cfg.cap.snippet === "function"
|
|
2026
|
+
? cfg.cap.snippet(byteLen, cfg.cap.bytes)
|
|
2027
|
+
: (cfg.cap.snippet || (noun + " input exceeds maxBytes " + cfg.cap.bytes));
|
|
2028
|
+
return { done: true, issues: [{ kind: capKind, severity: "high",
|
|
2029
|
+
ruleId: cfg.name + "." + capKind, snippet: capSnippet }] };
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
if (cfg.scanCodepoints === false) return { done: false, issues: [] };
|
|
2033
|
+
return { done: false, issues: codepointClass.detectCharThreats(input, opts, cfg.name) };
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
// Input contracts — the one place that knows how to turn a raw guard input
|
|
2037
|
+
// into the subject its detector expects (or flag bad input). Every guard's
|
|
2038
|
+
// `validate` differs only in its input shape; the contract captures that
|
|
2039
|
+
// difference so one `runIssueValidator` serves them all:
|
|
2040
|
+
//
|
|
2041
|
+
// - "text": string / Buffer → UTF-8 text; anything else is bad input. The
|
|
2042
|
+
// content guards (csv / html / markdown / json / ...) whose detector takes
|
|
2043
|
+
// a string but does NOT type-check it themselves.
|
|
2044
|
+
// - "bytes": string / Buffer is accepted and reaches the detector UNCHANGED
|
|
2045
|
+
// (no utf8 coercion); anything else is bad input. The guards that inspect
|
|
2046
|
+
// raw bytes before decoding (filename overlong-UTF-8, sql encoding gate) —
|
|
2047
|
+
// converting to text first would hide the very bytes they check. This is
|
|
2048
|
+
// exactly `badInputResultIfNotStringOrBuffer(input) ||
|
|
2049
|
+
// aggregateIssues(detect(input))`.
|
|
2050
|
+
// - "raw" (default): identity — the value reaches the detector untouched and
|
|
2051
|
+
// the detector owns its own (often typed) bad-input. The object-bag guards
|
|
2052
|
+
// (image / pdf metadata, archive entries) and the string guards whose
|
|
2053
|
+
// detector type-checks itself (email). This is exactly
|
|
2054
|
+
// `aggregateIssues(detect(input))`.
|
|
2055
|
+
//
|
|
2056
|
+
// A guard with a bespoke shape passes its own `function(input){ return
|
|
2057
|
+
// { subject } | { badInput: msg } }` instead of a name. New input shapes
|
|
2058
|
+
// extend the class by adding a contract, never by branching in defineGuard.
|
|
2059
|
+
var INPUT_CONTRACTS = {
|
|
2060
|
+
text: function (input) {
|
|
2061
|
+
if (typeof input === "string") return { subject: input };
|
|
2062
|
+
if (Buffer.isBuffer(input)) return { subject: input.toString("utf8") };
|
|
2063
|
+
return { badInput: "input is not string or Buffer" };
|
|
2064
|
+
},
|
|
2065
|
+
bytes: function (input) {
|
|
2066
|
+
if (typeof input === "string" || Buffer.isBuffer(input)) return { subject: input };
|
|
2067
|
+
return { badInput: "input is not string or Buffer" };
|
|
2068
|
+
},
|
|
2069
|
+
raw: function (input) { return { subject: input }; },
|
|
2070
|
+
};
|
|
2071
|
+
Object.freeze(INPUT_CONTRACTS);
|
|
2072
|
+
|
|
2073
|
+
function resolveInputContract(contract) {
|
|
2074
|
+
if (typeof contract === "function") return contract;
|
|
2075
|
+
return INPUT_CONTRACTS[contract] || INPUT_CONTRACTS.text;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
1450
2078
|
/**
|
|
1451
2079
|
* @primitive b.gateContract.runIssueValidator
|
|
1452
|
-
* @signature b.gateContract.runIssueValidator(input, opts, detector)
|
|
2080
|
+
* @signature b.gateContract.runIssueValidator(input, opts, detector, contract?)
|
|
1453
2081
|
* @since 0.7.5
|
|
1454
2082
|
* @status stable
|
|
1455
2083
|
* @related b.gateContract.aggregateIssues, b.gateContract.badInputResultIfNotStringOrBuffer
|
|
1456
2084
|
*
|
|
1457
|
-
*
|
|
1458
|
-
*
|
|
1459
|
-
*
|
|
1460
|
-
*
|
|
1461
|
-
*
|
|
1462
|
-
*
|
|
1463
|
-
* `
|
|
1464
|
-
*
|
|
1465
|
-
*
|
|
1466
|
-
*
|
|
2085
|
+
* The single `validate(input, opts)` engine for the whole guard family. An
|
|
2086
|
+
* input contract normalizes the raw input to the subject the detector expects
|
|
2087
|
+
* (or flags bad input), then the detector runs and its issues aggregate. One
|
|
2088
|
+
* engine spans every input shape in the family: `"text"` (the default) coerces
|
|
2089
|
+
* string / Buffer to UTF-8 and refuses anything else; `"raw"` hands the value
|
|
2090
|
+
* through so an object-bag or byte-level detector owns its own bad-input
|
|
2091
|
+
* (identical to `aggregateIssues(detector(input, opts))`); a guard with a
|
|
2092
|
+
* bespoke shape passes its own extractor `function(input) -> { subject } |
|
|
2093
|
+
* { badInput: message }`. Result `ok` is `true` only when no detected issue is
|
|
2094
|
+
* `critical` / `high` severity. The `opts` argument is forwarded verbatim as
|
|
2095
|
+
* the detector's second argument — its shape is detector-defined.
|
|
1467
2096
|
*
|
|
1468
2097
|
* @opts
|
|
1469
|
-
* ...: any, // detector-defined; passed through to detector(
|
|
2098
|
+
* ...: any, // detector-defined; passed through to detector(subject, opts)
|
|
1470
2099
|
*
|
|
1471
2100
|
* @example
|
|
1472
2101
|
* function detectFormulaTrigger(text) {
|
|
@@ -1481,18 +2110,16 @@ function badInputResultIfNotStringOrBuffer(input) {
|
|
|
1481
2110
|
* var ok = b.gateContract.runIssueValidator("ada,36", {}, detectFormulaTrigger);
|
|
1482
2111
|
* ok.ok; // → true
|
|
1483
2112
|
*/
|
|
1484
|
-
function runIssueValidator(input, opts, detector) {
|
|
1485
|
-
var
|
|
1486
|
-
|
|
1487
|
-
: (Buffer.isBuffer(input) ? input.toString("utf8") : null);
|
|
1488
|
-
if (text == null) {
|
|
2113
|
+
function runIssueValidator(input, opts, detector, contract) {
|
|
2114
|
+
var extracted = resolveInputContract(contract)(input);
|
|
2115
|
+
if (extracted && typeof extracted.badInput === "string") {
|
|
1489
2116
|
return {
|
|
1490
2117
|
ok: false,
|
|
1491
2118
|
issues: [{ kind: "bad-input", severity: "high",
|
|
1492
|
-
snippet:
|
|
2119
|
+
snippet: extracted.badInput }],
|
|
1493
2120
|
};
|
|
1494
2121
|
}
|
|
1495
|
-
return aggregateIssues(detector(
|
|
2122
|
+
return aggregateIssues(detector(extracted.subject, opts));
|
|
1496
2123
|
}
|
|
1497
2124
|
|
|
1498
2125
|
/**
|
|
@@ -1910,8 +2537,10 @@ function _ctxValueForKind(kind, ctx, override) {
|
|
|
1910
2537
|
* errorName: string, // defineClass name (mutually exclusive with errorClass)
|
|
1911
2538
|
* errorClass: function, // pre-built FrameworkError subclass
|
|
1912
2539
|
* profiles: object, // PROFILES (must include strict/balanced/permissive); required
|
|
1913
|
-
* defaults: object, // DEFAULTS baseline (default profiles.strict)
|
|
1914
|
-
* postures: object, // COMPLIANCE_POSTURES (default ALL_STRICT_POSTURES)
|
|
2540
|
+
* defaults: object, // DEFAULTS baseline (default profiles.strict, or strictDefaults(profiles, defaultsOverlay) when `base` is given)
|
|
2541
|
+
* postures: object, // COMPLIANCE_POSTURES (default ALL_STRICT_POSTURES, or compliancePostures(profiles, { base }) when `base` is given)
|
|
2542
|
+
* base: number, // forensic snippet budget — when given (and defaults/postures omitted), the factory derives both via strictDefaults + compliancePostures
|
|
2543
|
+
* defaultsOverlay: object, // per-guard default overrides merged into the derived strictDefaults (e.g. { maxRuntimeMs: ... }); only used with `base`
|
|
1915
2544
|
* mimeTypes: string[], // content guards only
|
|
1916
2545
|
* extensions: string[], // content guards only
|
|
1917
2546
|
* integrationFixtures: object, // INTEGRATION_FIXTURES (consumed by host harness)
|
|
@@ -1953,9 +2582,6 @@ function defineGuard(spec) {
|
|
|
1953
2582
|
}
|
|
1954
2583
|
validateOpts.requireObject(spec.profiles, "gateContract.defineGuard: profiles",
|
|
1955
2584
|
GateContractError);
|
|
1956
|
-
if (typeof spec.validate !== "function") {
|
|
1957
|
-
throw _err("gate-contract/bad-opt", "defineGuard: validate must be a function");
|
|
1958
|
-
}
|
|
1959
2585
|
if (spec.errorClass && spec.errorName) {
|
|
1960
2586
|
throw _err("gate-contract/bad-opt",
|
|
1961
2587
|
"defineGuard: pass errorClass OR errorName, not both");
|
|
@@ -1967,8 +2593,130 @@ function defineGuard(spec) {
|
|
|
1967
2593
|
spec.name.charAt(0).toUpperCase() + spec.name.slice(1) + "Error"),
|
|
1968
2594
|
{ alwaysPermanent: true });
|
|
1969
2595
|
var profiles = spec.profiles;
|
|
1970
|
-
|
|
1971
|
-
|
|
2596
|
+
// A guard may hand `defaults` / `postures` explicitly, OR pass `base` (the
|
|
2597
|
+
// forensic snippet budget) and let the factory derive the standard config —
|
|
2598
|
+
// `strictDefaults(profiles, defaultsOverlay)` + `compliancePostures(profiles,
|
|
2599
|
+
// { base })` — so the guard file needn't declare the two module-vars itself.
|
|
2600
|
+
var defaults = spec.defaults ||
|
|
2601
|
+
(typeof spec.base === "number" ? strictDefaults(profiles, spec.defaultsOverlay)
|
|
2602
|
+
: (profiles.strict || {}));
|
|
2603
|
+
var postures = spec.postures ||
|
|
2604
|
+
(typeof spec.base === "number" ? compliancePostures(profiles, { base: spec.base })
|
|
2605
|
+
: ALL_STRICT_POSTURES);
|
|
2606
|
+
|
|
2607
|
+
// Dynamic guard assembly — the upstream primitive absorbs the per-guard
|
|
2608
|
+
// binding wrappers. A guard may pass a raw `detect(input, opts) -> issues[]`
|
|
2609
|
+
// (the guard-specific detection logic) plus an optional `sanitizeTransform(
|
|
2610
|
+
// input, resolvedOpts) -> value`, instead of hand-rolling `_resolveOpts`,
|
|
2611
|
+
// `validate`, and the `sanitize` resolve→detect→throw boilerplate that every
|
|
2612
|
+
// guard otherwise duplicates. defineGuard already owns the profile/posture/
|
|
2613
|
+
// defaults/errorClass/prefix, so it binds the resolver here and builds
|
|
2614
|
+
// validate + sanitize. Behaviour matches the hand-written wrappers exactly:
|
|
2615
|
+
// validate runs detect on the RAW opts (detect resolves what it needs);
|
|
2616
|
+
// sanitize resolves first, then detect → throwOnRefusalSeverity → transform.
|
|
2617
|
+
// The bound profile/posture resolver — built once from the spec's binding
|
|
2618
|
+
// config (profiles/postures/defaults/errorClass/prefix) and EXPOSED on the
|
|
2619
|
+
// guard as `resolveOpts` (below), so a bespoke gate calls
|
|
2620
|
+
// `module.exports.resolveOpts(opts)` instead of each guard hand-rolling the
|
|
2621
|
+
// identical `function _resolveOpts(o){ return resolveProfileAndPosture(o,
|
|
2622
|
+
// {...}) }` binding wrapper. The binding config lives in ONE place.
|
|
2623
|
+
var _resolveGuardOpts = function (o) {
|
|
2624
|
+
return resolveProfileAndPosture(o || {}, {
|
|
2625
|
+
profiles: profiles,
|
|
2626
|
+
compliancePostures: postures,
|
|
2627
|
+
defaults: defaults,
|
|
2628
|
+
errorClass: ErrorClass,
|
|
2629
|
+
errCodePrefix: prefix,
|
|
2630
|
+
});
|
|
2631
|
+
};
|
|
2632
|
+
if (typeof spec.detect === "function") {
|
|
2633
|
+
var intOpts = Array.isArray(spec.intOpts) ? spec.intOpts.slice() : null;
|
|
2634
|
+
if (typeof spec.validate !== "function") {
|
|
2635
|
+
spec.validate = function (input, opts) {
|
|
2636
|
+
var resolved = _resolveGuardOpts(opts);
|
|
2637
|
+
if (intOpts) {
|
|
2638
|
+
numericBounds.requireAllPositiveFiniteIntIfPresent(resolved, intOpts,
|
|
2639
|
+
spec.name + ".validate", ErrorClass, prefix + ".bad-opt");
|
|
2640
|
+
}
|
|
2641
|
+
// One engine, the guard's input contract picks the shape. Default
|
|
2642
|
+
// "raw" reproduces the historical aggregateIssues(detect(input)) — the
|
|
2643
|
+
// detector owns its own bad-input (object-bag guards image/pdf, the
|
|
2644
|
+
// byte-level guards). A guard whose detector takes a string but does
|
|
2645
|
+
// not type-check it (e.g. csv returns [] on a non-string) sets
|
|
2646
|
+
// inputContract: "text" so non-text input is refused as bad-input.
|
|
2647
|
+
return runIssueValidator(input, resolved, spec.detect,
|
|
2648
|
+
spec.inputContract || "raw");
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2651
|
+
if (typeof spec.sanitizeTransform === "function" && typeof spec.sanitize !== "function") {
|
|
2652
|
+
// spec.sanitizeSeverities narrows which severities REFUSE (throw) vs are
|
|
2653
|
+
// stripped/repaired by sanitizeTransform. Default ['critical','high'];
|
|
2654
|
+
// a guard that repairs high-severity findings and refuses only the
|
|
2655
|
+
// unrepairable critical shapes (markdown / email / xml / yaml) passes
|
|
2656
|
+
// ['critical'] so the generated sanitize matches its hand-written one.
|
|
2657
|
+
// An empty array means "strip unconditionally, never refuse" (csv / text
|
|
2658
|
+
// best-effort scrubbers, whose sanitize never throws on a detected issue).
|
|
2659
|
+
var sanitizeSeverities = Array.isArray(spec.sanitizeSeverities)
|
|
2660
|
+
? spec.sanitizeSeverities.slice() : null;
|
|
2661
|
+
var refusesOnDetect = sanitizeSeverities === null || sanitizeSeverities.length > 0;
|
|
2662
|
+
// spec.sanitizeAmplificationCap (a string = the resolved-opts field name
|
|
2663
|
+
// carrying the max growth ratio) opts the guard into the "sanitize must
|
|
2664
|
+
// shrink, never grow" post-condition: the transform runs on extracted
|
|
2665
|
+
// text and the output length is capped at ratio×input. Used by the text
|
|
2666
|
+
// scrubbers (csv / text) whose hand-written sanitize threw
|
|
2667
|
+
// `<prefix>.sanitize-amplified`. When unset, sanitize keeps the raw input
|
|
2668
|
+
// (binary passthrough guards image / pdf must not be utf8-decoded).
|
|
2669
|
+
var ampCapField = typeof spec.sanitizeAmplificationCap === "string"
|
|
2670
|
+
? spec.sanitizeAmplificationCap : null;
|
|
2671
|
+
spec.sanitize = function (input, opts) {
|
|
2672
|
+
var resolved = _resolveGuardOpts(opts);
|
|
2673
|
+
var subject = input;
|
|
2674
|
+
if (ampCapField) {
|
|
2675
|
+
// Same text contract the validate engine uses — string/Buffer→text,
|
|
2676
|
+
// refuse anything else (here as a throw, sanitize's contract).
|
|
2677
|
+
var extracted = INPUT_CONTRACTS.text(input);
|
|
2678
|
+
if (extracted.badInput) {
|
|
2679
|
+
throw ErrorClass.factory(prefix + ".bad-input",
|
|
2680
|
+
"sanitize requires string or Buffer input");
|
|
2681
|
+
}
|
|
2682
|
+
subject = extracted.subject;
|
|
2683
|
+
}
|
|
2684
|
+
if (refusesOnDetect) {
|
|
2685
|
+
var issues = spec.detect(subject, resolved);
|
|
2686
|
+
// A `bad-input` issue means the input is UNPROCESSABLE (wrong type /
|
|
2687
|
+
// shape), not a content finding — a scrubber must never let it slip
|
|
2688
|
+
// into the transform (which would return the garbage verbatim). So it
|
|
2689
|
+
// refuses ALWAYS, independent of which CONTENT severities this guard's
|
|
2690
|
+
// sanitize tolerates via sanitizeSeverities. (csv/text reach the same
|
|
2691
|
+
// refusal earlier through the ampCapField text contract above.)
|
|
2692
|
+
for (var bi = 0; bi < issues.length; bi += 1) {
|
|
2693
|
+
if (issues[bi].kind === "bad-input") {
|
|
2694
|
+
throw ErrorClass.factory(prefix + ".bad-input",
|
|
2695
|
+
issues[bi].snippet || "sanitize: input is not processable");
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
var throwOpts = { errorClass: ErrorClass, codePrefix: prefix };
|
|
2699
|
+
if (sanitizeSeverities) throwOpts.severities = sanitizeSeverities;
|
|
2700
|
+
throwOnRefusalSeverity(issues, throwOpts);
|
|
2701
|
+
}
|
|
2702
|
+
var out = spec.sanitizeTransform(subject, resolved);
|
|
2703
|
+
if (ampCapField) {
|
|
2704
|
+
var cap = resolved[ampCapField];
|
|
2705
|
+
if (typeof cap === "number") {
|
|
2706
|
+
var amp = out.length / Math.max(subject.length, 1);
|
|
2707
|
+
if (amp > cap) {
|
|
2708
|
+
throw ErrorClass.factory(prefix + ".sanitize-amplified",
|
|
2709
|
+
"sanitize grew output " + amp.toFixed(2) + "x; cap " + cap);
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
return out;
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
if (typeof spec.validate !== "function") {
|
|
2718
|
+
throw _err("gate-contract/bad-opt", "defineGuard: validate (or detect) must be a function");
|
|
2719
|
+
}
|
|
1972
2720
|
|
|
1973
2721
|
var buildProfileFn = makeProfileBuilder(profiles);
|
|
1974
2722
|
function compliancePostureFn(name) {
|
|
@@ -2012,12 +2760,7 @@ function defineGuard(spec) {
|
|
|
2012
2760
|
var value = _ctxValueForKind(spec.kind, ctx, ctxFields);
|
|
2013
2761
|
if (!value) return { ok: true, action: "serve" };
|
|
2014
2762
|
var rv = spec.validate(value, opts);
|
|
2015
|
-
|
|
2016
|
-
var hasBlocking = rv.issues.some(function (i) {
|
|
2017
|
-
return i.severity === "critical" || i.severity === "high";
|
|
2018
|
-
});
|
|
2019
|
-
if (!hasBlocking) return { ok: true, action: "audit-only", issues: rv.issues };
|
|
2020
|
-
return { ok: false, action: "refuse", issues: rv.issues };
|
|
2763
|
+
return severityDisposition(rv.issues || []);
|
|
2021
2764
|
};
|
|
2022
2765
|
return buildGuardGate(
|
|
2023
2766
|
opts.name || (gateNamePrefix + ":" + (opts.profile || "default")),
|
|
@@ -2031,6 +2774,7 @@ function defineGuard(spec) {
|
|
|
2031
2774
|
NAME: spec.name,
|
|
2032
2775
|
KIND: spec.kind,
|
|
2033
2776
|
validate: spec.validate,
|
|
2777
|
+
resolveOpts: _resolveGuardOpts,
|
|
2034
2778
|
buildProfile: buildProfileFn,
|
|
2035
2779
|
compliancePosture: compliancePostureFn,
|
|
2036
2780
|
loadRulePack: rulePacks.load,
|
|
@@ -2259,6 +3003,81 @@ function defineParser(spec) {
|
|
|
2259
3003
|
* decision.action; // → "serve" | "refuse" | …
|
|
2260
3004
|
*/
|
|
2261
3005
|
|
|
3006
|
+
/**
|
|
3007
|
+
* @abiTemplate defineGuard
|
|
3008
|
+
* @method validate
|
|
3009
|
+
* @signature b.{NS}.validate(input, opts?)
|
|
3010
|
+
* @status stable
|
|
3011
|
+
* @related b.{NS}.gate, b.{NS}.sanitize
|
|
3012
|
+
*
|
|
3013
|
+
* Inspect `input` under a resolved profile + compliance posture and return a
|
|
3014
|
+
* structured result `{ ok, issues }` WITHOUT throwing — `ok` is false when any
|
|
3015
|
+
* `high` / `critical` issue fired, and `issues` lists every finding (kind,
|
|
3016
|
+
* severity, ruleId, snippet). `opts` selects the `profile` /
|
|
3017
|
+
* `compliancePosture`; omitted opts use this guard's default profile. Wired by
|
|
3018
|
+
* `gateContract.defineGuard` from the guard's detection logic through
|
|
3019
|
+
* `gateContract.aggregateIssues`, so the result shape and severity gating are
|
|
3020
|
+
* identical across the guard family.
|
|
3021
|
+
*
|
|
3022
|
+
* @opts
|
|
3023
|
+
* profile: string, // one of PROFILES; default this guard's default
|
|
3024
|
+
* compliancePosture: string, // overlay one of hipaa/pci-dss/gdpr/soc2
|
|
3025
|
+
*
|
|
3026
|
+
* @example
|
|
3027
|
+
* var rv = b.{NS}.validate(input, { profile: "strict" });
|
|
3028
|
+
* rv.ok; // → true | false
|
|
3029
|
+
* rv.issues; // → [ { kind, severity, … }, … ]
|
|
3030
|
+
*/
|
|
3031
|
+
|
|
3032
|
+
/**
|
|
3033
|
+
* @abiTemplate defineGuard
|
|
3034
|
+
* @method sanitize
|
|
3035
|
+
* @signature b.{NS}.sanitize(input, opts?)
|
|
3036
|
+
* @status stable
|
|
3037
|
+
* @related b.{NS}.validate, b.{NS}.gate
|
|
3038
|
+
*
|
|
3039
|
+
* Return a normalized form of `input` when no `high` / `critical` issue fires;
|
|
3040
|
+
* throw `{ERR}` on any such refusal (best-effort repair, never a silent pass).
|
|
3041
|
+
* Resolves the profile + posture, runs the guard's detection, throws via
|
|
3042
|
+
* `gateContract.throwOnRefusalSeverity` on a refusal, then applies the guard's
|
|
3043
|
+
* own safe transform. Wired by `gateContract.defineGuard`, so the
|
|
3044
|
+
* resolve → detect → throw → transform order is identical across the family; a
|
|
3045
|
+
* guard with no safe transform ships no `sanitize`.
|
|
3046
|
+
*
|
|
3047
|
+
* @opts
|
|
3048
|
+
* profile: string, // one of PROFILES; default this guard's default
|
|
3049
|
+
* compliancePosture: string, // overlay one of hipaa/pci-dss/gdpr/soc2
|
|
3050
|
+
*
|
|
3051
|
+
* @example
|
|
3052
|
+
* var safe = b.{NS}.sanitize(input, { profile: "permissive" });
|
|
3053
|
+
* safe; // → normalized value
|
|
3054
|
+
*/
|
|
3055
|
+
|
|
3056
|
+
/**
|
|
3057
|
+
* @abiTemplate defineGuard
|
|
3058
|
+
* @method resolveOpts
|
|
3059
|
+
* @signature b.{NS}.resolveOpts(opts?)
|
|
3060
|
+
* @status stable
|
|
3061
|
+
* @related b.{NS}.validate, b.{NS}.gate
|
|
3062
|
+
*
|
|
3063
|
+
* Resolve caller `opts` against this guard's `PROFILES` + compliance-posture
|
|
3064
|
+
* overlays into the fully-defaulted option set the guard runs on — the same
|
|
3065
|
+
* resolution `validate` / `sanitize` / `gate` apply internally. Wired by
|
|
3066
|
+
* `gateContract.defineGuard` from the guard's binding config (profiles /
|
|
3067
|
+
* postures / defaults / error class), so a guard's bespoke `gate` calls
|
|
3068
|
+
* `resolveOpts` instead of re-declaring the per-guard resolver wrapper. Throws
|
|
3069
|
+
* `{ERR}` with code `"{CODE}.bad-opt"` / `"{CODE}.bad-posture"` on an unknown
|
|
3070
|
+
* profile or posture name.
|
|
3071
|
+
*
|
|
3072
|
+
* @opts
|
|
3073
|
+
* profile: string, // one of PROFILES; default this guard's default
|
|
3074
|
+
* compliancePosture: string, // overlay one of hipaa/pci-dss/gdpr/soc2
|
|
3075
|
+
*
|
|
3076
|
+
* @example
|
|
3077
|
+
* var resolved = b.{NS}.resolveOpts({ profile: "strict" });
|
|
3078
|
+
* resolved.profile; // → "strict"
|
|
3079
|
+
*/
|
|
3080
|
+
|
|
2262
3081
|
/**
|
|
2263
3082
|
* @abiTemplate defineParser
|
|
2264
3083
|
* @method compliancePosture
|
|
@@ -2300,12 +3119,24 @@ module.exports = {
|
|
|
2300
3119
|
resolveProfileAndPosture: resolveProfileAndPosture,
|
|
2301
3120
|
runIssueValidator: runIssueValidator,
|
|
2302
3121
|
buildGuardGate: buildGuardGate,
|
|
3122
|
+
severityDisposition: severityDisposition,
|
|
3123
|
+
buildContentGate: buildContentGate,
|
|
3124
|
+
policyDisposition: policyDisposition,
|
|
3125
|
+
charThreatDisposition: charThreatDisposition,
|
|
2303
3126
|
extractBytesAsText: extractBytesAsText,
|
|
2304
3127
|
lookupCompliancePosture: lookupCompliancePosture,
|
|
2305
3128
|
ALL_STRICT_POSTURES: ALL_STRICT_POSTURES,
|
|
3129
|
+
CHAR_THREATS_REJECT_ALL: CHAR_THREATS_REJECT_ALL,
|
|
3130
|
+
DANGEROUS_URL_SCHEMES: DANGEROUS_URL_SCHEMES,
|
|
3131
|
+
SAFE_URL_SCHEMES: SAFE_URL_SCHEMES,
|
|
3132
|
+
identifierFixtures: identifierFixtures,
|
|
3133
|
+
compliancePostures: compliancePostures,
|
|
3134
|
+
strictDefaults: strictDefaults,
|
|
3135
|
+
detectStringInput: detectStringInput,
|
|
2306
3136
|
makeRulePackLoader: makeRulePackLoader,
|
|
2307
3137
|
makeProfileBuilder: makeProfileBuilder,
|
|
2308
3138
|
makeProfileResolver: makeProfileResolver,
|
|
3139
|
+
resolveProfileName: resolveProfileName,
|
|
2309
3140
|
throwOnRefusalSeverity: throwOnRefusalSeverity,
|
|
2310
3141
|
badInputResultIfNotStringOrBuffer: badInputResultIfNotStringOrBuffer,
|
|
2311
3142
|
aggregateIssues: aggregateIssues,
|