@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,577 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Live data-layer test against the docker Postgres container. Drives the
|
|
4
|
+
* cluster-mode SQL path of the framework's session / cache / nonce /
|
|
5
|
+
* rate-limit primitives end-to-end on a real Postgres server — the path
|
|
6
|
+
* advertised for cluster deployments but which smoke only ever exercises
|
|
7
|
+
* against local SQLite.
|
|
8
|
+
*
|
|
9
|
+
* In cluster mode every one of these primitives composes its SQL through
|
|
10
|
+
* b.sql and dispatches via b.clusterStorage -> b.externalDb -> the
|
|
11
|
+
* operator's Postgres. This test proves:
|
|
12
|
+
*
|
|
13
|
+
* - b.session create / verify / touch / rotate / destroy /
|
|
14
|
+
* destroyAllForUser / count / purgeExpired, with the
|
|
15
|
+
* at-rest sealing (userId/data sealed, userIdHash
|
|
16
|
+
* derived) round-tripping through real Postgres.
|
|
17
|
+
* - b.cache (cluster) get / set (tx upsert + tag) / CAS update /
|
|
18
|
+
* invalidateTag (whereLike prefix) / clear / has /
|
|
19
|
+
* size / getTags.
|
|
20
|
+
* - b.nonceStore (cluster) checkAndInsert (ON CONFLICT DO NOTHING atomic
|
|
21
|
+
* first-seen) + replay rejection + purgeExpired.
|
|
22
|
+
* - b.middleware.rateLimit (cluster) take() = ON CONFLICT increment with
|
|
23
|
+
* a CASE conflict action + RETURNING + the BIGINT
|
|
24
|
+
* count coercing back to a JS number (frameworkSchema
|
|
25
|
+
* COLUMN_TYPES) so count<=limit comparisons are
|
|
26
|
+
* numeric, not string.
|
|
27
|
+
*
|
|
28
|
+
* The "driver" is a persistent docker-exec psql shim (SQL fed over stdin,
|
|
29
|
+
* never argv) modelled on external-db-postgres.test.js. It faithfully
|
|
30
|
+
* reproduces a real node-postgres driver where it matters: BIGINT comes
|
|
31
|
+
* back as a STRING (clusterStorage.execute then coerces it through
|
|
32
|
+
* frameworkSchema.coerceRows), so a counter compared as a string would be
|
|
33
|
+
* caught. The framework tables are created with
|
|
34
|
+
* frameworkSchema.ensureSchema (real DDL, quoted camelCase columns).
|
|
35
|
+
*
|
|
36
|
+
* RUN: node scripts/test-integration.js --skip-service-check data-layer-pg
|
|
37
|
+
*/
|
|
38
|
+
var spawn = require("node:child_process").spawn;
|
|
39
|
+
var execFileSync = require("node:child_process").execFileSync;
|
|
40
|
+
var fs = require("node:fs");
|
|
41
|
+
var os = require("node:os");
|
|
42
|
+
var path = require("node:path");
|
|
43
|
+
var helpers = require("../helpers");
|
|
44
|
+
var check = helpers.check;
|
|
45
|
+
var services = require("../helpers/services");
|
|
46
|
+
var b = require("../../");
|
|
47
|
+
|
|
48
|
+
var CONTAINER = "blamejs-test-postgres";
|
|
49
|
+
var NULL_SENTINEL = "__BJNULL__";
|
|
50
|
+
|
|
51
|
+
// Soft assertion: records pass/fail without throwing so every section's
|
|
52
|
+
// findings are collected even when an earlier check fails (one real bug in
|
|
53
|
+
// one primitive shouldn't hide whether the others work on Postgres). All
|
|
54
|
+
// findings are replayed through the hard `check` at the end of run() so the
|
|
55
|
+
// file still FAILS when any contract is unmet.
|
|
56
|
+
var _findings = [];
|
|
57
|
+
function softCheck(label, condition) {
|
|
58
|
+
_findings.push({ label: label, ok: !!condition });
|
|
59
|
+
console.log((condition ? " ok " : " FAIL ") + label);
|
|
60
|
+
}
|
|
61
|
+
var PSQL_ARGS = "psql -U blamejs -d blamejs_test -A " +
|
|
62
|
+
"-v ON_ERROR_STOP=0 -P null=__BJNULL__ 2>&1";
|
|
63
|
+
|
|
64
|
+
// ---- one-shot psql (setup / teardown / out-of-band assertions) ----
|
|
65
|
+
function _psql(sql) {
|
|
66
|
+
var prelude = "\\pset fieldsep '\\t'\n";
|
|
67
|
+
var out = execFileSync(
|
|
68
|
+
"docker",
|
|
69
|
+
["exec", "-i", CONTAINER, "sh", "-c",
|
|
70
|
+
"psql -U blamejs -d blamejs_test -qtA -P null=__BJNULL__ 2>&1"],
|
|
71
|
+
{ input: prelude + sql + "\n", stdio: ["pipe", "pipe", "pipe"] }
|
|
72
|
+
).toString("utf8");
|
|
73
|
+
if (/^ERROR:/m.test(out)) {
|
|
74
|
+
throw new Error("psql setup failed for [" + sql + "]:\n" + out);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---- persistent-session docker-exec psql driver (faithful to pg) ----
|
|
80
|
+
var _seq = 0;
|
|
81
|
+
function _makeDockerPgDriver() {
|
|
82
|
+
return {
|
|
83
|
+
connect: function () {
|
|
84
|
+
return new Promise(function (resolve, reject) {
|
|
85
|
+
var child = spawn(
|
|
86
|
+
"docker",
|
|
87
|
+
["exec", "-i", CONTAINER, "sh", "-c",
|
|
88
|
+
PSQL_ARGS + " ; echo __BLAMEJS_PSQL_EXIT__"],
|
|
89
|
+
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
90
|
+
);
|
|
91
|
+
var client = { child: child, buf: "", pending: null, closed: false };
|
|
92
|
+
child.on("error", function (e) {
|
|
93
|
+
if (client.pending) { var p = client.pending; client.pending = null; p.reject(e); }
|
|
94
|
+
});
|
|
95
|
+
child.on("close", function () {
|
|
96
|
+
client.closed = true;
|
|
97
|
+
if (client.pending) {
|
|
98
|
+
var p = client.pending; client.pending = null;
|
|
99
|
+
p.reject(new Error("psql session closed mid-statement"));
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
child.stdout.on("data", function (chunk) {
|
|
103
|
+
client.buf += chunk.toString("utf8");
|
|
104
|
+
_drain(client);
|
|
105
|
+
});
|
|
106
|
+
var primeSentinel = "__BJ_PRIME__";
|
|
107
|
+
client.pending = {
|
|
108
|
+
sentinel: primeSentinel,
|
|
109
|
+
resolve: function () { resolve(client); },
|
|
110
|
+
reject: reject,
|
|
111
|
+
};
|
|
112
|
+
client.child.stdin.write(
|
|
113
|
+
"\\pset fieldsep '\\t'\n\\pset footer off\n\\set VERBOSITY verbose\n" +
|
|
114
|
+
"\\echo " + primeSentinel + "\n");
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
query: function (client, sql, params) {
|
|
119
|
+
params = params || [];
|
|
120
|
+
var bound = _bindParams(sql, params);
|
|
121
|
+
var sentinel = "__BJ_EOR_" + (++_seq) + "__";
|
|
122
|
+
return new Promise(function (resolve, reject) {
|
|
123
|
+
if (client.closed) { reject(new Error("psql session is closed")); return; }
|
|
124
|
+
client.pending = { sentinel: sentinel, resolve: resolve, reject: reject };
|
|
125
|
+
client.child.stdin.write(bound + "\n;\n\\echo " + sentinel + "\n");
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
close: function (client) {
|
|
130
|
+
return new Promise(function (resolve) {
|
|
131
|
+
if (client.closed) { resolve(); return; }
|
|
132
|
+
try { client.child.stdin.end("\\q\n"); } catch (_e) { /* best effort */ }
|
|
133
|
+
var done = false;
|
|
134
|
+
client.child.on("close", function () { if (!done) { done = true; resolve(); } });
|
|
135
|
+
setTimeout(function () {
|
|
136
|
+
if (done) return;
|
|
137
|
+
done = true;
|
|
138
|
+
try { client.child.kill("SIGKILL"); } catch (_e) {}
|
|
139
|
+
resolve();
|
|
140
|
+
}, 2000);
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
dialect: "postgres",
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function _drain(client) {
|
|
149
|
+
if (!client.pending) return;
|
|
150
|
+
var sentinel = client.pending.sentinel;
|
|
151
|
+
var marker = "\n" + sentinel + "\n";
|
|
152
|
+
var idx = client.buf.indexOf(marker);
|
|
153
|
+
var startAtZero = client.buf.indexOf(sentinel + "\n") === 0;
|
|
154
|
+
var block;
|
|
155
|
+
if (idx !== -1) {
|
|
156
|
+
block = client.buf.slice(0, idx);
|
|
157
|
+
client.buf = client.buf.slice(idx + marker.length);
|
|
158
|
+
} else if (startAtZero) {
|
|
159
|
+
block = "";
|
|
160
|
+
client.buf = client.buf.slice((sentinel + "\n").length);
|
|
161
|
+
} else {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
var p = client.pending;
|
|
165
|
+
client.pending = null;
|
|
166
|
+
var parsed;
|
|
167
|
+
try { parsed = _parseBlock(block); }
|
|
168
|
+
catch (e) { return p.reject(e); }
|
|
169
|
+
if (parsed.error) return p.reject(parsed.error);
|
|
170
|
+
p.resolve({ rows: parsed.rows, rowCount: parsed.rowCount });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Inline params: NULL / numbers raw / booleans / single-quote-escaped.
|
|
174
|
+
function _bindParams(sql, params) {
|
|
175
|
+
return sql.replace(/\$(\d+)/g, function (_m, n) {
|
|
176
|
+
var i = Number(n) - 1;
|
|
177
|
+
if (i < 0 || i >= params.length) {
|
|
178
|
+
throw new Error("placeholder $" + n + " has no matching param");
|
|
179
|
+
}
|
|
180
|
+
var v = params[i];
|
|
181
|
+
if (v === null || v === undefined) return "NULL";
|
|
182
|
+
if (typeof v === "number") return String(v);
|
|
183
|
+
if (typeof v === "boolean") return v ? "TRUE" : "FALSE";
|
|
184
|
+
return "'" + String(v).replace(/'/g, "''") + "'";
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
var _CMD_TAG_RE = /^(INSERT|UPDATE|DELETE|MERGE|SELECT|COPY|MOVE)\b(?:\s+\d+)*\s*$/;
|
|
189
|
+
var _CTRL_TAG_RE = /^(BEGIN|COMMIT|ROLLBACK|SET|RESET|SAVEPOINT|RELEASE|START|CREATE|DROP|ALTER|GRANT|REVOKE|TRUNCATE|COMMENT|DO|CALL|VACUUM|ANALYZE|EXPLAIN|TABLE|SHOW|DISCARD)\b/;
|
|
190
|
+
|
|
191
|
+
function _parseBlock(block) {
|
|
192
|
+
var lines = block.split(/\r?\n/);
|
|
193
|
+
while (lines.length && lines[lines.length - 1] === "") lines.pop();
|
|
194
|
+
|
|
195
|
+
for (var i = 0; i < lines.length; i++) {
|
|
196
|
+
var em = /^ERROR:\s+([0-9A-Za-z]{5}):\s*(.*)$/.exec(lines[i]);
|
|
197
|
+
if (em) {
|
|
198
|
+
var err = new Error("Postgres " + em[1] + ": " + em[2]);
|
|
199
|
+
err.code = em[1];
|
|
200
|
+
return { error: err };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
var affected = null;
|
|
205
|
+
var dataLines = [];
|
|
206
|
+
for (var j = 0; j < lines.length; j++) {
|
|
207
|
+
var ln = lines[j];
|
|
208
|
+
if (/^(NOTICE|WARNING|DETAIL|HINT|LINE|LOCATION|CONTEXT|STATEMENT):/.test(ln)) continue;
|
|
209
|
+
var tm = _CMD_TAG_RE.exec(ln);
|
|
210
|
+
if (tm) {
|
|
211
|
+
var nums = ln.trim().split(/\s+/).slice(1).map(Number);
|
|
212
|
+
if (nums.length) affected = nums[nums.length - 1];
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (_CTRL_TAG_RE.test(ln) && ln.indexOf("\t") === -1) continue;
|
|
216
|
+
dataLines.push(ln);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// dataLines carry a header row first (column names AS POSTGRES REPORTS
|
|
220
|
+
// THEM — quoted DDL preserves the camelCase), then data rows. Every cell
|
|
221
|
+
// stays a STRING — exactly the shape node-postgres returns for BIGINT
|
|
222
|
+
// (int8) columns, so clusterStorage's coerceRows is what must turn the
|
|
223
|
+
// framework's int columns back into JS numbers.
|
|
224
|
+
var rows = [];
|
|
225
|
+
if (dataLines.length >= 1) {
|
|
226
|
+
var headers = dataLines[0].split("\t");
|
|
227
|
+
for (var k = 1; k < dataLines.length; k++) {
|
|
228
|
+
var cells = dataLines[k].split("\t");
|
|
229
|
+
var row = {};
|
|
230
|
+
for (var c = 0; c < headers.length; c++) {
|
|
231
|
+
var cell = cells[c];
|
|
232
|
+
row[headers[c]] = (cell === NULL_SENTINEL || cell === undefined) ? null : cell;
|
|
233
|
+
}
|
|
234
|
+
rows.push(row);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
var rowCount = (affected !== null) ? affected : rows.length;
|
|
238
|
+
return { rows: rows, rowCount: rowCount, error: null };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Every framework table frameworkSchema.ensureSchema creates — dropped in
|
|
242
|
+
// setup AND teardown so the run starts clean and leaves the shared test
|
|
243
|
+
// database clean for sibling Postgres tests (which create their own
|
|
244
|
+
// tables). The primitives under test only touch sessions / cache /
|
|
245
|
+
// cache_tags / nonces / rate-limit counters, but ensureSchema materializes
|
|
246
|
+
// the whole framework surface, so all of it is swept.
|
|
247
|
+
var FRAMEWORK_TABLES = [
|
|
248
|
+
"_blamejs_sessions", "_blamejs_cache", "_blamejs_cache_tags",
|
|
249
|
+
"_blamejs_api_encrypt_nonces", "_blamejs_rate_limit_counters",
|
|
250
|
+
"_blamejs_audit_log", "_blamejs_consent_log", "_blamejs_audit_checkpoints",
|
|
251
|
+
"_blamejs_audit_tip", "_blamejs_consent_tip", "_blamejs_audit_purge_anchor",
|
|
252
|
+
"_blamejs_scheduler_ticks", "_blamejs_pubsub_messages", "_blamejs_api_keys",
|
|
253
|
+
"_blamejs_jobs", "_blamejs_seeders", "_blamejs_seeders_lock",
|
|
254
|
+
"_blamejs_break_glass_policies", "_blamejs_break_glass_grants",
|
|
255
|
+
// cluster.init's default DB-row leader-election provider creates these.
|
|
256
|
+
"_blamejs_leader", "_blamejs_cluster_state",
|
|
257
|
+
];
|
|
258
|
+
|
|
259
|
+
var rateLimitModule = require("../../lib/middleware/rate-limit");
|
|
260
|
+
|
|
261
|
+
async function run() {
|
|
262
|
+
var pg = await services.requireService("postgres");
|
|
263
|
+
if (!pg.ok) throw new Error("postgres unreachable: " + pg.reason);
|
|
264
|
+
|
|
265
|
+
// Drop our framework tables so the run starts clean.
|
|
266
|
+
_psql(FRAMEWORK_TABLES.map(function (t) {
|
|
267
|
+
return "DROP TABLE IF EXISTS " + t + " CASCADE;";
|
|
268
|
+
}).join("\n"));
|
|
269
|
+
|
|
270
|
+
// Full-framework bring-up (vault + db + cryptoField). db.init registers
|
|
271
|
+
// the _blamejs_sessions sealedFields (userId, data) + derived userIdHash,
|
|
272
|
+
// which b.session uses to seal/derive regardless of which backend the SQL
|
|
273
|
+
// routes to. The local SQLite db it opens is unused once cluster mode is
|
|
274
|
+
// active (session SQL dispatches to Postgres) but the cryptoField
|
|
275
|
+
// registry it populates is exactly what session needs.
|
|
276
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-datalayer-pg-"));
|
|
277
|
+
await helpers.setupTestDb(tmpDir);
|
|
278
|
+
|
|
279
|
+
var driver = _makeDockerPgDriver();
|
|
280
|
+
b.externalDb._resetForTest();
|
|
281
|
+
b.externalDb.init({
|
|
282
|
+
backends: {
|
|
283
|
+
ops: {
|
|
284
|
+
connect: driver.connect, query: driver.query, close: driver.close,
|
|
285
|
+
dialect: "postgres",
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Create the framework tables on real Postgres via the real DDL builder.
|
|
291
|
+
await b.frameworkSchema.ensureSchema({ externalDbBackend: "ops", dialect: "postgres" });
|
|
292
|
+
|
|
293
|
+
// Cluster mode: the framework's session/cache/nonce/rate-limit SQL now
|
|
294
|
+
// routes through clusterStorage -> externalDb -> Postgres. cluster.init
|
|
295
|
+
// runs a boot-time rollback check against the (empty) audit/consent tips
|
|
296
|
+
// ensureSchema just created; with no tip row it skips cleanly.
|
|
297
|
+
await b.cluster.init({
|
|
298
|
+
nodeId: "pg-data-node",
|
|
299
|
+
role: "leader",
|
|
300
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
301
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
302
|
+
externalDbBackend: "ops",
|
|
303
|
+
dialect: "postgres",
|
|
304
|
+
// This node's vault keypair is freshly minted in tmpDir; accept it as
|
|
305
|
+
// the canonical cluster vault key rather than refusing on first boot.
|
|
306
|
+
acceptVaultKeyRotation: true,
|
|
307
|
+
});
|
|
308
|
+
softCheck("cluster is in cluster mode (state routes to Postgres)",
|
|
309
|
+
b.cluster.isClusterMode() === true);
|
|
310
|
+
softCheck("cluster reports leadership on real Postgres", b.cluster.isLeader() === true);
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
await _section("session", _testSession);
|
|
314
|
+
await _section("cache", _testCacheCluster);
|
|
315
|
+
await _section("nonce", _testNonceCluster);
|
|
316
|
+
await _section("rate-limit", _testRateLimitCluster);
|
|
317
|
+
} finally {
|
|
318
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
319
|
+
b.cluster._resetForTest();
|
|
320
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
321
|
+
try { await helpers.teardownTestDb(tmpDir); } catch (_e) {}
|
|
322
|
+
_psql(FRAMEWORK_TABLES.map(function (t) {
|
|
323
|
+
return "DROP TABLE IF EXISTS " + t + " CASCADE;";
|
|
324
|
+
}).join("\n"));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Replay every recorded finding through the hard `check` so the file
|
|
328
|
+
// FAILS (and the runner reports it) when any contract is unmet. All
|
|
329
|
+
// findings have already been printed above.
|
|
330
|
+
var failures = _findings.filter(function (f) { return !f.ok; });
|
|
331
|
+
console.log("");
|
|
332
|
+
console.log("[data-layer-pg] " + (_findings.length - failures.length) + "/" +
|
|
333
|
+
_findings.length + " checks ok; " + failures.length + " failing");
|
|
334
|
+
for (var i = 0; i < _findings.length; i++) {
|
|
335
|
+
check(_findings[i].label, _findings[i].ok);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Run one primitive's section; a thrown driver error becomes a single
|
|
340
|
+
// FAILED finding so an unexpected error in one section doesn't hide the
|
|
341
|
+
// others' results.
|
|
342
|
+
async function _section(label, fn) {
|
|
343
|
+
try {
|
|
344
|
+
await fn();
|
|
345
|
+
} catch (e) {
|
|
346
|
+
softCheck(label + "(pg): section completed without an unexpected error " +
|
|
347
|
+
"— DETAIL: " + (((e && e.message) || String(e)).split(/\r?\n/)[0]), false);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ======================================================================
|
|
352
|
+
// b.session full lifecycle on real Postgres (sealed at rest).
|
|
353
|
+
// ======================================================================
|
|
354
|
+
async function _testSession() {
|
|
355
|
+
// create -> returns a sealed-cookie token; the DB row keys on sha3(sid),
|
|
356
|
+
// userId+data sealed, userIdHash derived.
|
|
357
|
+
var created = await b.session.create({
|
|
358
|
+
userId: "user-42",
|
|
359
|
+
data: { roles: ["admin"], theme: "dark" },
|
|
360
|
+
ttlMs: b.constants.TIME.hours(8),
|
|
361
|
+
});
|
|
362
|
+
softCheck("session(pg): create returns a sealed token + expiry",
|
|
363
|
+
created && typeof created.token === "string" &&
|
|
364
|
+
created.token.indexOf("vault:") === 0 && typeof created.expiresAt === "number");
|
|
365
|
+
|
|
366
|
+
// The row physically landed on Postgres; userId is NOT plaintext.
|
|
367
|
+
var rawRows = _psql('SELECT "userId", "userIdHash" FROM _blamejs_sessions;');
|
|
368
|
+
softCheck("session(pg): a session row physically landed on Postgres",
|
|
369
|
+
rawRows.trim().length > 0);
|
|
370
|
+
softCheck("session(pg): userId is sealed at rest (NOT the plaintext 'user-42')",
|
|
371
|
+
rawRows.indexOf("user-42") === -1);
|
|
372
|
+
|
|
373
|
+
// verify -> unseal round-trips userId + data through Postgres.
|
|
374
|
+
var info = await b.session.verify(created.token);
|
|
375
|
+
softCheck("session(pg): verify unseals userId from the Postgres row",
|
|
376
|
+
info && info.userId === "user-42");
|
|
377
|
+
softCheck("session(pg): verify unseals the data payload",
|
|
378
|
+
info && info.data && info.data.roles && info.data.roles[0] === "admin" &&
|
|
379
|
+
info.data.theme === "dark");
|
|
380
|
+
softCheck("session(pg): verify coerces createdAt/expiresAt to JS numbers " +
|
|
381
|
+
"(BIGINT-as-string would break the timeout math)",
|
|
382
|
+
typeof info.createdAt === "number" && typeof info.expiresAt === "number" &&
|
|
383
|
+
info.expiresAt > info.createdAt);
|
|
384
|
+
|
|
385
|
+
// count -> the live session is counted (BIGINT count coerced to number).
|
|
386
|
+
var liveCount = await b.session.count();
|
|
387
|
+
softCheck("session(pg): count() returns the one live session as a JS number",
|
|
388
|
+
typeof liveCount === "number" && liveCount === 1);
|
|
389
|
+
|
|
390
|
+
// touch with extendBy -> bumps lastActivity + expiresAt; affectedRows>0.
|
|
391
|
+
var touched = await b.session.touch(created.token, { extendBy: b.constants.TIME.hours(12) });
|
|
392
|
+
softCheck("session(pg): touch() updated the live row (returned true)", touched === true);
|
|
393
|
+
var afterTouch = await b.session.verify(created.token);
|
|
394
|
+
softCheck("session(pg): touch extended expiresAt past the original",
|
|
395
|
+
afterTouch && afterTouch.expiresAt >= created.expiresAt);
|
|
396
|
+
|
|
397
|
+
// rotate -> new sid swapped atomically; old token no longer verifies.
|
|
398
|
+
var rotated = await b.session.rotate(created.token, { reason: "mfa" });
|
|
399
|
+
softCheck("session(pg): rotate returns a fresh sealed token",
|
|
400
|
+
rotated && typeof rotated.token === "string" && rotated.token !== created.token);
|
|
401
|
+
softCheck("session(pg): the OLD token no longer verifies after rotate",
|
|
402
|
+
(await b.session.verify(created.token)) === null);
|
|
403
|
+
var rotatedInfo = await b.session.verify(rotated.token);
|
|
404
|
+
softCheck("session(pg): the NEW token verifies with the same userId",
|
|
405
|
+
rotatedInfo && rotatedInfo.userId === "user-42");
|
|
406
|
+
|
|
407
|
+
// updateData -> writes the sealed data column without rotating the sid.
|
|
408
|
+
// The SELECT-then-UPDATE both dialect-thread through _sessionSqlOpts(); the
|
|
409
|
+
// data column round-trips through the sealed Postgres TEXT column.
|
|
410
|
+
var updated = await b.session.updateData(rotated.token, { roles: ["admin"], step: "mfa-done" });
|
|
411
|
+
softCheck("session(pg): updateData wrote the sealed data column (returned true)",
|
|
412
|
+
updated === true);
|
|
413
|
+
var afterUpdate = await b.session.verify(rotated.token);
|
|
414
|
+
softCheck("session(pg): updateData payload round-trips through the sealed Postgres column",
|
|
415
|
+
afterUpdate && afterUpdate.data && afterUpdate.data.step === "mfa-done");
|
|
416
|
+
|
|
417
|
+
// destroyAllForUser -> deletes via the derived userIdHash; count drops.
|
|
418
|
+
var revoked = await b.session.destroyAllForUser("user-42");
|
|
419
|
+
softCheck("session(pg): destroyAllForUser deleted the session via userIdHash",
|
|
420
|
+
revoked === 1);
|
|
421
|
+
softCheck("session(pg): the session no longer verifies after revoke-all",
|
|
422
|
+
(await b.session.verify(rotated.token)) === null);
|
|
423
|
+
softCheck("session(pg): count() is 0 after revoke-all", (await b.session.count()) === 0);
|
|
424
|
+
|
|
425
|
+
// destroy single + purgeExpired side-effects.
|
|
426
|
+
var s2 = await b.session.create({ userId: "user-99", ttlMs: b.constants.TIME.hours(1) });
|
|
427
|
+
softCheck("session(pg): destroy(token) returns true for a live session",
|
|
428
|
+
(await b.session.destroy(s2.token)) === true);
|
|
429
|
+
softCheck("session(pg): destroy is idempotent (second destroy returns false)",
|
|
430
|
+
(await b.session.destroy(s2.token)) === false);
|
|
431
|
+
|
|
432
|
+
// Insert an already-expired session directly so purgeExpired has work.
|
|
433
|
+
var expiredSidRow = await b.session.create({ userId: "user-exp", ttlMs: b.constants.TIME.hours(1) });
|
|
434
|
+
void expiredSidRow;
|
|
435
|
+
_psql('UPDATE _blamejs_sessions SET "expiresAt" = 1 WHERE "expiresAt" > 1;');
|
|
436
|
+
var purged = await b.session.purgeExpired();
|
|
437
|
+
softCheck("session(pg): purgeExpired removed the expired row(s) (>=1)", purged >= 1);
|
|
438
|
+
softCheck("session(pg): count() is 0 after purge", (await b.session.count()) === 0);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ======================================================================
|
|
442
|
+
// b.cache cluster backend on real Postgres.
|
|
443
|
+
// ======================================================================
|
|
444
|
+
async function _testCacheCluster() {
|
|
445
|
+
var cache = b.cache.create({ backend: "cluster", namespace: "pgns", ttlMs: b.constants.TIME.minutes(5) });
|
|
446
|
+
|
|
447
|
+
await cache.set("k1", { hello: "world", n: 42 });
|
|
448
|
+
var got = await cache.get("k1");
|
|
449
|
+
softCheck("cache(pg): set + get JSON round-trips on real Postgres",
|
|
450
|
+
got && got.hello === "world" && got.n === 42);
|
|
451
|
+
|
|
452
|
+
var rowDirect = _psql("SELECT \"valueJson\" FROM _blamejs_cache WHERE \"cacheKey\" = 'pgns:k1';");
|
|
453
|
+
softCheck("cache(pg): row physically present under the composite key",
|
|
454
|
+
/"hello"/.test(rowDirect));
|
|
455
|
+
|
|
456
|
+
softCheck("cache(pg): has() returns true for a live key", (await cache.has("k1")) === true);
|
|
457
|
+
softCheck("cache(pg): del() removes the key", (await cache.del("k1")) === true);
|
|
458
|
+
softCheck("cache(pg): get() after del is undefined", (await cache.get("k1")) === undefined);
|
|
459
|
+
|
|
460
|
+
// tags + invalidateTag (whereLike prefix scoping).
|
|
461
|
+
await cache.set("a", "1", { tags: ["grp-x"] });
|
|
462
|
+
await cache.set("bk", "2", { tags: ["grp-x", "grp-y"] });
|
|
463
|
+
await cache.set("ck", "3", { tags: ["grp-y"] });
|
|
464
|
+
var tagsA = await cache.getTags("a");
|
|
465
|
+
softCheck("cache(pg): getTags returns the tags written for a key",
|
|
466
|
+
Array.isArray(tagsA) && tagsA.indexOf("grp-x") !== -1);
|
|
467
|
+
|
|
468
|
+
var purged = await cache.invalidateTag("grp-x");
|
|
469
|
+
softCheck("cache(pg): invalidateTag purged the grp-x keys (>=2)", purged >= 2);
|
|
470
|
+
softCheck("cache(pg): invalidateTag dropped a + bk",
|
|
471
|
+
(await cache.get("a")) === undefined && (await cache.get("bk")) === undefined);
|
|
472
|
+
softCheck("cache(pg): invalidateTag preserved grp-y-only key ck",
|
|
473
|
+
(await cache.get("ck")) === "3");
|
|
474
|
+
|
|
475
|
+
// atomic update (CAS) increment.
|
|
476
|
+
await cache.update("counter", function (n) { return { value: (n || 0) + 1 }; });
|
|
477
|
+
await cache.update("counter", function (n) { return { value: (n || 0) + 1 }; });
|
|
478
|
+
softCheck("cache(pg): atomic update() increments through transaction+CAS",
|
|
479
|
+
(await cache.get("counter")) === 2);
|
|
480
|
+
|
|
481
|
+
// size() counts only live namespaced rows.
|
|
482
|
+
var sz = await cache.size();
|
|
483
|
+
softCheck("cache(pg): size() counts live namespaced rows as a number",
|
|
484
|
+
typeof sz === "number" && sz >= 1);
|
|
485
|
+
|
|
486
|
+
var cleared = await cache.clear();
|
|
487
|
+
softCheck("cache(pg): clear() wiped the namespace (>=1)", cleared >= 1);
|
|
488
|
+
softCheck("cache(pg): get after clear is undefined", (await cache.get("ck")) === undefined);
|
|
489
|
+
|
|
490
|
+
await cache.close();
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// ======================================================================
|
|
494
|
+
// b.nonceStore cluster backend on real Postgres.
|
|
495
|
+
// ======================================================================
|
|
496
|
+
async function _testNonceCluster() {
|
|
497
|
+
var store = b.nonceStore.create({ backend: "cluster" });
|
|
498
|
+
var future = Date.now() + b.constants.TIME.minutes(10);
|
|
499
|
+
|
|
500
|
+
softCheck("nonce(pg): first checkAndInsert returns true (unseen)",
|
|
501
|
+
(await store.checkAndInsert("nonce-aaa", future)) === true);
|
|
502
|
+
var n1 = _psql("SELECT count(*) AS n FROM _blamejs_api_encrypt_nonces WHERE \"nonceHash\" = 'nonce-aaa';");
|
|
503
|
+
softCheck("nonce(pg): the nonce row physically landed", /\b1\b/.test(n1.trim()));
|
|
504
|
+
|
|
505
|
+
softCheck("nonce(pg): replay of the same nonce returns false (ON CONFLICT DO NOTHING)",
|
|
506
|
+
(await store.checkAndInsert("nonce-aaa", future)) === false);
|
|
507
|
+
softCheck("nonce(pg): a distinct nonce is accepted",
|
|
508
|
+
(await store.checkAndInsert("nonce-bbb", future)) === true);
|
|
509
|
+
|
|
510
|
+
await store.checkAndInsert("nonce-expired", Date.now() - 1000);
|
|
511
|
+
var purged = await store.purgeExpired();
|
|
512
|
+
softCheck("nonce(pg): purgeExpired removed the expired nonce (>=1)", purged >= 1);
|
|
513
|
+
softCheck("nonce(pg): a live nonce still rejects replay after purge",
|
|
514
|
+
(await store.checkAndInsert("nonce-aaa", future)) === false);
|
|
515
|
+
|
|
516
|
+
store.close();
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// ======================================================================
|
|
520
|
+
// b.middleware.rateLimit cluster backend on real Postgres.
|
|
521
|
+
// take() = ON CONFLICT increment with a CASE conflict action +
|
|
522
|
+
// RETURNING; the returned BIGINT count must coerce to a JS number so
|
|
523
|
+
// count<=limit is a numeric comparison (a string "10" <= 3 would be a
|
|
524
|
+
// silent always-allow).
|
|
525
|
+
// ======================================================================
|
|
526
|
+
async function _testRateLimitCluster() {
|
|
527
|
+
var backend = rateLimitModule._clusterBackend({
|
|
528
|
+
backend: "cluster", limit: 3, windowMs: b.constants.TIME.minutes(1),
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
var v1 = await backend.take("ratekey-1", 1);
|
|
532
|
+
softCheck("rate-limit(pg): first take() is allowed against real Postgres",
|
|
533
|
+
v1 && v1.allowed === true);
|
|
534
|
+
softCheck("rate-limit(pg): the take() verdict count math is numeric " +
|
|
535
|
+
"(remaining is a finite number, not NaN from a string compare)",
|
|
536
|
+
typeof v1.remaining === "number" && isFinite(v1.remaining) && v1.remaining === 2);
|
|
537
|
+
|
|
538
|
+
var rowAfter1 = _psql("SELECT \"count\" FROM _blamejs_rate_limit_counters WHERE \"key\" = 'ratekey-1';");
|
|
539
|
+
softCheck("rate-limit(pg): counter row landed with count=1", /\b1\b/.test(rowAfter1.trim()));
|
|
540
|
+
|
|
541
|
+
var v2 = await backend.take("ratekey-1", 1);
|
|
542
|
+
var v3 = await backend.take("ratekey-1", 1);
|
|
543
|
+
var v4 = await backend.take("ratekey-1", 1);
|
|
544
|
+
softCheck("rate-limit(pg): 2nd + 3rd allowed, 4th over the limit refused",
|
|
545
|
+
v2.allowed === true && v3.allowed === true && v4.allowed === false);
|
|
546
|
+
softCheck("rate-limit(pg): the over-limit verdict carries a positive retryAfter",
|
|
547
|
+
typeof v4.retryAfter === "number" && v4.retryAfter > 0);
|
|
548
|
+
var rowAfter4 = _psql("SELECT \"count\" FROM _blamejs_rate_limit_counters WHERE \"key\" = 'ratekey-1';");
|
|
549
|
+
softCheck("rate-limit(pg): counter incremented monotonically to 4",
|
|
550
|
+
/\b4\b/.test(rowAfter4.trim()));
|
|
551
|
+
|
|
552
|
+
// A window advance resets the count (the CASE conflict action's
|
|
553
|
+
// window-rollover branch). Force a stale window then take() again.
|
|
554
|
+
_psql("UPDATE _blamejs_rate_limit_counters SET \"windowStart\" = 0 WHERE \"key\" = 'ratekey-1';");
|
|
555
|
+
var vReset = await backend.take("ratekey-1", 1);
|
|
556
|
+
softCheck("rate-limit(pg): a fresh window resets the count (CASE rollover) — allowed again",
|
|
557
|
+
vReset.allowed === true);
|
|
558
|
+
var rowAfterReset = _psql("SELECT \"count\" FROM _blamejs_rate_limit_counters WHERE \"key\" = 'ratekey-1';");
|
|
559
|
+
softCheck("rate-limit(pg): count reset to 1 on window advance",
|
|
560
|
+
/\b1\b/.test(rowAfterReset.trim()));
|
|
561
|
+
|
|
562
|
+
// A distinct key is tracked independently.
|
|
563
|
+
var other = await backend.take("ratekey-2", 1);
|
|
564
|
+
softCheck("rate-limit(pg): a distinct key is counted independently",
|
|
565
|
+
other.allowed === true && other.remaining === 2);
|
|
566
|
+
|
|
567
|
+
if (typeof backend.close === "function") backend.close();
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
module.exports = { run: run };
|
|
571
|
+
|
|
572
|
+
if (require.main === module) {
|
|
573
|
+
run().then(
|
|
574
|
+
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); process.exit(0); },
|
|
575
|
+
function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
|
|
576
|
+
);
|
|
577
|
+
}
|