@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
|
@@ -5,12 +5,26 @@
|
|
|
5
5
|
* coverage lives in queue-redis.test.js (which skips cleanly when
|
|
6
6
|
* BLAMEJS_TEST_REDIS_URL is not set).
|
|
7
7
|
*/
|
|
8
|
+
var net = require("node:net");
|
|
8
9
|
var helpers = require("../helpers");
|
|
9
10
|
var check = helpers.check;
|
|
10
11
|
var redis = require("../../lib/redis-client");
|
|
11
12
|
|
|
12
13
|
function _bytes(s) { return Buffer.from(s, "utf8"); }
|
|
13
14
|
|
|
15
|
+
function _listen(onConn) {
|
|
16
|
+
return new Promise(function (resolve) {
|
|
17
|
+
var server = net.createServer(function (sock) {
|
|
18
|
+
sock.on("error", function () { /* absorb server-side socket errors */ });
|
|
19
|
+
if (onConn) onConn(sock);
|
|
20
|
+
});
|
|
21
|
+
server.listen(0, "127.0.0.1", function () {
|
|
22
|
+
resolve({ port: server.address().port, server: server,
|
|
23
|
+
close: function () { return new Promise(function (r) { server.close(r); }); } });
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
async function run() {
|
|
15
29
|
// ---- _parseRedisUrl ----
|
|
16
30
|
var u1 = redis._parseRedisUrl("redis://localhost:6379/0");
|
|
@@ -192,6 +206,105 @@ async function run() {
|
|
|
192
206
|
try { await client.connect(); } catch (_e) { /* closing guard should prevent any connect attempt */ }
|
|
193
207
|
check("redis: connect() after close is a no-op (closing guard — no socket opened)",
|
|
194
208
|
client._state().connected === false);
|
|
209
|
+
|
|
210
|
+
// ---- connect-failure does not wedge subsequent callers ----
|
|
211
|
+
// A connect that errors before the connection is fully ready (the socket
|
|
212
|
+
// drops mid-AUTH) must (a) reject the connect promise, (b) clear the
|
|
213
|
+
// shared connect promise so the next connect() starts fresh, and (c)
|
|
214
|
+
// NOT leave connected=true on a torn-down socket. A command issued after
|
|
215
|
+
// the give-up must settle (reject) rather than wedge in the backlog
|
|
216
|
+
// forever.
|
|
217
|
+
{
|
|
218
|
+
var dropMidAuth = await _listen(function (sock) {
|
|
219
|
+
sock.once("data", function () { sock.destroy(); }); // got AUTH → drop
|
|
220
|
+
});
|
|
221
|
+
var c2 = redis.create({
|
|
222
|
+
url: "redis://127.0.0.1:" + dropMidAuth.port + "/0",
|
|
223
|
+
password: "secret", connectTimeoutMs: 1000, commandTimeoutMs: 500,
|
|
224
|
+
maxReconnectAttempts: 0, // give up immediately on disconnect
|
|
225
|
+
});
|
|
226
|
+
var connErr = null;
|
|
227
|
+
try { await c2.connect(); } catch (e) { connErr = e; }
|
|
228
|
+
check("redis: failed connect (drop mid-AUTH) rejects", connErr !== null);
|
|
229
|
+
var st2 = c2._state();
|
|
230
|
+
check("redis: failed connect leaves connected=false", st2.connected === false);
|
|
231
|
+
check("redis: failed connect clears the shared connect promise (connecting=false)",
|
|
232
|
+
st2.connecting === false);
|
|
233
|
+
|
|
234
|
+
// A command after give-up must settle, not hang. Bound the await so a
|
|
235
|
+
// regression surfaces as a timeout instead of a stuck test.
|
|
236
|
+
var cmdSettled = false;
|
|
237
|
+
var cmdErr = null;
|
|
238
|
+
await helpers.withTestTimeout("redis: post-give-up command settles", async function () {
|
|
239
|
+
try { await c2.command("PING"); } catch (e) { cmdErr = e; }
|
|
240
|
+
cmdSettled = true;
|
|
241
|
+
}, { timeoutMs: 4000 });
|
|
242
|
+
check("redis: command after give-up settles (does not wedge)", cmdSettled === true);
|
|
243
|
+
check("redis: command after give-up rejects with RECONNECT_GAVE_UP",
|
|
244
|
+
cmdErr !== null && cmdErr.code === "RECONNECT_GAVE_UP");
|
|
245
|
+
await c2.close();
|
|
246
|
+
await dropMidAuth.close();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ---- a backlogged command times out if connect never completes ----
|
|
250
|
+
// Queued-while-disconnected commands must NOT outlive commandTimeoutMs —
|
|
251
|
+
// a backend that never comes up settles the caller with a clear error.
|
|
252
|
+
{
|
|
253
|
+
var c3 = redis.create({
|
|
254
|
+
url: "redis://127.0.0.1:1/0", // nothing listens; reconnect stays pending
|
|
255
|
+
connectTimeoutMs: 200, commandTimeoutMs: 150, maxReconnectAttempts: 5,
|
|
256
|
+
});
|
|
257
|
+
c3.connect().catch(function () { /* will keep retrying in the background */ });
|
|
258
|
+
var c3Err = null, c3Settled = false;
|
|
259
|
+
await helpers.withTestTimeout("redis: backlogged command times out", async function () {
|
|
260
|
+
try { await c3.command("GET", "k"); } catch (e) { c3Err = e; }
|
|
261
|
+
c3Settled = true;
|
|
262
|
+
}, { timeoutMs: 4000 });
|
|
263
|
+
check("redis: backlogged command settles (does not wedge)", c3Settled === true);
|
|
264
|
+
check("redis: backlogged command rejects with COMMAND_TIMEOUT",
|
|
265
|
+
c3Err !== null && c3Err.code === "COMMAND_TIMEOUT");
|
|
266
|
+
await c3.close();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ---- single-flight reconnect: error+close schedule ONE reconnect ----
|
|
270
|
+
// A lost socket surfaces as both an `error` and a `close` event (and the
|
|
271
|
+
// error handler destroys the socket, re-firing close). Each must NOT
|
|
272
|
+
// schedule its own reconnect timer — that doubles the backoff rate and
|
|
273
|
+
// opens redundant sockets. We count distinct inbound connections: after
|
|
274
|
+
// one induced drop, the first reconnect lands as connection #2 (not #3).
|
|
275
|
+
{
|
|
276
|
+
var conns = 0;
|
|
277
|
+
var firstSock = null;
|
|
278
|
+
var reconnectSrv = await _listen(function (sock) {
|
|
279
|
+
conns += 1;
|
|
280
|
+
if (conns === 1) {
|
|
281
|
+
firstSock = sock;
|
|
282
|
+
// Reset the first connection so the client sees error AND close.
|
|
283
|
+
if (sock.resetAndDestroy) sock.resetAndDestroy(); else sock.destroy();
|
|
284
|
+
}
|
|
285
|
+
// Reconnect (conns >= 2): hold open — no further failures.
|
|
286
|
+
});
|
|
287
|
+
var c4 = redis.create({
|
|
288
|
+
url: "redis://127.0.0.1:" + reconnectSrv.port + "/0",
|
|
289
|
+
connectTimeoutMs: 1000, commandTimeoutMs: 500, maxReconnectAttempts: 10,
|
|
290
|
+
});
|
|
291
|
+
await c4.connect().catch(function () {});
|
|
292
|
+
// The first backoff is 100ms; wait until exactly one reconnect lands
|
|
293
|
+
// (connection #2). If error AND close had each scheduled, the budget
|
|
294
|
+
// would have produced a SECOND reconnect (#3) at the bumped delay.
|
|
295
|
+
await helpers.waitUntil(function () { return conns >= 2; },
|
|
296
|
+
{ timeoutMs: 3000, label: "redis single-flight: first reconnect connects" });
|
|
297
|
+
var stallObserved = await helpers.passiveObserve(350,
|
|
298
|
+
"redis single-flight: no second reconnect from the same failure")
|
|
299
|
+
.then(function () { return conns; });
|
|
300
|
+
check("redis: single failure schedules exactly one reconnect (no double-schedule)",
|
|
301
|
+
stallObserved === 2);
|
|
302
|
+
check("redis: reconnect count reset toward stable after one successful reconnect",
|
|
303
|
+
c4._state().reconnectPending === false);
|
|
304
|
+
void firstSock;
|
|
305
|
+
await c4.close();
|
|
306
|
+
await reconnectSrv.close();
|
|
307
|
+
}
|
|
195
308
|
}
|
|
196
309
|
|
|
197
310
|
module.exports = { run: run };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #120: a retention dry-run / preview must NOT touch the database. Under a
|
|
3
|
+
// regulated posture (gdpr / hipaa / …) whose POSTURE_DEFAULTS sets
|
|
4
|
+
// requireVacuumAfterErase, cryptoField.eraseRow schedules a FULL VACUUM and
|
|
5
|
+
// emits db.vacuum_after_erase. retention._erase called eraseRow at the top of
|
|
6
|
+
// the function — BEFORE the `if (dryRun) return` gate — so previewing an
|
|
7
|
+
// erase rule ran a full-table VACUUM per candidate row: a preview that locks
|
|
8
|
+
// the database and rewrites the whole file, the opposite of "preview".
|
|
9
|
+
//
|
|
10
|
+
// RED on the buggy tree: a dryRun run() fires vacuumAfterErase (spy count >= 1).
|
|
11
|
+
// GREEN after the fix: dryRun fires it 0 times; a real (committing) run still
|
|
12
|
+
// vacuums under the regulated posture.
|
|
13
|
+
|
|
14
|
+
var fs = require("fs");
|
|
15
|
+
var path = require("path");
|
|
16
|
+
var os = require("os");
|
|
17
|
+
var helpers = require("../helpers");
|
|
18
|
+
var dbHelper = require("../helpers/db");
|
|
19
|
+
var b = helpers.b;
|
|
20
|
+
var check = helpers.check;
|
|
21
|
+
|
|
22
|
+
async function run() {
|
|
23
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-retention-dryrun-"));
|
|
24
|
+
await dbHelper.setupTestDb(tmpDir);
|
|
25
|
+
|
|
26
|
+
// An operator app table with a sealed column, so retention routes the
|
|
27
|
+
// candidate through _erase (sealed-column NULL-out) rather than the
|
|
28
|
+
// _hardDelete fallback. The actual value need not be sealed at rest for
|
|
29
|
+
// the bug to fire — _erase keys off getSealedFields(table) being non-empty.
|
|
30
|
+
b.cryptoField.registerTable("retention_pii", {
|
|
31
|
+
sealedFields: ["payload"],
|
|
32
|
+
rowIdField: "_id",
|
|
33
|
+
});
|
|
34
|
+
b.db.prepare(
|
|
35
|
+
"CREATE TABLE \"retention_pii\" (" +
|
|
36
|
+
"\"_id\" TEXT PRIMARY KEY, \"createdAt\" INTEGER, " +
|
|
37
|
+
"\"payload\" TEXT, \"__erasedAt\" INTEGER)"
|
|
38
|
+
).run();
|
|
39
|
+
var longAgo = Date.now() - b.constants.TIME.days(400);
|
|
40
|
+
b.db.prepare(
|
|
41
|
+
"INSERT INTO \"retention_pii\" (\"_id\", \"createdAt\", \"payload\", \"__erasedAt\") " +
|
|
42
|
+
"VALUES (?, ?, ?, NULL)"
|
|
43
|
+
).run("row-1", longAgo, "subject-secret-payload");
|
|
44
|
+
|
|
45
|
+
// Spy on the VACUUM seam. cryptoField.eraseRow resolves the db module via
|
|
46
|
+
// lazyRequire and reads .vacuumAfterErase at call time, so replacing the
|
|
47
|
+
// export observes (and still drives) the real call.
|
|
48
|
+
var realVacuum = b.db.vacuumAfterErase;
|
|
49
|
+
var vacuumCalls = 0;
|
|
50
|
+
b.db.vacuumAfterErase = function (opts) {
|
|
51
|
+
vacuumCalls++;
|
|
52
|
+
return realVacuum.call(b.db, opts);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// gdpr → requireVacuumAfterErase: true, so eraseRow auto-vacuums.
|
|
56
|
+
b.compliance.set("gdpr");
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
var retention = b.retention.create({ db: b.db, audit: false });
|
|
60
|
+
retention.declare({
|
|
61
|
+
name: "pii-ttl",
|
|
62
|
+
table: "retention_pii",
|
|
63
|
+
ageField: "createdAt",
|
|
64
|
+
ttlMs: b.constants.TIME.days(90),
|
|
65
|
+
action: "erase",
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ---- the bug: a dry-run must not VACUUM ----
|
|
69
|
+
vacuumCalls = 0;
|
|
70
|
+
var preview = await retention.run("pii-ttl", { dryRun: true });
|
|
71
|
+
check("#120 dry-run scans the past-TTL row",
|
|
72
|
+
preview && preview.scanned >= 1);
|
|
73
|
+
check("#120 dry-run reports it WOULD erase (no real work)",
|
|
74
|
+
preview && preview.processed >= 1);
|
|
75
|
+
check("#120 dry-run performs NO database VACUUM",
|
|
76
|
+
vacuumCalls === 0);
|
|
77
|
+
// The row must still be present + un-erased after a preview.
|
|
78
|
+
var afterPreview = b.db.prepare(
|
|
79
|
+
"SELECT \"payload\" FROM \"retention_pii\" WHERE \"_id\" = ?").get("row-1");
|
|
80
|
+
check("#120 dry-run leaves the row's sealed column intact",
|
|
81
|
+
afterPreview && afterPreview.payload === "subject-secret-payload");
|
|
82
|
+
|
|
83
|
+
// ---- the real path still vacuums under the regulated posture ----
|
|
84
|
+
vacuumCalls = 0;
|
|
85
|
+
var real = await retention.run("pii-ttl", { dryRun: false });
|
|
86
|
+
check("#120 real run erases the row", real && real.processed >= 1);
|
|
87
|
+
check("#120 real run DOES vacuum under gdpr", vacuumCalls >= 1);
|
|
88
|
+
var afterErase = b.db.prepare(
|
|
89
|
+
"SELECT \"payload\" FROM \"retention_pii\" WHERE \"_id\" = ?").get("row-1");
|
|
90
|
+
check("#120 real run NULLs the sealed column",
|
|
91
|
+
afterErase && afterErase.payload === null);
|
|
92
|
+
} finally {
|
|
93
|
+
b.db.vacuumAfterErase = realVacuum;
|
|
94
|
+
b.compliance.clear();
|
|
95
|
+
await dbHelper.teardownTestDb(tmpDir);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = { run: run };
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.router — path-scoped middleware: `use(prefix, mw)`.
|
|
4
|
+
*
|
|
5
|
+
* The router supports both `use(mw)` (global, runs on every request)
|
|
6
|
+
* and `use(prefix, mw)` (runs only when the request path is at or
|
|
7
|
+
* beneath `prefix`, matched on segment boundaries). The path-scoped
|
|
8
|
+
* form is the documented mounting shape for the framework's access-
|
|
9
|
+
* refusal middleware (`csrf`, `bearerAuth`, `requireAal`,
|
|
10
|
+
* `requireStepUp`, `requireMtls`, …): an operator writes
|
|
11
|
+
* `router.use("/admin", stepUpGate)` to gate everything under /admin.
|
|
12
|
+
*
|
|
13
|
+
* Validates, driving requests end-to-end through router.handle:
|
|
14
|
+
* - a path-scoped gate runs for a matching path and NOT for a
|
|
15
|
+
* non-matching one (no silent security-control bypass, no 500)
|
|
16
|
+
* - segment-boundary matching: "/admin" does not match "/administrator"
|
|
17
|
+
* - registration order: a gate registered before a route runs first
|
|
18
|
+
* - global `use(mw)` still runs on every request (back-compat)
|
|
19
|
+
* - multi-middleware scoped mounts preserve order
|
|
20
|
+
* - array-of-prefixes scopes a gate to several path roots
|
|
21
|
+
* - trailing-slash normalization ("/admin" == "/admin/")
|
|
22
|
+
* - config-time throws on a bad prefix / missing or non-function
|
|
23
|
+
* middleware (entry-point validation tier)
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
var helpers = require("../helpers");
|
|
27
|
+
var b = helpers.b;
|
|
28
|
+
var check = helpers.check;
|
|
29
|
+
|
|
30
|
+
function _req(url) {
|
|
31
|
+
// router.handle parses req.url against the Host header to derive
|
|
32
|
+
// req.pathname; a minimal request shape is enough to drive dispatch.
|
|
33
|
+
return { method: "GET", url: url, headers: { host: "localhost" } };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function _res() {
|
|
37
|
+
var res = {
|
|
38
|
+
statusCode: 0,
|
|
39
|
+
headersSent: false,
|
|
40
|
+
writableEnded: false,
|
|
41
|
+
_body: "",
|
|
42
|
+
writeHead: function (status, headers) {
|
|
43
|
+
res.statusCode = status;
|
|
44
|
+
res._headers = headers || {};
|
|
45
|
+
res.headersSent = true;
|
|
46
|
+
},
|
|
47
|
+
end: function (chunk) {
|
|
48
|
+
if (chunk !== undefined) res._body += chunk;
|
|
49
|
+
res.writableEnded = true;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
return res;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function testScopedGateRunsOnlyUnderPrefix() {
|
|
56
|
+
var r = b.router.create();
|
|
57
|
+
var gateRanFor = [];
|
|
58
|
+
r.use("/admin", function gate(req, res, next) {
|
|
59
|
+
gateRanFor.push(req.pathname);
|
|
60
|
+
next();
|
|
61
|
+
});
|
|
62
|
+
r.get("/admin/secret", function (req, res) { res.statusCode = 200; res.end("admin"); });
|
|
63
|
+
r.get("/public", function (req, res) { res.statusCode = 200; res.end("public"); });
|
|
64
|
+
|
|
65
|
+
var resPublic = _res();
|
|
66
|
+
await r.handle(_req("/public"), resPublic);
|
|
67
|
+
check("use('/admin', gate): gate does NOT run for /public",
|
|
68
|
+
gateRanFor.length === 0 && resPublic.statusCode === 200 &&
|
|
69
|
+
resPublic._body === "public");
|
|
70
|
+
|
|
71
|
+
var resAdmin = _res();
|
|
72
|
+
await r.handle(_req("/admin/secret"), resAdmin);
|
|
73
|
+
check("use('/admin', gate): gate runs for /admin/secret",
|
|
74
|
+
gateRanFor.length === 1 && gateRanFor[0] === "/admin/secret" &&
|
|
75
|
+
resAdmin.statusCode === 200 && resAdmin._body === "admin");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function testGateCanRefuseUnderPrefix() {
|
|
79
|
+
// A scoped gate that does NOT call next() halts the chain (a refusal)
|
|
80
|
+
// — and only for the scoped path. The route handler under the prefix
|
|
81
|
+
// never runs; an unrelated path is untouched.
|
|
82
|
+
var r = b.router.create();
|
|
83
|
+
r.use("/admin", function deny(req, res /*, next */) {
|
|
84
|
+
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
85
|
+
res.end("forbidden");
|
|
86
|
+
// no next() — chain stops
|
|
87
|
+
});
|
|
88
|
+
var adminHandlerRan = false;
|
|
89
|
+
r.get("/admin/secret", function (req, res) { adminHandlerRan = true; res.statusCode = 200; res.end("admin"); });
|
|
90
|
+
r.get("/public", function (req, res) { res.statusCode = 200; res.end("public"); });
|
|
91
|
+
|
|
92
|
+
var resAdmin = _res();
|
|
93
|
+
await r.handle(_req("/admin/secret"), resAdmin);
|
|
94
|
+
check("use('/admin', deny): refusal halts the chain under /admin",
|
|
95
|
+
resAdmin.statusCode === 403 && resAdmin._body === "forbidden" &&
|
|
96
|
+
adminHandlerRan === false);
|
|
97
|
+
|
|
98
|
+
var resPublic = _res();
|
|
99
|
+
await r.handle(_req("/public"), resPublic);
|
|
100
|
+
check("use('/admin', deny): /public unaffected by the /admin refusal",
|
|
101
|
+
resPublic.statusCode === 200 && resPublic._body === "public");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function testSegmentBoundaryNotPrefixSubstring() {
|
|
105
|
+
var r = b.router.create();
|
|
106
|
+
var gateRan = false;
|
|
107
|
+
r.use("/admin", function (req, res, next) { gateRan = true; next(); });
|
|
108
|
+
r.get("/administrator", function (req, res) { res.statusCode = 200; res.end("ator"); });
|
|
109
|
+
|
|
110
|
+
var res1 = _res();
|
|
111
|
+
await r.handle(_req("/administrator"), res1);
|
|
112
|
+
check("use('/admin', gate): '/admin' does NOT match '/administrator' (segment boundary)",
|
|
113
|
+
gateRan === false && res1.statusCode === 200 && res1._body === "ator");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function testExactPrefixMatch() {
|
|
117
|
+
var r = b.router.create();
|
|
118
|
+
var gateRan = false;
|
|
119
|
+
r.use("/admin", function (req, res, next) { gateRan = true; next(); });
|
|
120
|
+
r.get("/admin", function (req, res) { res.statusCode = 200; res.end("exact"); });
|
|
121
|
+
|
|
122
|
+
var res1 = _res();
|
|
123
|
+
await r.handle(_req("/admin"), res1);
|
|
124
|
+
check("use('/admin', gate): gate runs for the exact path '/admin'",
|
|
125
|
+
gateRan === true && res1.statusCode === 200 && res1._body === "exact");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function testRegistrationOrderPreserved() {
|
|
129
|
+
// A gate registered before a route must run before the route handler;
|
|
130
|
+
// a global middleware registered after the scoped gate runs after it.
|
|
131
|
+
var r = b.router.create();
|
|
132
|
+
var order = [];
|
|
133
|
+
r.use(function global1(req, res, next) { order.push("global1"); next(); });
|
|
134
|
+
r.use("/admin", function adminGate(req, res, next) { order.push("adminGate"); next(); });
|
|
135
|
+
r.use(function global2(req, res, next) { order.push("global2"); next(); });
|
|
136
|
+
r.get("/admin/x", function (req, res) { order.push("route"); res.statusCode = 200; res.end("x"); });
|
|
137
|
+
|
|
138
|
+
var res1 = _res();
|
|
139
|
+
await r.handle(_req("/admin/x"), res1);
|
|
140
|
+
check("registration order: global1 → adminGate → global2 → route",
|
|
141
|
+
order.join(",") === "global1,adminGate,global2,route");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function testGlobalUseStillRunsEverywhere() {
|
|
145
|
+
var r = b.router.create();
|
|
146
|
+
var seen = [];
|
|
147
|
+
r.use(function (req, res, next) { seen.push(req.pathname); next(); });
|
|
148
|
+
r.get("/a", function (req, res) { res.statusCode = 200; res.end("a"); });
|
|
149
|
+
r.get("/b", function (req, res) { res.statusCode = 200; res.end("b"); });
|
|
150
|
+
|
|
151
|
+
await r.handle(_req("/a"), _res());
|
|
152
|
+
await r.handle(_req("/b"), _res());
|
|
153
|
+
check("global use(mw) runs on every request (back-compat)",
|
|
154
|
+
seen.length === 2 && seen[0] === "/a" && seen[1] === "/b");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function testMultiMiddlewareScopedOrder() {
|
|
158
|
+
var r = b.router.create();
|
|
159
|
+
var order = [];
|
|
160
|
+
r.use("/api",
|
|
161
|
+
function (req, res, next) { order.push("first"); next(); },
|
|
162
|
+
function (req, res, next) { order.push("second"); next(); });
|
|
163
|
+
r.get("/api/users", function (req, res) { order.push("route"); res.statusCode = 200; res.end("u"); });
|
|
164
|
+
|
|
165
|
+
var res1 = _res();
|
|
166
|
+
await r.handle(_req("/api/users"), res1);
|
|
167
|
+
check("use(prefix, mw1, mw2): both run in order before the route",
|
|
168
|
+
order.join(",") === "first,second,route");
|
|
169
|
+
|
|
170
|
+
// And they do NOT run off-prefix.
|
|
171
|
+
var offOrder = order.length;
|
|
172
|
+
r.get("/other", function (req, res) { res.statusCode = 200; res.end("o"); });
|
|
173
|
+
await r.handle(_req("/other"), _res());
|
|
174
|
+
check("use(prefix, mw1, mw2): neither runs off-prefix",
|
|
175
|
+
order.length === offOrder);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function testArrayOfPrefixes() {
|
|
179
|
+
var r = b.router.create();
|
|
180
|
+
var hits = [];
|
|
181
|
+
r.use(["/caldav", "/carddav"], function (req, res, next) { hits.push(req.pathname); next(); });
|
|
182
|
+
r.get("/caldav/cal", function (req, res) { res.statusCode = 200; res.end("cal"); });
|
|
183
|
+
r.get("/carddav/card", function (req, res) { res.statusCode = 200; res.end("card"); });
|
|
184
|
+
r.get("/jmap", function (req, res) { res.statusCode = 200; res.end("jmap"); });
|
|
185
|
+
|
|
186
|
+
await r.handle(_req("/caldav/cal"), _res());
|
|
187
|
+
await r.handle(_req("/carddav/card"), _res());
|
|
188
|
+
await r.handle(_req("/jmap"), _res());
|
|
189
|
+
check("use([prefixA, prefixB], mw): runs under either prefix, not elsewhere",
|
|
190
|
+
hits.length === 2 &&
|
|
191
|
+
hits.indexOf("/caldav/cal") !== -1 &&
|
|
192
|
+
hits.indexOf("/carddav/card") !== -1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function testTrailingSlashNormalized() {
|
|
196
|
+
var r = b.router.create();
|
|
197
|
+
var gateRan = [];
|
|
198
|
+
r.use("/admin/", function (req, res, next) { gateRan.push(req.pathname); next(); });
|
|
199
|
+
r.get("/admin", function (req, res) { res.statusCode = 200; res.end("a"); });
|
|
200
|
+
r.get("/admin/x", function (req, res) { res.statusCode = 200; res.end("x"); });
|
|
201
|
+
|
|
202
|
+
await r.handle(_req("/admin"), _res());
|
|
203
|
+
await r.handle(_req("/admin/x"), _res());
|
|
204
|
+
check("use('/admin/', gate): trailing slash normalized — matches /admin and /admin/x",
|
|
205
|
+
gateRan.length === 2 &&
|
|
206
|
+
gateRan.indexOf("/admin") !== -1 &&
|
|
207
|
+
gateRan.indexOf("/admin/x") !== -1);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function _throwCode(fn) {
|
|
211
|
+
try { fn(); return null; }
|
|
212
|
+
catch (e) { return (e && e.code) || (e && e.constructor && e.constructor.name) || "threw"; }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function testConfigTimeThrows() {
|
|
216
|
+
var r = b.router.create();
|
|
217
|
+
check("use(123, mw) throws (non-string prefix)",
|
|
218
|
+
_throwCode(function () { r.use(123, function () {}); }) === "router/use-bad-first-arg");
|
|
219
|
+
check("use('/x', 'notafn') throws (non-function middleware)",
|
|
220
|
+
_throwCode(function () { r.use("/x", "notafn"); }) === "router/use-middleware-not-function");
|
|
221
|
+
check("use('noslash', mw) throws (prefix must begin with '/')",
|
|
222
|
+
_throwCode(function () { r.use("noslash", function () {}); }) === "router/use-prefix-not-absolute");
|
|
223
|
+
check("use() throws (no arguments)",
|
|
224
|
+
_throwCode(function () { r.use(); }) === "router/use-no-args");
|
|
225
|
+
check("use('/x') throws (no middleware after prefix)",
|
|
226
|
+
_throwCode(function () { r.use("/x"); }) === "router/use-no-middleware");
|
|
227
|
+
check("use([], mw) throws (empty prefix array)",
|
|
228
|
+
_throwCode(function () { r.use([], function () {}); }) === "router/use-empty-prefix-array");
|
|
229
|
+
check("use(['/x', 123], mw) throws (non-string entry in prefix array)",
|
|
230
|
+
_throwCode(function () { r.use(["/x", 123], function () {}); }) === "router/use-prefix-not-string");
|
|
231
|
+
check("use(null, mw) throws (null first arg)",
|
|
232
|
+
_throwCode(function () { r.use(null, function () {}); }) === "router/use-bad-first-arg");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function run() {
|
|
236
|
+
await testScopedGateRunsOnlyUnderPrefix();
|
|
237
|
+
await testGateCanRefuseUnderPrefix();
|
|
238
|
+
await testSegmentBoundaryNotPrefixSubstring();
|
|
239
|
+
await testExactPrefixMatch();
|
|
240
|
+
await testRegistrationOrderPreserved();
|
|
241
|
+
await testGlobalUseStillRunsEverywhere();
|
|
242
|
+
await testMultiMiddlewareScopedOrder();
|
|
243
|
+
await testArrayOfPrefixes();
|
|
244
|
+
await testTrailingSlashNormalized();
|
|
245
|
+
testConfigTimeThrows();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
module.exports = { run: run };
|
|
249
|
+
|
|
250
|
+
if (require.main === module) {
|
|
251
|
+
run().then(
|
|
252
|
+
function () { console.log("router path-scoped use tests passed"); process.exit(0); },
|
|
253
|
+
function (e) { console.error(e); process.exit(1); }
|
|
254
|
+
);
|
|
255
|
+
}
|