@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,1031 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
/**
|
|
5
|
+
* b.redact — operational-log redaction + outbound DLP.
|
|
6
|
+
*
|
|
7
|
+
* Covers the value-shape / field-name redactor (registerFieldRule,
|
|
8
|
+
* registerValueDetector, nested/array/circular/binary walks, the
|
|
9
|
+
* built-in detector chain), the CLASSIFIER_PATTERNS detectors, the
|
|
10
|
+
* classifyDefaults verdict engine, and the installOutboundDlp
|
|
11
|
+
* interceptors (httpClient / mail / webhook, posture presets,
|
|
12
|
+
* fail-closed classifier wrapping).
|
|
13
|
+
*
|
|
14
|
+
* Run standalone: `node test/layer-0-primitives/redact.test.js`
|
|
15
|
+
* Or via smoke: `node test/smoke.js`
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
var helpers = require("../helpers");
|
|
19
|
+
var b = helpers.b;
|
|
20
|
+
var check = helpers.check;
|
|
21
|
+
|
|
22
|
+
// ---- Injected stubs (no helper provides an httpClient/mail/webhook
|
|
23
|
+
// instance mock; these are minimal record-and-resolve fakes so the
|
|
24
|
+
// interceptor's `original(...)` call never touches the network). ----
|
|
25
|
+
|
|
26
|
+
function _fakeHttpClient() {
|
|
27
|
+
var calls = [];
|
|
28
|
+
return {
|
|
29
|
+
calls: calls,
|
|
30
|
+
request: function (o) { calls.push(o); return Promise.resolve({ ok: true, opts: o }); },
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function _fakeMail() {
|
|
35
|
+
var calls = [];
|
|
36
|
+
return {
|
|
37
|
+
calls: calls,
|
|
38
|
+
send: function (m) { calls.push(m); return Promise.resolve({ queued: true, message: m }); },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function _fakeWebhook() {
|
|
43
|
+
var calls = [];
|
|
44
|
+
return {
|
|
45
|
+
calls: calls,
|
|
46
|
+
send: function (i) { calls.push(i); return Promise.resolve({ ok: true, input: i }); },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function _grab(fn) { try { fn(); return null; } catch (e) { return e; } }
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// registerValueDetector
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
function testRegisterValueDetectorRedactsMatchingValue() {
|
|
57
|
+
b.redact._resetForTest();
|
|
58
|
+
try {
|
|
59
|
+
// Register a custom detector for an internal employee-id shape.
|
|
60
|
+
b.redact.registerValueDetector(
|
|
61
|
+
"employee-id",
|
|
62
|
+
function (v) { return /^ZZEMP-\d{6}$/.test(v); },
|
|
63
|
+
"[REDACTED-EMPID]");
|
|
64
|
+
var input = { owner: "ZZEMP-123456", note: "no match here" };
|
|
65
|
+
var out = b.redact.redact(input);
|
|
66
|
+
check("registerValueDetector: matching value replaced with the marker",
|
|
67
|
+
out.owner === "[REDACTED-EMPID]");
|
|
68
|
+
check("registerValueDetector: non-matching value untouched",
|
|
69
|
+
out.note === "no match here");
|
|
70
|
+
check("registerValueDetector: original input not mutated (redact returns a new value)",
|
|
71
|
+
input.owner === "ZZEMP-123456");
|
|
72
|
+
} finally {
|
|
73
|
+
b.redact._resetForTest();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function testRegisterValueDetectorFunctionReplacement() {
|
|
78
|
+
b.redact._resetForTest();
|
|
79
|
+
try {
|
|
80
|
+
// A function replacement receives the matched value and returns the
|
|
81
|
+
// substitution.
|
|
82
|
+
b.redact.registerValueDetector(
|
|
83
|
+
"fixed-shape",
|
|
84
|
+
function (v) { return v === "ZZTOKEN-ABCDEF"; },
|
|
85
|
+
function (v) { return "[len:" + v.length + "]"; });
|
|
86
|
+
// Field name `ref` is not on the sensitive-field list, so the value
|
|
87
|
+
// detector (not a field-name rule) is what fires here.
|
|
88
|
+
var out = b.redact.redact({ ref: "ZZTOKEN-ABCDEF" });
|
|
89
|
+
check("registerValueDetector: function replacement receives the matched value",
|
|
90
|
+
out.ref === "[len:14]");
|
|
91
|
+
} finally {
|
|
92
|
+
b.redact._resetForTest();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function testRegisterValueDetectorRunsAfterBuiltins() {
|
|
97
|
+
b.redact._resetForTest();
|
|
98
|
+
try {
|
|
99
|
+
// A custom detector cannot pre-empt a built-in: a PAN-shaped value still
|
|
100
|
+
// redacts to the built-in credit-card marker even with a custom detector
|
|
101
|
+
// registered, because custom detectors run AFTER the built-in chain.
|
|
102
|
+
var hits = 0;
|
|
103
|
+
b.redact.registerValueDetector(
|
|
104
|
+
"count-all-strings",
|
|
105
|
+
function () { hits += 1; return false; }, // never matches; just observes ordering
|
|
106
|
+
"[X]");
|
|
107
|
+
var out = b.redact.redact({ card: "4111 1111 1111 1111" });
|
|
108
|
+
check("registerValueDetector: built-in PAN detector still wins",
|
|
109
|
+
out.card === "[REDACTED-CC]");
|
|
110
|
+
check("registerValueDetector: custom detector was consulted for other strings",
|
|
111
|
+
hits >= 0);
|
|
112
|
+
} finally {
|
|
113
|
+
b.redact._resetForTest();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function testRegisterValueDetectorRejectsNonFunction() {
|
|
118
|
+
b.redact._resetForTest();
|
|
119
|
+
var threw = null;
|
|
120
|
+
try { b.redact.registerValueDetector("bad", "not-a-function", "[X]"); }
|
|
121
|
+
catch (e) { threw = e; }
|
|
122
|
+
check("registerValueDetector: throws when testFn is not a function",
|
|
123
|
+
threw && /requires a test function/.test(threw.message));
|
|
124
|
+
b.redact._resetForTest();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function testRegisterValueDetectorClearedOnReset() {
|
|
128
|
+
b.redact._resetForTest();
|
|
129
|
+
b.redact.registerValueDetector(
|
|
130
|
+
"employee-id",
|
|
131
|
+
function (v) { return /^ZZEMP-\d{6}$/.test(v); },
|
|
132
|
+
"[REDACTED-EMPID]");
|
|
133
|
+
b.redact._resetForTest();
|
|
134
|
+
var out = b.redact.redact({ owner: "ZZEMP-123456" });
|
|
135
|
+
check("registerValueDetector: detector removed after reset (no longer fires)",
|
|
136
|
+
out.owner === "ZZEMP-123456");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function testRegisterValueDetectorDefaultsToMarkerWhenReplacementOmitted() {
|
|
140
|
+
b.redact._resetForTest();
|
|
141
|
+
try {
|
|
142
|
+
// Omitting the replacement falls back to the redactor's default marker.
|
|
143
|
+
b.redact.registerValueDetector(
|
|
144
|
+
"no-replacement",
|
|
145
|
+
function (v) { return v === "ZZFALLBACK"; });
|
|
146
|
+
var out = b.redact.redact({ ref: "ZZFALLBACK" });
|
|
147
|
+
check("registerValueDetector: omitted replacement uses the default marker",
|
|
148
|
+
out.ref === "[REDACTED]");
|
|
149
|
+
} finally {
|
|
150
|
+
b.redact._resetForTest();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// registerFieldRule
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
function testRegisterFieldRuleRedactsRegisteredField() {
|
|
159
|
+
b.redact._resetForTest();
|
|
160
|
+
try {
|
|
161
|
+
// The replacement arg is accepted for symmetry but ignored — field-name
|
|
162
|
+
// hits always collapse to the configured marker.
|
|
163
|
+
b.redact.registerFieldRule("internal_token", "[IGNORED]");
|
|
164
|
+
var out = b.redact.redact({ internal_token: "abc-123", note: "safe" });
|
|
165
|
+
check("registerFieldRule: registered field collapses to marker",
|
|
166
|
+
out.internal_token === "[REDACTED]");
|
|
167
|
+
check("registerFieldRule: replacement argument is ignored (uses marker, not [IGNORED])",
|
|
168
|
+
out.note === "safe");
|
|
169
|
+
} finally {
|
|
170
|
+
b.redact._resetForTest();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function testRegisterFieldRuleSubstringMatch() {
|
|
175
|
+
b.redact._resetForTest();
|
|
176
|
+
try {
|
|
177
|
+
// Case-insensitive substring: registering `zebra` also redacts
|
|
178
|
+
// `myZebraField`.
|
|
179
|
+
b.redact.registerFieldRule("zebra");
|
|
180
|
+
var out = b.redact.redact({ myZebraField: "x", other: "y" });
|
|
181
|
+
check("registerFieldRule: substring match redacts containing field",
|
|
182
|
+
out.myZebraField === "[REDACTED]");
|
|
183
|
+
check("registerFieldRule: unrelated field untouched",
|
|
184
|
+
out.other === "y");
|
|
185
|
+
} finally {
|
|
186
|
+
b.redact._resetForTest();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function testRegisterFieldRuleRejectsNonString() {
|
|
191
|
+
b.redact._resetForTest();
|
|
192
|
+
var threw = _grab(function () { b.redact.registerFieldRule(12345); });
|
|
193
|
+
check("registerFieldRule: throws on non-string field name",
|
|
194
|
+
threw && /expects a string field name/.test(threw.message));
|
|
195
|
+
b.redact._resetForTest();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function testRegisterFieldRuleClearedOnReset() {
|
|
199
|
+
b.redact._resetForTest();
|
|
200
|
+
b.redact.registerFieldRule("zqfield");
|
|
201
|
+
b.redact._resetForTest();
|
|
202
|
+
var out = b.redact.redact({ zqfield: "kept" });
|
|
203
|
+
check("registerFieldRule: rule removed after reset (field no longer redacted)",
|
|
204
|
+
out.zqfield === "kept");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// redact walk — type branches, depth cap, sensitive-parent collapse
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
function testRedactPrimitivePassthroughs() {
|
|
212
|
+
b.redact._resetForTest();
|
|
213
|
+
// null / undefined pass through unchanged.
|
|
214
|
+
check("redact: null passes through", b.redact.redact(null) === null);
|
|
215
|
+
check("redact: undefined passes through", b.redact.redact(undefined) === undefined);
|
|
216
|
+
// number / boolean values are preserved verbatim.
|
|
217
|
+
var out = b.redact.redact({ n: 42, ok: true, f: false });
|
|
218
|
+
check("redact: number preserved", out.n === 42);
|
|
219
|
+
check("redact: boolean true preserved", out.ok === true);
|
|
220
|
+
check("redact: boolean false preserved", out.f === false);
|
|
221
|
+
// A value type the walk doesn't special-case (bigint) falls through
|
|
222
|
+
// unchanged.
|
|
223
|
+
var big = b.redact.redact({ big: 10n });
|
|
224
|
+
check("redact: bigint value falls through unchanged", big.big === 10n);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function testRedactBinaryValuesAlwaysMarker() {
|
|
228
|
+
b.redact._resetForTest();
|
|
229
|
+
// Raw binary is never logged — Buffer and Uint8Array both collapse.
|
|
230
|
+
var outBuf = b.redact.redact({ blob: Buffer.from("secret-bytes") });
|
|
231
|
+
check("redact: Buffer value collapses to marker", outBuf.blob === "[REDACTED]");
|
|
232
|
+
var outU8 = b.redact.redact({ blob: new Uint8Array([1, 2, 3]) });
|
|
233
|
+
check("redact: Uint8Array value collapses to marker", outU8.blob === "[REDACTED]");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function testRedactArrayWalk() {
|
|
237
|
+
b.redact._resetForTest();
|
|
238
|
+
var out = b.redact.redact(["4111111111111111", "plain", { password: "p" }]);
|
|
239
|
+
check("redact: array element credit-card redacted", out[0] === "[REDACTED-CC]");
|
|
240
|
+
check("redact: array element plain string kept", out[1] === "plain");
|
|
241
|
+
check("redact: array element nested sensitive field redacted",
|
|
242
|
+
out[2].password === "[REDACTED]");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function testRedactSensitiveParentCollapsesComposite() {
|
|
246
|
+
b.redact._resetForTest();
|
|
247
|
+
// A sensitive parent key collapses the ENTIRE value (array OR object),
|
|
248
|
+
// not just scalars — the CWE-532 telemetry-egress guard.
|
|
249
|
+
var arrOut = b.redact.redact({ authorization: ["Bearer abc", "Bearer def"] });
|
|
250
|
+
check("redact: sensitive-key array value collapses whole",
|
|
251
|
+
arrOut.authorization === "[REDACTED]");
|
|
252
|
+
var objOut = b.redact.redact({ password: { nested: "still-secret" } });
|
|
253
|
+
check("redact: sensitive-key object value collapses whole",
|
|
254
|
+
objOut.password === "[REDACTED]");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function testRedactMaxDepthCap() {
|
|
258
|
+
b.redact._resetForTest();
|
|
259
|
+
// Recursion past maxDepth collapses to the marker.
|
|
260
|
+
var out = b.redact.redact({ a: { b: { c: "deep" } } }, { maxDepth: 1 });
|
|
261
|
+
check("redact: value beyond maxDepth collapses to marker",
|
|
262
|
+
out.a.b === "[REDACTED]");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function testRedactCustomMarker() {
|
|
266
|
+
b.redact._resetForTest();
|
|
267
|
+
var out = b.redact.redact({ password: "x" }, { marker: "[SCRUBBED]" });
|
|
268
|
+
check("redact: custom marker option applied", out.password === "[SCRUBBED]");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function testRedactIgnoresInheritedProps() {
|
|
272
|
+
b.redact._resetForTest();
|
|
273
|
+
// Inherited (non-own) enumerable properties are skipped by the walk.
|
|
274
|
+
var proto = { inheritedSecret: "leak" };
|
|
275
|
+
var obj = Object.create(proto);
|
|
276
|
+
obj.own = "kept";
|
|
277
|
+
var out = b.redact.redact(obj);
|
|
278
|
+
check("redact: own property retained", out.own === "kept");
|
|
279
|
+
check("redact: inherited property not copied into output",
|
|
280
|
+
!Object.prototype.hasOwnProperty.call(out, "inheritedSecret"));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function testRedactNonStringParentKeyIsNotSensitive() {
|
|
284
|
+
b.redact._resetForTest();
|
|
285
|
+
// A non-string parentKey seed must not be treated as a sensitive field
|
|
286
|
+
// name (the field-name check bails on non-strings).
|
|
287
|
+
var out = b.redact.redact("plain-value", { parentKey: 12345 });
|
|
288
|
+
check("redact: numeric parentKey does not trigger field-name collapse",
|
|
289
|
+
out === "plain-value");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function testRedactSensitiveTopLevelParentKeyCollapses() {
|
|
293
|
+
b.redact._resetForTest();
|
|
294
|
+
// A sensitive parentKey seed collapses a top-level scalar — the walk
|
|
295
|
+
// checks parentKey before the type branches.
|
|
296
|
+
var out = b.redact.redact("bearer-abc-secret", { parentKey: "authorization" });
|
|
297
|
+
check("redact: sensitive top-level parentKey collapses scalar to marker",
|
|
298
|
+
out === "[REDACTED]");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
// built-in value detectors reached through redact()
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
|
|
305
|
+
function testRedactUrlBearerQueryReplacement() {
|
|
306
|
+
b.redact._resetForTest();
|
|
307
|
+
// The url-bearer-query detector uses a replacement FUNCTION that scrubs
|
|
308
|
+
// only the token query params, preserving the path for triage.
|
|
309
|
+
var out = b.redact.redact({
|
|
310
|
+
url: "https://api.example.com/v1/x?access_token=secret123&foo=bar",
|
|
311
|
+
});
|
|
312
|
+
check("redact: url bearer-query token value scrubbed",
|
|
313
|
+
out.url === "https://api.example.com/v1/x?access_token=[REDACTED]&foo=bar");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function testRedactConnectionStringBounds() {
|
|
317
|
+
b.redact._resetForTest();
|
|
318
|
+
// Valid credentialed connection string → redacted whole.
|
|
319
|
+
var valid = b.redact.redact({ dsn: "postgres://user:pass@db.internal/app" });
|
|
320
|
+
check("redact: credentialed connection string redacted",
|
|
321
|
+
valid.dsn === "[REDACTED-CONN-STRING]");
|
|
322
|
+
// Too-short value (below the 8-byte floor) short-circuits before the
|
|
323
|
+
// regex — even though "a://:x@" would otherwise match the shape.
|
|
324
|
+
var tiny = b.redact.redact({ dsn: "a://:x@" });
|
|
325
|
+
check("redact: sub-floor connection-string candidate left untouched",
|
|
326
|
+
tiny.dsn === "a://:x@");
|
|
327
|
+
// Oversize value (above the 8 KiB cap) is rejected before the regex, so
|
|
328
|
+
// the credential-shaped value passes through unredacted.
|
|
329
|
+
var huge = "postgres://user:pass@" + "h".repeat(9000);
|
|
330
|
+
var over = b.redact.redact({ dsn: huge });
|
|
331
|
+
check("redact: oversize connection-string candidate left untouched",
|
|
332
|
+
over.dsn === huge);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
// CLASSIFIER_PATTERNS detectors (exported surface)
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
|
|
339
|
+
function testClassifierPatternsPanAndCreditCard() {
|
|
340
|
+
var P = b.redact.CLASSIFIER_PATTERNS;
|
|
341
|
+
check("classifier pan: exact Luhn-valid PAN detected",
|
|
342
|
+
P.pan.detect("4111111111111111") === true);
|
|
343
|
+
check("classifier pan: PAN with a doubled digit >9 (Luhn carry) detected",
|
|
344
|
+
P.pan.detect("5555555555554444") === true);
|
|
345
|
+
check("classifier pan: embedded spaced PAN in a longer string detected",
|
|
346
|
+
P.pan.detect("payment card 4111 1111 1111 1111 posted") === true);
|
|
347
|
+
check("classifier pan: non-string input rejected",
|
|
348
|
+
P.pan.detect(12345) === false);
|
|
349
|
+
check("classifier pan: non-Luhn digit run rejected",
|
|
350
|
+
P.pan.detect("1234567890123456") === false);
|
|
351
|
+
check("classifier credit-card: exact Luhn PAN detected",
|
|
352
|
+
P["credit-card"].detect("5555555555554444") === true);
|
|
353
|
+
check("classifier credit-card: embedded PAN detected",
|
|
354
|
+
P["credit-card"].detect("card 4111 1111 1111 1111 end") === true);
|
|
355
|
+
check("classifier credit-card: non-string rejected",
|
|
356
|
+
P["credit-card"].detect(null) === false);
|
|
357
|
+
// A 16-digit non-Luhn run passes the length gate but fails validation,
|
|
358
|
+
// reaching the trailing reject.
|
|
359
|
+
check("classifier credit-card: non-Luhn digit run rejected",
|
|
360
|
+
P["credit-card"].detect("1234567890123456") === false);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function testClassifierPatternsIban() {
|
|
364
|
+
var P = b.redact.CLASSIFIER_PATTERNS;
|
|
365
|
+
check("classifier iban: valid mod-97 IBAN detected",
|
|
366
|
+
P.iban.detect("GB82 WEST 1234 5698 7654 32") === true);
|
|
367
|
+
check("classifier iban: bad checksum rejected",
|
|
368
|
+
P.iban.detect("GB00WEST12345698765432") === false);
|
|
369
|
+
check("classifier iban: malformed shape rejected",
|
|
370
|
+
P.iban.detect("not-an-iban") === false);
|
|
371
|
+
check("classifier iban: illegal character in body rejected",
|
|
372
|
+
P.iban.detect("GB82WEST1234569876543!") === false);
|
|
373
|
+
check("classifier iban: non-string rejected", P.iban.detect(42) === false);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function testClassifierPatternsApiKeyShape() {
|
|
377
|
+
var P = b.redact.CLASSIFIER_PATTERNS;
|
|
378
|
+
check("classifier api-key-shape: long mixed token detected",
|
|
379
|
+
P["api-key-shape"].detect("ABCDEFGHIJ0123456789KLMNO") === true);
|
|
380
|
+
check("classifier api-key-shape: short token rejected",
|
|
381
|
+
P["api-key-shape"].detect("ABC123") === false);
|
|
382
|
+
check("classifier api-key-shape: no-uppercase rejected",
|
|
383
|
+
P["api-key-shape"].detect("abcdefghij0123456789klmno") === false);
|
|
384
|
+
check("classifier api-key-shape: no-digit rejected",
|
|
385
|
+
P["api-key-shape"].detect("ABCDEFGHIJKLMNOPQRSTUVWX") === false);
|
|
386
|
+
check("classifier api-key-shape: illegal character rejected",
|
|
387
|
+
P["api-key-shape"].detect("ABCDEFGHIJ0123456789KLM!!") === false);
|
|
388
|
+
check("classifier api-key-shape: non-string rejected",
|
|
389
|
+
P["api-key-shape"].detect({}) === false);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function testClassifierPatternsJwtAwsPemSsh() {
|
|
393
|
+
var P = b.redact.CLASSIFIER_PATTERNS;
|
|
394
|
+
check("classifier jwt: JWS triplet detected",
|
|
395
|
+
P.jwt.detect("eyJhbGci.eyJzdWI.SflKxwRJ") === true);
|
|
396
|
+
check("classifier jwt: non-string rejected", P.jwt.detect(1) === false);
|
|
397
|
+
check("classifier aws-access-key: access-key-id detected",
|
|
398
|
+
P["aws-access-key"].detect("AKIAIOSFODNN7EXAMPLE") === true);
|
|
399
|
+
check("classifier aws-access-key: non-string rejected",
|
|
400
|
+
P["aws-access-key"].detect(false) === false);
|
|
401
|
+
check("classifier pem: PEM header detected",
|
|
402
|
+
P.pem.detect("-----BEGIN PRIVATE KEY-----") === true);
|
|
403
|
+
check("classifier pem: non-string rejected", P.pem.detect(0) === false);
|
|
404
|
+
check("classifier ssh-private: OpenSSH header detected",
|
|
405
|
+
P["ssh-private"].detect("-----BEGIN OPENSSH PRIVATE KEY-----") === true);
|
|
406
|
+
check("classifier ssh-private: non-string rejected",
|
|
407
|
+
P["ssh-private"].detect(null) === false);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function testClassifierPatternsSsnEinPhi() {
|
|
411
|
+
var P = b.redact.CLASSIFIER_PATTERNS;
|
|
412
|
+
check("classifier ssn: SSN shape detected", P.ssn.detect("id 123-45-6789 x") === true);
|
|
413
|
+
check("classifier ssn: non-string rejected", P.ssn.detect(9) === false);
|
|
414
|
+
check("classifier ein: EIN shape detected", P.ein.detect("ein 12-3456789 x") === true);
|
|
415
|
+
check("classifier ein: non-string rejected", P.ein.detect(9) === false);
|
|
416
|
+
check("classifier phi-shape: SSN fragment flags PHI",
|
|
417
|
+
P["phi-shape"].detect("patient 123-45-6789") === true);
|
|
418
|
+
check("classifier phi-shape: DOB fragment flags PHI",
|
|
419
|
+
P["phi-shape"].detect("dob 01/15/1990") === true);
|
|
420
|
+
check("classifier phi-shape: MRN fragment flags PHI",
|
|
421
|
+
P["phi-shape"].detect("MRN: 12345") === true);
|
|
422
|
+
check("classifier phi-shape: ICD-10 fragment flags PHI",
|
|
423
|
+
P["phi-shape"].detect("dx E11") === true);
|
|
424
|
+
check("classifier phi-shape: benign text not flagged",
|
|
425
|
+
P["phi-shape"].detect("nothing sensitive here") === false);
|
|
426
|
+
check("classifier phi-shape: non-string rejected",
|
|
427
|
+
P["phi-shape"].detect(3) === false);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ---------------------------------------------------------------------------
|
|
431
|
+
// classifyDefaults — validation
|
|
432
|
+
// ---------------------------------------------------------------------------
|
|
433
|
+
|
|
434
|
+
function testClassifyDefaultsValidationErrors() {
|
|
435
|
+
check("classifyDefaults: empty patterns array throws no-patterns",
|
|
436
|
+
(_grab(function () { b.redact.classifyDefaults({ patterns: [] }); }) || {}).code
|
|
437
|
+
=== "redact-dlp/no-patterns");
|
|
438
|
+
check("classifyDefaults: non-string pattern throws bad-pattern",
|
|
439
|
+
(_grab(function () { b.redact.classifyDefaults({ patterns: [123] }); }) || {}).code
|
|
440
|
+
=== "redact-dlp/bad-pattern");
|
|
441
|
+
check("classifyDefaults: unknown pattern throws unknown-pattern",
|
|
442
|
+
(_grab(function () { b.redact.classifyDefaults({ patterns: ["nope"] }); }) || {}).code
|
|
443
|
+
=== "redact-dlp/unknown-pattern");
|
|
444
|
+
check("classifyDefaults: bad overrideAction throws bad-action",
|
|
445
|
+
(_grab(function () { b.redact.classifyDefaults({ patterns: ["ssn"], overrideAction: "nope" }); }) || {}).code
|
|
446
|
+
=== "redact-dlp/bad-action");
|
|
447
|
+
var unknownOpt = _grab(function () { b.redact.classifyDefaults({ bogus: 1 }); });
|
|
448
|
+
check("classifyDefaults: unknown option key rejected",
|
|
449
|
+
unknownOpt && /unknown option/.test(unknownOpt.message));
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function testClassifyDefaultsDefaultsToAllPatterns() {
|
|
453
|
+
// No patterns → the full CLASSIFIER_PATTERNS set backs the classifier.
|
|
454
|
+
var classify = b.redact.classifyDefaults({});
|
|
455
|
+
var v = classify({ body: { card: "4111111111111111" } });
|
|
456
|
+
check("classifyDefaults: default pattern set flags a PAN as refuse",
|
|
457
|
+
v.verdict === "refuse");
|
|
458
|
+
// No-argument call still yields a working classifier over all patterns.
|
|
459
|
+
var noArg = b.redact.classifyDefaults();
|
|
460
|
+
check("classifyDefaults: no-argument call returns a usable classifier",
|
|
461
|
+
typeof noArg === "function" && noArg({ body: { card: "4111111111111111" } }).verdict === "refuse");
|
|
462
|
+
// No-argument classify() falls back to an empty input.
|
|
463
|
+
check("classifyDefaults: classifier tolerates a null input",
|
|
464
|
+
noArg(null).verdict === "clean" && noArg().verdict === "clean");
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function testClassifyDefaultsExtraPattern() {
|
|
468
|
+
// An extra pattern extends the known set and is resolvable.
|
|
469
|
+
var classify = b.redact.classifyDefaults({
|
|
470
|
+
patterns: ["ssn", "widget-id"],
|
|
471
|
+
extra: {
|
|
472
|
+
"widget-id": {
|
|
473
|
+
detect: function (v) { return typeof v === "string" && /^WIDGET-\d+$/.test(v); },
|
|
474
|
+
action: "refuse",
|
|
475
|
+
label: "widget-id",
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
});
|
|
479
|
+
var v = classify({ body: { id: "WIDGET-42" } });
|
|
480
|
+
check("classifyDefaults: extra pattern participates and refuses",
|
|
481
|
+
v.verdict === "refuse" && v.hits.some(function (h) { return h.label === "widget-id"; }));
|
|
482
|
+
|
|
483
|
+
// An extra pattern that omits `label` falls back to the pattern name in
|
|
484
|
+
// the hit record.
|
|
485
|
+
var classify2 = b.redact.classifyDefaults({
|
|
486
|
+
patterns: ["gadget-id"],
|
|
487
|
+
extra: {
|
|
488
|
+
"gadget-id": {
|
|
489
|
+
detect: function (val) { return typeof val === "string" && /^GADGET-\d+$/.test(val); },
|
|
490
|
+
action: "refuse",
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
});
|
|
494
|
+
var v2 = classify2({ body: { id: "GADGET-9" } });
|
|
495
|
+
check("classifyDefaults: label-less extra pattern uses the pattern name",
|
|
496
|
+
v2.hits.length === 1 && v2.hits[0].label === "gadget-id");
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// ---------------------------------------------------------------------------
|
|
500
|
+
// classifyDefaults — body shapes + verdict precedence
|
|
501
|
+
// ---------------------------------------------------------------------------
|
|
502
|
+
|
|
503
|
+
function testClassifyObjectBodyRefuseRedactClean() {
|
|
504
|
+
var classify = b.redact.classifyDefaults({ patterns: ["pan", "ssn", "jwt"] });
|
|
505
|
+
var refuse = classify({ body: { card: "4111111111111111", note: "ok" } });
|
|
506
|
+
check("classify: PAN in object body → refuse", refuse.verdict === "refuse");
|
|
507
|
+
check("classify: refuse hit records where-path",
|
|
508
|
+
refuse.hits[0].where === "body.card");
|
|
509
|
+
var redactSsn = classify({ body: { s: "my ssn 123-45-6789" } });
|
|
510
|
+
check("classify: SSN in object body → redact", redactSsn.verdict === "redact");
|
|
511
|
+
check("classify: SSN scrubbed inline in redactedBody",
|
|
512
|
+
redactSsn.redactedBody.s === "my ssn [REDACTED]");
|
|
513
|
+
var redactJwt = classify({ body: { tok: "eyJhbGci.eyJzdWI.SflKxwRJ" } });
|
|
514
|
+
check("classify: JWT redact replaces whole string (non ssn/ein shape)",
|
|
515
|
+
redactJwt.redactedBody.tok === "[REDACTED]");
|
|
516
|
+
var clean = classify({ body: { note: "nothing sensitive" } });
|
|
517
|
+
check("classify: benign object body → clean", clean.verdict === "clean");
|
|
518
|
+
check("classify: clean redactedBody preserved", clean.redactedBody.note === "nothing sensitive");
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function testClassifyStringBufferAndScalarBodies() {
|
|
522
|
+
var classify = b.redact.classifyDefaults({ patterns: ["ssn", "pan"] });
|
|
523
|
+
// string body
|
|
524
|
+
var strV = classify({ body: "contains ssn 123-45-6789" });
|
|
525
|
+
check("classify: string body redacted", strV.verdict === "redact");
|
|
526
|
+
check("classify: string redactedBody scrubbed", strV.redactedBody === "contains ssn [REDACTED]");
|
|
527
|
+
// Buffer body that changes → new Buffer returned
|
|
528
|
+
var bufRedact = classify({ body: Buffer.from("ssn 123-45-6789") });
|
|
529
|
+
check("classify: Buffer body redact verdict", bufRedact.verdict === "redact");
|
|
530
|
+
check("classify: redacted Buffer body is a Buffer",
|
|
531
|
+
Buffer.isBuffer(bufRedact.redactedBody));
|
|
532
|
+
check("classify: redacted Buffer body text scrubbed",
|
|
533
|
+
bufRedact.redactedBody.toString("utf8") === "ssn [REDACTED]");
|
|
534
|
+
// Buffer body that doesn't change → original Buffer returned
|
|
535
|
+
var srcBuf = Buffer.from("nothing here");
|
|
536
|
+
var bufClean = classify({ body: srcBuf });
|
|
537
|
+
check("classify: clean Buffer body returns the original buffer instance",
|
|
538
|
+
bufClean.redactedBody === srcBuf);
|
|
539
|
+
// scalar (number) body → returned as-is
|
|
540
|
+
var numV = classify({ body: 42 });
|
|
541
|
+
check("classify: numeric body passes through", numV.redactedBody === 42 && numV.verdict === "clean");
|
|
542
|
+
// missing body → undefined
|
|
543
|
+
var noBody = classify({});
|
|
544
|
+
check("classify: absent body yields undefined redactedBody",
|
|
545
|
+
noBody.redactedBody === undefined && noBody.verdict === "clean");
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function testClassifyWalksNestedArraysAndBinaryFields() {
|
|
549
|
+
var classify = b.redact.classifyDefaults({ patterns: ["ssn"] });
|
|
550
|
+
var v = classify({
|
|
551
|
+
body: {
|
|
552
|
+
items: ["ok", "ssn 123-45-6789"],
|
|
553
|
+
count: 3,
|
|
554
|
+
active: true,
|
|
555
|
+
blob: Buffer.from("raw"),
|
|
556
|
+
empty: "",
|
|
557
|
+
nested: { deep: "ssn 111-22-3333" },
|
|
558
|
+
},
|
|
559
|
+
});
|
|
560
|
+
check("classify: nested array element scrubbed",
|
|
561
|
+
v.redactedBody.items[1] === "ssn [REDACTED]");
|
|
562
|
+
check("classify: number field preserved in walk", v.redactedBody.count === 3);
|
|
563
|
+
check("classify: boolean field preserved in walk", v.redactedBody.active === true);
|
|
564
|
+
check("classify: Buffer field preserved verbatim in walk",
|
|
565
|
+
Buffer.isBuffer(v.redactedBody.blob));
|
|
566
|
+
check("classify: empty-string field preserved", v.redactedBody.empty === "");
|
|
567
|
+
check("classify: deeply nested field scrubbed",
|
|
568
|
+
v.redactedBody.nested.deep === "ssn [REDACTED]");
|
|
569
|
+
check("classify: any hit yields redact verdict", v.verdict === "redact");
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function testClassifyHeadersScanned() {
|
|
573
|
+
var classify = b.redact.classifyDefaults({ patterns: ["jwt"] });
|
|
574
|
+
var v = classify({ body: {}, headers: { authorization: "eyJhbGci.eyJzdWI.SflKxwRJ" } });
|
|
575
|
+
check("classify: header value scanned and flagged", v.verdict === "redact");
|
|
576
|
+
check("classify: header hit records where-path",
|
|
577
|
+
v.hits.some(function (h) { return h.where === "headers.authorization"; }));
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function testClassifyWalkSkipsInheritedAndUnknownTypes() {
|
|
581
|
+
var classify = b.redact.classifyDefaults({ patterns: ["ssn"] });
|
|
582
|
+
// Body and headers carry inherited (non-own) enumerable props that the
|
|
583
|
+
// walk must skip, plus a bigint value type the walk passes through.
|
|
584
|
+
var bodyProto = { inheritedField: "ssn 123-45-6789" };
|
|
585
|
+
var body = Object.create(bodyProto);
|
|
586
|
+
body.own = "ssn 999-88-7777";
|
|
587
|
+
body.big = 100n;
|
|
588
|
+
var hdrProto = { inheritedHeader: "eyJa.eyJb.sig" };
|
|
589
|
+
var headers = Object.create(hdrProto);
|
|
590
|
+
headers.authorization = "plain";
|
|
591
|
+
var v = classify({ body: body, headers: headers });
|
|
592
|
+
check("classify: inherited body prop not copied into redactedBody",
|
|
593
|
+
!Object.prototype.hasOwnProperty.call(v.redactedBody, "inheritedField"));
|
|
594
|
+
check("classify: own body prop scrubbed", v.redactedBody.own === "ssn [REDACTED]");
|
|
595
|
+
check("classify: bigint body value passes through walk", v.redactedBody.big === 100n);
|
|
596
|
+
check("classify: only the own field produced a hit",
|
|
597
|
+
v.hits.length === 1 && v.hits[0].where === "body.own");
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function testClassifyAuditOnlyOverride() {
|
|
601
|
+
var classify = b.redact.classifyDefaults({ patterns: ["ssn"], overrideAction: "audit-only" });
|
|
602
|
+
var v = classify({ body: { s: "123-45-6789" } });
|
|
603
|
+
check("classify: overrideAction downgrades verdict to audit-only",
|
|
604
|
+
v.verdict === "audit-only");
|
|
605
|
+
check("classify: audit-only leaves the body unscrubbed",
|
|
606
|
+
v.redactedBody.s === "123-45-6789");
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function testClassifyCustomMarker() {
|
|
610
|
+
var classify = b.redact.classifyDefaults({ patterns: ["ssn"], marker: "[GONE]" });
|
|
611
|
+
var v = classify({ body: { s: "ssn 123-45-6789" } });
|
|
612
|
+
check("classify: custom marker used for inline scrub",
|
|
613
|
+
v.redactedBody.s === "ssn [GONE]");
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// ---------------------------------------------------------------------------
|
|
617
|
+
// installOutboundDlp — httpClient interceptor
|
|
618
|
+
// ---------------------------------------------------------------------------
|
|
619
|
+
|
|
620
|
+
async function testInstallHttpClientCleanRefuseRedact() {
|
|
621
|
+
b.redact._resetForTest();
|
|
622
|
+
var http = _fakeHttpClient();
|
|
623
|
+
var scanCount = 0, refuseCount = 0, redactCount = 0;
|
|
624
|
+
var dlp = b.redact.installOutboundDlp({
|
|
625
|
+
httpClient: http,
|
|
626
|
+
classifier: b.redact.classifyDefaults({ patterns: ["pan", "ssn"] }),
|
|
627
|
+
onScan: function () { scanCount += 1; },
|
|
628
|
+
onRefuse: function () { refuseCount += 1; },
|
|
629
|
+
onRedact: function () { redactCount += 1; },
|
|
630
|
+
});
|
|
631
|
+
try {
|
|
632
|
+
check("installOutboundDlp: httpClient reported installed", dlp.installed.httpClient === true);
|
|
633
|
+
check("installOutboundDlp: isOutboundDlpInstalled true after install",
|
|
634
|
+
b.redact.isOutboundDlpInstalled() === true);
|
|
635
|
+
|
|
636
|
+
// clean → original called with the untouched opts
|
|
637
|
+
var cleanRes = await http.request({ url: "https://x", body: { note: "ok" } });
|
|
638
|
+
check("installOutboundDlp: clean request reaches original client",
|
|
639
|
+
cleanRes.ok === true && http.calls.length === 1);
|
|
640
|
+
|
|
641
|
+
// redact → original called with a sanitized body
|
|
642
|
+
await http.request({ url: "https://x", body: { s: "ssn 123-45-6789" } });
|
|
643
|
+
check("installOutboundDlp: redact request forwarded with scrubbed body",
|
|
644
|
+
http.calls[1].body.s === "ssn [REDACTED]");
|
|
645
|
+
check("installOutboundDlp: onRedact hook fired", redactCount === 1);
|
|
646
|
+
|
|
647
|
+
// refuse → rejects with DlpError, original NOT called again
|
|
648
|
+
var threw = null;
|
|
649
|
+
try { await http.request({ url: "https://x", body: { card: "4111111111111111" } }); }
|
|
650
|
+
catch (e) { threw = e; }
|
|
651
|
+
check("installOutboundDlp: refuse rejects with DlpError",
|
|
652
|
+
threw instanceof b.redact.DlpError && threw.code === "redact-dlp/refused");
|
|
653
|
+
check("installOutboundDlp: refused request never reaches original client",
|
|
654
|
+
http.calls.length === 2);
|
|
655
|
+
check("installOutboundDlp: onRefuse hook fired", refuseCount === 1);
|
|
656
|
+
check("installOutboundDlp: onScan hook fired for every request", scanCount === 3);
|
|
657
|
+
} finally {
|
|
658
|
+
dlp.uninstall();
|
|
659
|
+
b.redact._resetForTest();
|
|
660
|
+
}
|
|
661
|
+
check("installOutboundDlp: isOutboundDlpInstalled false after uninstall",
|
|
662
|
+
b.redact.isOutboundDlpInstalled() === false);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
async function testInstallHttpClientIdempotentAndUninstallRestores() {
|
|
666
|
+
b.redact._resetForTest();
|
|
667
|
+
var http = _fakeHttpClient();
|
|
668
|
+
var original = http.request;
|
|
669
|
+
var dlp1 = b.redact.installOutboundDlp({
|
|
670
|
+
httpClient: http,
|
|
671
|
+
classifier: b.redact.classifyDefaults({ patterns: ["pan"] }),
|
|
672
|
+
});
|
|
673
|
+
try {
|
|
674
|
+
check("installOutboundDlp: first install wraps request",
|
|
675
|
+
http.request !== original && dlp1.installed.httpClient === true);
|
|
676
|
+
// Second install on the SAME instance no-ops.
|
|
677
|
+
var dlp2 = b.redact.installOutboundDlp({ httpClient: http });
|
|
678
|
+
check("installOutboundDlp: second install on same instance no-ops",
|
|
679
|
+
dlp2.installed.httpClient === false);
|
|
680
|
+
// While installed, a PAN payload is refused.
|
|
681
|
+
var refused = null;
|
|
682
|
+
try { await http.request({ body: { card: "4111111111111111" } }); }
|
|
683
|
+
catch (e) { refused = e; }
|
|
684
|
+
check("installOutboundDlp: PAN refused while interceptor installed",
|
|
685
|
+
refused instanceof b.redact.DlpError);
|
|
686
|
+
} finally {
|
|
687
|
+
dlp1.uninstall();
|
|
688
|
+
b.redact._resetForTest();
|
|
689
|
+
}
|
|
690
|
+
// After uninstall the interceptor is gone — the same PAN payload now
|
|
691
|
+
// reaches the original client untouched (no DlpError).
|
|
692
|
+
var afterRes = await http.request({ body: { card: "4111111111111111" } });
|
|
693
|
+
check("installOutboundDlp: uninstall restores original request path",
|
|
694
|
+
afterRes.ok === true && afterRes.opts.body.card === "4111111111111111");
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// ---------------------------------------------------------------------------
|
|
698
|
+
// installOutboundDlp — mail interceptor
|
|
699
|
+
// ---------------------------------------------------------------------------
|
|
700
|
+
|
|
701
|
+
async function testInstallMailRedactRefuseClean() {
|
|
702
|
+
b.redact._resetForTest();
|
|
703
|
+
var mail = _fakeMail();
|
|
704
|
+
var dlp = b.redact.installOutboundDlp({
|
|
705
|
+
mail: mail,
|
|
706
|
+
classifier: b.redact.classifyDefaults({ patterns: ["ssn", "pan"] }),
|
|
707
|
+
});
|
|
708
|
+
try {
|
|
709
|
+
// redact → text/html/subject scrubbed, other fields preserved
|
|
710
|
+
await mail.send({ to: "a@b.c", subject: "hi", text: "ssn 123-45-6789", html: "<p>123-45-6789</p>" });
|
|
711
|
+
var sent = mail.calls[0];
|
|
712
|
+
check("installOutboundDlp mail: redacted text scrubbed", sent.text === "ssn [REDACTED]");
|
|
713
|
+
check("installOutboundDlp mail: redacted html scrubbed", sent.html === "<p>[REDACTED]</p>");
|
|
714
|
+
check("installOutboundDlp mail: recipient preserved", sent.to === "a@b.c");
|
|
715
|
+
|
|
716
|
+
// clean → forwarded unchanged
|
|
717
|
+
await mail.send({ to: "x@y.z", text: "hello" });
|
|
718
|
+
check("installOutboundDlp mail: clean message forwarded",
|
|
719
|
+
mail.calls[1].text === "hello");
|
|
720
|
+
|
|
721
|
+
// refuse → rejects, original not called
|
|
722
|
+
var threw = null;
|
|
723
|
+
try { await mail.send({ text: "4111111111111111" }); }
|
|
724
|
+
catch (e) { threw = e; }
|
|
725
|
+
check("installOutboundDlp mail: PAN refuses with DlpError",
|
|
726
|
+
threw instanceof b.redact.DlpError && threw.code === "redact-dlp/refused");
|
|
727
|
+
check("installOutboundDlp mail: refused message never sent", mail.calls.length === 2);
|
|
728
|
+
|
|
729
|
+
// Second install on the same mail instance no-ops.
|
|
730
|
+
var dlp2 = b.redact.installOutboundDlp({ mail: mail });
|
|
731
|
+
check("installOutboundDlp mail: idempotent per instance",
|
|
732
|
+
dlp2.installed.mail === false);
|
|
733
|
+
} finally {
|
|
734
|
+
dlp.uninstall();
|
|
735
|
+
b.redact._resetForTest();
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
async function testInterceptorNoArgumentCalls() {
|
|
740
|
+
b.redact._resetForTest();
|
|
741
|
+
var http = _fakeHttpClient();
|
|
742
|
+
var mail = _fakeMail();
|
|
743
|
+
var wh = _fakeWebhook();
|
|
744
|
+
var clean = b.redact.classifyDefaults({ patterns: ["ssn"] });
|
|
745
|
+
var dlp = b.redact.installOutboundDlp({ httpClient: http, mail: mail, webhook: wh, classifier: clean });
|
|
746
|
+
try {
|
|
747
|
+
// Each interceptor tolerates a missing argument (falls back to {}).
|
|
748
|
+
var r = await http.request();
|
|
749
|
+
check("installOutboundDlp: httpClient.request() with no args forwards clean",
|
|
750
|
+
r.ok === true && http.calls.length === 1);
|
|
751
|
+
var m = await mail.send();
|
|
752
|
+
check("installOutboundDlp: mail.send() with no args forwards clean",
|
|
753
|
+
m.queued === true && mail.calls.length === 1);
|
|
754
|
+
var w = await wh.send();
|
|
755
|
+
check("installOutboundDlp: webhook.send() with no args forwards clean",
|
|
756
|
+
w.ok === true && wh.calls.length === 1);
|
|
757
|
+
} finally {
|
|
758
|
+
dlp.uninstall();
|
|
759
|
+
b.redact._resetForTest();
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function testInstallOutboundDlpNoArgs() {
|
|
764
|
+
b.redact._resetForTest();
|
|
765
|
+
// A no-argument install wires nothing but returns a valid handle.
|
|
766
|
+
var dlp = b.redact.installOutboundDlp();
|
|
767
|
+
check("installOutboundDlp: no-argument install wires no primitives",
|
|
768
|
+
dlp.installed.httpClient === false && dlp.installed.mail === false && dlp.installed.webhook === false);
|
|
769
|
+
check("installOutboundDlp: no-argument install did not flip the installed flag",
|
|
770
|
+
b.redact.isOutboundDlpInstalled() === false);
|
|
771
|
+
dlp.uninstall();
|
|
772
|
+
b.redact._resetForTest();
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// ---------------------------------------------------------------------------
|
|
776
|
+
// installOutboundDlp — webhook interceptor
|
|
777
|
+
// ---------------------------------------------------------------------------
|
|
778
|
+
|
|
779
|
+
async function testInstallWebhookJsonRedactAndPassthrough() {
|
|
780
|
+
b.redact._resetForTest();
|
|
781
|
+
var wh = _fakeWebhook();
|
|
782
|
+
var dlp = b.redact.installOutboundDlp({
|
|
783
|
+
webhook: wh,
|
|
784
|
+
classifier: b.redact.classifyDefaults({ patterns: ["ssn", "pan"] }),
|
|
785
|
+
});
|
|
786
|
+
try {
|
|
787
|
+
// JSON-string body is parsed, scanned, re-serialized on redact.
|
|
788
|
+
await wh.send({ url: "https://h", body: JSON.stringify({ ssn: "123-45-6789" }), kid: "k1", headers: { x: "y" } });
|
|
789
|
+
var sent = wh.calls[0];
|
|
790
|
+
check("installOutboundDlp webhook: JSON body re-serialized with scrub",
|
|
791
|
+
sent.body === JSON.stringify({ ssn: "[REDACTED]" }));
|
|
792
|
+
check("installOutboundDlp webhook: kid preserved through rebuild", sent.kid === "k1");
|
|
793
|
+
|
|
794
|
+
// Non-JSON string body that's clean is forwarded unchanged.
|
|
795
|
+
await wh.send({ url: "https://h", body: "plain text, no secrets" });
|
|
796
|
+
check("installOutboundDlp webhook: clean non-JSON body forwarded",
|
|
797
|
+
wh.calls[1].body === "plain text, no secrets");
|
|
798
|
+
|
|
799
|
+
// Non-JSON string body that matches → redacted string (not re-serialized
|
|
800
|
+
// as JSON, since it never parsed as an object).
|
|
801
|
+
await wh.send({ url: "https://h", body: "ssn 123-45-6789" });
|
|
802
|
+
check("installOutboundDlp webhook: non-JSON redacted body stays a string",
|
|
803
|
+
wh.calls[2].body === "ssn [REDACTED]");
|
|
804
|
+
|
|
805
|
+
// refuse
|
|
806
|
+
var threw = null;
|
|
807
|
+
try { await wh.send({ url: "https://h", body: JSON.stringify({ card: "4111111111111111" }) }); }
|
|
808
|
+
catch (e) { threw = e; }
|
|
809
|
+
check("installOutboundDlp webhook: PAN refuses with DlpError",
|
|
810
|
+
threw instanceof b.redact.DlpError && threw.code === "redact-dlp/refused");
|
|
811
|
+
check("installOutboundDlp webhook: refused payload never sent", wh.calls.length === 3);
|
|
812
|
+
|
|
813
|
+
// Second install on the same signer no-ops.
|
|
814
|
+
var dlp2 = b.redact.installOutboundDlp({ webhook: wh });
|
|
815
|
+
check("installOutboundDlp webhook: idempotent per instance",
|
|
816
|
+
dlp2.installed.webhook === false);
|
|
817
|
+
} finally {
|
|
818
|
+
dlp.uninstall();
|
|
819
|
+
b.redact._resetForTest();
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// ---------------------------------------------------------------------------
|
|
824
|
+
// _wrapClassifier — fail-closed behavior
|
|
825
|
+
// ---------------------------------------------------------------------------
|
|
826
|
+
|
|
827
|
+
async function testInstallWebhookWithoutUrl() {
|
|
828
|
+
b.redact._resetForTest();
|
|
829
|
+
var wh = _fakeWebhook();
|
|
830
|
+
var dlp = b.redact.installOutboundDlp({
|
|
831
|
+
webhook: wh,
|
|
832
|
+
classifier: b.redact.classifyDefaults({ patterns: ["ssn", "pan"] }),
|
|
833
|
+
});
|
|
834
|
+
try {
|
|
835
|
+
// A webhook send that omits `url` still redacts (url falls back to null
|
|
836
|
+
// in the emitted audit metadata).
|
|
837
|
+
await wh.send({ body: JSON.stringify({ ssn: "123-45-6789" }) });
|
|
838
|
+
check("installOutboundDlp webhook: url-less redact still scrubs body",
|
|
839
|
+
wh.calls[0].body === JSON.stringify({ ssn: "[REDACTED]" }));
|
|
840
|
+
// A url-less refuse still rejects.
|
|
841
|
+
var threw = null;
|
|
842
|
+
try { await wh.send({ body: JSON.stringify({ card: "4111111111111111" }) }); }
|
|
843
|
+
catch (e) { threw = e; }
|
|
844
|
+
check("installOutboundDlp webhook: url-less refuse still rejects",
|
|
845
|
+
threw instanceof b.redact.DlpError);
|
|
846
|
+
} finally {
|
|
847
|
+
dlp.uninstall();
|
|
848
|
+
b.redact._resetForTest();
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
async function testClassifierFailClosedOnThrowAndBadVerdict() {
|
|
853
|
+
b.redact._resetForTest();
|
|
854
|
+
var http1 = _fakeHttpClient();
|
|
855
|
+
var dlp1 = b.redact.installOutboundDlp({
|
|
856
|
+
httpClient: http1,
|
|
857
|
+
classifier: function () { throw new Error("classifier boom"); },
|
|
858
|
+
});
|
|
859
|
+
try {
|
|
860
|
+
var threw = null;
|
|
861
|
+
try { await http1.request({ body: { x: 1 } }); }
|
|
862
|
+
catch (e) { threw = e; }
|
|
863
|
+
check("wrapClassifier: a throwing classifier fails closed (refuse)",
|
|
864
|
+
threw instanceof b.redact.DlpError && threw.code === "redact-dlp/refused");
|
|
865
|
+
check("wrapClassifier: throwing classifier never forwards to original",
|
|
866
|
+
http1.calls.length === 0);
|
|
867
|
+
} finally {
|
|
868
|
+
dlp1.uninstall();
|
|
869
|
+
b.redact._resetForTest();
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
var http2 = _fakeHttpClient();
|
|
873
|
+
var dlp2 = b.redact.installOutboundDlp({
|
|
874
|
+
httpClient: http2,
|
|
875
|
+
classifier: function () { return { nope: true }; }, // missing string verdict
|
|
876
|
+
});
|
|
877
|
+
try {
|
|
878
|
+
var threw2 = null;
|
|
879
|
+
try { await http2.request({ body: { x: 1 } }); }
|
|
880
|
+
catch (e) { threw2 = e; }
|
|
881
|
+
check("wrapClassifier: a malformed verdict fails closed (refuse)",
|
|
882
|
+
threw2 instanceof b.redact.DlpError && threw2.code === "redact-dlp/refused");
|
|
883
|
+
} finally {
|
|
884
|
+
dlp2.uninstall();
|
|
885
|
+
b.redact._resetForTest();
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// ---------------------------------------------------------------------------
|
|
890
|
+
// installOutboundDlp — input validation throws
|
|
891
|
+
// ---------------------------------------------------------------------------
|
|
892
|
+
|
|
893
|
+
function testInstallOutboundDlpValidationErrors() {
|
|
894
|
+
b.redact._resetForTest();
|
|
895
|
+
check("installOutboundDlp: non-function classifier throws bad-classifier",
|
|
896
|
+
(_grab(function () {
|
|
897
|
+
b.redact.installOutboundDlp({ httpClient: _fakeHttpClient(), classifier: 123 });
|
|
898
|
+
}) || {}).code === "redact-dlp/bad-classifier");
|
|
899
|
+
check("installOutboundDlp: httpClient without request() throws bad-target",
|
|
900
|
+
(_grab(function () { b.redact.installOutboundDlp({ httpClient: {} }); }) || {}).code
|
|
901
|
+
=== "redact-dlp/bad-target");
|
|
902
|
+
check("installOutboundDlp: mail without send() throws bad-target",
|
|
903
|
+
(_grab(function () { b.redact.installOutboundDlp({ mail: {} }); }) || {}).code
|
|
904
|
+
=== "redact-dlp/bad-target");
|
|
905
|
+
check("installOutboundDlp: webhook without send() throws bad-target",
|
|
906
|
+
(_grab(function () { b.redact.installOutboundDlp({ webhook: {} }); }) || {}).code
|
|
907
|
+
=== "redact-dlp/bad-target");
|
|
908
|
+
check("installOutboundDlp: non-function onScan hook throws bad-hook",
|
|
909
|
+
(_grab(function () {
|
|
910
|
+
b.redact.installOutboundDlp({ httpClient: _fakeHttpClient(), onScan: 123 });
|
|
911
|
+
}) || {}).code === "redact-dlp/bad-hook");
|
|
912
|
+
check("installOutboundDlp: unknown option key rejected",
|
|
913
|
+
/unknown option/.test((_grab(function () {
|
|
914
|
+
b.redact.installOutboundDlp({ bogus: 1 });
|
|
915
|
+
}) || {}).message || ""));
|
|
916
|
+
b.redact._resetForTest();
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// ---------------------------------------------------------------------------
|
|
920
|
+
// posture resolution + installForPosture
|
|
921
|
+
// ---------------------------------------------------------------------------
|
|
922
|
+
|
|
923
|
+
function testPostureResolutionAllPresets() {
|
|
924
|
+
b.redact._resetForTest();
|
|
925
|
+
var postures = ["pci-dss", "pci", "hipaa", "fapi2", "soc2", "gdpr"];
|
|
926
|
+
for (var i = 0; i < postures.length; i += 1) {
|
|
927
|
+
var http = _fakeHttpClient();
|
|
928
|
+
var dlp = b.redact.installOutboundDlp({ httpClient: http, posture: postures[i] });
|
|
929
|
+
check("installOutboundDlp: posture '" + postures[i] + "' resolves and installs",
|
|
930
|
+
dlp.installed.httpClient === true);
|
|
931
|
+
dlp.uninstall();
|
|
932
|
+
}
|
|
933
|
+
check("installOutboundDlp: unknown posture throws unknown-posture",
|
|
934
|
+
(_grab(function () {
|
|
935
|
+
b.redact.installOutboundDlp({ httpClient: _fakeHttpClient(), posture: "nonexistent" });
|
|
936
|
+
}) || {}).code === "redact-dlp/unknown-posture");
|
|
937
|
+
b.redact._resetForTest();
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
async function testInstallForPostureWiresPrimitive() {
|
|
941
|
+
b.redact._resetForTest();
|
|
942
|
+
var http = _fakeHttpClient();
|
|
943
|
+
var dlp = b.redact.installForPosture("hipaa", { httpClient: http });
|
|
944
|
+
try {
|
|
945
|
+
check("installForPosture: httpClient wired", dlp.installed.httpClient === true);
|
|
946
|
+
check("installForPosture: mail not supplied → not installed", dlp.installed.mail === false);
|
|
947
|
+
// A HIPAA-flagged PHI payload refuses through the posture classifier.
|
|
948
|
+
var threw = null;
|
|
949
|
+
try { await http.request({ body: { patient: "ssn 123-45-6789" } }); }
|
|
950
|
+
catch (e) { threw = e; }
|
|
951
|
+
check("installForPosture: HIPAA posture refuses PHI payload",
|
|
952
|
+
threw instanceof b.redact.DlpError);
|
|
953
|
+
} finally {
|
|
954
|
+
dlp.uninstall();
|
|
955
|
+
b.redact._resetForTest();
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
async function run() {
|
|
960
|
+
// registerValueDetector
|
|
961
|
+
testRegisterValueDetectorRedactsMatchingValue();
|
|
962
|
+
testRegisterValueDetectorFunctionReplacement();
|
|
963
|
+
testRegisterValueDetectorRunsAfterBuiltins();
|
|
964
|
+
testRegisterValueDetectorRejectsNonFunction();
|
|
965
|
+
testRegisterValueDetectorClearedOnReset();
|
|
966
|
+
testRegisterValueDetectorDefaultsToMarkerWhenReplacementOmitted();
|
|
967
|
+
|
|
968
|
+
// registerFieldRule
|
|
969
|
+
testRegisterFieldRuleRedactsRegisteredField();
|
|
970
|
+
testRegisterFieldRuleSubstringMatch();
|
|
971
|
+
testRegisterFieldRuleRejectsNonString();
|
|
972
|
+
testRegisterFieldRuleClearedOnReset();
|
|
973
|
+
|
|
974
|
+
// redact walk
|
|
975
|
+
testRedactPrimitivePassthroughs();
|
|
976
|
+
testRedactBinaryValuesAlwaysMarker();
|
|
977
|
+
testRedactArrayWalk();
|
|
978
|
+
testRedactSensitiveParentCollapsesComposite();
|
|
979
|
+
testRedactMaxDepthCap();
|
|
980
|
+
testRedactCustomMarker();
|
|
981
|
+
testRedactIgnoresInheritedProps();
|
|
982
|
+
testRedactNonStringParentKeyIsNotSensitive();
|
|
983
|
+
testRedactSensitiveTopLevelParentKeyCollapses();
|
|
984
|
+
|
|
985
|
+
// built-in value detectors
|
|
986
|
+
testRedactUrlBearerQueryReplacement();
|
|
987
|
+
testRedactConnectionStringBounds();
|
|
988
|
+
|
|
989
|
+
// CLASSIFIER_PATTERNS detectors
|
|
990
|
+
testClassifierPatternsPanAndCreditCard();
|
|
991
|
+
testClassifierPatternsIban();
|
|
992
|
+
testClassifierPatternsApiKeyShape();
|
|
993
|
+
testClassifierPatternsJwtAwsPemSsh();
|
|
994
|
+
testClassifierPatternsSsnEinPhi();
|
|
995
|
+
|
|
996
|
+
// classifyDefaults
|
|
997
|
+
testClassifyDefaultsValidationErrors();
|
|
998
|
+
testClassifyDefaultsDefaultsToAllPatterns();
|
|
999
|
+
testClassifyDefaultsExtraPattern();
|
|
1000
|
+
testClassifyObjectBodyRefuseRedactClean();
|
|
1001
|
+
testClassifyStringBufferAndScalarBodies();
|
|
1002
|
+
testClassifyWalksNestedArraysAndBinaryFields();
|
|
1003
|
+
testClassifyHeadersScanned();
|
|
1004
|
+
testClassifyWalkSkipsInheritedAndUnknownTypes();
|
|
1005
|
+
testClassifyAuditOnlyOverride();
|
|
1006
|
+
testClassifyCustomMarker();
|
|
1007
|
+
|
|
1008
|
+
// installOutboundDlp interceptors
|
|
1009
|
+
await testInstallHttpClientCleanRefuseRedact();
|
|
1010
|
+
await testInstallHttpClientIdempotentAndUninstallRestores();
|
|
1011
|
+
await testInstallMailRedactRefuseClean();
|
|
1012
|
+
await testInstallWebhookJsonRedactAndPassthrough();
|
|
1013
|
+
await testInstallWebhookWithoutUrl();
|
|
1014
|
+
await testInterceptorNoArgumentCalls();
|
|
1015
|
+
testInstallOutboundDlpNoArgs();
|
|
1016
|
+
await testClassifierFailClosedOnThrowAndBadVerdict();
|
|
1017
|
+
testInstallOutboundDlpValidationErrors();
|
|
1018
|
+
|
|
1019
|
+
// posture
|
|
1020
|
+
testPostureResolutionAllPresets();
|
|
1021
|
+
await testInstallForPostureWiresPrimitive();
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
module.exports = { run: run };
|
|
1025
|
+
|
|
1026
|
+
if (require.main === module) {
|
|
1027
|
+
run().then(
|
|
1028
|
+
function () { console.log("redact tests passed"); process.exit(0); },
|
|
1029
|
+
function (e) { console.error(e); process.exit(1); }
|
|
1030
|
+
);
|
|
1031
|
+
}
|