@blamejs/blamejs-shop 0.5.12 → 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +632 -416
- package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +21 -10
- package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +30 -9
- package/lib/vendor/blamejs/.github/dependabot.yml +8 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/ci.yml +21 -21
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +4 -4
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +7 -7
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
- package/lib/vendor/blamejs/.gitignore +2 -0
- package/lib/vendor/blamejs/CHANGELOG.md +122 -0
- package/lib/vendor/blamejs/NOTICE +26 -1
- package/lib/vendor/blamejs/SECURITY.md +1 -1
- package/lib/vendor/blamejs/api-snapshot.json +207 -5
- package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +29 -0
- package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +1 -0
- package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +31 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +109 -0
- package/lib/vendor/blamejs/examples/wiki/nginx.conf +106 -0
- package/lib/vendor/blamejs/examples/wiki/package-lock.json +1 -1
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +92 -0
- package/lib/vendor/blamejs/fuzz/asn1-der.fuzz.js +28 -0
- package/lib/vendor/blamejs/fuzz/cms-codec.fuzz.js +17 -0
- package/lib/vendor/blamejs/fuzz/link-header.fuzz.js +18 -0
- package/lib/vendor/blamejs/index.js +6 -0
- package/lib/vendor/blamejs/lib/a2a.js +4 -4
- package/lib/vendor/blamejs/lib/agent-idempotency.js +18 -2
- package/lib/vendor/blamejs/lib/agent-saga.js +36 -8
- package/lib/vendor/blamejs/lib/agent-snapshot.js +63 -6
- package/lib/vendor/blamejs/lib/app-shutdown.js +23 -20
- package/lib/vendor/blamejs/lib/archive-read.js +8 -0
- package/lib/vendor/blamejs/lib/archive-tar.js +12 -2
- package/lib/vendor/blamejs/lib/arg-parser.js +49 -5
- package/lib/vendor/blamejs/lib/asn1-der.js +12 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +47 -1
- package/lib/vendor/blamejs/lib/audit.js +71 -8
- package/lib/vendor/blamejs/lib/auth/ciba.js +14 -2
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +58 -47
- package/lib/vendor/blamejs/lib/auth/jwt.js +34 -0
- package/lib/vendor/blamejs/lib/auth/oauth.js +90 -13
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +20 -17
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +214 -23
- package/lib/vendor/blamejs/lib/auth/saml.js +38 -20
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +10 -4
- package/lib/vendor/blamejs/lib/backup/bundle.js +13 -3
- package/lib/vendor/blamejs/lib/backup/index.js +16 -5
- package/lib/vendor/blamejs/lib/backup/manifest.js +18 -5
- package/lib/vendor/blamejs/lib/base32.js +21 -0
- package/lib/vendor/blamejs/lib/break-glass.js +19 -10
- package/lib/vendor/blamejs/lib/bundler.js +5 -1
- package/lib/vendor/blamejs/lib/cache-status.js +3 -7
- package/lib/vendor/blamejs/lib/calendar.js +21 -1
- package/lib/vendor/blamejs/lib/cbor.js +12 -1
- package/lib/vendor/blamejs/lib/cert.js +29 -11
- package/lib/vendor/blamejs/lib/cli.js +94 -24
- package/lib/vendor/blamejs/lib/cloud-events.js +2 -2
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +13 -3
- package/lib/vendor/blamejs/lib/codepoint-class.js +85 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +8 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +19 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act.js +6 -8
- package/lib/vendor/blamejs/lib/content-credentials.js +1 -1
- package/lib/vendor/blamejs/lib/content-digest.js +2 -1
- package/lib/vendor/blamejs/lib/cookies.js +13 -3
- package/lib/vendor/blamejs/lib/cose.js +31 -16
- package/lib/vendor/blamejs/lib/crypto-field.js +32 -0
- package/lib/vendor/blamejs/lib/crypto-oprf.js +11 -2
- package/lib/vendor/blamejs/lib/crypto-xwing.js +22 -4
- package/lib/vendor/blamejs/lib/crypto.js +314 -19
- package/lib/vendor/blamejs/lib/csp.js +11 -3
- package/lib/vendor/blamejs/lib/daemon.js +270 -24
- package/lib/vendor/blamejs/lib/dark-patterns.js +5 -2
- package/lib/vendor/blamejs/lib/db-declare-view.js +2 -2
- package/lib/vendor/blamejs/lib/db-query.js +34 -7
- package/lib/vendor/blamejs/lib/db.js +18 -6
- package/lib/vendor/blamejs/lib/dbsc.js +17 -0
- package/lib/vendor/blamejs/lib/dev.js +21 -2
- package/lib/vendor/blamejs/lib/dsr.js +157 -102
- package/lib/vendor/blamejs/lib/external-db.js +41 -2
- package/lib/vendor/blamejs/lib/file-upload.js +25 -7
- package/lib/vendor/blamejs/lib/flag-providers.js +27 -15
- package/lib/vendor/blamejs/lib/forms.js +113 -27
- package/lib/vendor/blamejs/lib/gate-contract.js +23 -10
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +8 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +1 -1
- package/lib/vendor/blamejs/lib/guard-archive.js +2 -2
- package/lib/vendor/blamejs/lib/guard-cidr.js +27 -1
- package/lib/vendor/blamejs/lib/guard-graphql.js +30 -11
- package/lib/vendor/blamejs/lib/guard-html.js +26 -53
- package/lib/vendor/blamejs/lib/guard-image.js +24 -5
- package/lib/vendor/blamejs/lib/guard-imap-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-jmap.js +19 -3
- package/lib/vendor/blamejs/lib/guard-jwt.js +18 -2
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -2
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-markdown.js +11 -24
- package/lib/vendor/blamejs/lib/guard-pdf.js +18 -2
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-regex.js +8 -5
- package/lib/vendor/blamejs/lib/guard-svg.js +31 -32
- package/lib/vendor/blamejs/lib/guard-time.js +1 -1
- package/lib/vendor/blamejs/lib/guard-uuid.js +2 -2
- package/lib/vendor/blamejs/lib/html-balance.js +3 -3
- package/lib/vendor/blamejs/lib/http-client-cache.js +82 -4
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +16 -1
- package/lib/vendor/blamejs/lib/http-client.js +39 -7
- package/lib/vendor/blamejs/lib/http-message-signature.js +35 -1
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +31 -6
- package/lib/vendor/blamejs/lib/i18n.js +35 -21
- package/lib/vendor/blamejs/lib/incident-report.js +8 -1
- package/lib/vendor/blamejs/lib/ip-utils.js +11 -1
- package/lib/vendor/blamejs/lib/json-path.js +23 -1
- package/lib/vendor/blamejs/lib/jwk.js +20 -3
- package/lib/vendor/blamejs/lib/link-header.js +2 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream.js +8 -3
- package/lib/vendor/blamejs/lib/mail-auth.js +123 -22
- package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
- package/lib/vendor/blamejs/lib/mail-bounce.js +8 -0
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +46 -10
- package/lib/vendor/blamejs/lib/mail-dav.js +22 -3
- package/lib/vendor/blamejs/lib/mail-dkim.js +96 -30
- package/lib/vendor/blamejs/lib/mail-helo.js +6 -1
- package/lib/vendor/blamejs/lib/mail-scan.js +23 -16
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +34 -8
- package/lib/vendor/blamejs/lib/mail-server-imap.js +3 -4
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +7 -11
- package/lib/vendor/blamejs/lib/mail-server-mx.js +93 -7
- package/lib/vendor/blamejs/lib/mail-server-registry.js +8 -2
- package/lib/vendor/blamejs/lib/mail.js +15 -5
- package/lib/vendor/blamejs/lib/metrics.js +268 -63
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +7 -1
- package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +19 -4
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +5 -9
- package/lib/vendor/blamejs/lib/middleware/csp-report.js +40 -8
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +24 -10
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -5
- package/lib/vendor/blamejs/lib/middleware/index.js +6 -1
- package/lib/vendor/blamejs/lib/middleware/nel.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +12 -1
- package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +6 -2
- package/lib/vendor/blamejs/lib/mtls-ca.js +18 -2
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +11 -1
- package/lib/vendor/blamejs/lib/network-dns.js +21 -2
- package/lib/vendor/blamejs/lib/network-tls.js +69 -7
- package/lib/vendor/blamejs/lib/nonce-store.js +9 -1
- package/lib/vendor/blamejs/lib/object-store/gcs.js +7 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +31 -0
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +1 -1
- package/lib/vendor/blamejs/lib/outbox.js +1 -1
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +9 -1
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +10 -1
- package/lib/vendor/blamejs/lib/permissions.js +6 -3
- package/lib/vendor/blamejs/lib/pid-probe.js +55 -0
- package/lib/vendor/blamejs/lib/pqc-agent.js +8 -1
- package/lib/vendor/blamejs/lib/pqc-software.js +3 -3
- package/lib/vendor/blamejs/lib/pubsub.js +20 -9
- package/lib/vendor/blamejs/lib/queue-local.js +8 -1
- package/lib/vendor/blamejs/lib/queue-redis.js +5 -0
- package/lib/vendor/blamejs/lib/redact.js +54 -0
- package/lib/vendor/blamejs/lib/restore-bundle.js +10 -2
- package/lib/vendor/blamejs/lib/restore.js +19 -0
- package/lib/vendor/blamejs/lib/retention.js +12 -4
- package/lib/vendor/blamejs/lib/router.js +60 -6
- package/lib/vendor/blamejs/lib/safe-buffer.js +69 -0
- package/lib/vendor/blamejs/lib/safe-decompress.js +8 -2
- package/lib/vendor/blamejs/lib/safe-dns.js +25 -20
- package/lib/vendor/blamejs/lib/safe-ical.js +7 -3
- package/lib/vendor/blamejs/lib/safe-mime.js +12 -1
- package/lib/vendor/blamejs/lib/safe-object.js +80 -0
- package/lib/vendor/blamejs/lib/safe-path.js +52 -4
- package/lib/vendor/blamejs/lib/safe-redirect.js +10 -2
- package/lib/vendor/blamejs/lib/safe-schema.js +4 -1
- package/lib/vendor/blamejs/lib/safe-sieve.js +33 -1
- package/lib/vendor/blamejs/lib/safe-sql.js +88 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +7 -3
- package/lib/vendor/blamejs/lib/scheduler.js +8 -1
- package/lib/vendor/blamejs/lib/security-assert.js +6 -6
- package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +74 -27
- package/lib/vendor/blamejs/lib/self-update.js +517 -87
- package/lib/vendor/blamejs/lib/server-timing.js +2 -4
- package/lib/vendor/blamejs/lib/session.js +34 -12
- package/lib/vendor/blamejs/lib/sql.js +15 -1
- package/lib/vendor/blamejs/lib/ssrf-guard.js +52 -0
- package/lib/vendor/blamejs/lib/static.js +55 -13
- package/lib/vendor/blamejs/lib/storage.js +8 -2
- package/lib/vendor/blamejs/lib/subject.js +6 -1
- package/lib/vendor/blamejs/lib/tenant-quota.js +52 -13
- package/lib/vendor/blamejs/lib/tls-exporter.js +3 -3
- package/lib/vendor/blamejs/lib/tsa.js +34 -13
- package/lib/vendor/blamejs/lib/uri-template.js +8 -1
- package/lib/vendor/blamejs/lib/vault/index.js +13 -0
- package/lib/vendor/blamejs/lib/vc.js +44 -5
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +26 -26
- package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +792 -1
- package/lib/vendor/blamejs/lib/vendor/noble-curves.cjs +3443 -1
- package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +2737 -1
- package/lib/vendor/blamejs/lib/vendor/pki.cjs +39550 -13
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +55 -53
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5551 -5554
- package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +22459 -32
- package/lib/vendor/blamejs/lib/watcher.js +89 -17
- package/lib/vendor/blamejs/lib/webhook-dispatcher.js +25 -3
- package/lib/vendor/blamejs/lib/webhook.js +12 -2
- package/lib/vendor/blamejs/lib/ws-client.js +32 -1
- package/lib/vendor/blamejs/lib/xml-c14n.js +29 -1
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +1 -1
- package/lib/vendor/blamejs/package-lock.json +2 -2
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.x.json +1462 -0
- package/lib/vendor/blamejs/release-notes/v0.17.0.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.1.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.10.json +30 -0
- package/lib/vendor/blamejs/release-notes/v0.17.11.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.12.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.13.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.17.14.json +40 -0
- package/lib/vendor/blamejs/release-notes/v0.17.15.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.16.json +27 -0
- package/lib/vendor/blamejs/release-notes/v0.17.17.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.18.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.19.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.2.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.20.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.21.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.22.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.3.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.4.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.5.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.6.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.7.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.8.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.9.json +26 -0
- package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +4 -0
- package/lib/vendor/blamejs/scripts/vendor-update.sh +293 -9
- package/lib/vendor/blamejs/socket.yml +94 -0
- package/lib/vendor/blamejs/test/00-primitives.js +605 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +12 -9
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +90 -1
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +122 -1
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +30 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +29 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-mysql.test.js +479 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-pg.test.js +487 -0
- package/lib/vendor/blamejs/test/integration/federation-auth.test.js +313 -5
- package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +80 -0
- package/lib/vendor/blamejs/test/integration/openid-federation-chain.test.js +275 -0
- package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +1649 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +33 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +60 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +891 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +128 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-adapters.test.js +271 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +628 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-read.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-wrap.test.js +227 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +141 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-tools.test.js +624 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit.test.js +769 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth.test.js +1697 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oid4vp.test.js +495 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-password.test.js +500 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml.test.js +1809 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +243 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index.test.js +2226 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/base32.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +693 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +603 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +557 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/case-insensitive-security-token-guard.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cbor.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +843 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba-authreqid-binding.test.js +77 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli.test.js +2790 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster-lease-renewal-ttl.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster.test.js +1032 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +739 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +8 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +194 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-lifecycle-proto-key.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +254 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +613 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/cookies.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +407 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-adversarial.test.js +352 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +24 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-oprf.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-spki-pin.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-xwing.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto.test.js +345 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +459 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query.test.js +1019 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db.test.js +1621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +462 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +323 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-verify.test.js +562 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +901 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +249 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +561 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db.test.js +1214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +46 -20
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +147 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +907 -38
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload.test.js +1135 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/forms.test.js +747 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract.test.js +1855 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-auth.test.js +117 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-cidr.test.js +151 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-domain.test.js +422 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +424 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-gate-disposition-coverage.test.js → guard-gate-disposition.test.js} +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-graphql.test.js +340 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +519 -45
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jsonpath.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jwt.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +16 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mime.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-oauth.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-regex.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-shell.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-sql-coverage.test.js → guard-sql.test.js} +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-template.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-time.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-uuid.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache-authorization.test.js +347 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +908 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client.test.js +2874 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +643 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +1153 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +494 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jsdoc-example-execution.test.js +177 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-path.test.js +103 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-schema.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwk.test.js +41 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +276 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream.test.js +241 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +441 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +2136 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +759 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +811 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +401 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +751 -8
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +864 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +830 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +635 -86
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +199 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +686 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +778 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +1438 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +449 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +773 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +623 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +13 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +877 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +326 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +242 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +1596 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +440 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics.test.js +1230 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-assetlinks.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-body-parser.test.js +1169 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-bot-disclose.test.js +123 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-cookies.test.js +108 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-gpc.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-headers.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-host-allowlist.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-require-content-type.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{middleware-tus-upload-coverage.test.js → middleware-tus-upload.test.js} +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-web-app-manifest.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-crl-fingerprint-only.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +1727 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/network-nts.test.js +699 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy.test.js +1181 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +1876 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-enforce-replay-nonboolean.test.js +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ntp-check.test.js +122 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +378 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oid4vci.test.js +1016 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/openid-federation.test.js +946 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/own-property-membership-guards.test.js +109 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-toml.test.js +543 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{parsers-safe-yaml-coverage.test.js → parsers-safe-yaml.test.js} +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +649 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +22 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +295 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact.test.js +1031 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/require-bound-key.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/restore-empty-manifest-wipe.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention.test.js +600 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-host-path-injection.test.js +231 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router.test.js +1277 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +245 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ini.test.js +376 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-json.test.js +175 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +518 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-object.test.js +62 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-schema.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-sql.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +26 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scheduler.test.js +157 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +396 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +930 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +755 -82
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +1053 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +200 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-strict-binding-missing-failclosed.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +636 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-offset-without-limit.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +1190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +850 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage.test.js +621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/subject.test.js +47 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/template.test.js +582 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +587 -25
- package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +11 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tsa.test.js +524 -63
- package/lib/vendor/blamejs/test/layer-0-primitives/uri-template.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-rotate.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +215 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +550 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +388 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +262 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket.test.js +1294 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +629 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/xml-c14n-attr-normalization.test.js +164 -0
- package/lib/vendor/blamejs/test/smoke.js +2 -0
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.0.json +0 -44
- package/lib/vendor/blamejs/release-notes/v0.16.1.json +0 -36
- package/lib/vendor/blamejs/release-notes/v0.16.2.json +0 -71
- package/lib/vendor/blamejs/release-notes/v0.16.3.json +0 -71
- package/lib/vendor/blamejs/test/layer-0-primitives/acme-coverage.test.js +0 -441
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth-coverage.test.js +0 -482
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml-coverage.test.js +0 -671
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index-coverage.test.js +0 -690
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-coverage.test.js +0 -238
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js +0 -319
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain-coverage.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth-coverage.test.js +0 -437
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js +0 -443
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js +0 -395
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy-coverage.test.js +0 -565
- package/lib/vendor/blamejs/test/layer-0-primitives/router-coverage.test.js +0 -592
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-coverage.test.js +0 -422
- package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +0 -573
|
@@ -0,0 +1,1855 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
/**
|
|
5
|
+
* Layer 0 — b.gateContract shared guard substrate.
|
|
6
|
+
*
|
|
7
|
+
* Exercises the profile-and-posture composition surface, the gate
|
|
8
|
+
* lifecycle, the dispatch/wrapper gates, and the guard-module factories
|
|
9
|
+
* through their real consumer paths:
|
|
10
|
+
*
|
|
11
|
+
* - Profile composition — buildProfile (baseProfile / extends string +
|
|
12
|
+
* array / overrides / removes / array-union / nested-merge / cycle /
|
|
13
|
+
* unknown), makeProfileBuilder, makeProfileResolver, resolveProfileName.
|
|
14
|
+
* - Posture resolution — resolveProfileAndPosture (profile + posture
|
|
15
|
+
* overlay, bad-profile / bad-posture throws, the b.compliance.set()
|
|
16
|
+
* global-posture fallback, and the one-time unmapped-posture warning),
|
|
17
|
+
* lookupCompliancePosture (unknown / prototype-key rejection / clone),
|
|
18
|
+
* makePostureAccessor, compliancePostures, strictDefaults.
|
|
19
|
+
* - Gate lifecycle — defineGate / buildGuardGate error branches plus the
|
|
20
|
+
* full check() path: hooks (beforeCheck skip/transform/throw, afterCheck,
|
|
21
|
+
* onIssue suppress/promote/replace, onSanitize, onRefuse, onAudit), the
|
|
22
|
+
* runtime cap, decision cache (hit + best-effort failure), forensic
|
|
23
|
+
* snapshot + evidence store, mode-posture translation, and the
|
|
24
|
+
* check-threw refusal.
|
|
25
|
+
* - Dispatch + wrapper gates — composeGates / multiplexGates /
|
|
26
|
+
* contentTypeMux / byActorTier / byRoute / byDirection / shadowMode /
|
|
27
|
+
* canaryGate / cachingGate / workerThreadGate.
|
|
28
|
+
* - Issue vocabulary — ISSUE_SEVERITIES, aggregateIssues, summarizeIssues,
|
|
29
|
+
* severityDisposition, policyDisposition, charThreatDisposition.
|
|
30
|
+
* - Validate substrate — runIssueValidator + INPUT_CONTRACTS,
|
|
31
|
+
* detectStringInput, badInputResultIfNotStringOrBuffer, composeHooks,
|
|
32
|
+
* throwOnRefusalSeverity, extractBytesAsText, identifierFixtures,
|
|
33
|
+
* makeRulePackLoader, buildContentGate, and the defineGuard /
|
|
34
|
+
* defineParser assemblers.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
var helpers = require("../helpers");
|
|
38
|
+
var check = helpers.check;
|
|
39
|
+
var b = helpers.b;
|
|
40
|
+
|
|
41
|
+
var GC = b.gateContract;
|
|
42
|
+
var GCE = GC.GateContractError;
|
|
43
|
+
|
|
44
|
+
// ---- small operator-supplied handle collectors (defineGate's real
|
|
45
|
+
// opts.audit / opts.observability / forensicEvidenceStore surface;
|
|
46
|
+
// no shared helper provides a gate-contract audit/obs collector). ----
|
|
47
|
+
function makeAuditCollector() {
|
|
48
|
+
return { rows: [], safeEmit: function (entry) { this.rows.push(entry); } };
|
|
49
|
+
}
|
|
50
|
+
function makeObsCollector() {
|
|
51
|
+
return {
|
|
52
|
+
events: [],
|
|
53
|
+
safeEvent: function (name, value, labels) {
|
|
54
|
+
this.events.push({ name: name, value: value, labels: labels });
|
|
55
|
+
},
|
|
56
|
+
has: function (name) { return this.events.some(function (e) { return e.name === name; }); },
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function makeForensicStore() {
|
|
60
|
+
return { writes: [], write: async function (rec) { this.writes.push(rec); } };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function serveCheck() { return { ok: true, action: "serve" }; }
|
|
64
|
+
function refuseCheck() {
|
|
65
|
+
return { ok: false, action: "refuse",
|
|
66
|
+
issues: [{ kind: "x.bad", severity: "high", snippet: "bad" }] };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ------------------------------------------------------------------
|
|
70
|
+
// Issue-severity vocabulary + audit projection (advertised semantics).
|
|
71
|
+
// ------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
function testIssueSeverities() {
|
|
74
|
+
var sevs = b.gateContract.ISSUE_SEVERITIES;
|
|
75
|
+
check("ISSUE_SEVERITIES: exact ladder",
|
|
76
|
+
JSON.stringify(sevs) === JSON.stringify(["info", "warn", "high", "critical"]));
|
|
77
|
+
check("ISSUE_SEVERITIES: frozen", Object.isFrozen(sevs) === true);
|
|
78
|
+
check("ISSUE_SEVERITIES: critical highest", sevs.indexOf("critical") === 3);
|
|
79
|
+
check("ISSUE_SEVERITIES: info lowest", sevs.indexOf("info") === 0);
|
|
80
|
+
|
|
81
|
+
sevs.forEach(function (sev) {
|
|
82
|
+
var result = GC.aggregateIssues([{ kind: "k." + sev, severity: sev }]);
|
|
83
|
+
var expectOk = (sev === "info" || sev === "warn");
|
|
84
|
+
check("aggregateIssues(" + sev + ") ok=" + expectOk, result.ok === expectOk);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
var mixed = GC.aggregateIssues([
|
|
88
|
+
{ kind: "a", severity: "info" },
|
|
89
|
+
{ kind: "b", severity: "high" },
|
|
90
|
+
{ kind: "c", severity: "warn" },
|
|
91
|
+
]);
|
|
92
|
+
check("aggregateIssues: mixed with high → ok false", mixed.ok === false);
|
|
93
|
+
check("aggregateIssues: mixed keeps all issues", mixed.issues.length === 3);
|
|
94
|
+
check("aggregateIssues: empty → ok true", GC.aggregateIssues([]).ok === true);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function testSummarizeIssues() {
|
|
98
|
+
var summary = GC.summarizeIssues([
|
|
99
|
+
{ kind: "csv.bidi", severity: "high", ruleId: "BIDI-OVERRIDE",
|
|
100
|
+
snippet: "U+202E raw bytes that must never reach the audit log" },
|
|
101
|
+
{ kind: "csv.trailing-whitespace", severity: "info", ruleId: "TRIM" },
|
|
102
|
+
]);
|
|
103
|
+
check("summarizeIssues: length preserved", summary.length === 2);
|
|
104
|
+
check("summarizeIssues: kind preserved", summary[0].kind === "csv.bidi");
|
|
105
|
+
check("summarizeIssues: severity preserved", summary[0].severity === "high");
|
|
106
|
+
check("summarizeIssues: ruleId preserved", summary[0].ruleId === "BIDI-OVERRIDE");
|
|
107
|
+
check("summarizeIssues: snippet stripped", summary[0].snippet === undefined);
|
|
108
|
+
check("summarizeIssues: exactly 3 keys",
|
|
109
|
+
Object.keys(summary[0]).sort().join(",") === "kind,ruleId,severity");
|
|
110
|
+
check("summarizeIssues: second snippet absent",
|
|
111
|
+
Object.prototype.hasOwnProperty.call(summary[1], "snippet") === false);
|
|
112
|
+
|
|
113
|
+
var noRule = GC.summarizeIssues([{ kind: "x", severity: "warn" }]);
|
|
114
|
+
check("summarizeIssues: absent ruleId → undefined", noRule[0].ruleId === undefined);
|
|
115
|
+
check("summarizeIssues: non-array → []",
|
|
116
|
+
Array.isArray(GC.summarizeIssues("nope")) && GC.summarizeIssues("nope").length === 0);
|
|
117
|
+
check("summarizeIssues: undefined → []", GC.summarizeIssues(undefined).length === 0);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function testSeverityAndPolicyDispositions() {
|
|
121
|
+
check("severityDisposition: [] → serve",
|
|
122
|
+
GC.severityDisposition([]).action === "serve");
|
|
123
|
+
var lo = GC.severityDisposition([{ kind: "k", severity: "low" }]);
|
|
124
|
+
check("severityDisposition: low → audit-only", lo.action === "audit-only" && lo.ok === true);
|
|
125
|
+
var hi = GC.severityDisposition([{ kind: "k", severity: "high" }]);
|
|
126
|
+
check("severityDisposition: high → refuse", hi.action === "refuse" && hi.ok === false);
|
|
127
|
+
var crit = GC.severityDisposition([{ kind: "k", severity: "critical" }]);
|
|
128
|
+
check("severityDisposition: critical → refuse", crit.action === "refuse");
|
|
129
|
+
|
|
130
|
+
check("policyDisposition: reject → refuse", GC.policyDisposition("reject") === "refuse");
|
|
131
|
+
check("policyDisposition: audit → audit", GC.policyDisposition("audit") === "audit");
|
|
132
|
+
check("policyDisposition: audit-only → audit", GC.policyDisposition("audit-only") === "audit");
|
|
133
|
+
check("policyDisposition: strip → sanitize", GC.policyDisposition("strip") === "sanitize");
|
|
134
|
+
check("policyDisposition: redact → sanitize", GC.policyDisposition("redact") === "sanitize");
|
|
135
|
+
check("policyDisposition: prefix-tab → sanitize", GC.policyDisposition("prefix-tab") === "sanitize");
|
|
136
|
+
check("policyDisposition: typo → refuse (fail closed)", GC.policyDisposition("rejet") === "refuse");
|
|
137
|
+
check("policyDisposition: undefined → refuse (fail closed)",
|
|
138
|
+
GC.policyDisposition(undefined) === "refuse");
|
|
139
|
+
|
|
140
|
+
check("charThreatDisposition: bidi-override honors bidiPolicy",
|
|
141
|
+
GC.charThreatDisposition({ kind: "bidi-override" }, { bidiPolicy: "reject" }) === "refuse");
|
|
142
|
+
check("charThreatDisposition: null-byte honors nullBytePolicy",
|
|
143
|
+
GC.charThreatDisposition({ kind: "null-byte" }, { nullBytePolicy: "strip" }) === "sanitize");
|
|
144
|
+
check("charThreatDisposition: control-char honors controlPolicy",
|
|
145
|
+
GC.charThreatDisposition({ kind: "control-char" }, { controlPolicy: "audit" }) === "audit");
|
|
146
|
+
check("charThreatDisposition: zero-width honors zeroWidthPolicy (unknown → refuse)",
|
|
147
|
+
GC.charThreatDisposition({ kind: "zero-width" }, { zeroWidthPolicy: "allow" }) === "refuse");
|
|
148
|
+
check("charThreatDisposition: unknown kind → null",
|
|
149
|
+
GC.charThreatDisposition({ kind: "other" }, {}) === null);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ------------------------------------------------------------------
|
|
153
|
+
// buildProfile / makeProfileBuilder — composition, merge, remove, cycle.
|
|
154
|
+
// ------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
var PROFILES = {
|
|
157
|
+
base: { tags: ["p", "a"], attrs: { a: ["href", "target"] }, mode: "keep" },
|
|
158
|
+
withImg: { extends: ["base"], tags: ["img"] },
|
|
159
|
+
cycA: { extends: ["cycB"] },
|
|
160
|
+
cycB: { extends: ["cycA"] },
|
|
161
|
+
};
|
|
162
|
+
function resolveProfile(name) { return PROFILES[name] || null; }
|
|
163
|
+
|
|
164
|
+
function testBuildProfileComposition() {
|
|
165
|
+
// requireObject + resolveProfile-required error branches.
|
|
166
|
+
var threw = false;
|
|
167
|
+
try { GC.buildProfile("not-an-object"); } catch (e) { threw = e instanceof GCE; }
|
|
168
|
+
check("buildProfile: non-object opts throws GateContractError", threw);
|
|
169
|
+
|
|
170
|
+
threw = false;
|
|
171
|
+
try { GC.buildProfile({ baseProfile: "base" }); }
|
|
172
|
+
catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
173
|
+
check("buildProfile: missing resolveProfile → bad-opt", threw);
|
|
174
|
+
|
|
175
|
+
// baseProfile walk pulls its nested `extends` in; arrays union.
|
|
176
|
+
var withImg = GC.buildProfile({ baseProfile: "withImg", resolveProfile: resolveProfile });
|
|
177
|
+
check("buildProfile: baseProfile walk unions extended tags",
|
|
178
|
+
JSON.stringify(withImg.tags) === JSON.stringify(["p", "a", "img"]));
|
|
179
|
+
check("buildProfile: nested object carried from extended base",
|
|
180
|
+
JSON.stringify(withImg.attrs.a) === JSON.stringify(["href", "target"]));
|
|
181
|
+
|
|
182
|
+
// extends as a bare string normalizes to a one-element list.
|
|
183
|
+
var strExtend = GC.buildProfile({ extends: "base", resolveProfile: resolveProfile });
|
|
184
|
+
check("buildProfile: extends string composes",
|
|
185
|
+
JSON.stringify(strExtend.tags) === JSON.stringify(["p", "a"]));
|
|
186
|
+
|
|
187
|
+
// extends as an array.
|
|
188
|
+
var arrExtend = GC.buildProfile({ extends: ["base"], resolveProfile: resolveProfile });
|
|
189
|
+
check("buildProfile: extends array composes", arrExtend.mode === "keep");
|
|
190
|
+
|
|
191
|
+
// overrides merge after extends (array union + new scalar key).
|
|
192
|
+
var overridden = GC.buildProfile({
|
|
193
|
+
baseProfile: "base", overrides: { tags: ["em"], added: 1 }, resolveProfile: resolveProfile,
|
|
194
|
+
});
|
|
195
|
+
check("buildProfile: overrides union onto array",
|
|
196
|
+
JSON.stringify(overridden.tags) === JSON.stringify(["p", "a", "em"]));
|
|
197
|
+
check("buildProfile: overrides add scalar key", overridden.added === 1);
|
|
198
|
+
|
|
199
|
+
// removes: array-entry filter, nested-object removal, whole-key delete.
|
|
200
|
+
var removed = GC.buildProfile({
|
|
201
|
+
baseProfile: "base",
|
|
202
|
+
removes: { tags: ["a"], attrs: { a: ["target"] }, mode: true },
|
|
203
|
+
resolveProfile: resolveProfile,
|
|
204
|
+
});
|
|
205
|
+
check("buildProfile: removes filters array entry",
|
|
206
|
+
JSON.stringify(removed.tags) === JSON.stringify(["p"]));
|
|
207
|
+
check("buildProfile: removes nested array entry",
|
|
208
|
+
JSON.stringify(removed.attrs.a) === JSON.stringify(["href"]));
|
|
209
|
+
check("buildProfile: removes deletes whole key",
|
|
210
|
+
Object.prototype.hasOwnProperty.call(removed, "mode") === false);
|
|
211
|
+
|
|
212
|
+
// Scalar override through _mergeProfile (later source wins).
|
|
213
|
+
var scalar = GC.buildProfile({
|
|
214
|
+
baseProfile: "base", overrides: { mode: "swap" }, resolveProfile: resolveProfile,
|
|
215
|
+
});
|
|
216
|
+
check("buildProfile: scalar override replaces", scalar.mode === "swap");
|
|
217
|
+
|
|
218
|
+
// Nested-object recursion in _mergeProfile: both base and override carry a
|
|
219
|
+
// nested object under the same key → recursive merge (array union + new key).
|
|
220
|
+
var nested = GC.buildProfile({
|
|
221
|
+
baseProfile: "base",
|
|
222
|
+
overrides: { attrs: { a: ["extra"], img: ["src"] } },
|
|
223
|
+
resolveProfile: resolveProfile,
|
|
224
|
+
});
|
|
225
|
+
check("buildProfile: nested object merges recursively (array union)",
|
|
226
|
+
JSON.stringify(nested.attrs.a) === JSON.stringify(["href", "target", "extra"]));
|
|
227
|
+
check("buildProfile: nested object merges recursively (new sub-key)",
|
|
228
|
+
JSON.stringify(nested.attrs.img) === JSON.stringify(["src"]));
|
|
229
|
+
|
|
230
|
+
// Cycle detection.
|
|
231
|
+
threw = false;
|
|
232
|
+
try { GC.buildProfile({ baseProfile: "cycA", resolveProfile: resolveProfile }); }
|
|
233
|
+
catch (e) { threw = e.code === "gate-contract/profile-cycle"; }
|
|
234
|
+
check("buildProfile: cycle throws profile-cycle", threw);
|
|
235
|
+
|
|
236
|
+
// Unknown profile name.
|
|
237
|
+
threw = false;
|
|
238
|
+
try { GC.buildProfile({ baseProfile: "ghost", resolveProfile: resolveProfile }); }
|
|
239
|
+
catch (e) { threw = e.code === "gate-contract/unknown-profile"; }
|
|
240
|
+
check("buildProfile: unknown baseProfile throws unknown-profile", threw);
|
|
241
|
+
|
|
242
|
+
threw = false;
|
|
243
|
+
try { GC.buildProfile({ extends: ["ghost"], resolveProfile: resolveProfile }); }
|
|
244
|
+
catch (e) { threw = e.code === "gate-contract/unknown-profile"; }
|
|
245
|
+
check("buildProfile: unknown extends throws unknown-profile", threw);
|
|
246
|
+
|
|
247
|
+
// Empty opts → empty base object.
|
|
248
|
+
var empty = GC.buildProfile({ resolveProfile: resolveProfile });
|
|
249
|
+
check("buildProfile: no baseProfile/extends → {}", Object.keys(empty).length === 0);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function testMakeProfileBuilder() {
|
|
253
|
+
var build = GC.makeProfileBuilder(PROFILES);
|
|
254
|
+
var custom = build({ baseProfile: "withImg", overrides: { tags: ["em"] } });
|
|
255
|
+
check("makeProfileBuilder: resolves through closed-over table",
|
|
256
|
+
custom.tags.indexOf("img") !== -1 && custom.tags.indexOf("em") !== -1);
|
|
257
|
+
var threw = false;
|
|
258
|
+
try { build({ baseProfile: "ghost" }); }
|
|
259
|
+
catch (e) { threw = e.code === "gate-contract/unknown-profile"; }
|
|
260
|
+
check("makeProfileBuilder: unknown name throws unknown-profile", threw);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function testMakeProfileResolverAndName() {
|
|
264
|
+
var resolver = GC.makeProfileResolver({
|
|
265
|
+
profiles: { strict: { a: 1 }, balanced: { a: 2 } },
|
|
266
|
+
postures: { hipaa: "strict" }, defaults: "strict",
|
|
267
|
+
errorClass: GCE, codePrefix: "csv",
|
|
268
|
+
});
|
|
269
|
+
check("makeProfileResolver: posture-first", resolver({ posture: "hipaa" }) === "strict");
|
|
270
|
+
check("makeProfileResolver: explicit profile", resolver({ profile: "balanced" }) === "balanced");
|
|
271
|
+
check("makeProfileResolver: default fallback", resolver({}) === "strict");
|
|
272
|
+
check("makeProfileResolver: unmapped posture falls to default",
|
|
273
|
+
resolver({ posture: "nope" }) === "strict");
|
|
274
|
+
var threw = false;
|
|
275
|
+
try { resolver({ profile: "ghost" }); }
|
|
276
|
+
catch (e) { threw = e.code === "csv/bad-profile"; }
|
|
277
|
+
check("makeProfileResolver: unknown profile throws bad-profile", threw);
|
|
278
|
+
|
|
279
|
+
var byObject = GC.makeProfileResolver({
|
|
280
|
+
profiles: { strict: { a: 1 } }, postures: { hipaa: "strict" },
|
|
281
|
+
defaults: "strict", errorClass: GCE, codePrefix: "csv", byObject: true,
|
|
282
|
+
});
|
|
283
|
+
check("makeProfileResolver: byObject returns config object",
|
|
284
|
+
byObject({ profile: "strict" }).a === 1);
|
|
285
|
+
check("makeProfileResolver: byObject posture returns object",
|
|
286
|
+
byObject({ posture: "hipaa" }).a === 1);
|
|
287
|
+
|
|
288
|
+
var POSTURES = { hipaa: "strict" };
|
|
289
|
+
check("resolveProfileName: explicit profile wins",
|
|
290
|
+
GC.resolveProfileName({ profile: "balanced" }, POSTURES, "strict") === "balanced");
|
|
291
|
+
check("resolveProfileName: posture mapped",
|
|
292
|
+
GC.resolveProfileName({ posture: "hipaa" }, POSTURES, "strict") === "strict");
|
|
293
|
+
check("resolveProfileName: default when neither",
|
|
294
|
+
GC.resolveProfileName({}, POSTURES, "strict") === "strict");
|
|
295
|
+
check("resolveProfileName: prototype-key posture → default (proto-shadow safe)",
|
|
296
|
+
GC.resolveProfileName({ posture: "constructor" }, {}, "strict") === "strict");
|
|
297
|
+
check("resolveProfileName: null opts → default",
|
|
298
|
+
GC.resolveProfileName(null, POSTURES, "strict") === "strict");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ------------------------------------------------------------------
|
|
302
|
+
// resolveProfileAndPosture — overlays, throws, global-posture fallback,
|
|
303
|
+
// unmapped-posture warning.
|
|
304
|
+
// ------------------------------------------------------------------
|
|
305
|
+
|
|
306
|
+
function testResolveProfileAndPosture() {
|
|
307
|
+
var RP = { strict: { a: 1, x: "s" }, balanced: { a: 2, x: "b" } };
|
|
308
|
+
var RPOST = { hipaa: { p: "redact" }, "pci-dss": { p: "redact" } };
|
|
309
|
+
var cfg = {
|
|
310
|
+
profiles: RP, compliancePostures: RPOST, defaults: { a: 0, d: 9 },
|
|
311
|
+
errorClass: GCE, errCodePrefix: "csv",
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// requireObject(cfg).
|
|
315
|
+
var threw = false;
|
|
316
|
+
try { GC.resolveProfileAndPosture({}, null); }
|
|
317
|
+
catch (e) { threw = e instanceof GCE; }
|
|
318
|
+
check("resolveProfileAndPosture: non-object cfg throws", threw);
|
|
319
|
+
|
|
320
|
+
// Profile overlay + defaults + inline-opts-last precedence.
|
|
321
|
+
var byProfile = GC.resolveProfileAndPosture({ profile: "balanced" }, cfg);
|
|
322
|
+
check("resolveProfileAndPosture: profile overlay applied", byProfile.x === "b");
|
|
323
|
+
check("resolveProfileAndPosture: defaults kept when not overlaid", byProfile.d === 9);
|
|
324
|
+
|
|
325
|
+
var inlineWins = GC.resolveProfileAndPosture({ profile: "balanced", a: 42 }, cfg);
|
|
326
|
+
check("resolveProfileAndPosture: inline opt wins over overlay", inlineWins.a === 42);
|
|
327
|
+
|
|
328
|
+
// bad-profile.
|
|
329
|
+
threw = false;
|
|
330
|
+
try { GC.resolveProfileAndPosture({ profile: "ghost" }, cfg); }
|
|
331
|
+
catch (e) { threw = e.code === "csv.bad-profile"; }
|
|
332
|
+
check("resolveProfileAndPosture: unknown profile → bad-profile", threw);
|
|
333
|
+
|
|
334
|
+
// Posture overlay.
|
|
335
|
+
var byPosture = GC.resolveProfileAndPosture({ compliancePosture: "hipaa" }, cfg);
|
|
336
|
+
check("resolveProfileAndPosture: posture overlay applied", byPosture.p === "redact");
|
|
337
|
+
|
|
338
|
+
// bad-posture.
|
|
339
|
+
threw = false;
|
|
340
|
+
try { GC.resolveProfileAndPosture({ compliancePosture: "ghost" }, cfg); }
|
|
341
|
+
catch (e) { threw = e.code === "csv.bad-posture"; }
|
|
342
|
+
check("resolveProfileAndPosture: unknown posture → bad-posture", threw);
|
|
343
|
+
|
|
344
|
+
// Default errorClass + prefix branch (cfg without errorClass/errCodePrefix).
|
|
345
|
+
threw = false;
|
|
346
|
+
try { GC.resolveProfileAndPosture({ profile: "ghost" }, { profiles: RP }); }
|
|
347
|
+
catch (e) { threw = e instanceof GCE && e.code === "guard.bad-profile"; }
|
|
348
|
+
check("resolveProfileAndPosture: default errorClass/prefix on bad-profile", threw);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function testGlobalPostureFallback() {
|
|
352
|
+
var RP = { strict: { a: 1 }, balanced: { a: 2 } };
|
|
353
|
+
var RPOST = { hipaa: { p: "redact" } };
|
|
354
|
+
var cfg = {
|
|
355
|
+
profiles: RP, compliancePostures: RPOST, defaults: { a: 0 },
|
|
356
|
+
errorClass: GCE, errCodePrefix: "csv",
|
|
357
|
+
};
|
|
358
|
+
try {
|
|
359
|
+
b.compliance.set("hipaa");
|
|
360
|
+
var resolved = GC.resolveProfileAndPosture({}, cfg);
|
|
361
|
+
check("global posture: b.compliance.set('hipaa') picked up as fallback overlay",
|
|
362
|
+
resolved.p === "redact");
|
|
363
|
+
} finally {
|
|
364
|
+
if (typeof b.compliance._resetForTest === "function") b.compliance._resetForTest();
|
|
365
|
+
GC._resetForTest();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function testUnmappedPostureWarning() {
|
|
370
|
+
var RP = { strict: { a: 1 } };
|
|
371
|
+
// No compliancePostures overlay for the pinned posture → safe-default
|
|
372
|
+
// fall-through + a one-time grep-able warning (dedupe on second call).
|
|
373
|
+
var cfg = {
|
|
374
|
+
profiles: RP, compliancePostures: { hipaa: { p: "redact" } },
|
|
375
|
+
defaults: { a: 0, d: 7 }, errorClass: GCE, errCodePrefix: "csv",
|
|
376
|
+
};
|
|
377
|
+
try {
|
|
378
|
+
GC._resetForTest();
|
|
379
|
+
b.compliance.set("fedramp-rev5-moderate");
|
|
380
|
+
var r1 = GC.resolveProfileAndPosture({}, cfg);
|
|
381
|
+
check("unmapped posture: safe-default fall-through (no overlay applied)",
|
|
382
|
+
r1.p === undefined && r1.d === 7);
|
|
383
|
+
// Second call hits the dedupe early-return in _warnUnmappedPosture.
|
|
384
|
+
var r2 = GC.resolveProfileAndPosture({}, cfg);
|
|
385
|
+
check("unmapped posture: dedupe keeps safe default on repeat", r2.d === 7);
|
|
386
|
+
} finally {
|
|
387
|
+
if (typeof b.compliance._resetForTest === "function") b.compliance._resetForTest();
|
|
388
|
+
GC._resetForTest();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// ------------------------------------------------------------------
|
|
393
|
+
// Compliance-posture lookups + posture/defaults factories.
|
|
394
|
+
// ------------------------------------------------------------------
|
|
395
|
+
|
|
396
|
+
function testLookupCompliancePosture() {
|
|
397
|
+
var POST = { hipaa: { p: "redact", n: 1 } };
|
|
398
|
+
var got = GC.lookupCompliancePosture("hipaa", POST, GCE.factory, "csv");
|
|
399
|
+
check("lookupCompliancePosture: returns posture values", got.p === "redact");
|
|
400
|
+
got.p = "MUTATED";
|
|
401
|
+
check("lookupCompliancePosture: returns a clone (source untouched)",
|
|
402
|
+
POST.hipaa.p === "redact");
|
|
403
|
+
|
|
404
|
+
var threw = false;
|
|
405
|
+
try { GC.lookupCompliancePosture("ghost", POST, GCE.factory, "csv"); }
|
|
406
|
+
catch (e) { threw = e.code === "csv.bad-posture"; }
|
|
407
|
+
check("lookupCompliancePosture: unknown name throws bad-posture", threw);
|
|
408
|
+
|
|
409
|
+
// Prototype-key must not resolve to an inherited value.
|
|
410
|
+
threw = false;
|
|
411
|
+
try { GC.lookupCompliancePosture("constructor", POST, GCE.factory, "csv"); }
|
|
412
|
+
catch (e) { threw = e.code === "csv.bad-posture"; }
|
|
413
|
+
check("lookupCompliancePosture: prototype key rejected", threw);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function testMakePostureAccessor() {
|
|
417
|
+
var acc = GC.makePostureAccessor({ hipaa: "strict" });
|
|
418
|
+
check("makePostureAccessor: known posture → mapped name", acc("hipaa") === "strict");
|
|
419
|
+
check("makePostureAccessor: unknown → null default", acc("nope") === null);
|
|
420
|
+
check("makePostureAccessor: prototype key → fallback (proto-shadow safe)",
|
|
421
|
+
acc("constructor") === null);
|
|
422
|
+
var accF = GC.makePostureAccessor({ hipaa: "strict" }, { fallback: "none" });
|
|
423
|
+
check("makePostureAccessor: custom fallback", accF("nope") === "none");
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function testCompliancePosturesFactory() {
|
|
427
|
+
var built = GC.compliancePostures({ strict: { s: 1 }, balanced: { z: 2 } }, { base: 64 });
|
|
428
|
+
check("compliancePostures: hipaa is strict tier + budget",
|
|
429
|
+
built.hipaa.s === 1 && built.hipaa.forensicSnippetBytes === 64);
|
|
430
|
+
check("compliancePostures: gdpr is balanced tier + half budget",
|
|
431
|
+
built.gdpr.z === 2 && built.gdpr.forensicSnippetBytes === 32);
|
|
432
|
+
check("compliancePostures: soc2 keeps double budget",
|
|
433
|
+
built.soc2.forensicSnippetBytes === 128);
|
|
434
|
+
check("compliancePostures: postures frozen", Object.isFrozen(built.hipaa));
|
|
435
|
+
|
|
436
|
+
var overlaid = GC.compliancePostures({ strict: {}, balanced: {} },
|
|
437
|
+
{ base: 64, overlays: { gdpr: { extra: true } } });
|
|
438
|
+
check("compliancePostures: per-posture overlay merged", overlaid.gdpr.extra === true);
|
|
439
|
+
|
|
440
|
+
var threw = false;
|
|
441
|
+
try { GC.compliancePostures({ strict: {} }, { base: 64 }); }
|
|
442
|
+
catch (e) { threw = e.code === "gate-contract/bad-profiles"; }
|
|
443
|
+
check("compliancePostures: missing balanced → bad-profiles", threw);
|
|
444
|
+
|
|
445
|
+
threw = false;
|
|
446
|
+
try { GC.compliancePostures({ strict: {}, balanced: {} }, { base: 63 }); }
|
|
447
|
+
catch (e) { threw = e.code === "gate-contract/bad-base"; }
|
|
448
|
+
check("compliancePostures: odd base → bad-base", threw);
|
|
449
|
+
|
|
450
|
+
threw = false;
|
|
451
|
+
try { GC.compliancePostures({ strict: {}, balanced: {} }, { base: 0 }); }
|
|
452
|
+
catch (e) { threw = e.code === "gate-contract/bad-base"; }
|
|
453
|
+
check("compliancePostures: zero base → bad-base", threw);
|
|
454
|
+
|
|
455
|
+
threw = false;
|
|
456
|
+
try { GC.compliancePostures(null, { base: 64 }); }
|
|
457
|
+
catch (e) { threw = e instanceof GCE; }
|
|
458
|
+
check("compliancePostures: non-object profiles throws", threw);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function testStrictDefaults() {
|
|
462
|
+
var d = GC.strictDefaults({ strict: { a: 1 } });
|
|
463
|
+
check("strictDefaults: strict profile + enforce mode", d.a === 1 && d.mode === "enforce");
|
|
464
|
+
check("strictDefaults: frozen", Object.isFrozen(d));
|
|
465
|
+
var overlaid = GC.strictDefaults({ strict: { a: 1 } }, { maxRuntimeMs: 10 });
|
|
466
|
+
check("strictDefaults: overlay merged last", overlaid.maxRuntimeMs === 10);
|
|
467
|
+
|
|
468
|
+
var threw = false;
|
|
469
|
+
try { GC.strictDefaults({}); }
|
|
470
|
+
catch (e) { threw = e.code === "gate-contract/bad-profiles"; }
|
|
471
|
+
check("strictDefaults: missing strict → bad-profiles", threw);
|
|
472
|
+
|
|
473
|
+
threw = false;
|
|
474
|
+
try { GC.strictDefaults(null); }
|
|
475
|
+
catch (e) { threw = e instanceof GCE; }
|
|
476
|
+
check("strictDefaults: non-object profiles throws", threw);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ------------------------------------------------------------------
|
|
480
|
+
// Validate substrate — contracts, detectStringInput, composeHooks,
|
|
481
|
+
// throwOnRefusalSeverity, extractBytesAsText, fixtures, rule packs.
|
|
482
|
+
// ------------------------------------------------------------------
|
|
483
|
+
|
|
484
|
+
function detectFormula(text) {
|
|
485
|
+
if (/^[=+@]/.test(String(text))) {
|
|
486
|
+
return [{ kind: "csv.formula-injection", severity: "high", snippet: String(text).slice(0, 8) }];
|
|
487
|
+
}
|
|
488
|
+
return [];
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function testRunIssueValidatorContracts() {
|
|
492
|
+
var bad = GC.runIssueValidator("=cmd|x", {}, detectFormula);
|
|
493
|
+
check("runIssueValidator: text contract flags high issue", bad.ok === false);
|
|
494
|
+
var ok = GC.runIssueValidator("ada,36", {}, detectFormula);
|
|
495
|
+
check("runIssueValidator: text contract clean → ok", ok.ok === true);
|
|
496
|
+
|
|
497
|
+
var fromBuf = GC.runIssueValidator(Buffer.from("=x"), {}, detectFormula);
|
|
498
|
+
check("runIssueValidator: text contract coerces Buffer", fromBuf.ok === false);
|
|
499
|
+
|
|
500
|
+
var badInput = GC.runIssueValidator(42, {}, detectFormula);
|
|
501
|
+
check("runIssueValidator: text contract non-string → bad-input",
|
|
502
|
+
badInput.ok === false && badInput.issues[0].kind === "bad-input");
|
|
503
|
+
|
|
504
|
+
// raw contract: object passes through to the detector untouched.
|
|
505
|
+
var rawOk = GC.runIssueValidator({ meta: 1 }, {}, function () { return []; }, "raw");
|
|
506
|
+
check("runIssueValidator: raw contract passes object through", rawOk.ok === true);
|
|
507
|
+
|
|
508
|
+
// bytes contract rejects non-string/Buffer.
|
|
509
|
+
var bytesBad = GC.runIssueValidator(42, {}, detectFormula, "bytes");
|
|
510
|
+
check("runIssueValidator: bytes contract non-string → bad-input", bytesBad.ok === false);
|
|
511
|
+
var bytesOk = GC.runIssueValidator(Buffer.from("ada"), {}, detectFormula, "bytes");
|
|
512
|
+
check("runIssueValidator: bytes contract Buffer passes", bytesOk.ok === true);
|
|
513
|
+
|
|
514
|
+
// Custom function contract.
|
|
515
|
+
var custom = function (input) {
|
|
516
|
+
return input === "bad" ? { badInput: "custom rejects" } : { subject: input };
|
|
517
|
+
};
|
|
518
|
+
var cBad = GC.runIssueValidator("bad", {}, detectFormula, custom);
|
|
519
|
+
check("runIssueValidator: custom contract badInput", cBad.ok === false);
|
|
520
|
+
var cOk = GC.runIssueValidator("good", {}, function () { return []; }, custom);
|
|
521
|
+
check("runIssueValidator: custom contract subject", cOk.ok === true);
|
|
522
|
+
|
|
523
|
+
// Unknown contract name falls back to text.
|
|
524
|
+
var unknown = GC.runIssueValidator(42, {}, detectFormula, "no-such-contract");
|
|
525
|
+
check("runIssueValidator: unknown contract name → text (rejects non-string)",
|
|
526
|
+
unknown.ok === false);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function testBadInputResult() {
|
|
530
|
+
check("badInputResultIfNotStringOrBuffer: string → null",
|
|
531
|
+
GC.badInputResultIfNotStringOrBuffer("hi") === null);
|
|
532
|
+
check("badInputResultIfNotStringOrBuffer: Buffer → null",
|
|
533
|
+
GC.badInputResultIfNotStringOrBuffer(Buffer.from("x")) === null);
|
|
534
|
+
var bad = GC.badInputResultIfNotStringOrBuffer(42);
|
|
535
|
+
check("badInputResultIfNotStringOrBuffer: number → bad-input result",
|
|
536
|
+
bad.ok === false && bad.issues[0].kind === "bad-input");
|
|
537
|
+
check("badInputResultIfNotStringOrBuffer: null → bad-input result",
|
|
538
|
+
GC.badInputResultIfNotStringOrBuffer(null).ok === false);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function testDetectStringInput() {
|
|
542
|
+
var threw = false;
|
|
543
|
+
try { GC.detectStringInput("x", {}, null); } catch (e) { threw = e instanceof GCE; }
|
|
544
|
+
check("detectStringInput: non-object cfg throws", threw);
|
|
545
|
+
|
|
546
|
+
threw = false;
|
|
547
|
+
try { GC.detectStringInput("x", {}, {}); }
|
|
548
|
+
catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
549
|
+
check("detectStringInput: missing cfg.name throws bad-opt", threw);
|
|
550
|
+
|
|
551
|
+
var nonStr = GC.detectStringInput(42, {}, { name: "cidr" });
|
|
552
|
+
check("detectStringInput: non-string → done bad-input",
|
|
553
|
+
nonStr.done === true && nonStr.issues[0].ruleId === "cidr.bad-input");
|
|
554
|
+
|
|
555
|
+
var emptyIssue = GC.detectStringInput("", {}, { name: "cidr" });
|
|
556
|
+
check("detectStringInput: empty (default) → done empty issue",
|
|
557
|
+
emptyIssue.done === true && emptyIssue.issues[0].kind === "empty");
|
|
558
|
+
|
|
559
|
+
var emptyOk = GC.detectStringInput("", {}, { name: "cidr", emptyMode: "ok" });
|
|
560
|
+
check("detectStringInput: emptyMode ok → done with []",
|
|
561
|
+
emptyOk.done === true && emptyOk.issues.length === 0);
|
|
562
|
+
|
|
563
|
+
var emptySkip = GC.detectStringInput("", {}, {
|
|
564
|
+
name: "cidr", emptyMode: "skip", scanCodepoints: false,
|
|
565
|
+
});
|
|
566
|
+
check("detectStringInput: emptyMode skip + no scan → not done, []",
|
|
567
|
+
emptySkip.done === false && emptySkip.issues.length === 0);
|
|
568
|
+
|
|
569
|
+
var capped = GC.detectStringInput("abcdef", {}, { name: "cidr", cap: { bytes: 3 } });
|
|
570
|
+
check("detectStringInput: over cap → default cap issue",
|
|
571
|
+
capped.done === true && capped.issues[0].kind === "cidr-cap" &&
|
|
572
|
+
capped.issues[0].ruleId === "cidr.cidr-cap");
|
|
573
|
+
|
|
574
|
+
var capFn = GC.detectStringInput("abcdef", {}, {
|
|
575
|
+
name: "cidr", cap: { bytes: 3, kind: "too-big", snippet: function (len) { return "len=" + len; } },
|
|
576
|
+
});
|
|
577
|
+
check("detectStringInput: cap function snippet + custom kind",
|
|
578
|
+
capFn.issues[0].kind === "too-big" && capFn.issues[0].snippet === "len=6");
|
|
579
|
+
|
|
580
|
+
var scanned = GC.detectStringInput("clean", {}, { name: "cidr" });
|
|
581
|
+
check("detectStringInput: clean input → not done, codepoint list",
|
|
582
|
+
scanned.done === false && Array.isArray(scanned.issues));
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
async function testComposeHooks() {
|
|
586
|
+
check("composeHooks: empty → null", GC.composeHooks([]) === null);
|
|
587
|
+
check("composeHooks: single → identity", typeof GC.composeHooks([serveCheck]) === "function");
|
|
588
|
+
|
|
589
|
+
var redact = function (issue) { return Object.assign({}, issue, { snippet: "<redacted>" }); };
|
|
590
|
+
var dropInfo = function (issue) { return issue.severity === "info" ? { suppress: true } : null; };
|
|
591
|
+
var chain = GC.composeHooks([dropInfo, redact]);
|
|
592
|
+
var infoHit = await chain({ kind: "trim", severity: "info" });
|
|
593
|
+
check("composeHooks: suppress short-circuits chain", infoHit.suppress === true);
|
|
594
|
+
var bidi = await chain({ kind: "bidi", severity: "high", snippet: "U+202E" });
|
|
595
|
+
check("composeHooks: last non-null result wins", bidi.snippet === "<redacted>");
|
|
596
|
+
|
|
597
|
+
var skipHook = function () { return { skip: true }; };
|
|
598
|
+
var neverRuns = function () { throw new Error("should not run"); };
|
|
599
|
+
var skipChain = GC.composeHooks([skipHook, neverRuns]);
|
|
600
|
+
var skipRv = await skipChain({});
|
|
601
|
+
check("composeHooks: skip short-circuits chain", skipRv.skip === true);
|
|
602
|
+
|
|
603
|
+
var nullFirst = GC.composeHooks([function () { return null; }, function () { return { x: 1 }; }]);
|
|
604
|
+
check("composeHooks: null then value → value", (await nullFirst({})).x === 1);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function testThrowOnRefusalSeverity() {
|
|
608
|
+
var threw = null;
|
|
609
|
+
try {
|
|
610
|
+
GC.throwOnRefusalSeverity([{ severity: "high", ruleId: "R1", snippet: "boom" }],
|
|
611
|
+
{ errorClass: GCE, codePrefix: "csv" });
|
|
612
|
+
} catch (e) { threw = e; }
|
|
613
|
+
check("throwOnRefusalSeverity: high throws with ruleId code",
|
|
614
|
+
threw && threw.code === "R1" && /boom/.test(threw.message));
|
|
615
|
+
|
|
616
|
+
threw = null;
|
|
617
|
+
try {
|
|
618
|
+
GC.throwOnRefusalSeverity([{ severity: "critical", snippet: "x" }],
|
|
619
|
+
{ errorClass: GCE, codePrefix: "csv" });
|
|
620
|
+
} catch (e) { threw = e; }
|
|
621
|
+
check("throwOnRefusalSeverity: fallback code = <prefix>.refused",
|
|
622
|
+
threw && threw.code === "csv.refused");
|
|
623
|
+
|
|
624
|
+
// Custom severities: high is NOT a refusal severity → no throw.
|
|
625
|
+
var noThrow = true;
|
|
626
|
+
try {
|
|
627
|
+
GC.throwOnRefusalSeverity([{ severity: "high", snippet: "x" }],
|
|
628
|
+
{ errorClass: GCE, codePrefix: "csv", severities: ["critical"] });
|
|
629
|
+
} catch (_e) { noThrow = false; }
|
|
630
|
+
check("throwOnRefusalSeverity: narrowed severities skip high", noThrow);
|
|
631
|
+
|
|
632
|
+
// Below-refusal severities → no throw.
|
|
633
|
+
noThrow = true;
|
|
634
|
+
try {
|
|
635
|
+
GC.throwOnRefusalSeverity([{ severity: "info", snippet: "x" }],
|
|
636
|
+
{ errorClass: GCE, codePrefix: "csv" });
|
|
637
|
+
} catch (_e) { noThrow = false; }
|
|
638
|
+
check("throwOnRefusalSeverity: info does not throw", noThrow);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function testExtractBytesAsText() {
|
|
642
|
+
check("extractBytesAsText: null ctx → ''", GC.extractBytesAsText(null) === "");
|
|
643
|
+
check("extractBytesAsText: no bytes → ''", GC.extractBytesAsText({}) === "");
|
|
644
|
+
check("extractBytesAsText: string bytes → identity",
|
|
645
|
+
GC.extractBytesAsText({ bytes: "x,y" }) === "x,y");
|
|
646
|
+
check("extractBytesAsText: Buffer → utf8",
|
|
647
|
+
GC.extractBytesAsText({ bytes: Buffer.from("abc") }) === "abc");
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function testIdentifierFixtures() {
|
|
651
|
+
var fx = GC.identifierFixtures("example.com", "192.168.1.1");
|
|
652
|
+
check("identifierFixtures: benignIdentifier", fx.benignIdentifier === "example.com");
|
|
653
|
+
check("identifierFixtures: benignBytes derived",
|
|
654
|
+
Buffer.isBuffer(fx.benignBytes) && fx.benignBytes.toString() === "example.com");
|
|
655
|
+
check("identifierFixtures: kind identifier + frozen",
|
|
656
|
+
fx.kind === "identifier" && Object.isFrozen(fx));
|
|
657
|
+
var asciiFx = GC.identifierFixtures("x", "y", "ascii");
|
|
658
|
+
check("identifierFixtures: ascii encoding accepted", asciiFx.hostileBytes.toString() === "y");
|
|
659
|
+
|
|
660
|
+
var threw = false;
|
|
661
|
+
try { GC.identifierFixtures("", "x"); } catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
662
|
+
check("identifierFixtures: empty benign throws bad-opt", threw);
|
|
663
|
+
threw = false;
|
|
664
|
+
try { GC.identifierFixtures("x", ""); } catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
665
|
+
check("identifierFixtures: empty hostile throws bad-opt", threw);
|
|
666
|
+
threw = false;
|
|
667
|
+
try { GC.identifierFixtures("x", "y", "not-an-encoding"); }
|
|
668
|
+
catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
669
|
+
check("identifierFixtures: invalid encoding throws bad-opt", threw);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function testMakeRulePackLoader() {
|
|
673
|
+
var packs = GC.makeRulePackLoader(GCE, "csv");
|
|
674
|
+
packs.load({ id: "p1", rules: [{ id: "ssn" }] });
|
|
675
|
+
check("makeRulePackLoader: get returns stored pack", packs.get("p1").rules.length === 1);
|
|
676
|
+
check("makeRulePackLoader: list returns packs", packs.list().length === 1);
|
|
677
|
+
check("makeRulePackLoader: get missing → null", packs.get("nope") === null);
|
|
678
|
+
|
|
679
|
+
var threw = false;
|
|
680
|
+
try { packs.load(null); } catch (e) { threw = e instanceof GCE; }
|
|
681
|
+
check("makeRulePackLoader: non-object pack throws", threw);
|
|
682
|
+
threw = false;
|
|
683
|
+
try { packs.load({ rules: [] }); } catch (e) { threw = e.code === "csv.bad-opt"; }
|
|
684
|
+
check("makeRulePackLoader: missing pack.id throws bad-opt", threw);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// ------------------------------------------------------------------
|
|
688
|
+
// validateGateShape / runGate.
|
|
689
|
+
// ------------------------------------------------------------------
|
|
690
|
+
|
|
691
|
+
async function testValidateGateShapeAndRunGate() {
|
|
692
|
+
var goodGate = GC.defineGate({ name: "vgs", check: async function () { return serveCheck(); } });
|
|
693
|
+
check("validateGateShape: valid gate returned unchanged",
|
|
694
|
+
GC.validateGateShape(goodGate, "uploads") === goodGate);
|
|
695
|
+
|
|
696
|
+
var cases = [
|
|
697
|
+
[null, "non-object"],
|
|
698
|
+
[{}, "check-not-fn"],
|
|
699
|
+
[{ check: function () {}, mode: "yolo" }, "bad-mode"],
|
|
700
|
+
[{ check: function () {}, metrics: 5 }, "metrics-not-fn"],
|
|
701
|
+
[{ check: function () {}, close: 5 }, "close-not-fn"],
|
|
702
|
+
];
|
|
703
|
+
cases.forEach(function (c) {
|
|
704
|
+
var threw = false;
|
|
705
|
+
try { GC.validateGateShape(c[0], "lbl"); }
|
|
706
|
+
catch (e) { threw = e.code === "gate-contract/bad-shape"; }
|
|
707
|
+
check("validateGateShape: " + c[1] + " → bad-shape", threw);
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
check("runGate: null gate → serve",
|
|
711
|
+
(await GC.runGate(null, {})).action === "serve");
|
|
712
|
+
check("runGate: gate without check → serve",
|
|
713
|
+
(await GC.runGate({}, {})).action === "serve");
|
|
714
|
+
var d = await GC.runGate(goodGate, { bytes: Buffer.from("x") });
|
|
715
|
+
check("runGate: real gate returns its decision", d.action === "serve");
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// ------------------------------------------------------------------
|
|
719
|
+
// defineGate / buildGuardGate — construction errors + full lifecycle.
|
|
720
|
+
// ------------------------------------------------------------------
|
|
721
|
+
|
|
722
|
+
function testDefineGateBadOpts() {
|
|
723
|
+
var threw = false;
|
|
724
|
+
try { GC.defineGate("nope"); } catch (e) { threw = e instanceof GCE; }
|
|
725
|
+
check("defineGate: non-object opts throws", threw);
|
|
726
|
+
|
|
727
|
+
threw = false;
|
|
728
|
+
try { GC.defineGate({}); } catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
729
|
+
check("defineGate: missing name throws bad-opt", threw);
|
|
730
|
+
|
|
731
|
+
threw = false;
|
|
732
|
+
try { GC.defineGate({ name: "g" }); } catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
733
|
+
check("defineGate: missing check throws bad-opt", threw);
|
|
734
|
+
|
|
735
|
+
threw = false;
|
|
736
|
+
try { GC.defineGate({ name: "g", check: function () {}, mode: "yolo" }); }
|
|
737
|
+
catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
738
|
+
check("defineGate: invalid mode throws bad-opt", threw);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
async function testDefineGateLifecycle() {
|
|
742
|
+
var obs = makeObsCollector();
|
|
743
|
+
var audit = makeAuditCollector();
|
|
744
|
+
var gate = GC.defineGate({
|
|
745
|
+
name: "lc", audit: audit, observability: obs,
|
|
746
|
+
check: async function (ctx) {
|
|
747
|
+
return GC.extractBytesAsText(ctx) === "REFUSE" ? refuseCheck() : serveCheck();
|
|
748
|
+
},
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
var served = await gate.check({ bytes: Buffer.from("hello") });
|
|
752
|
+
check("defineGate: serve decision ok", served.ok === true && served.action === "serve");
|
|
753
|
+
check("defineGate: forensicHash computed from bytes", typeof served.forensicHash === "string");
|
|
754
|
+
check("defineGate: cacheKey present when bytes present", typeof served.cacheKey === "string");
|
|
755
|
+
check("defineGate: runtimeMs numeric", typeof served.runtimeMs === "number");
|
|
756
|
+
check("defineGate: audit handle received serve entry",
|
|
757
|
+
audit.rows.some(function (r) { return r.action === "lc.serve"; }));
|
|
758
|
+
check("defineGate: observability handle received serve counter", obs.has("lc.serve"));
|
|
759
|
+
|
|
760
|
+
var refused = await gate.check({ bytes: Buffer.from("REFUSE") });
|
|
761
|
+
check("defineGate: refuse decision", refused.action === "refuse" && refused.ok === false);
|
|
762
|
+
check("defineGate: refuse audit outcome denied",
|
|
763
|
+
audit.rows.some(function (r) { return r.action === "lc.refuse" && r.outcome === "denied"; }));
|
|
764
|
+
|
|
765
|
+
var m = gate.metrics();
|
|
766
|
+
check("defineGate: metrics passed counted", m.passed === 1);
|
|
767
|
+
check("defineGate: metrics refused counted", m.refused === 1);
|
|
768
|
+
check("defineGate: metrics p50 numeric", typeof m.p50RuntimeMs === "number");
|
|
769
|
+
|
|
770
|
+
// String bytes also hash; no-bytes → null hash/cacheKey.
|
|
771
|
+
var strServed = await gate.check({ bytes: "plainstring" });
|
|
772
|
+
check("defineGate: string bytes hashed", typeof strServed.forensicHash === "string");
|
|
773
|
+
var noBytes = await gate.check({});
|
|
774
|
+
check("defineGate: no bytes → null forensicHash", noBytes.forensicHash === null);
|
|
775
|
+
check("defineGate: no bytes → null cacheKey", noBytes.cacheKey === null);
|
|
776
|
+
|
|
777
|
+
// reset zeroes counters.
|
|
778
|
+
gate.reset();
|
|
779
|
+
check("defineGate: reset zeroes counters", gate.metrics().passed === 0 && gate.metrics().refused === 0);
|
|
780
|
+
|
|
781
|
+
// dryRun runs the check; policyDiff surfaces both rule hashes.
|
|
782
|
+
var dry = await gate.dryRun({ bytes: Buffer.from("x") });
|
|
783
|
+
check("defineGate: dryRun returns a decision", dry.action === "serve");
|
|
784
|
+
var other = GC.defineGate({ name: "other", check: async function () { return serveCheck(); } });
|
|
785
|
+
var diff = gate.policyDiff(other);
|
|
786
|
+
check("defineGate: policyDiff carries self + other ruleHash",
|
|
787
|
+
diff.selfRuleHash === gate.ruleHash && diff.otherRuleHash === other.ruleHash);
|
|
788
|
+
check("defineGate: exposes name/version/ruleHash/mode",
|
|
789
|
+
gate.name === "lc" && gate.version === "1.0.0" &&
|
|
790
|
+
typeof gate.ruleHash === "string" && gate.mode === "enforce");
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
async function testDefineGateCheckThrows() {
|
|
794
|
+
var obs = makeObsCollector();
|
|
795
|
+
var refuseFlag = false;
|
|
796
|
+
var gate = GC.defineGate({
|
|
797
|
+
name: "boom", observability: obs,
|
|
798
|
+
onRefuse: function () { refuseFlag = true; },
|
|
799
|
+
check: async function () { throw new Error("detector blew up"); },
|
|
800
|
+
});
|
|
801
|
+
var d = await gate.check({ bytes: Buffer.from("x") });
|
|
802
|
+
check("defineGate: thrown check → refuse", d.action === "refuse" && d.ok === false);
|
|
803
|
+
check("defineGate: thrown check → check-threw issue", d.issues[0].kind === "check-threw");
|
|
804
|
+
check("defineGate: thrown check counted as refused", gate.metrics().refused === 1);
|
|
805
|
+
check("defineGate: thrown check emits check_threw obs", obs.has("boom.check_threw"));
|
|
806
|
+
await helpers.waitUntil(function () { return refuseFlag; },
|
|
807
|
+
{ timeoutMs: 3000, label: "defineGate check-threw: onRefuse fired" });
|
|
808
|
+
check("defineGate: onRefuse hook fired on thrown check", refuseFlag === true);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
async function testDefineGateTimeout() {
|
|
812
|
+
var pending = [];
|
|
813
|
+
var gate = GC.defineGate({
|
|
814
|
+
name: "slow", maxRuntimeMs: 1,
|
|
815
|
+
check: function () {
|
|
816
|
+
return new Promise(function (resolve) {
|
|
817
|
+
// Latency simulator, not a condition-wait: the check must run
|
|
818
|
+
// longer than the gate's maxRuntimeMs:1 so the timeout fires
|
|
819
|
+
// first. The setTimeout IS the simulated latency (file is
|
|
820
|
+
// allowlisted for test-promise-settimeout-sleep like
|
|
821
|
+
// audit-use-store.test.js's slow-callback simulator).
|
|
822
|
+
var t = setTimeout(function () { resolve(serveCheck()); }, 5000);
|
|
823
|
+
if (t.unref) t.unref();
|
|
824
|
+
pending.push(t);
|
|
825
|
+
});
|
|
826
|
+
},
|
|
827
|
+
});
|
|
828
|
+
try {
|
|
829
|
+
var d = await gate.check({ bytes: Buffer.from("x") });
|
|
830
|
+
check("defineGate: runtime cap exceeded → refuse", d.action === "refuse");
|
|
831
|
+
check("defineGate: runtime-cap refusal is a check-threw", d.issues[0].kind === "check-threw");
|
|
832
|
+
} finally {
|
|
833
|
+
pending.forEach(function (t) { clearTimeout(t); });
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
async function testDefineGateBeforeCheckHooks() {
|
|
838
|
+
// skip → early serve, check never runs.
|
|
839
|
+
var checkRan = false;
|
|
840
|
+
var skipGate = GC.defineGate({
|
|
841
|
+
name: "skip",
|
|
842
|
+
beforeCheck: function () { return { skip: true }; },
|
|
843
|
+
check: async function () { checkRan = true; return refuseCheck(); },
|
|
844
|
+
});
|
|
845
|
+
var skipped = await skipGate.check({ bytes: Buffer.from("x") });
|
|
846
|
+
check("defineGate: beforeCheck skip → serve", skipped.action === "serve");
|
|
847
|
+
check("defineGate: beforeCheck skip bypasses check", checkRan === false);
|
|
848
|
+
|
|
849
|
+
// transform → mutated ctx reaches the check.
|
|
850
|
+
var seen = null;
|
|
851
|
+
var xformGate = GC.defineGate({
|
|
852
|
+
name: "xform",
|
|
853
|
+
beforeCheck: function () { return { transform: { bytes: Buffer.from("XFORM") } }; },
|
|
854
|
+
check: async function (ctx) { seen = GC.extractBytesAsText(ctx); return serveCheck(); },
|
|
855
|
+
});
|
|
856
|
+
await xformGate.check({ bytes: Buffer.from("original") });
|
|
857
|
+
check("defineGate: beforeCheck transform mutates ctx", seen === "XFORM");
|
|
858
|
+
|
|
859
|
+
// throwing hook → hook_threw, treated as null, proceeds normally.
|
|
860
|
+
var obs = makeObsCollector();
|
|
861
|
+
var throwGate = GC.defineGate({
|
|
862
|
+
name: "hookthrow", observability: obs,
|
|
863
|
+
beforeCheck: function () { throw new Error("hook boom"); },
|
|
864
|
+
check: async function () { return serveCheck(); },
|
|
865
|
+
});
|
|
866
|
+
var afterThrow = await throwGate.check({ bytes: Buffer.from("x") });
|
|
867
|
+
check("defineGate: throwing beforeCheck still serves", afterThrow.action === "serve");
|
|
868
|
+
check("defineGate: throwing hook emits hook_threw obs", obs.has("hookthrow.hook_threw"));
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
async function testDefineGateAfterAndIssueHooks() {
|
|
872
|
+
// afterCheck amends the decision.
|
|
873
|
+
var amendGate = GC.defineGate({
|
|
874
|
+
name: "amend",
|
|
875
|
+
afterCheck: function () {
|
|
876
|
+
return { ok: false, action: "refuse", issues: [{ kind: "amended", severity: "high" }] };
|
|
877
|
+
},
|
|
878
|
+
check: async function () { return serveCheck(); },
|
|
879
|
+
});
|
|
880
|
+
var amended = await amendGate.check({ bytes: Buffer.from("x") });
|
|
881
|
+
check("defineGate: afterCheck amends decision", amended.action === "refuse");
|
|
882
|
+
|
|
883
|
+
// onIssue suppress + promote.
|
|
884
|
+
var suppressPromoteGate = GC.defineGate({
|
|
885
|
+
name: "issues1",
|
|
886
|
+
onIssue: function (issue) {
|
|
887
|
+
if (issue.severity === "info") return { suppress: true };
|
|
888
|
+
if (issue.severity === "high") return { promote: "critical" };
|
|
889
|
+
return null;
|
|
890
|
+
},
|
|
891
|
+
check: async function () {
|
|
892
|
+
return { ok: false, action: "refuse", issues: [
|
|
893
|
+
{ kind: "a", severity: "info" }, { kind: "b", severity: "high" },
|
|
894
|
+
] };
|
|
895
|
+
},
|
|
896
|
+
});
|
|
897
|
+
var sp = await suppressPromoteGate.check({ bytes: Buffer.from("x") });
|
|
898
|
+
check("defineGate: onIssue suppress drops the info issue", sp.issues.length === 1);
|
|
899
|
+
check("defineGate: onIssue promote raises severity", sp.issues[0].severity === "critical");
|
|
900
|
+
|
|
901
|
+
// onIssue replace (returns a new issue object) + passthrough (null).
|
|
902
|
+
var replaceGate = GC.defineGate({
|
|
903
|
+
name: "issues2",
|
|
904
|
+
onIssue: function (issue) {
|
|
905
|
+
return issue.kind === "replace-me"
|
|
906
|
+
? { kind: "replaced", severity: "warn" } : null;
|
|
907
|
+
},
|
|
908
|
+
check: async function () {
|
|
909
|
+
return { ok: false, action: "refuse", issues: [
|
|
910
|
+
{ kind: "replace-me", severity: "high" }, { kind: "keep", severity: "warn" },
|
|
911
|
+
] };
|
|
912
|
+
},
|
|
913
|
+
});
|
|
914
|
+
var rp = await replaceGate.check({ bytes: Buffer.from("x") });
|
|
915
|
+
check("defineGate: onIssue replace substitutes the issue",
|
|
916
|
+
rp.issues[0].kind === "replaced");
|
|
917
|
+
check("defineGate: onIssue null passes issue through", rp.issues[1].kind === "keep");
|
|
918
|
+
|
|
919
|
+
// onSanitize final transform on a sanitize decision.
|
|
920
|
+
var sanitizeGate = GC.defineGate({
|
|
921
|
+
name: "san",
|
|
922
|
+
onSanitize: function () { return Buffer.from("scrubbed"); },
|
|
923
|
+
check: async function () {
|
|
924
|
+
return { ok: true, action: "sanitize", sanitized: Buffer.from("raw") };
|
|
925
|
+
},
|
|
926
|
+
});
|
|
927
|
+
var san = await sanitizeGate.check({ bytes: Buffer.from("dirty") });
|
|
928
|
+
check("defineGate: onSanitize replaces sanitized bytes",
|
|
929
|
+
Buffer.isBuffer(san.sanitized) && san.sanitized.toString() === "scrubbed");
|
|
930
|
+
check("defineGate: sanitize counted", sanitizeGate.metrics().sanitized === 1);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
async function testDefineGateAuditHooks() {
|
|
934
|
+
// onAudit returning false suppresses emission.
|
|
935
|
+
var suppressAudit = makeAuditCollector();
|
|
936
|
+
var suppressGate = GC.defineGate({
|
|
937
|
+
name: "auditsup", audit: suppressAudit,
|
|
938
|
+
onAudit: function () { return false; },
|
|
939
|
+
check: async function () { return serveCheck(); },
|
|
940
|
+
});
|
|
941
|
+
await suppressGate.check({ bytes: Buffer.from("x") });
|
|
942
|
+
check("defineGate: onAudit false suppresses emission", suppressAudit.rows.length === 0);
|
|
943
|
+
|
|
944
|
+
// onAudit returning an object replaces the default entry.
|
|
945
|
+
var replaceAudit = makeAuditCollector();
|
|
946
|
+
var replaceGate = GC.defineGate({
|
|
947
|
+
name: "auditrep", audit: replaceAudit,
|
|
948
|
+
onAudit: function () { return { action: "custom.audit", outcome: "success", tag: "replaced" }; },
|
|
949
|
+
check: async function () { return serveCheck(); },
|
|
950
|
+
});
|
|
951
|
+
await replaceGate.check({ bytes: Buffer.from("x") });
|
|
952
|
+
check("defineGate: onAudit object replaces entry",
|
|
953
|
+
replaceAudit.rows.some(function (r) { return r.tag === "replaced"; }));
|
|
954
|
+
|
|
955
|
+
// No onAudit hook → default entry emitted.
|
|
956
|
+
var defaultAudit = makeAuditCollector();
|
|
957
|
+
var defaultGate = GC.defineGate({
|
|
958
|
+
name: "auditdef", audit: defaultAudit,
|
|
959
|
+
check: async function () { return serveCheck(); },
|
|
960
|
+
});
|
|
961
|
+
await defaultGate.check({ bytes: Buffer.from("x") });
|
|
962
|
+
check("defineGate: no onAudit → default entry emitted",
|
|
963
|
+
defaultAudit.rows.some(function (r) { return r.action === "auditdef.serve"; }));
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
async function testDefineGateModes() {
|
|
967
|
+
var warnGate = GC.defineGate({
|
|
968
|
+
name: "warnmode", mode: "warn-only",
|
|
969
|
+
check: async function () { return refuseCheck(); },
|
|
970
|
+
});
|
|
971
|
+
var warned = await warnGate.check({ bytes: Buffer.from("x") });
|
|
972
|
+
check("defineGate: warn-only translates refuse → warn",
|
|
973
|
+
warned.action === "warn" && warned.ok === true);
|
|
974
|
+
check("defineGate: warn-only counts warned", warnGate.metrics().warned === 1);
|
|
975
|
+
|
|
976
|
+
var auditOnlyGate = GC.defineGate({
|
|
977
|
+
name: "auditmode", mode: "audit-only",
|
|
978
|
+
check: async function () { return refuseCheck(); },
|
|
979
|
+
});
|
|
980
|
+
var auditOnly = await auditOnlyGate.check({ bytes: Buffer.from("x") });
|
|
981
|
+
check("defineGate: audit-only mode → audit-only action",
|
|
982
|
+
auditOnly.action === "audit-only" && auditOnly.ok === true);
|
|
983
|
+
|
|
984
|
+
var logOnlyGate = GC.defineGate({
|
|
985
|
+
name: "logmode", mode: "log-only",
|
|
986
|
+
check: async function () { return refuseCheck(); },
|
|
987
|
+
});
|
|
988
|
+
check("defineGate: log-only mode → audit-only action",
|
|
989
|
+
(await logOnlyGate.check({ bytes: Buffer.from("x") })).action === "audit-only");
|
|
990
|
+
|
|
991
|
+
var shadowGate = GC.defineGate({
|
|
992
|
+
name: "shadowmode", mode: "shadow",
|
|
993
|
+
check: async function () { return refuseCheck(); },
|
|
994
|
+
});
|
|
995
|
+
var shadowed = await shadowGate.check({ bytes: Buffer.from("x") });
|
|
996
|
+
check("defineGate: shadow mode never refuses",
|
|
997
|
+
shadowed.action === "audit-only" && shadowed.ok === true);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
async function testDefineGateForensicAndCache() {
|
|
1001
|
+
// Forensic snapshot on a refusal + operator evidence store write.
|
|
1002
|
+
var store = makeForensicStore();
|
|
1003
|
+
var forensicGate = GC.defineGate({
|
|
1004
|
+
name: "forensic", forensicSnippetBytes: 8, forensicEvidenceStore: store,
|
|
1005
|
+
check: async function () { return refuseCheck(); },
|
|
1006
|
+
});
|
|
1007
|
+
var fd = await forensicGate.check({ bytes: Buffer.from("hostile-payload-bytes"), actor: "u1", route: "/x" });
|
|
1008
|
+
check("defineGate: forensic snapshot captured on refuse",
|
|
1009
|
+
Buffer.isBuffer(fd.forensicSnapshot) && fd.forensicSnapshot.length === 8);
|
|
1010
|
+
await helpers.waitUntil(function () { return store.writes.length >= 1; },
|
|
1011
|
+
{ timeoutMs: 3000, label: "defineGate forensic: evidence store write" });
|
|
1012
|
+
check("defineGate: evidence store received the snippet",
|
|
1013
|
+
store.writes[0].gate === "forensic" && Buffer.isBuffer(store.writes[0].snippet));
|
|
1014
|
+
|
|
1015
|
+
// Evidence-store write throwing is best-effort (decision still refuses).
|
|
1016
|
+
var throwingStore = { write: async function () { throw new Error("store down"); } };
|
|
1017
|
+
var bestEffortGate = GC.defineGate({
|
|
1018
|
+
name: "forensic2", forensicSnippetBytes: 4, forensicEvidenceStore: throwingStore,
|
|
1019
|
+
check: async function () { return refuseCheck(); },
|
|
1020
|
+
});
|
|
1021
|
+
var be = await bestEffortGate.check({ bytes: Buffer.from("payload") });
|
|
1022
|
+
check("defineGate: forensic store failure is best-effort", be.action === "refuse");
|
|
1023
|
+
|
|
1024
|
+
// Real per-gate decision cache: second identical input hits the cache.
|
|
1025
|
+
var cache = b.cache.create({ namespace: "gc-cov-cache", backend: "memory", maxEntries: 50 });
|
|
1026
|
+
try {
|
|
1027
|
+
var callCount = 0;
|
|
1028
|
+
var cachedGate = GC.defineGate({
|
|
1029
|
+
name: "cachedgate", cache: cache, cacheTtlMs: 60000,
|
|
1030
|
+
check: async function () { callCount += 1; return serveCheck(); },
|
|
1031
|
+
});
|
|
1032
|
+
var c1 = await cachedGate.check({ bytes: Buffer.from("identical") });
|
|
1033
|
+
var c2 = await cachedGate.check({ bytes: Buffer.from("identical") });
|
|
1034
|
+
check("defineGate: cache first miss runs check", callCount === 1);
|
|
1035
|
+
check("defineGate: cache hit skips check re-run", c1.action === "serve" && c2.action === "serve");
|
|
1036
|
+
} finally {
|
|
1037
|
+
await cache.close();
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// Cache backend throwing on get/set is best-effort.
|
|
1041
|
+
var badCache = {
|
|
1042
|
+
get: async function () { throw new Error("get boom"); },
|
|
1043
|
+
set: async function () { throw new Error("set boom"); },
|
|
1044
|
+
};
|
|
1045
|
+
var badCacheGate = GC.defineGate({
|
|
1046
|
+
name: "badcache", cache: badCache, cacheTtlMs: 1000,
|
|
1047
|
+
check: async function () { return serveCheck(); },
|
|
1048
|
+
});
|
|
1049
|
+
var bc = await badCacheGate.check({ bytes: Buffer.from("x") });
|
|
1050
|
+
check("defineGate: cache get/set failures are best-effort", bc.action === "serve");
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
async function testBuildGuardGate() {
|
|
1054
|
+
var obs = makeObsCollector();
|
|
1055
|
+
var audit = makeAuditCollector();
|
|
1056
|
+
// Full opts bag forwarded to defineGate.
|
|
1057
|
+
var gate = GC.buildGuardGate("myGuard:strict", {
|
|
1058
|
+
mode: "enforce", audit: audit, observability: obs,
|
|
1059
|
+
forensicSnippetBytes: 0, cacheTtlMs: 0, maxRuntimeMs: 0,
|
|
1060
|
+
beforeCheck: null, afterCheck: null, onIssue: null,
|
|
1061
|
+
onSanitize: null, onRefuse: null, onAudit: null,
|
|
1062
|
+
}, async function (ctx) {
|
|
1063
|
+
var text = GC.extractBytesAsText(ctx);
|
|
1064
|
+
if (text.length === 0) return { ok: true, action: "serve" };
|
|
1065
|
+
if (/\s/.test(text)) {
|
|
1066
|
+
return { ok: false, action: "refuse", issues: [{ kind: "whitespace", severity: "high" }] };
|
|
1067
|
+
}
|
|
1068
|
+
return { ok: true, action: "serve" };
|
|
1069
|
+
});
|
|
1070
|
+
check("buildGuardGate: result satisfies validateGateShape",
|
|
1071
|
+
GC.validateGateShape(gate, "gg") === gate);
|
|
1072
|
+
check("buildGuardGate: clean input serves",
|
|
1073
|
+
(await gate.check({ bytes: Buffer.from("hello") })).action === "serve");
|
|
1074
|
+
check("buildGuardGate: whitespace refuses",
|
|
1075
|
+
(await gate.check({ bytes: Buffer.from("a b") })).action === "refuse");
|
|
1076
|
+
|
|
1077
|
+
// Error branches inherited from defineGate.
|
|
1078
|
+
var threw = false;
|
|
1079
|
+
try { GC.buildGuardGate("bad", {}, 5); }
|
|
1080
|
+
catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
1081
|
+
check("buildGuardGate: non-function check throws bad-opt", threw);
|
|
1082
|
+
threw = false;
|
|
1083
|
+
try { GC.buildGuardGate("bad", { mode: "yolo" }, async function () {}); }
|
|
1084
|
+
catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
1085
|
+
check("buildGuardGate: invalid mode throws bad-opt", threw);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// ------------------------------------------------------------------
|
|
1089
|
+
// Dispatch + wrapper gates.
|
|
1090
|
+
// ------------------------------------------------------------------
|
|
1091
|
+
|
|
1092
|
+
function serveGate(name) {
|
|
1093
|
+
return GC.defineGate({ name: name || "serve", check: async function () { return serveCheck(); } });
|
|
1094
|
+
}
|
|
1095
|
+
function refuseGate(name) {
|
|
1096
|
+
return GC.defineGate({ name: name || "refuse", check: async function () { return refuseCheck(); } });
|
|
1097
|
+
}
|
|
1098
|
+
function sanitizeGate(name) {
|
|
1099
|
+
return GC.defineGate({
|
|
1100
|
+
name: name || "san",
|
|
1101
|
+
check: async function () {
|
|
1102
|
+
return { ok: true, action: "sanitize", sanitized: Buffer.from("clean") };
|
|
1103
|
+
},
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
async function testComposeGates() {
|
|
1108
|
+
var refuseFirst = GC.composeGates([serveGate("a"), refuseGate("b")], { name: "chain" });
|
|
1109
|
+
check("composeGates: first refusal wins",
|
|
1110
|
+
(await refuseFirst.check({ bytes: Buffer.from("x") })).action === "refuse");
|
|
1111
|
+
|
|
1112
|
+
var allServe = GC.composeGates([serveGate("a"), serveGate("b")]);
|
|
1113
|
+
check("composeGates: all-serve → serve",
|
|
1114
|
+
(await allServe.check({ bytes: Buffer.from("x") })).action === "serve");
|
|
1115
|
+
|
|
1116
|
+
// sanitize feeds the scrubbed bytes forward when firstRefusalWins (default).
|
|
1117
|
+
var seenBytes = null;
|
|
1118
|
+
var sink = GC.defineGate({
|
|
1119
|
+
name: "sink",
|
|
1120
|
+
check: async function (ctx) { seenBytes = GC.extractBytesAsText(ctx); return serveCheck(); },
|
|
1121
|
+
});
|
|
1122
|
+
var feedChain = GC.composeGates([sanitizeGate("s"), sink]);
|
|
1123
|
+
await feedChain.check({ bytes: Buffer.from("dirty") });
|
|
1124
|
+
check("composeGates: sanitize output feeds next gate", seenBytes === "clean");
|
|
1125
|
+
|
|
1126
|
+
// firstRefusalWins:false does not feed forward.
|
|
1127
|
+
seenBytes = null;
|
|
1128
|
+
var noFeed = GC.composeGates([sanitizeGate("s2"), GC.defineGate({
|
|
1129
|
+
name: "sink2",
|
|
1130
|
+
check: async function (ctx) { seenBytes = GC.extractBytesAsText(ctx); return serveCheck(); },
|
|
1131
|
+
})], { firstRefusalWins: false });
|
|
1132
|
+
var nf = await noFeed.check({ bytes: Buffer.from("dirty") });
|
|
1133
|
+
check("composeGates: firstRefusalWins false skips feed", seenBytes === "dirty" && nf.action === "serve");
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
async function testMultiplexAndContentTypeMux() {
|
|
1137
|
+
var mux = GC.multiplexGates({
|
|
1138
|
+
".csv": serveGate("csv"), "default": refuseGate("def"),
|
|
1139
|
+
});
|
|
1140
|
+
check("multiplexGates: extension match dispatches",
|
|
1141
|
+
(await mux.check({ bytes: Buffer.from("x"), filename: "a.CSV" })).action === "serve");
|
|
1142
|
+
check("multiplexGates: unmatched → default fallback",
|
|
1143
|
+
(await mux.check({ bytes: Buffer.from("x"), filename: "a.txt" })).action === "refuse");
|
|
1144
|
+
|
|
1145
|
+
var noFallback = GC.multiplexGates({ ".csv": serveGate("csv") });
|
|
1146
|
+
check("multiplexGates: no match + no fallback → serve",
|
|
1147
|
+
(await noFallback.check({ bytes: Buffer.from("x"), filename: "a.bin" })).action === "serve");
|
|
1148
|
+
check("multiplexGates: no filename → serve",
|
|
1149
|
+
(await noFallback.check({ bytes: Buffer.from("x") })).action === "serve");
|
|
1150
|
+
|
|
1151
|
+
var ctMux = GC.contentTypeMux({
|
|
1152
|
+
"text/csv": serveGate("csv"), "default": refuseGate("def"),
|
|
1153
|
+
});
|
|
1154
|
+
check("contentTypeMux: strips params + matches",
|
|
1155
|
+
(await ctMux.check({ bytes: Buffer.from("x"), contentType: "text/CSV; charset=utf-8" })).action === "serve");
|
|
1156
|
+
check("contentTypeMux: unknown → default",
|
|
1157
|
+
(await ctMux.check({ bytes: Buffer.from("x"), contentType: "application/json" })).action === "refuse");
|
|
1158
|
+
|
|
1159
|
+
var ctNoFallback = GC.contentTypeMux({ "text/csv": serveGate("csv") });
|
|
1160
|
+
check("contentTypeMux: no match + no fallback → serve",
|
|
1161
|
+
(await ctNoFallback.check({ bytes: Buffer.from("x"), contentType: "image/png" })).action === "serve");
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
async function testTierRouteDirectionDispatch() {
|
|
1165
|
+
var byTier = GC.byActorTier({ free: refuseGate("free"), paid: serveGate("paid"), default: refuseGate("d") });
|
|
1166
|
+
check("byActorTier: tier match dispatches",
|
|
1167
|
+
(await byTier.check({ bytes: Buffer.from("x"), actor: { tier: "paid" } })).action === "serve");
|
|
1168
|
+
check("byActorTier: missing tier → default",
|
|
1169
|
+
(await byTier.check({ bytes: Buffer.from("x") })).action === "refuse");
|
|
1170
|
+
var byTierNoDefault = GC.byActorTier({ paid: serveGate("paid") });
|
|
1171
|
+
check("byActorTier: unmapped + no default → serve",
|
|
1172
|
+
(await byTierNoDefault.check({ bytes: Buffer.from("x"), actor: { tier: "free" } })).action === "serve");
|
|
1173
|
+
|
|
1174
|
+
var byPath = GC.byRoute({ "/admin/*": serveGate("admin"), "*": refuseGate("pub") });
|
|
1175
|
+
check("byRoute: prefix glob matches",
|
|
1176
|
+
(await byPath.check({ bytes: Buffer.from("x"), route: "/admin/imports" })).action === "serve");
|
|
1177
|
+
check("byRoute: fallback star matches",
|
|
1178
|
+
(await byPath.check({ bytes: Buffer.from("x"), route: "/public/x" })).action === "refuse");
|
|
1179
|
+
var exactRoute = GC.byRoute({ "/exact": serveGate("exact") });
|
|
1180
|
+
check("byRoute: exact pattern matches",
|
|
1181
|
+
(await exactRoute.check({ bytes: Buffer.from("x"), route: "/exact" })).action === "serve");
|
|
1182
|
+
check("byRoute: no match + no fallback → serve",
|
|
1183
|
+
(await exactRoute.check({ bytes: Buffer.from("x"), route: "/nope" })).action === "serve");
|
|
1184
|
+
|
|
1185
|
+
var byDir = GC.byDirection({ inbound: refuseGate("in"), outbound: serveGate("out") });
|
|
1186
|
+
check("byDirection: inbound dispatch",
|
|
1187
|
+
(await byDir.check({ bytes: Buffer.from("x"), direction: "inbound" })).action === "refuse");
|
|
1188
|
+
check("byDirection: default outbound",
|
|
1189
|
+
(await byDir.check({ bytes: Buffer.from("x") })).action === "serve");
|
|
1190
|
+
var byDirEmpty = GC.byDirection({ inbound: refuseGate("in") });
|
|
1191
|
+
check("byDirection: unmapped direction → serve",
|
|
1192
|
+
(await byDirEmpty.check({ bytes: Buffer.from("x"), direction: "sideways" })).action === "serve");
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
async function testShadowCanaryCachingWorkerGates() {
|
|
1196
|
+
// shadowMode: primary decision honored; divergent candidate runs async.
|
|
1197
|
+
var candidateRan = false;
|
|
1198
|
+
var candidate = GC.defineGate({
|
|
1199
|
+
name: "cand",
|
|
1200
|
+
check: async function () { candidateRan = true; return refuseCheck(); },
|
|
1201
|
+
});
|
|
1202
|
+
var staged = GC.shadowMode(serveGate("prim"), candidate, { name: "staged" });
|
|
1203
|
+
var sd = await staged.check({ bytes: Buffer.from("x") });
|
|
1204
|
+
check("shadowMode: primary decision honored", sd.action === "serve");
|
|
1205
|
+
await helpers.waitUntil(function () { return candidateRan; },
|
|
1206
|
+
{ timeoutMs: 3000, label: "shadowMode: candidate ran (divergence branch)" });
|
|
1207
|
+
check("shadowMode: candidate executed", candidateRan === true);
|
|
1208
|
+
|
|
1209
|
+
// canaryGate: rate 0 downgrades refuse → warn; rate 1 keeps refuse.
|
|
1210
|
+
var canaryDowngrade = GC.canaryGate(refuseGate("c1"), { rate: 0 });
|
|
1211
|
+
check("canaryGate: rate 0 downgrades refuse to warn",
|
|
1212
|
+
(await canaryDowngrade.check({ bytes: Buffer.from("x") })).action === "warn");
|
|
1213
|
+
var canaryEnforce = GC.canaryGate(refuseGate("c2"), { rate: 1 });
|
|
1214
|
+
check("canaryGate: rate 1 enforces refuse",
|
|
1215
|
+
(await canaryEnforce.check({ bytes: Buffer.from("x") })).action === "refuse");
|
|
1216
|
+
var canaryServe = GC.canaryGate(serveGate("c3"));
|
|
1217
|
+
check("canaryGate: non-refuse passes through (default rate)",
|
|
1218
|
+
(await canaryServe.check({ bytes: Buffer.from("x") })).action === "serve");
|
|
1219
|
+
|
|
1220
|
+
// cachingGate: bad backend throws; good backend wraps + delegates.
|
|
1221
|
+
var threw = false;
|
|
1222
|
+
try { GC.cachingGate(serveGate("cg"), { backend: { get: function () {} } }); }
|
|
1223
|
+
catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
1224
|
+
check("cachingGate: backend missing set → bad-opt", threw);
|
|
1225
|
+
|
|
1226
|
+
var cache = b.cache.create({ namespace: "gc-cov-wrap", backend: "memory", maxEntries: 10 });
|
|
1227
|
+
try {
|
|
1228
|
+
var cached = GC.cachingGate(serveGate("wrapped"), { backend: cache, ttlMs: 60000 });
|
|
1229
|
+
check("cachingGate: wraps and delegates",
|
|
1230
|
+
(await cached.check({ bytes: Buffer.from("x") })).action === "serve");
|
|
1231
|
+
check("cachingGate: default name derives from wrapped gate", cached.name === "wrapped:cached");
|
|
1232
|
+
} finally {
|
|
1233
|
+
await cache.close();
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// workerThreadGate: missing worker throws; worker stub delegates.
|
|
1237
|
+
threw = false;
|
|
1238
|
+
try { GC.workerThreadGate(serveGate("w"), {}); }
|
|
1239
|
+
catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
1240
|
+
check("workerThreadGate: missing worker → bad-opt", threw);
|
|
1241
|
+
var worker = { run: async function () { return { ok: true, action: "serve" }; } };
|
|
1242
|
+
var offloaded = GC.workerThreadGate(serveGate("wg"), { worker: worker });
|
|
1243
|
+
check("workerThreadGate: delegates to worker.run",
|
|
1244
|
+
(await offloaded.check({ bytes: Buffer.from("x") })).action === "serve");
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// ------------------------------------------------------------------
|
|
1248
|
+
// buildContentGate — serve / refuse / sanitize / audit dispositions.
|
|
1249
|
+
// ------------------------------------------------------------------
|
|
1250
|
+
|
|
1251
|
+
async function testBuildContentGate() {
|
|
1252
|
+
// Clean serve + empty subject serve.
|
|
1253
|
+
var cleanGate = GC.buildContentGate({
|
|
1254
|
+
name: "cg-clean", opts: {},
|
|
1255
|
+
validate: function () { return { ok: true, issues: [] }; },
|
|
1256
|
+
produceSanitized: function (s) { return s; },
|
|
1257
|
+
});
|
|
1258
|
+
check("buildContentGate: clean input serves",
|
|
1259
|
+
(await cleanGate.check({ bytes: Buffer.from("ok") })).action === "serve");
|
|
1260
|
+
check("buildContentGate: empty subject serves",
|
|
1261
|
+
(await cleanGate.check({})).action === "serve");
|
|
1262
|
+
|
|
1263
|
+
// Refuse via issue.disposition.
|
|
1264
|
+
var refuseGate2 = GC.buildContentGate({
|
|
1265
|
+
name: "cg-refuse", opts: {},
|
|
1266
|
+
validate: function () {
|
|
1267
|
+
return { ok: false, issues: [{ kind: "k", severity: "high", disposition: "refuse" }] };
|
|
1268
|
+
},
|
|
1269
|
+
produceSanitized: function (s) { return s; },
|
|
1270
|
+
});
|
|
1271
|
+
check("buildContentGate: refuse disposition refuses",
|
|
1272
|
+
(await refuseGate2.check({ bytes: Buffer.from("x") })).action === "refuse");
|
|
1273
|
+
|
|
1274
|
+
// Sanitize via disposition, verified producer output.
|
|
1275
|
+
var sanGate = GC.buildContentGate({
|
|
1276
|
+
name: "cg-san", opts: {},
|
|
1277
|
+
validate: function () {
|
|
1278
|
+
return { ok: false, issues: [{ kind: "k", severity: "high", disposition: "sanitize" }] };
|
|
1279
|
+
},
|
|
1280
|
+
produceSanitized: function () { return "scrubbed"; },
|
|
1281
|
+
});
|
|
1282
|
+
var sanD = await sanGate.check({ bytes: Buffer.from("x") });
|
|
1283
|
+
check("buildContentGate: sanitize disposition sanitizes",
|
|
1284
|
+
sanD.action === "sanitize" && sanD.sanitized.toString() === "scrubbed");
|
|
1285
|
+
|
|
1286
|
+
// Sanitize blocked by sanitizeBlockingKinds → refuse.
|
|
1287
|
+
var blockedGate = GC.buildContentGate({
|
|
1288
|
+
name: "cg-block", opts: {}, sanitizeBlockingKinds: ["svgz"],
|
|
1289
|
+
validate: function () {
|
|
1290
|
+
return { ok: false, issues: [{ kind: "svgz", severity: "high", disposition: "sanitize" }] };
|
|
1291
|
+
},
|
|
1292
|
+
produceSanitized: function () { return "x"; },
|
|
1293
|
+
});
|
|
1294
|
+
check("buildContentGate: sanitizeBlockingKinds forces refuse",
|
|
1295
|
+
(await blockedGate.check({ bytes: Buffer.from("x") })).action === "refuse");
|
|
1296
|
+
|
|
1297
|
+
// Producer throws → refuse.
|
|
1298
|
+
var throwProducer = GC.buildContentGate({
|
|
1299
|
+
name: "cg-throw", opts: {},
|
|
1300
|
+
validate: function () {
|
|
1301
|
+
return { ok: false, issues: [{ kind: "k", severity: "high", disposition: "sanitize" }] };
|
|
1302
|
+
},
|
|
1303
|
+
produceSanitized: function () { throw new Error("cannot repair"); },
|
|
1304
|
+
});
|
|
1305
|
+
check("buildContentGate: producer throw → refuse",
|
|
1306
|
+
(await throwProducer.check({ bytes: Buffer.from("x") })).action === "refuse");
|
|
1307
|
+
|
|
1308
|
+
// Audit-only disposition.
|
|
1309
|
+
var auditGate = GC.buildContentGate({
|
|
1310
|
+
name: "cg-audit", opts: {},
|
|
1311
|
+
validate: function () {
|
|
1312
|
+
return { ok: true, issues: [{ kind: "k", severity: "low", disposition: "audit" }] };
|
|
1313
|
+
},
|
|
1314
|
+
produceSanitized: function (s) { return s; },
|
|
1315
|
+
});
|
|
1316
|
+
check("buildContentGate: audit disposition → audit-only",
|
|
1317
|
+
(await auditGate.check({ bytes: Buffer.from("x") })).action === "audit-only");
|
|
1318
|
+
|
|
1319
|
+
// dispositionFor from the guard overrides.
|
|
1320
|
+
var dispForGate = GC.buildContentGate({
|
|
1321
|
+
name: "cg-dispfor", opts: {},
|
|
1322
|
+
validate: function () { return { ok: false, issues: [{ kind: "k", severity: "high" }] }; },
|
|
1323
|
+
dispositionFor: function () { return "audit"; },
|
|
1324
|
+
produceSanitized: function (s) { return s; },
|
|
1325
|
+
});
|
|
1326
|
+
check("buildContentGate: dispositionFor downgrades to audit-only",
|
|
1327
|
+
(await dispForGate.check({ bytes: Buffer.from("x") })).action === "audit-only");
|
|
1328
|
+
|
|
1329
|
+
// Severity fallback (no disposition, no dispositionFor) → refuse on high.
|
|
1330
|
+
var sevFallback = GC.buildContentGate({
|
|
1331
|
+
name: "cg-sev", opts: {},
|
|
1332
|
+
validate: function () { return { ok: false, issues: [{ kind: "k", severity: "high" }] }; },
|
|
1333
|
+
produceSanitized: function (s) { return s; },
|
|
1334
|
+
});
|
|
1335
|
+
check("buildContentGate: severity fallback refuses on high",
|
|
1336
|
+
(await sevFallback.check({ bytes: Buffer.from("x") })).action === "refuse");
|
|
1337
|
+
|
|
1338
|
+
// extraIssues (operator detect-only) + ctxField "bytes".
|
|
1339
|
+
var extraGate = GC.buildContentGate({
|
|
1340
|
+
name: "cg-extra", opts: {}, ctxField: "bytes",
|
|
1341
|
+
validate: function () { return { ok: true, issues: [] }; },
|
|
1342
|
+
extraIssues: function () { return [{ kind: "op", severity: "high" }]; },
|
|
1343
|
+
produceSanitized: function (s) { return s; },
|
|
1344
|
+
});
|
|
1345
|
+
check("buildContentGate: extraIssues high refuses (ctxField bytes)",
|
|
1346
|
+
(await extraGate.check({ bytes: Buffer.from("x") })).action === "refuse");
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// ------------------------------------------------------------------
|
|
1350
|
+
// defineGuard / defineParser — the full guard-module assemblers, driven
|
|
1351
|
+
// through their real consumer surface.
|
|
1352
|
+
// ------------------------------------------------------------------
|
|
1353
|
+
|
|
1354
|
+
var GUARD_PROFILES = {
|
|
1355
|
+
strict: { maxBytes: 256, bidiPolicy: "reject" },
|
|
1356
|
+
balanced: { maxBytes: 512, bidiPolicy: "strip" },
|
|
1357
|
+
permissive: { maxBytes: 1024, bidiPolicy: "allow" },
|
|
1358
|
+
};
|
|
1359
|
+
|
|
1360
|
+
function guardDetect(input) {
|
|
1361
|
+
if (String(input).indexOf("BAD") === 0) {
|
|
1362
|
+
return [{ kind: "content.bad", severity: "high", ruleId: "gccov.bad", snippet: "BAD" }];
|
|
1363
|
+
}
|
|
1364
|
+
return [];
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
async function testDefineGuard() {
|
|
1368
|
+
var guard = GC.defineGuard({
|
|
1369
|
+
name: "gccov", kind: "content", errorName: "GcCoverageContentError",
|
|
1370
|
+
profiles: GUARD_PROFILES, base: 64,
|
|
1371
|
+
mimeTypes: ["text/gccov"], extensions: [".gccov"],
|
|
1372
|
+
integrationFixtures: { kind: "content", benignBytes: Buffer.from("ok") },
|
|
1373
|
+
detect: guardDetect, inputContract: "text", intOpts: ["maxBytes"],
|
|
1374
|
+
sanitizeTransform: function (subject) { return String(subject).replace("BAD", ""); },
|
|
1375
|
+
extra: { helperConst: 7 },
|
|
1376
|
+
});
|
|
1377
|
+
|
|
1378
|
+
check("defineGuard: NAME/KIND exported", guard.NAME === "gccov" && guard.KIND === "content");
|
|
1379
|
+
check("defineGuard: content kind exports MIME/EXTENSIONS",
|
|
1380
|
+
guard.MIME_TYPES[0] === "text/gccov" && guard.EXTENSIONS[0] === ".gccov");
|
|
1381
|
+
check("defineGuard: INTEGRATION_FIXTURES surfaced",
|
|
1382
|
+
guard.INTEGRATION_FIXTURES.kind === "content");
|
|
1383
|
+
check("defineGuard: base derives COMPLIANCE_POSTURES (strict tier + budget)",
|
|
1384
|
+
guard.COMPLIANCE_POSTURES.hipaa.forensicSnippetBytes === 64);
|
|
1385
|
+
check("defineGuard: base derives DEFAULTS via strictDefaults",
|
|
1386
|
+
guard.DEFAULTS.mode === "enforce" && guard.DEFAULTS.maxBytes === 256);
|
|
1387
|
+
check("defineGuard: extra merged verbatim", guard.helperConst === 7);
|
|
1388
|
+
check("defineGuard: error class exported under its name",
|
|
1389
|
+
typeof guard.GcCoverageContentError === "function");
|
|
1390
|
+
|
|
1391
|
+
// Generated validate (runIssueValidator + detect).
|
|
1392
|
+
check("defineGuard: validate clean → ok", guard.validate("hello", {}).ok === true);
|
|
1393
|
+
check("defineGuard: validate hostile → not ok", guard.validate("BADxyz", {}).ok === false);
|
|
1394
|
+
check("defineGuard: validate non-text → bad-input (text contract)",
|
|
1395
|
+
guard.validate(42, {}).ok === false);
|
|
1396
|
+
|
|
1397
|
+
// Generated sanitize (resolve → detect → throwOnRefusalSeverity → transform).
|
|
1398
|
+
check("defineGuard: sanitize clean returns transform output",
|
|
1399
|
+
guard.sanitize("okvalue", {}) === "okvalue");
|
|
1400
|
+
var threw = false;
|
|
1401
|
+
try { guard.sanitize("BADxyz", {}); }
|
|
1402
|
+
catch (e) { threw = e instanceof guard.GcCoverageContentError; }
|
|
1403
|
+
check("defineGuard: sanitize refuses on high finding", threw);
|
|
1404
|
+
|
|
1405
|
+
// resolveOpts (and the no-argument default path in validate / resolveOpts).
|
|
1406
|
+
check("defineGuard: resolveOpts applies profile",
|
|
1407
|
+
guard.resolveOpts({ profile: "balanced" }).maxBytes === 512);
|
|
1408
|
+
check("defineGuard: validate with no opts arg resolves defaults",
|
|
1409
|
+
guard.validate("hello").ok === true);
|
|
1410
|
+
check("defineGuard: resolveOpts with no opts arg → defaults",
|
|
1411
|
+
guard.resolveOpts().maxBytes === 256);
|
|
1412
|
+
|
|
1413
|
+
// buildProfile (makeProfileBuilder-backed).
|
|
1414
|
+
var custom = guard.buildProfile({ extends: "strict", overrides: { maxBytes: 99 } });
|
|
1415
|
+
check("defineGuard: buildProfile composes strict + override",
|
|
1416
|
+
custom.maxBytes === 99 && custom.bidiPolicy === "reject");
|
|
1417
|
+
|
|
1418
|
+
// compliancePosture (lookupCompliancePosture-backed clone).
|
|
1419
|
+
check("defineGuard: compliancePosture returns overlay clone",
|
|
1420
|
+
guard.compliancePosture("hipaa").forensicSnippetBytes === 64);
|
|
1421
|
+
threw = false;
|
|
1422
|
+
try { guard.compliancePosture("nope"); }
|
|
1423
|
+
catch (e) { threw = e.code === "gccov.bad-posture"; }
|
|
1424
|
+
check("defineGuard: compliancePosture unknown throws bad-posture", threw);
|
|
1425
|
+
|
|
1426
|
+
// loadRulePack.
|
|
1427
|
+
guard.loadRulePack({ id: "pk", rules: [] });
|
|
1428
|
+
check("defineGuard: loadRulePack accepts a pack", true);
|
|
1429
|
+
|
|
1430
|
+
// Default gate (content KIND → extractBytesAsText dispatch); gate() with no
|
|
1431
|
+
// opts exercises the defaultGate resolve-with-{} default.
|
|
1432
|
+
var gate = guard.gate({});
|
|
1433
|
+
var gateNoOpts = guard.gate();
|
|
1434
|
+
check("defineGuard: gate() with no opts serves clean bytes",
|
|
1435
|
+
(await gateNoOpts.check({ bytes: Buffer.from("hello") })).action === "serve");
|
|
1436
|
+
check("defineGuard: default gate serves clean bytes",
|
|
1437
|
+
(await gate.check({ bytes: Buffer.from("hello") })).action === "serve");
|
|
1438
|
+
check("defineGuard: default gate refuses hostile bytes",
|
|
1439
|
+
(await gate.check({ bytes: Buffer.from("BADxyz") })).action === "refuse");
|
|
1440
|
+
check("defineGuard: default gate serves when no ctx value",
|
|
1441
|
+
(await gate.check({})).action === "serve");
|
|
1442
|
+
|
|
1443
|
+
// Construction error branches.
|
|
1444
|
+
threw = false;
|
|
1445
|
+
try {
|
|
1446
|
+
GC.defineGuard({ name: "gccov2", kind: "weird-kind",
|
|
1447
|
+
profiles: GUARD_PROFILES, errorClass: GCE, validate: function () { return { ok: true, issues: [] }; } });
|
|
1448
|
+
} catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
1449
|
+
check("defineGuard: custom kind without gate throws bad-opt", threw);
|
|
1450
|
+
|
|
1451
|
+
threw = false;
|
|
1452
|
+
try {
|
|
1453
|
+
GC.defineGuard({ name: "gccov3", kind: "content", profiles: GUARD_PROFILES,
|
|
1454
|
+
errorClass: GCE, errorName: "Dup" });
|
|
1455
|
+
} catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
1456
|
+
check("defineGuard: errorClass + errorName both → bad-opt", threw);
|
|
1457
|
+
|
|
1458
|
+
threw = false;
|
|
1459
|
+
try {
|
|
1460
|
+
GC.defineGuard({ name: "gccov4", kind: "content", profiles: GUARD_PROFILES, errorClass: GCE });
|
|
1461
|
+
} catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
1462
|
+
check("defineGuard: missing validate/detect → bad-opt", threw);
|
|
1463
|
+
|
|
1464
|
+
threw = false;
|
|
1465
|
+
try { GC.defineGuard({ kind: "content" }); }
|
|
1466
|
+
catch (e) { threw = e instanceof GCE; }
|
|
1467
|
+
check("defineGuard: missing name throws", threw);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
async function testDefineGuardIdentifierKind() {
|
|
1471
|
+
// Identifier KIND with explicit defaults + postures (non-base branch) —
|
|
1472
|
+
// covers the ctx-field dispatch loop in _ctxValueForKind.
|
|
1473
|
+
var guard = GC.defineGuard({
|
|
1474
|
+
name: "gcid", kind: "identifier", errorName: "GcCoverageIdentError",
|
|
1475
|
+
profiles: { strict: { maxBytes: 64 }, balanced: { maxBytes: 128 }, permissive: { maxBytes: 256 } },
|
|
1476
|
+
defaults: { maxBytes: 64, mode: "enforce" },
|
|
1477
|
+
postures: { hipaa: "strict", "pci-dss": "strict", gdpr: "strict", soc2: "strict" },
|
|
1478
|
+
validate: function (value) {
|
|
1479
|
+
if (String(value).indexOf(" ") !== -1) {
|
|
1480
|
+
return { ok: false, issues: [{ kind: "id.space", severity: "high", snippet: "space" }] };
|
|
1481
|
+
}
|
|
1482
|
+
// No `issues` field on the clean path exercises the default gate's
|
|
1483
|
+
// `rv.issues || []` fallback.
|
|
1484
|
+
return { ok: true };
|
|
1485
|
+
},
|
|
1486
|
+
});
|
|
1487
|
+
check("defineGuard(identifier): explicit DEFAULTS kept", guard.DEFAULTS.maxBytes === 64);
|
|
1488
|
+
check("defineGuard(identifier): explicit COMPLIANCE_POSTURES kept",
|
|
1489
|
+
guard.COMPLIANCE_POSTURES.hipaa === "strict");
|
|
1490
|
+
var gate = guard.gate({});
|
|
1491
|
+
check("defineGuard(identifier): reads ctx.identifier field",
|
|
1492
|
+
(await gate.check({ identifier: "cleanid" })).action === "serve");
|
|
1493
|
+
check("defineGuard(identifier): reads ctx.token fallback field",
|
|
1494
|
+
(await gate.check({ token: "has space" })).action === "refuse");
|
|
1495
|
+
check("defineGuard(identifier): no ctx value → serve",
|
|
1496
|
+
(await gate.check({})).action === "serve");
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
async function testDefineGuardDerivedDefaults() {
|
|
1500
|
+
// Neither `base` nor explicit `defaults` / `postures`, and a `detect`
|
|
1501
|
+
// without `inputContract` — exercises the profiles.strict default, the
|
|
1502
|
+
// ALL_STRICT_POSTURES default, and the "raw" input-contract default.
|
|
1503
|
+
var guard = GC.defineGuard({
|
|
1504
|
+
name: "gcraw", kind: "content", errorName: "GcCoverageRawError",
|
|
1505
|
+
profiles: GUARD_PROFILES,
|
|
1506
|
+
detect: function (input) {
|
|
1507
|
+
return String(input).indexOf("NO") === 0
|
|
1508
|
+
? [{ kind: "raw.bad", severity: "high", snippet: "no" }] : [];
|
|
1509
|
+
},
|
|
1510
|
+
});
|
|
1511
|
+
check("defineGuard(raw): DEFAULTS falls back to profiles.strict",
|
|
1512
|
+
guard.DEFAULTS.maxBytes === 256 && guard.DEFAULTS.bidiPolicy === "reject");
|
|
1513
|
+
check("defineGuard(raw): postures default to ALL_STRICT_POSTURES",
|
|
1514
|
+
guard.COMPLIANCE_POSTURES.hipaa === "strict");
|
|
1515
|
+
check("defineGuard(raw): generated validate (raw contract) clean → ok",
|
|
1516
|
+
guard.validate("fine", {}).ok === true);
|
|
1517
|
+
check("defineGuard(raw): generated validate (raw contract) hostile → not ok",
|
|
1518
|
+
guard.validate("NOxyz", {}).ok === false);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
async function testResidualBranches() {
|
|
1522
|
+
// validateGateShape with an explicit custom errorClass argument.
|
|
1523
|
+
var threw = false;
|
|
1524
|
+
try { GC.validateGateShape(null, "lbl", GCE); }
|
|
1525
|
+
catch (e) { threw = e instanceof GCE && e.code === "gate-contract/bad-shape"; }
|
|
1526
|
+
check("validateGateShape: explicit errorClass argument used", threw);
|
|
1527
|
+
|
|
1528
|
+
// check() with no ctx + a check returning undefined → default serve decision.
|
|
1529
|
+
var noCtxGate = GC.defineGate({ name: "noctx", check: async function () { return undefined; } });
|
|
1530
|
+
var noCtx = await noCtxGate.check();
|
|
1531
|
+
check("defineGate: no ctx + undefined decision → default serve", noCtx.action === "serve");
|
|
1532
|
+
|
|
1533
|
+
// onAudit returning null emits the framework's default entry.
|
|
1534
|
+
var audit = makeAuditCollector();
|
|
1535
|
+
var nullAuditGate = GC.defineGate({
|
|
1536
|
+
name: "auditnull", audit: audit,
|
|
1537
|
+
onAudit: function () { return null; },
|
|
1538
|
+
check: async function () { return serveCheck(); },
|
|
1539
|
+
});
|
|
1540
|
+
await nullAuditGate.check({ bytes: Buffer.from("x") });
|
|
1541
|
+
check("defineGate: onAudit null → default entry emitted",
|
|
1542
|
+
audit.rows.some(function (r) { return r.action === "auditnull.serve"; }));
|
|
1543
|
+
|
|
1544
|
+
// contentTypeMux with no contentType → "" → default fallback.
|
|
1545
|
+
var ctMux = GC.contentTypeMux({ "text/csv": serveGate("c"), "default": refuseGate("d") });
|
|
1546
|
+
check("contentTypeMux: missing contentType → default",
|
|
1547
|
+
(await ctMux.check({ bytes: Buffer.from("x") })).action === "refuse");
|
|
1548
|
+
|
|
1549
|
+
// byRoute with no route + a "default" fallback key.
|
|
1550
|
+
var byRouteDefault = GC.byRoute({ "/admin/*": serveGate("a"), "default": refuseGate("d") });
|
|
1551
|
+
check("byRoute: missing route + default-key fallback",
|
|
1552
|
+
(await byRouteDefault.check({ bytes: Buffer.from("x") })).action === "refuse");
|
|
1553
|
+
|
|
1554
|
+
// buildContentGate severity fallback on a critical finding (no disposition).
|
|
1555
|
+
var critGate = GC.buildContentGate({
|
|
1556
|
+
name: "cg-crit", opts: {},
|
|
1557
|
+
validate: function () { return { ok: false, issues: [{ kind: "k", severity: "critical" }] }; },
|
|
1558
|
+
produceSanitized: function (s) { return s; },
|
|
1559
|
+
});
|
|
1560
|
+
check("buildContentGate: critical severity fallback refuses",
|
|
1561
|
+
(await critGate.check({ bytes: Buffer.from("x") })).action === "refuse");
|
|
1562
|
+
|
|
1563
|
+
// buildContentGate extraIssues returning undefined → treated as no issues.
|
|
1564
|
+
var extraNone = GC.buildContentGate({
|
|
1565
|
+
name: "cg-extranone", opts: {},
|
|
1566
|
+
validate: function () { return { ok: true, issues: [] }; },
|
|
1567
|
+
extraIssues: function () { return undefined; },
|
|
1568
|
+
produceSanitized: function (s) { return s; },
|
|
1569
|
+
});
|
|
1570
|
+
check("buildContentGate: extraIssues undefined → serve",
|
|
1571
|
+
(await extraNone.check({ bytes: Buffer.from("x") })).action === "serve");
|
|
1572
|
+
|
|
1573
|
+
// buildContentGate producer returning a Buffer passes through unchanged.
|
|
1574
|
+
var bufSan = GC.buildContentGate({
|
|
1575
|
+
name: "cg-buf", opts: {},
|
|
1576
|
+
validate: function () {
|
|
1577
|
+
return { ok: false, issues: [{ kind: "k", severity: "high", disposition: "sanitize" }] };
|
|
1578
|
+
},
|
|
1579
|
+
produceSanitized: function () { return Buffer.from("bufclean"); },
|
|
1580
|
+
});
|
|
1581
|
+
var bufD = await bufSan.check({ bytes: Buffer.from("x") });
|
|
1582
|
+
check("buildContentGate: Buffer producer output preserved",
|
|
1583
|
+
bufD.action === "sanitize" && bufD.sanitized.toString() === "bufclean");
|
|
1584
|
+
|
|
1585
|
+
// resolveProfileAndPosture with a cfg that omits `defaults`.
|
|
1586
|
+
var noDefaults = GC.resolveProfileAndPosture({ profile: "strict" },
|
|
1587
|
+
{ profiles: { strict: { a: 1 } }, compliancePostures: {}, errorClass: GCE, errCodePrefix: "csv" });
|
|
1588
|
+
check("resolveProfileAndPosture: cfg without defaults still resolves", noDefaults.a === 1);
|
|
1589
|
+
|
|
1590
|
+
// resolveProfileAndPosture with omitted opts → default {}.
|
|
1591
|
+
var noOpts = GC.resolveProfileAndPosture(undefined,
|
|
1592
|
+
{ profiles: { strict: { a: 1 } }, compliancePostures: {}, defaults: { d: 1 },
|
|
1593
|
+
errorClass: GCE, errCodePrefix: "csv" });
|
|
1594
|
+
check("resolveProfileAndPosture: omitted opts defaults to {}", noOpts.d === 1);
|
|
1595
|
+
|
|
1596
|
+
// validateGateShape with no label uses the default "gate" label.
|
|
1597
|
+
var vg = serveGate("vg-nolabel");
|
|
1598
|
+
check("validateGateShape: default label path returns gate", GC.validateGateShape(vg) === vg);
|
|
1599
|
+
|
|
1600
|
+
// Forensic snapshot on STRING bytes + a refusal carrying no issues array.
|
|
1601
|
+
var store = makeForensicStore();
|
|
1602
|
+
var strForensic = GC.defineGate({
|
|
1603
|
+
name: "strforensic", forensicSnippetBytes: 4, forensicEvidenceStore: store,
|
|
1604
|
+
check: async function () { return { ok: false, action: "refuse" }; },
|
|
1605
|
+
});
|
|
1606
|
+
var sf = await strForensic.check({ bytes: "hostile-string-payload" });
|
|
1607
|
+
check("defineGate: forensic snapshot from string bytes",
|
|
1608
|
+
Buffer.isBuffer(sf.forensicSnapshot) && sf.forensicSnapshot.length === 4);
|
|
1609
|
+
await helpers.waitUntil(function () { return store.writes.length >= 1; },
|
|
1610
|
+
{ timeoutMs: 3000, label: "residual: string-bytes forensic write" });
|
|
1611
|
+
check("defineGate: forensic write defaults issues to []",
|
|
1612
|
+
Array.isArray(store.writes[0].issues) && store.writes[0].issues.length === 0);
|
|
1613
|
+
|
|
1614
|
+
// shadowMode with no opts → default "shadow" name.
|
|
1615
|
+
var candRan = false;
|
|
1616
|
+
var stagedDefault = GC.shadowMode(serveGate("prim2"),
|
|
1617
|
+
GC.defineGate({ name: "cand2", check: async function () { candRan = true; return serveCheck(); } }));
|
|
1618
|
+
check("shadowMode: default name", stagedDefault.name === "shadow");
|
|
1619
|
+
await stagedDefault.check({ bytes: Buffer.from("x") });
|
|
1620
|
+
await helpers.waitUntil(function () { return candRan; },
|
|
1621
|
+
{ timeoutMs: 3000, label: "residual: shadow default-opts candidate ran" });
|
|
1622
|
+
|
|
1623
|
+
// cachingGate / workerThreadGate with no opts argument.
|
|
1624
|
+
threw = false;
|
|
1625
|
+
try { GC.cachingGate(serveGate("cg2")); }
|
|
1626
|
+
catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
1627
|
+
check("cachingGate: no opts → bad-opt (missing backend)", threw);
|
|
1628
|
+
threw = false;
|
|
1629
|
+
try { GC.workerThreadGate(serveGate("w2")); }
|
|
1630
|
+
catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
1631
|
+
check("workerThreadGate: no opts → bad-opt (missing worker)", threw);
|
|
1632
|
+
|
|
1633
|
+
// makeProfileResolver's returned function with no opts → default profile.
|
|
1634
|
+
var resolver = GC.makeProfileResolver({
|
|
1635
|
+
profiles: { strict: { a: 1 } }, postures: {}, defaults: "strict",
|
|
1636
|
+
errorClass: GCE, codePrefix: "csv",
|
|
1637
|
+
});
|
|
1638
|
+
check("makeProfileResolver: no opts → default profile", resolver() === "strict");
|
|
1639
|
+
|
|
1640
|
+
// compliancePostures with no spec → bad-base (spec.base undefined).
|
|
1641
|
+
threw = false;
|
|
1642
|
+
try { GC.compliancePostures({ strict: {}, balanced: {} }); }
|
|
1643
|
+
catch (e) { threw = e.code === "gate-contract/bad-base"; }
|
|
1644
|
+
check("compliancePostures: omitted spec → bad-base", threw);
|
|
1645
|
+
|
|
1646
|
+
// buildGuardGate with undefined opts.
|
|
1647
|
+
var bgg = GC.buildGuardGate("bgg-noopts", undefined, async function () { return serveCheck(); });
|
|
1648
|
+
check("buildGuardGate: undefined opts still builds",
|
|
1649
|
+
(await bgg.check({ bytes: Buffer.from("x") })).action === "serve");
|
|
1650
|
+
|
|
1651
|
+
// buildContentGate with no opts on the spec.
|
|
1652
|
+
var cgNoOpts = GC.buildContentGate({
|
|
1653
|
+
name: "cg-noopts",
|
|
1654
|
+
validate: function () { return { ok: true, issues: [] }; },
|
|
1655
|
+
produceSanitized: function (s) { return s; },
|
|
1656
|
+
});
|
|
1657
|
+
check("buildContentGate: spec without opts serves clean",
|
|
1658
|
+
(await cgNoOpts.check({ bytes: Buffer.from("x") })).action === "serve");
|
|
1659
|
+
|
|
1660
|
+
// buildContentGate low-severity fallback → audit-only (ternary audit arm).
|
|
1661
|
+
var lowGate = GC.buildContentGate({
|
|
1662
|
+
name: "cg-low", opts: {},
|
|
1663
|
+
validate: function () { return { ok: true, issues: [{ kind: "k", severity: "low" }] }; },
|
|
1664
|
+
produceSanitized: function (s) { return s; },
|
|
1665
|
+
});
|
|
1666
|
+
check("buildContentGate: low-severity fallback → audit-only",
|
|
1667
|
+
(await lowGate.check({ bytes: Buffer.from("x") })).action === "audit-only");
|
|
1668
|
+
|
|
1669
|
+
// composeHooks with no argument → null.
|
|
1670
|
+
check("composeHooks: no argument → null", GC.composeHooks() === null);
|
|
1671
|
+
|
|
1672
|
+
// defineGuard / defineParser with no errorName + no errorClass mint a
|
|
1673
|
+
// default-named error class.
|
|
1674
|
+
var autoGuard = GC.defineGuard({
|
|
1675
|
+
name: "gccovauto", kind: "content", profiles: GUARD_PROFILES,
|
|
1676
|
+
validate: function () { return { ok: true, issues: [] }; },
|
|
1677
|
+
});
|
|
1678
|
+
check("defineGuard: default error class minted",
|
|
1679
|
+
Object.keys(autoGuard).some(function (k) { return /Error$/.test(k) && typeof autoGuard[k] === "function"; }));
|
|
1680
|
+
var autoParser = GC.defineParser({
|
|
1681
|
+
name: "gccovautoparse", entry: function () { return true; }, profiles: { strict: {} },
|
|
1682
|
+
});
|
|
1683
|
+
check("defineParser: default error class minted",
|
|
1684
|
+
Object.keys(autoParser).some(function (k) { return /Error$/.test(k) && typeof autoParser[k] === "function"; }));
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
async function testDefineGuardSanitizeAmplification() {
|
|
1688
|
+
// The text-scrubber shape: sanitizeAmplificationCap opts into the
|
|
1689
|
+
// "sanitize must shrink, never grow" post-condition, and sanitizeSeverities
|
|
1690
|
+
// narrows the refusal set. Drives the generated-sanitize bad-input +
|
|
1691
|
+
// amplification-cap branches.
|
|
1692
|
+
var guard = GC.defineGuard({
|
|
1693
|
+
name: "gcamp", kind: "content", errorName: "GcCoverageAmpError",
|
|
1694
|
+
profiles: {
|
|
1695
|
+
strict: { maxGrowth: 2 },
|
|
1696
|
+
balanced: { maxGrowth: 2 },
|
|
1697
|
+
permissive: { maxGrowth: 2 },
|
|
1698
|
+
},
|
|
1699
|
+
base: 64,
|
|
1700
|
+
detect: function (input) {
|
|
1701
|
+
var s = String(input);
|
|
1702
|
+
if (s.indexOf("NOSNIP") === 0) {
|
|
1703
|
+
// bad-input finding with no snippet → the default-message fallback.
|
|
1704
|
+
return [{ kind: "bad-input", severity: "high" }];
|
|
1705
|
+
}
|
|
1706
|
+
if (s.indexOf("BADIN") === 0) {
|
|
1707
|
+
return [{ kind: "bad-input", severity: "high", snippet: "unprocessable shape" }];
|
|
1708
|
+
}
|
|
1709
|
+
if (s.indexOf("HI") === 0) {
|
|
1710
|
+
return [{ kind: "content.high", severity: "high", ruleId: "gcamp.high", snippet: "hi" }];
|
|
1711
|
+
}
|
|
1712
|
+
return [];
|
|
1713
|
+
},
|
|
1714
|
+
inputContract: "text",
|
|
1715
|
+
sanitizeSeverities: ["critical", "high"],
|
|
1716
|
+
sanitizeAmplificationCap: "maxGrowth",
|
|
1717
|
+
sanitizeTransform: function (subject) {
|
|
1718
|
+
return subject.indexOf("GROW") === 0 ? subject + "x".repeat(1000) : subject;
|
|
1719
|
+
},
|
|
1720
|
+
});
|
|
1721
|
+
|
|
1722
|
+
// Non-string input rejected by the ampCapField text contract.
|
|
1723
|
+
var threw = false;
|
|
1724
|
+
try { guard.sanitize(42, {}); }
|
|
1725
|
+
catch (e) { threw = e.code === "gcamp.bad-input"; }
|
|
1726
|
+
check("defineGuard(amp): non-string sanitize input → bad-input", threw);
|
|
1727
|
+
|
|
1728
|
+
// A bad-input FINDING (on a string subject) refuses always.
|
|
1729
|
+
threw = false;
|
|
1730
|
+
try { guard.sanitize("BADINxyz", {}); }
|
|
1731
|
+
catch (e) { threw = e.code === "gcamp.bad-input"; }
|
|
1732
|
+
check("defineGuard(amp): bad-input finding refuses", threw);
|
|
1733
|
+
|
|
1734
|
+
// A bad-input finding with no snippet uses the default message.
|
|
1735
|
+
var noSnipMsg = null;
|
|
1736
|
+
try { guard.sanitize("NOSNIPxyz", {}); }
|
|
1737
|
+
catch (e) { noSnipMsg = e.message; }
|
|
1738
|
+
check("defineGuard(amp): bad-input without snippet → default message",
|
|
1739
|
+
/not processable/.test(noSnipMsg || ""));
|
|
1740
|
+
|
|
1741
|
+
// A high finding refuses via throwOnRefusalSeverity (narrowed severities set).
|
|
1742
|
+
threw = false;
|
|
1743
|
+
try { guard.sanitize("HIxyz", {}); }
|
|
1744
|
+
catch (e) { threw = e instanceof guard.GcCoverageAmpError; }
|
|
1745
|
+
check("defineGuard(amp): high finding refuses", threw);
|
|
1746
|
+
|
|
1747
|
+
// Amplifying transform trips the growth cap.
|
|
1748
|
+
threw = false;
|
|
1749
|
+
try { guard.sanitize("GROWvalue", {}); }
|
|
1750
|
+
catch (e) { threw = e.code === "gcamp.sanitize-amplified"; }
|
|
1751
|
+
check("defineGuard(amp): output over growth cap → sanitize-amplified", threw);
|
|
1752
|
+
|
|
1753
|
+
// Clean, non-amplifying input returns the transform output.
|
|
1754
|
+
check("defineGuard(amp): clean input returns transform output",
|
|
1755
|
+
guard.sanitize("okvalue", {}) === "okvalue");
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
function testDefineParser() {
|
|
1759
|
+
var parser = GC.defineParser({
|
|
1760
|
+
name: "gcparser", entry: function (line) { return { ok: line.length > 0 }; },
|
|
1761
|
+
errorName: "GcCoverageParserError",
|
|
1762
|
+
profiles: { strict: { maxLine: 512 } },
|
|
1763
|
+
extra: { KNOWN_VERBS: ["USER", "PASS"] },
|
|
1764
|
+
});
|
|
1765
|
+
check("defineParser: entry exported under default 'validate'",
|
|
1766
|
+
typeof parser.validate === "function" && parser.validate("USER x").ok === true);
|
|
1767
|
+
check("defineParser: PROFILES surfaced", parser.PROFILES.strict.maxLine === 512);
|
|
1768
|
+
check("defineParser: COMPLIANCE_POSTURES defaults to ALL_STRICT",
|
|
1769
|
+
parser.COMPLIANCE_POSTURES.hipaa === "strict");
|
|
1770
|
+
check("defineParser: compliancePosture returns profile name",
|
|
1771
|
+
parser.compliancePosture("hipaa") === "strict");
|
|
1772
|
+
check("defineParser: compliancePosture unknown → null",
|
|
1773
|
+
parser.compliancePosture("not-a-regime") === null);
|
|
1774
|
+
check("defineParser: extra merged", parser.KNOWN_VERBS.length === 2);
|
|
1775
|
+
check("defineParser: error class exported",
|
|
1776
|
+
typeof parser.GcCoverageParserError === "function");
|
|
1777
|
+
|
|
1778
|
+
// entryName override.
|
|
1779
|
+
var custom = GC.defineParser({
|
|
1780
|
+
name: "gcparser2", entry: function () { return true; }, entryName: "parse",
|
|
1781
|
+
errorClass: GCE, profiles: { strict: {} },
|
|
1782
|
+
});
|
|
1783
|
+
check("defineParser: entryName override", typeof custom.parse === "function");
|
|
1784
|
+
|
|
1785
|
+
var threw = false;
|
|
1786
|
+
try {
|
|
1787
|
+
GC.defineParser({ name: "gcp3", entry: 5, profiles: { strict: {} }, errorClass: GCE });
|
|
1788
|
+
} catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
1789
|
+
check("defineParser: non-function entry → bad-opt", threw);
|
|
1790
|
+
|
|
1791
|
+
threw = false;
|
|
1792
|
+
try {
|
|
1793
|
+
GC.defineParser({ name: "gcp4", entry: function () {}, profiles: { strict: {} },
|
|
1794
|
+
errorClass: GCE, errorName: "Dup" });
|
|
1795
|
+
} catch (e) { threw = e.code === "gate-contract/bad-opt"; }
|
|
1796
|
+
check("defineParser: errorClass + errorName both → bad-opt", threw);
|
|
1797
|
+
|
|
1798
|
+
threw = false;
|
|
1799
|
+
try { GC.defineParser({ entry: function () {}, profiles: {} }); }
|
|
1800
|
+
catch (e) { threw = e instanceof GCE; }
|
|
1801
|
+
check("defineParser: missing name throws", threw);
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
async function run() {
|
|
1805
|
+
testIssueSeverities();
|
|
1806
|
+
testSummarizeIssues();
|
|
1807
|
+
testSeverityAndPolicyDispositions();
|
|
1808
|
+
testBuildProfileComposition();
|
|
1809
|
+
testMakeProfileBuilder();
|
|
1810
|
+
testMakeProfileResolverAndName();
|
|
1811
|
+
testResolveProfileAndPosture();
|
|
1812
|
+
testGlobalPostureFallback();
|
|
1813
|
+
testUnmappedPostureWarning();
|
|
1814
|
+
testLookupCompliancePosture();
|
|
1815
|
+
testMakePostureAccessor();
|
|
1816
|
+
testCompliancePosturesFactory();
|
|
1817
|
+
testStrictDefaults();
|
|
1818
|
+
testRunIssueValidatorContracts();
|
|
1819
|
+
testBadInputResult();
|
|
1820
|
+
testDetectStringInput();
|
|
1821
|
+
await testComposeHooks();
|
|
1822
|
+
testThrowOnRefusalSeverity();
|
|
1823
|
+
testExtractBytesAsText();
|
|
1824
|
+
testIdentifierFixtures();
|
|
1825
|
+
testMakeRulePackLoader();
|
|
1826
|
+
await testValidateGateShapeAndRunGate();
|
|
1827
|
+
testDefineGateBadOpts();
|
|
1828
|
+
await testDefineGateLifecycle();
|
|
1829
|
+
await testDefineGateCheckThrows();
|
|
1830
|
+
await testDefineGateTimeout();
|
|
1831
|
+
await testDefineGateBeforeCheckHooks();
|
|
1832
|
+
await testDefineGateAfterAndIssueHooks();
|
|
1833
|
+
await testDefineGateAuditHooks();
|
|
1834
|
+
await testDefineGateModes();
|
|
1835
|
+
await testDefineGateForensicAndCache();
|
|
1836
|
+
await testBuildGuardGate();
|
|
1837
|
+
await testComposeGates();
|
|
1838
|
+
await testMultiplexAndContentTypeMux();
|
|
1839
|
+
await testTierRouteDirectionDispatch();
|
|
1840
|
+
await testShadowCanaryCachingWorkerGates();
|
|
1841
|
+
await testBuildContentGate();
|
|
1842
|
+
await testDefineGuard();
|
|
1843
|
+
await testDefineGuardIdentifierKind();
|
|
1844
|
+
await testDefineGuardDerivedDefaults();
|
|
1845
|
+
await testDefineGuardSanitizeAmplification();
|
|
1846
|
+
await testResidualBranches();
|
|
1847
|
+
testDefineParser();
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
module.exports = { run: run };
|
|
1851
|
+
|
|
1852
|
+
if (require.main === module) {
|
|
1853
|
+
run().then(function () { console.log("OK — " + helpers.getChecks() + " checks passed"); })
|
|
1854
|
+
.catch(function (e) { console.error(e); process.exit(1); });
|
|
1855
|
+
}
|