@blamejs/blamejs-shop 0.4.31 → 0.4.32
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 +392 -278
- package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
- package/lib/vendor/blamejs/.gitignore +6 -0
- package/lib/vendor/blamejs/CHANGELOG.md +26 -0
- package/lib/vendor/blamejs/MIGRATING.md +43 -0
- package/lib/vendor/blamejs/README.md +8 -6
- package/lib/vendor/blamejs/SECURITY.md +19 -3
- package/lib/vendor/blamejs/api-snapshot.json +2190 -664
- package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
- package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
- package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
- package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
- package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
- package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
- package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
- package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
- package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
- package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
- package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
- package/lib/vendor/blamejs/index.js +4 -0
- package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
- package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
- package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
- package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
- package/lib/vendor/blamejs/lib/api-key.js +158 -77
- package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
- package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
- package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
- package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
- package/lib/vendor/blamejs/lib/audit.js +259 -123
- package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
- package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
- package/lib/vendor/blamejs/lib/backup/index.js +45 -10
- package/lib/vendor/blamejs/lib/break-glass.js +355 -147
- package/lib/vendor/blamejs/lib/cache.js +174 -105
- package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
- package/lib/vendor/blamejs/lib/cli.js +19 -14
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
- package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
- package/lib/vendor/blamejs/lib/cluster.js +119 -71
- package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
- package/lib/vendor/blamejs/lib/compliance.js +206 -4
- package/lib/vendor/blamejs/lib/consent.js +82 -29
- package/lib/vendor/blamejs/lib/constants.js +27 -11
- package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
- package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
- package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
- package/lib/vendor/blamejs/lib/db-query.js +882 -260
- package/lib/vendor/blamejs/lib/db-schema.js +228 -44
- package/lib/vendor/blamejs/lib/db.js +249 -99
- package/lib/vendor/blamejs/lib/dsr.js +385 -55
- package/lib/vendor/blamejs/lib/error-page.js +14 -1
- package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
- package/lib/vendor/blamejs/lib/external-db.js +549 -34
- package/lib/vendor/blamejs/lib/file-upload.js +52 -7
- package/lib/vendor/blamejs/lib/framework-error.js +20 -1
- package/lib/vendor/blamejs/lib/framework-files.js +73 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
- package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
- package/lib/vendor/blamejs/lib/guard-all.js +1 -0
- package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
- package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
- package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
- package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
- package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
- package/lib/vendor/blamejs/lib/guard-email.js +47 -69
- package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
- package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
- package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
- package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
- package/lib/vendor/blamejs/lib/guard-html.js +53 -108
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
- package/lib/vendor/blamejs/lib/guard-image.js +46 -103
- package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
- package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
- package/lib/vendor/blamejs/lib/guard-json.js +38 -108
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
- package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
- package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
- package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
- package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
- package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
- package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
- package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
- package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
- package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
- package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
- package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
- package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
- package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
- package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
- package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
- package/lib/vendor/blamejs/lib/guard-template.js +35 -172
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-time.js +32 -154
- package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
- package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
- package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
- package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
- package/lib/vendor/blamejs/lib/http-client.js +37 -9
- package/lib/vendor/blamejs/lib/inbox.js +120 -107
- package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
- package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
- package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
- package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
- package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
- package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
- package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
- package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
- package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
- package/lib/vendor/blamejs/lib/mail-store.js +293 -154
- package/lib/vendor/blamejs/lib/mail.js +8 -4
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
- package/lib/vendor/blamejs/lib/migrations.js +108 -66
- package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
- package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
- package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
- package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
- package/lib/vendor/blamejs/lib/observability.js +124 -0
- package/lib/vendor/blamejs/lib/otel-export.js +12 -3
- package/lib/vendor/blamejs/lib/outbox.js +184 -83
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
- package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
- package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
- package/lib/vendor/blamejs/lib/queue-local.js +225 -140
- package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
- package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
- package/lib/vendor/blamejs/lib/queue.js +7 -0
- package/lib/vendor/blamejs/lib/redact.js +68 -11
- package/lib/vendor/blamejs/lib/redis-client.js +160 -31
- package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
- package/lib/vendor/blamejs/lib/retention.js +101 -40
- package/lib/vendor/blamejs/lib/router.js +212 -5
- package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
- package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
- package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
- package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
- package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
- package/lib/vendor/blamejs/lib/safe-url.js +170 -3
- package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
- package/lib/vendor/blamejs/lib/scheduler.js +35 -12
- package/lib/vendor/blamejs/lib/seeders.js +122 -74
- package/lib/vendor/blamejs/lib/session-stores.js +42 -14
- package/lib/vendor/blamejs/lib/session.js +175 -77
- package/lib/vendor/blamejs/lib/sql.js +3842 -0
- package/lib/vendor/blamejs/lib/sse.js +26 -0
- package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
- package/lib/vendor/blamejs/lib/static.js +177 -34
- package/lib/vendor/blamejs/lib/subject.js +96 -49
- package/lib/vendor/blamejs/lib/vault/index.js +3 -2
- package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
- package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
- package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
- package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
- package/lib/vendor/blamejs/lib/websocket.js +35 -5
- package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
- package/lib/vendor/blamejs/package.json +2 -2
- package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
- package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
- package/lib/vendor/blamejs/scripts/check-services.js +21 -0
- package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
- package/lib/vendor/blamejs/scripts/release.js +398 -38
- package/lib/vendor/blamejs/test/00-primitives.js +117 -0
- package/lib/vendor/blamejs/test/10-state.js +140 -14
- package/lib/vendor/blamejs/test/20-db.js +65 -2
- package/lib/vendor/blamejs/test/helpers/db.js +9 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
- package/lib/vendor/blamejs/test/helpers/services.js +21 -0
- package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
- package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
- package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
- package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
- package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
- package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
- package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
- package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
- package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
- package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
- package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
- package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
- package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
- package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
- package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
- package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
- package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
- package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
- package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
- package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
- package/lib/vendor/blamejs/test/smoke.js +79 -21
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
- package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
- package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
- package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
- package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
- package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
- package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
- package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
- package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
- package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
- package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
- package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
- package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
- package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.safeUrl.canonicalize — URL/host canonicalizer for safe comparison and
|
|
4
|
+
* SSRF-allowlist defense.
|
|
5
|
+
*
|
|
6
|
+
* Every obfuscated spelling of one destination must reduce to one string so a
|
|
7
|
+
* host allowlist / dedup key / SSRF pre-check compares equal. The adversarial
|
|
8
|
+
* cases below are the exact shapes an attacker reaches for to slip a naive
|
|
9
|
+
* `===` allowlist: IPv4 in octal / hex / decimal-int / shorthand, IPv4-mapped
|
|
10
|
+
* and zero-compressed IPv6, IDN homographs, trailing-dot and default-port and
|
|
11
|
+
* case variations, and over-encoded path bytes.
|
|
12
|
+
*
|
|
13
|
+
* Source-discipline: NO attack characters are typed as literals. The one
|
|
14
|
+
* mixed-script case is expressed as its already-encoded punycode A-label
|
|
15
|
+
* (`xn--ggle-55da.com`, same fixture the homograph suite uses) so this file
|
|
16
|
+
* stays pure ASCII.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var b = require("../..");
|
|
20
|
+
var check = require("../helpers/check").check;
|
|
21
|
+
|
|
22
|
+
// ---- Equivalence classes: obfuscated loopback collapses to one string ----
|
|
23
|
+
|
|
24
|
+
function testIpv4LoopbackEquivalenceClass() {
|
|
25
|
+
// Decimal-dotted, octal, hex-word, decimal-int, and shorthand spellings of
|
|
26
|
+
// 127.0.0.1 — in the OLD world (raw string compare, no canonicalizer) every
|
|
27
|
+
// pair below compared UNEQUAL, so an allowlist of "127.0.0.1" let them all
|
|
28
|
+
// through. canonicalize collapses them to one form.
|
|
29
|
+
var forms = [
|
|
30
|
+
"http://127.0.0.1/",
|
|
31
|
+
"http://0177.0.0.1/", // octal first octet
|
|
32
|
+
"http://0x7f.1/", // hex octet + shorthand
|
|
33
|
+
"http://0x7f000001/", // single hex dword
|
|
34
|
+
"http://2130706433/", // single decimal dword
|
|
35
|
+
"http://127.1/", // a.d shorthand
|
|
36
|
+
];
|
|
37
|
+
var expected = "http://127.0.0.1/";
|
|
38
|
+
for (var i = 0; i < forms.length; i += 1) {
|
|
39
|
+
var got = b.safeUrl.canonicalize(forms[i]);
|
|
40
|
+
check("ipv4 loopback form '" + forms[i] + "' canonicalizes to " + expected,
|
|
41
|
+
got === expected);
|
|
42
|
+
// The pairwise equality the allowlist actually relies on.
|
|
43
|
+
check("ipv4 loopback '" + forms[i] + "' === canonical '127.0.0.1' form",
|
|
44
|
+
b.safeUrl.canonicalize(forms[i]) === b.safeUrl.canonicalize(forms[0]));
|
|
45
|
+
// Prove the test is meaningful: the RAW strings were NOT equal.
|
|
46
|
+
if (forms[i] !== forms[0]) {
|
|
47
|
+
check("raw '" + forms[i] + "' !== raw canonical (old-world bypass shape)",
|
|
48
|
+
forms[i] !== forms[0]);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function testIpv6MappedEquivalenceClass() {
|
|
54
|
+
// IPv4-mapped IPv6 in dotted, hex, and fully-expanded spellings — all the
|
|
55
|
+
// same 16 bytes, all collapse to one bracketed RFC 5952 form.
|
|
56
|
+
var forms = [
|
|
57
|
+
"http://[::ffff:127.0.0.1]/",
|
|
58
|
+
"http://[::ffff:7f00:1]/",
|
|
59
|
+
"http://[0:0:0:0:0:ffff:7f00:1]/",
|
|
60
|
+
"http://[0:0:0:0:0:FFFF:7F00:1]/", // mixed-case hex
|
|
61
|
+
];
|
|
62
|
+
var first = b.safeUrl.canonicalize(forms[0]);
|
|
63
|
+
check("ipv4-mapped IPv6 canonical is bracketed RFC 5952",
|
|
64
|
+
first === "http://[::ffff:7f00:1]/");
|
|
65
|
+
for (var i = 1; i < forms.length; i += 1) {
|
|
66
|
+
check("ipv4-mapped form '" + forms[i] + "' === first canonical",
|
|
67
|
+
b.safeUrl.canonicalize(forms[i]) === first);
|
|
68
|
+
check("raw '" + forms[i] + "' !== raw '" + forms[0] + "' (old-world unequal)",
|
|
69
|
+
forms[i] !== forms[0]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function testIpv6ZeroCompressionEquivalenceClass() {
|
|
74
|
+
// ::1 in compressed, fully-expanded, and zero-padded spellings.
|
|
75
|
+
var forms = [
|
|
76
|
+
"http://[::1]/",
|
|
77
|
+
"http://[0:0:0:0:0:0:0:1]/",
|
|
78
|
+
"http://[0000:0000:0000:0000:0000:0000:0000:0001]/",
|
|
79
|
+
];
|
|
80
|
+
var first = b.safeUrl.canonicalize(forms[0]);
|
|
81
|
+
check("IPv6 loopback canonical is [::1]", first === "http://[::1]/");
|
|
82
|
+
for (var i = 1; i < forms.length; i += 1) {
|
|
83
|
+
check("IPv6 loopback form '" + forms[i] + "' === [::1] canonical",
|
|
84
|
+
b.safeUrl.canonicalize(forms[i]) === first);
|
|
85
|
+
check("raw '" + forms[i] + "' !== raw '[::1]' (old-world unequal)",
|
|
86
|
+
forms[i] !== forms[0]);
|
|
87
|
+
}
|
|
88
|
+
// A documentation address with an INTERIOR zero run picks the longest run
|
|
89
|
+
// (RFC 5952 §4.2.3) and stays lower-hex.
|
|
90
|
+
check("interior zero-run compresses correctly",
|
|
91
|
+
b.safeUrl.canonicalize("https://[2001:0DB8:0:0:0:0:0:1]/") ===
|
|
92
|
+
"https://[2001:db8::1]/");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ---- IP-literal cross-check against the SSRF classifier's byte parser ----
|
|
96
|
+
|
|
97
|
+
function testIpCanonicalAgreesWithClassifier() {
|
|
98
|
+
// The canonical host string and the SSRF verdict are derived from the SAME
|
|
99
|
+
// bytes — so the canonical loopback string classifies as loopback and the
|
|
100
|
+
// canonical mapped form re-classifies its embedded v4 as loopback too.
|
|
101
|
+
check("canonical 127.0.0.1 classifies loopback",
|
|
102
|
+
b.ssrfGuard.classify("127.0.0.1") === "loopback");
|
|
103
|
+
// canonicalizeHost is the IP-byte + case layer: it byte-canonicalizes
|
|
104
|
+
// net.isIP-recognized literals (the numeric-base decode of 0x.../octal/dword
|
|
105
|
+
// is the WHATWG-URL-parser layer that safeUrl.canonicalize runs first).
|
|
106
|
+
check("ssrfGuard.canonicalizeHost passes an already-canonical dotted-quad",
|
|
107
|
+
b.ssrfGuard.canonicalizeHost("127.0.0.1") === "127.0.0.1");
|
|
108
|
+
check("ssrfGuard.canonicalizeHost collapses expanded v6 to ::1",
|
|
109
|
+
b.ssrfGuard.canonicalizeHost("[0:0:0:0:0:0:0:1]") === "::1");
|
|
110
|
+
check("ssrfGuard.canonicalizeHost compresses + lower-hexes a mixed-case v6",
|
|
111
|
+
b.ssrfGuard.canonicalizeHost("2001:0DB8:0:0:0:0:0:1") === "2001:db8::1");
|
|
112
|
+
check("ssrfGuard.canonicalizeHost lowercases + strips trailing dot",
|
|
113
|
+
b.ssrfGuard.canonicalizeHost("Example.COM.") === "example.com");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ---- IDN: A-label emission + homograph refusal ----
|
|
117
|
+
|
|
118
|
+
function testIdnEmittedAsPunycode() {
|
|
119
|
+
// A pure-Cyrillic+Latin-tld host parses (single script per label) and is
|
|
120
|
+
// emitted as its xn-- A-label, never the Unicode form.
|
|
121
|
+
var got = b.safeUrl.canonicalize("https://xn--80akhbyknj4f.com/");
|
|
122
|
+
check("IDN host emitted as xn-- A-label",
|
|
123
|
+
got === "https://xn--80akhbyknj4f.com/");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function testConfusableHostThrows() {
|
|
127
|
+
// xn--ggle-55da.com decodes to a label mixing Cyrillic + Latin (the
|
|
128
|
+
// gооgle.com homograph). canonicalize must THROW, never silently pass it.
|
|
129
|
+
var threw = false;
|
|
130
|
+
var code = null;
|
|
131
|
+
try { b.safeUrl.canonicalize("https://xn--ggle-55da.com/"); }
|
|
132
|
+
catch (e) { threw = true; code = e.code; }
|
|
133
|
+
check("confusable / mixed-script host throws", threw === true);
|
|
134
|
+
check("confusable host throw carries safe-url/idn-homograph",
|
|
135
|
+
code === "safe-url/idn-homograph");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function testMixedScriptOptInCanonicalizes() {
|
|
139
|
+
// Operators with a legitimate non-Latin host opt in; the A-label is then
|
|
140
|
+
// canonicalized rather than refused.
|
|
141
|
+
var got = b.safeUrl.canonicalize("https://xn--ggle-55da.com/", {
|
|
142
|
+
allowMixedScript: true,
|
|
143
|
+
});
|
|
144
|
+
check("allowMixedScript:true canonicalizes the mixed-script host",
|
|
145
|
+
got === "https://xn--ggle-55da.com/");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---- scheme / host case, default port, trailing dot, path ----
|
|
149
|
+
|
|
150
|
+
function testEndToEndNormalization() {
|
|
151
|
+
// The headline end-to-end case from the spec: case-folded host, stripped
|
|
152
|
+
// default port, resolved `..`, all in one.
|
|
153
|
+
check("https://Example.COM:443/a/../b -> https://example.com/b",
|
|
154
|
+
b.safeUrl.canonicalize("https://Example.COM:443/a/../b") ===
|
|
155
|
+
"https://example.com/b");
|
|
156
|
+
check("http default :80 stripped + host lowercased",
|
|
157
|
+
b.safeUrl.canonicalize("http://Example.COM:80/") ===
|
|
158
|
+
"http://example.com/");
|
|
159
|
+
check("trailing-dot host removed",
|
|
160
|
+
b.safeUrl.canonicalize("https://example.com./path") ===
|
|
161
|
+
"https://example.com/path");
|
|
162
|
+
check("non-default port preserved",
|
|
163
|
+
b.safeUrl.canonicalize("https://example.com:8443/") ===
|
|
164
|
+
"https://example.com:8443/");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function testPathPercentNormalization() {
|
|
168
|
+
// RFC 3986 §6.2.2: escapes of unreserved chars are decoded, hex digits
|
|
169
|
+
// uppercased — in the PATH only.
|
|
170
|
+
check("path %7E decodes to ~ and %2D to -",
|
|
171
|
+
b.safeUrl.canonicalize("https://example.com/%7Euser/a%2Db") ===
|
|
172
|
+
"https://example.com/~user/a-b");
|
|
173
|
+
// A reserved char (%2F = '/') is NOT decoded (would change structure); its
|
|
174
|
+
// hex is uppercased.
|
|
175
|
+
check("reserved %2f path escape uppercased, NOT decoded",
|
|
176
|
+
b.safeUrl.canonicalize("https://example.com/a%2fb") ===
|
|
177
|
+
"https://example.com/a%2Fb");
|
|
178
|
+
// Query semantics are conservative: a %2D in the QUERY is left verbatim
|
|
179
|
+
// (lowercase, undecoded) so value semantics are never altered.
|
|
180
|
+
check("query percent-escape left byte-for-byte",
|
|
181
|
+
b.safeUrl.canonicalize("https://example.com/?x=%2d") ===
|
|
182
|
+
"https://example.com/?x=%2d");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ---- throw paths: disallowed scheme + unparseable + scheme allowlist ----
|
|
186
|
+
|
|
187
|
+
function testDisallowedSchemeThrows() {
|
|
188
|
+
var threw = false;
|
|
189
|
+
var code = null;
|
|
190
|
+
try { b.safeUrl.canonicalize("ftp://example.com/file"); }
|
|
191
|
+
catch (e) { threw = true; code = e.code; }
|
|
192
|
+
check("ftp scheme throws", threw === true);
|
|
193
|
+
check("disallowed-scheme code is safe-url/protocol-disallowed",
|
|
194
|
+
code === "safe-url/protocol-disallowed");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function testUnparseableThrows() {
|
|
198
|
+
var threw = false;
|
|
199
|
+
var code = null;
|
|
200
|
+
try { b.safeUrl.canonicalize("this is not a url"); }
|
|
201
|
+
catch (e) { threw = true; code = e.code; }
|
|
202
|
+
check("unparseable input throws", threw === true);
|
|
203
|
+
check("unparseable code is safe-url/malformed", code === "safe-url/malformed");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function testMissingInputThrows() {
|
|
207
|
+
var threw = false;
|
|
208
|
+
var code = null;
|
|
209
|
+
try { b.safeUrl.canonicalize(""); }
|
|
210
|
+
catch (e) { threw = true; code = e.code; }
|
|
211
|
+
check("empty input throws safe-url/missing", threw === true && code === "safe-url/missing");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function testCustomSchemeAllowlist() {
|
|
215
|
+
// Narrow the allowlist: https-only refuses an http URL.
|
|
216
|
+
var threw = false;
|
|
217
|
+
var code = null;
|
|
218
|
+
try {
|
|
219
|
+
b.safeUrl.canonicalize("http://example.com/", {
|
|
220
|
+
allowedSchemes: b.safeUrl.ALLOW_HTTP_TLS,
|
|
221
|
+
});
|
|
222
|
+
} catch (e) { threw = true; code = e.code; }
|
|
223
|
+
check("custom allowedSchemes (https-only) refuses http",
|
|
224
|
+
threw === true && code === "safe-url/protocol-disallowed");
|
|
225
|
+
// ws canonicalizes under the default ALLOW_ANY.
|
|
226
|
+
check("ws scheme canonicalizes under default allowlist",
|
|
227
|
+
b.safeUrl.canonicalize("ws://Example.COM:80/s") === "ws://example.com/s");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function testIdempotence() {
|
|
231
|
+
// canonicalize(canonicalize(x)) === canonicalize(x) for the whole adversarial
|
|
232
|
+
// set — a canonical form must be a fixed point.
|
|
233
|
+
var inputs = [
|
|
234
|
+
"http://0177.0.0.1/",
|
|
235
|
+
"http://[0:0:0:0:0:ffff:7f00:1]/",
|
|
236
|
+
"https://Example.COM:443/a/../b",
|
|
237
|
+
"https://example.com/%7Euser",
|
|
238
|
+
"https://xn--80akhbyknj4f.com/",
|
|
239
|
+
];
|
|
240
|
+
for (var i = 0; i < inputs.length; i += 1) {
|
|
241
|
+
var once = b.safeUrl.canonicalize(inputs[i]);
|
|
242
|
+
var twice = b.safeUrl.canonicalize(once);
|
|
243
|
+
check("canonicalize is idempotent for '" + inputs[i] + "'", once === twice);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function testUncanonicalizableCodeIsRegistered() {
|
|
248
|
+
// The post-parse fallback code is reachable as a documented SafeUrlError
|
|
249
|
+
// code — constructing it directly proves the class accepts it.
|
|
250
|
+
var e = new b.safeUrl.SafeUrlError("safe-url/uncanonicalizable", "x");
|
|
251
|
+
check("safe-url/uncanonicalizable is a valid SafeUrlError code",
|
|
252
|
+
e.code === "safe-url/uncanonicalizable" && e.isSafeUrlError === true);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ---- Credentials are never carried into the canonical form ----
|
|
256
|
+
|
|
257
|
+
function testUserinfoDroppedFromCanonicalForm() {
|
|
258
|
+
// The canonical string is built to be compared, used as a dedup / cache key,
|
|
259
|
+
// or logged — it must never carry user:pass credentials, and the creds are
|
|
260
|
+
// not part of the target identity for an allowlist / SSRF decision. parse()
|
|
261
|
+
// refuses userinfo by default; even opted-in, the canonical output omits it.
|
|
262
|
+
var user = "alice";
|
|
263
|
+
var token = "s3cr" + "et-pw-9f3a"; // split so no secret-shaped literal sits in source
|
|
264
|
+
var withCreds = "https://" + user + ":" + token + "@host.example.com/p";
|
|
265
|
+
|
|
266
|
+
var deniedCode = null;
|
|
267
|
+
try { b.safeUrl.canonicalize(withCreds); }
|
|
268
|
+
catch (e) { deniedCode = e && e.code; }
|
|
269
|
+
check("canonicalize refuses userinfo by default",
|
|
270
|
+
deniedCode === "safe-url/userinfo-disallowed");
|
|
271
|
+
|
|
272
|
+
var canon = b.safeUrl.canonicalize(withCreds, { allowUserinfo: true });
|
|
273
|
+
check("canonicalize drops the userinfo delimiter from the canonical form",
|
|
274
|
+
canon.indexOf("@") === -1);
|
|
275
|
+
check("canonicalize does not carry the password into the canonical form",
|
|
276
|
+
canon.indexOf(token) === -1);
|
|
277
|
+
check("canonicalize does not carry the username into the canonical form",
|
|
278
|
+
canon.indexOf("//" + user) === -1);
|
|
279
|
+
check("the credential-stripped canonical form is the bare target",
|
|
280
|
+
canon === "https://host.example.com/p");
|
|
281
|
+
check("URLs differing only in credentials canonicalize equal",
|
|
282
|
+
b.safeUrl.canonicalize("https://x:y@host.example.com/p", { allowUserinfo: true }) === canon);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function run() {
|
|
286
|
+
testUserinfoDroppedFromCanonicalForm();
|
|
287
|
+
testIpv4LoopbackEquivalenceClass();
|
|
288
|
+
testIpv6MappedEquivalenceClass();
|
|
289
|
+
testIpv6ZeroCompressionEquivalenceClass();
|
|
290
|
+
testIpCanonicalAgreesWithClassifier();
|
|
291
|
+
testIdnEmittedAsPunycode();
|
|
292
|
+
testConfusableHostThrows();
|
|
293
|
+
testMixedScriptOptInCanonicalizes();
|
|
294
|
+
testEndToEndNormalization();
|
|
295
|
+
testPathPercentNormalization();
|
|
296
|
+
testDisallowedSchemeThrows();
|
|
297
|
+
testUnparseableThrows();
|
|
298
|
+
testMissingInputThrows();
|
|
299
|
+
testCustomSchemeAllowlist();
|
|
300
|
+
testIdempotence();
|
|
301
|
+
testUncanonicalizableCodeIsRegistered();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
module.exports = { run: run };
|
|
305
|
+
|
|
306
|
+
if (require.main === module) {
|
|
307
|
+
run().then(function () { console.log("OK"); })
|
|
308
|
+
.catch(function (e) { console.error(e.stack || e); process.exit(1); });
|
|
309
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.parsers.xml.parse — security-focused XML parser.
|
|
4
|
+
*
|
|
5
|
+
* Covers the structural defaults (XXE / DOCTYPE / billion-laughs / depth
|
|
6
|
+
* + element + attribute caps) and the prototype-pollution posture: the
|
|
7
|
+
* result tree and every nested object it contains carry a null prototype,
|
|
8
|
+
* and element / attribute names equal to __proto__ / constructor /
|
|
9
|
+
* prototype are rejected with xml/forbidden-name (CWE-1321 / OWASP
|
|
10
|
+
* prototype-pollution).
|
|
11
|
+
*
|
|
12
|
+
* Run standalone: `node test/layer-0-primitives/safe-xml.test.js`
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
var helpers = require("../helpers");
|
|
16
|
+
var b = helpers.b;
|
|
17
|
+
var check = helpers.check;
|
|
18
|
+
|
|
19
|
+
var xml = b.parsers.xml;
|
|
20
|
+
|
|
21
|
+
// ---- Baseline parse + shape ----
|
|
22
|
+
|
|
23
|
+
function testParsesAttributedElement() {
|
|
24
|
+
var result = xml.parse('<root id="x"><child>text</child></root>');
|
|
25
|
+
check("xml.parse returns the root key",
|
|
26
|
+
result && result.root && result.root["@attrs"] && result.root["@attrs"].id === "x");
|
|
27
|
+
check("xml.parse groups child element",
|
|
28
|
+
result.root.child === "text");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function testRejectsDoctype() {
|
|
32
|
+
var threw = null;
|
|
33
|
+
try { xml.parse('<!DOCTYPE x SYSTEM "file:///etc/passwd"><root/>'); }
|
|
34
|
+
catch (e) { threw = e; }
|
|
35
|
+
check("xml.parse rejects DOCTYPE (XXE defense)",
|
|
36
|
+
threw && threw.code === "xml/doctype");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function testRejectsCustomEntity() {
|
|
40
|
+
var threw = null;
|
|
41
|
+
try { xml.parse("<root>&xxe;</root>"); }
|
|
42
|
+
catch (e) { threw = e; }
|
|
43
|
+
check("xml.parse rejects custom entity (XXE defense)",
|
|
44
|
+
threw && threw.code === "xml/external-entity");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---- Prototype-pollution posture (CWE-1321) ----
|
|
48
|
+
|
|
49
|
+
function testResultTreeHasNullPrototype() {
|
|
50
|
+
// A document that exercises every keyed accumulator: attributes (attrs),
|
|
51
|
+
// grouped child elements (grouped / obj), and the element-name wrapper
|
|
52
|
+
// (out). Each must carry a null prototype so a consumer reading an
|
|
53
|
+
// absent key sees undefined, never an inherited Object member.
|
|
54
|
+
var result = xml.parse(
|
|
55
|
+
'<root attr="v"><a>1</a><a>2</a><b><c>deep</c></b></root>'
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
check("result wrapper has null prototype",
|
|
59
|
+
Object.getPrototypeOf(result) === null);
|
|
60
|
+
check("element object has null prototype",
|
|
61
|
+
Object.getPrototypeOf(result.root) === null);
|
|
62
|
+
check("@attrs object has null prototype",
|
|
63
|
+
Object.getPrototypeOf(result.root["@attrs"]) === null);
|
|
64
|
+
check("nested element object has null prototype",
|
|
65
|
+
Object.getPrototypeOf(result.root.b) === null);
|
|
66
|
+
check("repeated child became an array",
|
|
67
|
+
Array.isArray(result.root.a) && result.root.a.length === 2);
|
|
68
|
+
|
|
69
|
+
// No inherited Object.prototype members leak through any accumulator —
|
|
70
|
+
// a plain {} would surface a function here.
|
|
71
|
+
check("no inherited toString on result",
|
|
72
|
+
result.toString === undefined);
|
|
73
|
+
check("no inherited hasOwnProperty on element object",
|
|
74
|
+
result.root.hasOwnProperty === undefined);
|
|
75
|
+
check("no inherited constructor on @attrs object",
|
|
76
|
+
result.root["@attrs"].constructor === undefined);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function testRejectsProtoAttributeName() {
|
|
80
|
+
var threw = null;
|
|
81
|
+
try { xml.parse('<root __proto__="polluted"/>'); }
|
|
82
|
+
catch (e) { threw = e; }
|
|
83
|
+
check("xml.parse rejects __proto__ attribute name",
|
|
84
|
+
threw && threw.code === "xml/forbidden-name");
|
|
85
|
+
// The global Object.prototype was not mutated either way.
|
|
86
|
+
check("Object.prototype not polluted by __proto__ attribute",
|
|
87
|
+
({}).polluted === undefined);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function testRejectsProtoElementName() {
|
|
91
|
+
var threw = null;
|
|
92
|
+
try { xml.parse("<__proto__><x>y</x></__proto__>"); }
|
|
93
|
+
catch (e) { threw = e; }
|
|
94
|
+
check("xml.parse rejects __proto__ element name",
|
|
95
|
+
threw && threw.code === "xml/forbidden-name");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function testRejectsConstructorElementName() {
|
|
99
|
+
var threw = null;
|
|
100
|
+
try { xml.parse("<root><constructor>x</constructor></root>"); }
|
|
101
|
+
catch (e) { threw = e; }
|
|
102
|
+
check("xml.parse rejects constructor child element name",
|
|
103
|
+
threw && threw.code === "xml/forbidden-name");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function testRejectsPrototypeElementName() {
|
|
107
|
+
var threw = null;
|
|
108
|
+
try { xml.parse("<prototype/>"); }
|
|
109
|
+
catch (e) { threw = e; }
|
|
110
|
+
check("xml.parse rejects prototype element name",
|
|
111
|
+
threw && threw.code === "xml/forbidden-name");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function testConstructorAttributeNoFalseDuplicate() {
|
|
115
|
+
// With a plain-object accumulator, attrs["constructor"] would read the
|
|
116
|
+
// inherited Object constructor (not undefined) and a single occurrence
|
|
117
|
+
// would falsely trip the duplicate-attribute guard. The forbidden-name
|
|
118
|
+
// rejection now fires first; assert the code is forbidden-name, not
|
|
119
|
+
// duplicate-attr, so the diagnostic points at the real cause.
|
|
120
|
+
var threw = null;
|
|
121
|
+
try { xml.parse('<root constructor="once"/>'); }
|
|
122
|
+
catch (e) { threw = e; }
|
|
123
|
+
check("constructor attribute → forbidden-name (not false duplicate-attr)",
|
|
124
|
+
threw && threw.code === "xml/forbidden-name");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
(function run() {
|
|
128
|
+
try {
|
|
129
|
+
testParsesAttributedElement();
|
|
130
|
+
testRejectsDoctype();
|
|
131
|
+
testRejectsCustomEntity();
|
|
132
|
+
testResultTreeHasNullPrototype();
|
|
133
|
+
testRejectsProtoAttributeName();
|
|
134
|
+
testRejectsProtoElementName();
|
|
135
|
+
testRejectsConstructorElementName();
|
|
136
|
+
testRejectsPrototypeElementName();
|
|
137
|
+
testConstructorAttributeNoFalseDuplicate();
|
|
138
|
+
} catch (e) {
|
|
139
|
+
console.error(e);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
console.log("OK — safe-xml tests");
|
|
143
|
+
})();
|