@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
|
@@ -7510,6 +7510,53 @@ async function testOAuthVerifyIdTokenRoundTrip() {
|
|
|
7510
7510
|
threw = null;
|
|
7511
7511
|
try { await oa.verifyIdToken(wrongIss, {}); } catch (e) { threw = e; }
|
|
7512
7512
|
check("verifyIdToken: wrong iss rejected", threw && threw.code === "auth-oauth/iss-mismatch");
|
|
7513
|
+
|
|
7514
|
+
// #137 — skipExpCheck is ONLY valid for OIDC Back-Channel-Logout tokens
|
|
7515
|
+
// (no exp claim per §2.4). A caller must NOT be able to disable exp
|
|
7516
|
+
// validation on a regular ID token — that verifies an expired/replayed
|
|
7517
|
+
// credential clean. The option is gated to tokens carrying the logout
|
|
7518
|
+
// event claim, and even then bounded by an iat freshness floor.
|
|
7519
|
+
var LOGOUT_EVENT = "http://schemas.openid.net/event/backchannel-logout";
|
|
7520
|
+
threw = null;
|
|
7521
|
+
try { await oa.verifyIdToken(expired, { skipExpCheck: true }); } catch (e) { threw = e; }
|
|
7522
|
+
check("#137 verifyIdToken: skipExpCheck on a non-logout token is rejected",
|
|
7523
|
+
threw && threw.code === "auth-oauth/skip-exp-check-not-allowed");
|
|
7524
|
+
|
|
7525
|
+
// A genuine logout token (events claim, recent iat, no exp) still verifies
|
|
7526
|
+
// under skipExpCheck — the internal back-channel-logout path relies on it.
|
|
7527
|
+
var freshLogout = _signRs256({
|
|
7528
|
+
iss: issuerUrl, sub: "user-1", aud: clientId, iat: nowSec,
|
|
7529
|
+
events: (function () { var e = {}; e[LOGOUT_EVENT] = {}; return e; })(),
|
|
7530
|
+
}, { kid: "test-kid-1" }, kp.privateKey);
|
|
7531
|
+
var logoutOk = await oa.verifyIdToken(freshLogout, { skipExpCheck: true, skipNonceCheck: true });
|
|
7532
|
+
check("#137 verifyIdToken: a fresh logout token verifies under skipExpCheck",
|
|
7533
|
+
logoutOk && logoutOk.claims && logoutOk.claims.sub === "user-1");
|
|
7534
|
+
|
|
7535
|
+
// A stale logout token (iat older than the freshness floor) is rejected —
|
|
7536
|
+
// skipExpCheck removes exp, so iat is the only freshness bound.
|
|
7537
|
+
var staleLogout = _signRs256({
|
|
7538
|
+
iss: issuerUrl, sub: "user-1", aud: clientId, iat: nowSec - 86400,
|
|
7539
|
+
events: (function () { var e = {}; e[LOGOUT_EVENT] = {}; return e; })(),
|
|
7540
|
+
}, { kid: "test-kid-1" }, kp.privateKey);
|
|
7541
|
+
threw = null;
|
|
7542
|
+
try { await oa.verifyIdToken(staleLogout, { skipExpCheck: true, skipNonceCheck: true }); } catch (e) { threw = e; }
|
|
7543
|
+
check("#137 verifyIdToken: a stale logout token is rejected on iat floor",
|
|
7544
|
+
threw && threw.code === "auth-oauth/logout-token-stale");
|
|
7545
|
+
|
|
7546
|
+
// The iat floor must honor a configured maxAgeSec (back-channel-logout
|
|
7547
|
+
// deployments can widen the replay window). A 10-min-old logout token is
|
|
7548
|
+
// rejected under the 5-min default but accepted under maxAgeSec: 20 min.
|
|
7549
|
+
var agedLogout = _signRs256({
|
|
7550
|
+
iss: issuerUrl, sub: "user-1", aud: clientId, iat: nowSec - 600,
|
|
7551
|
+
events: (function () { var e = {}; e[LOGOUT_EVENT] = {}; return e; })(),
|
|
7552
|
+
}, { kid: "test-kid-1" }, kp.privateKey);
|
|
7553
|
+
threw = null;
|
|
7554
|
+
try { await oa.verifyIdToken(agedLogout, { skipExpCheck: true, skipNonceCheck: true }); } catch (e) { threw = e; }
|
|
7555
|
+
check("#137 verifyIdToken: a 10-min-old logout token is stale under the 5-min default",
|
|
7556
|
+
threw && threw.code === "auth-oauth/logout-token-stale");
|
|
7557
|
+
var agedOk = await oa.verifyIdToken(agedLogout, { skipExpCheck: true, skipNonceCheck: true, maxAgeSec: 1200 });
|
|
7558
|
+
check("#137 verifyIdToken: the configured maxAgeSec widens the iat freshness window",
|
|
7559
|
+
agedOk && agedOk.claims && agedOk.claims.sub === "user-1");
|
|
7513
7560
|
} finally { server.close(); }
|
|
7514
7561
|
}
|
|
7515
7562
|
|
|
@@ -12418,6 +12465,74 @@ function testErrorsPageHookOptsRejectNonFunction() {
|
|
|
12418
12465
|
check("renderHtml non-function throws at config time", threwHtml);
|
|
12419
12466
|
}
|
|
12420
12467
|
|
|
12468
|
+
async function testErrorsPageAuditRedactsSecretsInStackAndReason() {
|
|
12469
|
+
// CWE-532: a secret embedded in an exception message/stack (e.g. a
|
|
12470
|
+
// database connection string a driver dropped into Error.message) must
|
|
12471
|
+
// NOT be persisted verbatim in the tamper-evident audit chain. The 5xx
|
|
12472
|
+
// audit emission goes through audit.safeEmit, whose b.redact.redact()
|
|
12473
|
+
// pass scrubs connection-string / JWT / PEM / AWS-key shapes — including
|
|
12474
|
+
// nested metadata.stack — before the record reaches the chain.
|
|
12475
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-errpg-"));
|
|
12476
|
+
try {
|
|
12477
|
+
await setupTestDb(tmpDir);
|
|
12478
|
+
// The default audit action is "request.error"; its namespace is not a
|
|
12479
|
+
// framework namespace, so register it the way an operator would.
|
|
12480
|
+
b.audit.registerNamespace("request");
|
|
12481
|
+
|
|
12482
|
+
var secret = "postgres://user:s3cr3t@db.internal/app";
|
|
12483
|
+
var handler = b.errorPage.create({ mode: "prod" }); // audit on by default
|
|
12484
|
+
var req = { method: "POST", url: "/api/widget", headers: { accept: "application/json" }, id: "req-redact-1" };
|
|
12485
|
+
var res = _makeFakeRes();
|
|
12486
|
+
// Generic Error → 500. Its message (and therefore its stack) carries
|
|
12487
|
+
// the secret-shaped connection string.
|
|
12488
|
+
handler(new Error("connect failed for " + secret), req, res);
|
|
12489
|
+
|
|
12490
|
+
// Response behavior is unchanged: 500 status, JSON body, and the
|
|
12491
|
+
// generic message — the original (secret-bearing) message never
|
|
12492
|
+
// reaches the client on an unclassified 500.
|
|
12493
|
+
check("audit-redact: response is 500", res.statusCode === 500);
|
|
12494
|
+
check("audit-redact: response is JSON", /application\/json/.test(res.headers["Content-Type"]));
|
|
12495
|
+
var payload = JSON.parse(res.body);
|
|
12496
|
+
check("audit-redact: response hides operator message", res.body.indexOf("s3cr3t") === -1);
|
|
12497
|
+
check("audit-redact: response shows generic message",
|
|
12498
|
+
payload.error.message === "Internal Server Error");
|
|
12499
|
+
|
|
12500
|
+
await b.audit.flush();
|
|
12501
|
+
await helpers.waitUntil(async function () {
|
|
12502
|
+
var rows = await b.audit.query({ action: "request.error" });
|
|
12503
|
+
return rows.length >= 1;
|
|
12504
|
+
}, { timeoutMs: 5000, label: "errors-page audit: request.error row persisted" });
|
|
12505
|
+
|
|
12506
|
+
var events = await b.audit.query({ action: "request.error" });
|
|
12507
|
+
check("audit-redact: 5xx audit row persisted", events.length === 1);
|
|
12508
|
+
|
|
12509
|
+
var row = events[0];
|
|
12510
|
+
var meta = row.metadata;
|
|
12511
|
+
if (typeof meta === "string") meta = JSON.parse(meta);
|
|
12512
|
+
|
|
12513
|
+
// The whole persisted row must be free of the secret — neither the
|
|
12514
|
+
// nested metadata.stack nor the reason (original error message) may
|
|
12515
|
+
// carry it verbatim.
|
|
12516
|
+
var rowJson = JSON.stringify(row);
|
|
12517
|
+
check("audit-redact: secret absent from full persisted row",
|
|
12518
|
+
rowJson.indexOf("s3cr3t") === -1);
|
|
12519
|
+
check("audit-redact: secret absent from metadata.stack",
|
|
12520
|
+
!meta || typeof meta.stack !== "string" || meta.stack.indexOf("s3cr3t") === -1);
|
|
12521
|
+
check("audit-redact: secret absent from reason",
|
|
12522
|
+
typeof row.reason !== "string" || row.reason.indexOf("s3cr3t") === -1);
|
|
12523
|
+
// The connection-string redactor leaves a marker so triage stays
|
|
12524
|
+
// useful — confirm the field was scanned, not merely dropped.
|
|
12525
|
+
check("audit-redact: stack carries the connection-string marker",
|
|
12526
|
+
!!meta && typeof meta.stack === "string" &&
|
|
12527
|
+
meta.stack.indexOf("[REDACTED-CONN-STRING]") !== -1);
|
|
12528
|
+
// Non-secret triage fields survive redaction.
|
|
12529
|
+
check("audit-redact: non-secret metadata preserved",
|
|
12530
|
+
!!meta && meta.status === 500 && meta.method === "POST" && meta.url === "/api/widget");
|
|
12531
|
+
} finally {
|
|
12532
|
+
await teardownTestDb(tmpDir);
|
|
12533
|
+
}
|
|
12534
|
+
}
|
|
12535
|
+
|
|
12421
12536
|
// ---- log ----
|
|
12422
12537
|
//
|
|
12423
12538
|
// Each test creates an instance with a captured-buffer destination so
|
|
@@ -18347,6 +18462,7 @@ async function run() {
|
|
|
18347
18462
|
testErrorsPageJsonFormatterTakesOverBody();
|
|
18348
18463
|
testErrorsPageRenderHtmlHook();
|
|
18349
18464
|
testErrorsPageHookOptsRejectNonFunction();
|
|
18465
|
+
await testErrorsPageAuditRedactsSecretsInStackAndReason();
|
|
18350
18466
|
// log — structured JSON logging with request-id correlation
|
|
18351
18467
|
testLogSurface();
|
|
18352
18468
|
testLogEmitsJsonLineToStdout();
|
|
@@ -19048,6 +19164,7 @@ module.exports = {
|
|
|
19048
19164
|
testErrorsPageJsonFormatterTakesOverBody: testErrorsPageJsonFormatterTakesOverBody,
|
|
19049
19165
|
testErrorsPageRenderHtmlHook: testErrorsPageRenderHtmlHook,
|
|
19050
19166
|
testErrorsPageHookOptsRejectNonFunction: testErrorsPageHookOptsRejectNonFunction,
|
|
19167
|
+
testErrorsPageAuditRedactsSecretsInStackAndReason: testErrorsPageAuditRedactsSecretsInStackAndReason,
|
|
19051
19168
|
testLogSurface: testLogSurface,
|
|
19052
19169
|
testLogEmitsJsonLineToStdout: testLogEmitsJsonLineToStdout,
|
|
19053
19170
|
testLogRoutesErrorAndFatalToStderr: testLogRoutesErrorAndFatalToStderr,
|
|
@@ -372,9 +372,16 @@ async function testClusterProviderMysqlDialect() {
|
|
|
372
372
|
check("mysql provider: emitted INSERT...ON DUPLICATE KEY",
|
|
373
373
|
sqlSeen.some(function (e) { return /ON DUPLICATE KEY UPDATE/.test(e.sql); }));
|
|
374
374
|
check("mysql provider: uses ? placeholders not $1",
|
|
375
|
-
sqlSeen.some(function (e) {
|
|
375
|
+
sqlSeen.some(function (e) {
|
|
376
|
+
// The provider composes the upsert through b.sql, which binds
|
|
377
|
+
// every value (scope included) as a `?` rather than inlining
|
|
378
|
+
// the 'leader' literal — so the MySQL form is an all-`?` VALUES
|
|
379
|
+
// list with no Postgres `$1` numbering.
|
|
380
|
+
return /INSERT INTO _blamejs_leader[\s\S]*VALUES\s*\(\s*\?(?:\s*,\s*\?)*\s*\)/.test(e.sql) &&
|
|
381
|
+
!/\$1/.test(e.sql);
|
|
382
|
+
}));
|
|
376
383
|
check("mysql provider: gates fencingToken with IF(expiresAt < ?, ...)",
|
|
377
|
-
sqlSeen.some(function (e) { return
|
|
384
|
+
sqlSeen.some(function (e) { return /`fencingToken` = IF\(`expiresAt` < \?, `fencingToken` \+ 1, `fencingToken`\)/.test(e.sql); }));
|
|
378
385
|
|
|
379
386
|
// Second node blocked while A holds.
|
|
380
387
|
var leaseB = await pB.acquireLease("node-B", b.constants.TIME.seconds(30));
|
|
@@ -558,20 +565,137 @@ function testFrameworkSchemaTableNameMapping() {
|
|
|
558
565
|
b.frameworkSchema.tableName("custom_table") === "custom_table");
|
|
559
566
|
check("LOCAL_TO_EXTERNAL is frozen",
|
|
560
567
|
Object.isFrozen(b.frameworkSchema.LOCAL_TO_EXTERNAL));
|
|
568
|
+
|
|
569
|
+
// ---- Configurable table prefix ----
|
|
570
|
+
// Default prefix is byte-identical to the historical names.
|
|
571
|
+
check("getTablePrefix default is _blamejs_",
|
|
572
|
+
b.frameworkSchema.getTablePrefix() === "_blamejs_");
|
|
573
|
+
check("DEFAULT_TABLE_PREFIX is _blamejs_",
|
|
574
|
+
b.frameworkSchema.DEFAULT_TABLE_PREFIX === "_blamejs_");
|
|
575
|
+
|
|
576
|
+
// setTablePrefix throws config-time on a non-identifier prefix.
|
|
577
|
+
var threwEmpty = false;
|
|
578
|
+
try { b.frameworkSchema.setTablePrefix(""); }
|
|
579
|
+
catch (e) { threwEmpty = e.code === "framework-schema/invalid-prefix"; }
|
|
580
|
+
check("setTablePrefix('') throws invalid-prefix", threwEmpty);
|
|
581
|
+
var threwBad = false;
|
|
582
|
+
try { b.frameworkSchema.setTablePrefix("bad-prefix!"); }
|
|
583
|
+
catch (e) { threwBad = e.code === "framework-schema/invalid-prefix"; }
|
|
584
|
+
check("setTablePrefix('bad-prefix!') throws invalid-prefix", threwBad);
|
|
585
|
+
var threwType = false;
|
|
586
|
+
try { b.frameworkSchema.setTablePrefix(123); }
|
|
587
|
+
catch (e) { threwType = e.code === "framework-schema/invalid-prefix"; }
|
|
588
|
+
check("setTablePrefix(non-string) throws invalid-prefix", threwType);
|
|
589
|
+
// A bad prefix must not have mutated state.
|
|
590
|
+
check("getTablePrefix unchanged after rejected prefix",
|
|
591
|
+
b.frameworkSchema.getTablePrefix() === "_blamejs_");
|
|
592
|
+
|
|
593
|
+
// A valid prefix swaps the leading default across every framework name.
|
|
594
|
+
try {
|
|
595
|
+
b.frameworkSchema.setTablePrefix("acme_");
|
|
596
|
+
check("getTablePrefix reflects configured prefix",
|
|
597
|
+
b.frameworkSchema.getTablePrefix() === "acme_");
|
|
598
|
+
check("tableName('audit_log') honors configured prefix",
|
|
599
|
+
b.frameworkSchema.tableName("audit_log") === "acme_audit_log");
|
|
600
|
+
check("tableName('_blamejs_sessions') honors configured prefix",
|
|
601
|
+
b.frameworkSchema.tableName("_blamejs_sessions") === "acme_sessions");
|
|
602
|
+
check("tableName(unknown) still identity under configured prefix",
|
|
603
|
+
b.frameworkSchema.tableName("custom_table") === "custom_table");
|
|
604
|
+
} finally {
|
|
605
|
+
// Restore the default so the rest of the suite (and the running DB)
|
|
606
|
+
// see the historical names — this state is module-global.
|
|
607
|
+
b.frameworkSchema.setTablePrefix("_blamejs_");
|
|
608
|
+
}
|
|
609
|
+
check("getTablePrefix restored to default",
|
|
610
|
+
b.frameworkSchema.getTablePrefix() === "_blamejs_");
|
|
561
611
|
}
|
|
562
612
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
613
|
+
// resolveTables rewrites bare framework table names to their prefixed
|
|
614
|
+
// external form ONLY in cluster mode, and honors a configured tablePrefix
|
|
615
|
+
// (set config-time). Proves _REWRITE_TABLE is prefix-aware (rebuilt when the
|
|
616
|
+
// prefix changes), so cluster-mode DML targets the same prefixed tables the
|
|
617
|
+
// DDL builders create — both the local-mapped names (audit_log) and the
|
|
618
|
+
// already-`_blamejs_`-prefixed identity names (_blamejs_scheduler_ticks),
|
|
619
|
+
// which only need rewriting under a custom prefix.
|
|
620
|
+
async function testClusterStoragePrefixRewrite() {
|
|
621
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-clpfx-"));
|
|
622
|
+
var dbPath = path.join(tmpDir, "cluster.db");
|
|
623
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
624
|
+
try {
|
|
625
|
+
b.externalDb.init({
|
|
626
|
+
backends: {
|
|
627
|
+
"ops": { connect: driver.connect, query: driver.query, close: driver.close },
|
|
628
|
+
},
|
|
629
|
+
});
|
|
630
|
+
b.cluster._resetForTest();
|
|
631
|
+
await b.cluster.init({
|
|
632
|
+
nodeId: "pfx-node-1",
|
|
633
|
+
externalDbBackend: "ops",
|
|
634
|
+
dialect: "sqlite",
|
|
635
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
636
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
// Default prefix: local-mapped name rewrites to its _blamejs_ external;
|
|
640
|
+
// identity-mapped name passes through unchanged.
|
|
641
|
+
check("resolveTables rewrites audit_log under default prefix",
|
|
642
|
+
b.clusterStorage.resolveTables("SELECT id FROM audit_log WHERE n > ?") ===
|
|
643
|
+
"SELECT id FROM _blamejs_audit_log WHERE n > ?");
|
|
644
|
+
check("resolveTables leaves _blamejs_scheduler_ticks under default prefix",
|
|
645
|
+
b.clusterStorage.resolveTables("DELETE FROM _blamejs_scheduler_ticks") ===
|
|
646
|
+
"DELETE FROM _blamejs_scheduler_ticks");
|
|
647
|
+
|
|
648
|
+
// Custom prefix: BOTH the local-mapped and the identity-mapped names
|
|
649
|
+
// rewrite to <prefix>* so cluster DML matches the prefixed DDL. App
|
|
650
|
+
// tables are never rewritten.
|
|
651
|
+
try {
|
|
652
|
+
b.frameworkSchema.setTablePrefix("acme_");
|
|
653
|
+
check("resolveTables rewrites audit_log to the configured prefix",
|
|
654
|
+
b.clusterStorage.resolveTables("SELECT id FROM audit_log WHERE n > ?") ===
|
|
655
|
+
"SELECT id FROM acme_audit_log WHERE n > ?");
|
|
656
|
+
check("resolveTables rewrites identity name _blamejs_scheduler_ticks to the prefix",
|
|
657
|
+
b.clusterStorage.resolveTables("DELETE FROM _blamejs_scheduler_ticks") ===
|
|
658
|
+
"DELETE FROM acme_scheduler_ticks");
|
|
659
|
+
check("resolveTables leaves a non-framework table untouched under prefix",
|
|
660
|
+
b.clusterStorage.resolveTables("SELECT * FROM my_app_table") ===
|
|
661
|
+
"SELECT * FROM my_app_table");
|
|
662
|
+
} finally {
|
|
663
|
+
b.frameworkSchema.setTablePrefix("_blamejs_");
|
|
664
|
+
}
|
|
665
|
+
} finally {
|
|
666
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
667
|
+
b.cluster._resetForTest();
|
|
668
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
669
|
+
driver._close();
|
|
670
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
async function testFrameworkSchemaInvalidDialect() {
|
|
675
|
+
// postgres, sqlite, and mysql are the supported dialects. A dialect
|
|
676
|
+
// outside that set is rejected at the config-validation gate with
|
|
677
|
+
// framework-schema/unsupported-dialect, BEFORE any backend dispatch — so
|
|
678
|
+
// an operator typo surfaces at boot. (The mysql DDL path itself is proven
|
|
679
|
+
// end-to-end against a live server in framework-schema-mysql.test.js.)
|
|
680
|
+
var unsupportedErr = null;
|
|
681
|
+
try {
|
|
682
|
+
await b.frameworkSchema.ensureSchema({ externalDbBackend: "ops", dialect: "oracle" });
|
|
683
|
+
} catch (e) { unsupportedErr = e; }
|
|
684
|
+
check("ensureSchema rejects an unsupported dialect at the config gate",
|
|
685
|
+
unsupportedErr !== null &&
|
|
686
|
+
unsupportedErr.code === "framework-schema/unsupported-dialect");
|
|
687
|
+
|
|
688
|
+
// mysql is now a SUPPORTED dialect, so it passes the dialect-validation
|
|
689
|
+
// gate — a mysql ensureSchema no longer fails with the unsupported-dialect
|
|
690
|
+
// code (it proceeds to backend dispatch; the live DDL is covered in the
|
|
691
|
+
// integration suite). Assert the gate no longer rejects it.
|
|
692
|
+
var mysqlErr = null;
|
|
693
|
+
try {
|
|
694
|
+
await b.frameworkSchema.ensureSchema({ externalDbBackend: "ops", dialect: "mysql" });
|
|
695
|
+
} catch (e) { mysqlErr = e; }
|
|
696
|
+
check("ensureSchema accepts mysql at the dialect gate (no unsupported-dialect rejection)",
|
|
697
|
+
mysqlErr === null ||
|
|
698
|
+
mysqlErr.code !== "framework-schema/unsupported-dialect");
|
|
575
699
|
}
|
|
576
700
|
|
|
577
701
|
// ---- run() ----
|
|
@@ -597,6 +721,7 @@ async function run() {
|
|
|
597
721
|
// framework-schema (DDL emitter + table-name resolver)
|
|
598
722
|
await testFrameworkSchemaEnsure();
|
|
599
723
|
testFrameworkSchemaTableNameMapping();
|
|
724
|
+
await testClusterStoragePrefixRewrite();
|
|
600
725
|
await testFrameworkSchemaInvalidDialect();
|
|
601
726
|
}
|
|
602
727
|
|
|
@@ -618,5 +743,6 @@ module.exports = {
|
|
|
618
743
|
testClusterInitAndRequireLeader: testClusterInitAndRequireLeader,
|
|
619
744
|
testFrameworkSchemaEnsure: testFrameworkSchemaEnsure,
|
|
620
745
|
testFrameworkSchemaTableNameMapping: testFrameworkSchemaTableNameMapping,
|
|
746
|
+
testClusterStoragePrefixRewrite: testClusterStoragePrefixRewrite,
|
|
621
747
|
testFrameworkSchemaInvalidDialect: testFrameworkSchemaInvalidDialect,
|
|
622
748
|
};
|
|
@@ -46,7 +46,7 @@ async function testDbBasic() {
|
|
|
46
46
|
var rawRow = rawStmt.get(inserted._id);
|
|
47
47
|
check("on-disk email is sealed", typeof rawRow.email === "string" && rawRow.email.startsWith("vault:"));
|
|
48
48
|
check("on-disk name is sealed", typeof rawRow.name === "string" && rawRow.name.startsWith("vault:"));
|
|
49
|
-
check("emailHash is computed", typeof rawRow.emailHash === "string" && rawRow.emailHash
|
|
49
|
+
check("emailHash is computed", typeof rawRow.emailHash === "string" && /^[0-9a-f]{32,}$/.test(rawRow.emailHash));
|
|
50
50
|
check("emailHash is normalized", rawRow.emailHash === b.db.hashFor("users", "email", "ALICE@example.com"));
|
|
51
51
|
|
|
52
52
|
// Query via plain field name (sealed → translated to emailHash)
|
|
@@ -510,7 +510,8 @@ async function testEncryptedTmpfsCorruptionAutoRecovers() {
|
|
|
510
510
|
// must be discarded and db.enc re-decrypted — boot must succeed.
|
|
511
511
|
setTestPassphraseEnv();
|
|
512
512
|
b.audit._resetForTest();
|
|
513
|
-
await b.db.init({ dataDir: tmpDir, tmpDir: path.join(tmpDir, "tmpfs"), schema: schema
|
|
513
|
+
await b.db.init({ dataDir: tmpDir, tmpDir: path.join(tmpDir, "tmpfs"), schema: schema,
|
|
514
|
+
allowNonTmpfsTmpDir: true }); // .test-output scratch is not a tmpfs mount (v0.15.0 refusal opt-out)
|
|
514
515
|
var row = b.db.prepare("SELECT v FROM recovery_t WHERE _id = ?").get("r1");
|
|
515
516
|
check("corrupt tmpfs working copy auto-recovers from db.enc (no crash loop)",
|
|
516
517
|
row && row.v === "survives");
|
|
@@ -583,6 +584,7 @@ async function testTmpfsLowSpaceRefusesWritesFailClear() {
|
|
|
583
584
|
await b.db.init({
|
|
584
585
|
dataDir: tmpDir, tmpDir: path.join(tmpDir, "tmpfs"), schema: schema,
|
|
585
586
|
minFreeBytes: BYTES.mib(16), _statfsForTest: fakeStatfs,
|
|
587
|
+
allowNonTmpfsTmpDir: true, // .test-output scratch is not a tmpfs mount (v0.15.0 refusal opt-out)
|
|
586
588
|
});
|
|
587
589
|
check("low-space test runs in encrypted mode", b.db.getMode() === "encrypted");
|
|
588
590
|
|
|
@@ -643,6 +645,66 @@ async function testExitHandlerRegisteredOnce() {
|
|
|
643
645
|
check("exit handler registered once across init/close cycles (no listener leak)", added <= 1);
|
|
644
646
|
}
|
|
645
647
|
|
|
648
|
+
async function testEncryptedNonTmpfsTmpDirRefusedByDefault() {
|
|
649
|
+
// v0.15.0: encrypted mode REFUSES a tmpDir that does not resolve under a
|
|
650
|
+
// recognized tmpfs mount (a persistent-disk working copy leaks the
|
|
651
|
+
// decrypted DB into backups / forensic images). The documented opt-out
|
|
652
|
+
// is allowNonTmpfsTmpDir:true. The heuristic is Linux-only — on other
|
|
653
|
+
// platforms the gate emits a warning and does not throw, so the
|
|
654
|
+
// fail-closed assertion only runs on Linux. A repo-local .test-output
|
|
655
|
+
// path is provably outside /dev/shm /run/shm /run/user /tmp.
|
|
656
|
+
if (process.platform !== "linux") {
|
|
657
|
+
check("non-tmpfs tmpDir gate is Linux-only (skipped off-Linux)", true);
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
var scratchBase = path.join(__dirname, "..", ".test-output");
|
|
661
|
+
fs.mkdirSync(scratchBase, { recursive: true });
|
|
662
|
+
var schema = [{ name: "tmpfs_gate_t", columns: { _id: "TEXT PRIMARY KEY", v: "TEXT" } }];
|
|
663
|
+
|
|
664
|
+
// --- default: refuse ---
|
|
665
|
+
var tmpDir = fs.mkdtempSync(path.join(scratchBase, "db-nontmpfs-refuse-"));
|
|
666
|
+
try {
|
|
667
|
+
process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
|
|
668
|
+
setTestPassphraseEnv();
|
|
669
|
+
b.cluster._resetForTest();
|
|
670
|
+
b.audit._resetForTest();
|
|
671
|
+
b.vault._resetForTest();
|
|
672
|
+
b.db._resetForTest();
|
|
673
|
+
await b.vault.init({ dataDir: tmpDir });
|
|
674
|
+
var refuseErr = null;
|
|
675
|
+
try {
|
|
676
|
+
await b.db.init({ dataDir: tmpDir, tmpDir: path.join(tmpDir, "persist"), schema: schema });
|
|
677
|
+
} catch (e) { refuseErr = e; }
|
|
678
|
+
check("encrypted non-tmpfs tmpDir refused by default (fail-closed)",
|
|
679
|
+
refuseErr && refuseErr.code === "db/tmpdir-not-tmpfs");
|
|
680
|
+
} finally {
|
|
681
|
+
try { b.db._resetForTest(); } catch (_e) { /* best effort */ }
|
|
682
|
+
await teardownTestDb(tmpDir);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// --- documented opt-out: allowNonTmpfsTmpDir:true boots ---
|
|
686
|
+
var tmpDir2 = fs.mkdtempSync(path.join(scratchBase, "db-nontmpfs-allow-"));
|
|
687
|
+
try {
|
|
688
|
+
process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
|
|
689
|
+
setTestPassphraseEnv();
|
|
690
|
+
b.cluster._resetForTest();
|
|
691
|
+
b.audit._resetForTest();
|
|
692
|
+
b.vault._resetForTest();
|
|
693
|
+
b.db._resetForTest();
|
|
694
|
+
await b.vault.init({ dataDir: tmpDir2 });
|
|
695
|
+
var allowErr = null;
|
|
696
|
+
try {
|
|
697
|
+
await b.db.init({ dataDir: tmpDir2, tmpDir: path.join(tmpDir2, "persist"),
|
|
698
|
+
schema: schema, allowNonTmpfsTmpDir: true });
|
|
699
|
+
} catch (e) { allowErr = e; }
|
|
700
|
+
check("allowNonTmpfsTmpDir:true opts out of the refusal (boots)", allowErr === null);
|
|
701
|
+
check("opt-out boot runs in encrypted mode", b.db.getMode() === "encrypted");
|
|
702
|
+
} finally {
|
|
703
|
+
try { b.db._resetForTest(); } catch (_e) { /* best effort */ }
|
|
704
|
+
await teardownTestDb(tmpDir2);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
646
708
|
// ---- run() ----
|
|
647
709
|
|
|
648
710
|
async function run() {
|
|
@@ -652,6 +714,7 @@ async function run() {
|
|
|
652
714
|
await testEncryptedTmpfsCorruptionAutoRecovers();
|
|
653
715
|
await testEncryptedCloseKeepsPlaintextWhenEncryptFails();
|
|
654
716
|
await testTmpfsLowSpaceRefusesWritesFailClear();
|
|
717
|
+
await testEncryptedNonTmpfsTmpDirRefusedByDefault();
|
|
655
718
|
await testExitHandlerRegisteredOnce();
|
|
656
719
|
await testDbWriteOps();
|
|
657
720
|
await testDbSealedWithoutDerived();
|
|
@@ -39,6 +39,12 @@ async function setupTestDb(tmpDir, schemaOverrides) {
|
|
|
39
39
|
await b.db.init({
|
|
40
40
|
dataDir: tmpDir,
|
|
41
41
|
tmpDir: path.join(tmpDir, "tmpfs"),
|
|
42
|
+
// Test scratch dir is a plain directory, not a real tmpfs mount, and may
|
|
43
|
+
// live under the repo-local .test-output (outside the /tmp heuristic) for
|
|
44
|
+
// tests that corrupt their working file in place. Encrypted mode now
|
|
45
|
+
// refuses a non-tmpfs tmpDir by default (v0.15.0); the fixture knowingly
|
|
46
|
+
// uses one, so opt out explicitly.
|
|
47
|
+
allowNonTmpfsTmpDir: true,
|
|
42
48
|
schema: schemaOverrides || [
|
|
43
49
|
{
|
|
44
50
|
name: "users",
|
|
@@ -82,6 +88,9 @@ async function setupTestDbForMW() {
|
|
|
82
88
|
await b.db.init({
|
|
83
89
|
dataDir: tmpDir,
|
|
84
90
|
tmpDir: path.join(tmpDir, "tmpfs"),
|
|
91
|
+
// Plain scratch directory, not a real tmpfs mount — opt out of the
|
|
92
|
+
// v0.15.0 encrypted-mode non-tmpfs refusal for the test fixture.
|
|
93
|
+
allowNonTmpfsTmpDir: true,
|
|
85
94
|
schema: [],
|
|
86
95
|
});
|
|
87
96
|
global._mwTmpDir = tmpDir;
|
|
@@ -118,18 +118,24 @@ function _makeFakeMysqlDriver() {
|
|
|
118
118
|
var inLeader = /INSERT INTO _blamejs_leader/i.test(t) &&
|
|
119
119
|
/ON DUPLICATE KEY UPDATE/i.test(t);
|
|
120
120
|
if (inLeader) {
|
|
121
|
-
//
|
|
122
|
-
//
|
|
121
|
+
// The provider composes this upsert through b.sql, which binds the
|
|
122
|
+
// VALUES list (scope first) then the ON DUPLICATE KEY UPDATE IF()
|
|
123
|
+
// params. Layout:
|
|
124
|
+
// VALUES: [0]=scope 'leader', [1]=nodeId, [2]=leaseId,
|
|
125
|
+
// [3]=acquiredAt, [4]=expiresAt, [5]=fencingToken(1),
|
|
126
|
+
// [6]=endpoint
|
|
127
|
+
// then the IF(expiresAt < ?, <new>, <old>) guards + proposed
|
|
128
|
+
// values; every guard binds nowMs, the first at [7].
|
|
123
129
|
var newRow = {
|
|
124
|
-
scope: "leader", nodeId: params[
|
|
125
|
-
acquiredAt: params[
|
|
130
|
+
scope: "leader", nodeId: params[1], leaseId: params[2],
|
|
131
|
+
acquiredAt: params[3], expiresAt: params[4], endpoint: params[6],
|
|
126
132
|
fencingToken: 1,
|
|
127
133
|
};
|
|
128
134
|
var existing = rows._blamejs_leader;
|
|
129
135
|
if (!existing) {
|
|
130
136
|
rows._blamejs_leader = newRow;
|
|
131
137
|
} else {
|
|
132
|
-
var nowPredicate = params[
|
|
138
|
+
var nowPredicate = params[7];
|
|
133
139
|
if (existing.expiresAt < nowPredicate) {
|
|
134
140
|
// Steal — IF() → use VALUES(*) for non-fencingToken cols,
|
|
135
141
|
// bump fencingToken.
|
|
@@ -145,12 +151,15 @@ function _makeFakeMysqlDriver() {
|
|
|
145
151
|
return { rows: [], affectedRows: 1 };
|
|
146
152
|
}
|
|
147
153
|
|
|
148
|
-
// UPDATE _blamejs_leader SET expiresAt = ?, endpoint = ?
|
|
149
|
-
// WHERE scope=
|
|
150
|
-
|
|
154
|
+
// UPDATE _blamejs_leader SET `expiresAt` = ?, `endpoint` = ?
|
|
155
|
+
// WHERE `scope` = ? AND `nodeId` = ? AND `leaseId` = ? (renew).
|
|
156
|
+
// The provider composes this through b.sql: identifiers are
|
|
157
|
+
// backtick-quoted and the scope value binds (params[2]='leader')
|
|
158
|
+
// ahead of nodeId (params[3]) / leaseId (params[4]).
|
|
159
|
+
var renewMatch = /^UPDATE _blamejs_leader SET[\s\S]*`?expiresAt`? = \?[\s\S]*`?endpoint`? = \?[\s\S]*WHERE `?scope`? = \? AND `?nodeId`? = \? AND `?leaseId`? = \?/i.test(t);
|
|
151
160
|
if (renewMatch) {
|
|
152
161
|
var r = rows._blamejs_leader;
|
|
153
|
-
if (r && r.nodeId === params[
|
|
162
|
+
if (r && r.nodeId === params[3] && r.leaseId === params[4]) {
|
|
154
163
|
r.expiresAt = params[0];
|
|
155
164
|
r.endpoint = params[1];
|
|
156
165
|
return { rows: [], affectedRows: 1 };
|
|
@@ -158,18 +167,21 @@ function _makeFakeMysqlDriver() {
|
|
|
158
167
|
return { rows: [], affectedRows: 0 };
|
|
159
168
|
}
|
|
160
169
|
|
|
161
|
-
// UPDATE _blamejs_leader SET expiresAt =
|
|
162
|
-
|
|
170
|
+
// UPDATE _blamejs_leader SET `expiresAt` = ? WHERE ... (release).
|
|
171
|
+
// b.sql binds expiresAt=0 (params[0]) + scope (params[1]) + nodeId
|
|
172
|
+
// (params[2]) + leaseId (params[3]).
|
|
173
|
+
var releaseMatch = /^UPDATE _blamejs_leader SET[\s\S]*`?expiresAt`? = \?\s+WHERE/i.test(t);
|
|
163
174
|
if (releaseMatch) {
|
|
164
175
|
var rr = rows._blamejs_leader;
|
|
165
|
-
if (rr && rr.nodeId === params[
|
|
166
|
-
rr.expiresAt = 0;
|
|
176
|
+
if (rr && rr.nodeId === params[2] && rr.leaseId === params[3]) {
|
|
177
|
+
rr.expiresAt = params[0];
|
|
167
178
|
}
|
|
168
179
|
return { rows: [], affectedRows: 1 };
|
|
169
180
|
}
|
|
170
181
|
|
|
171
|
-
// SELECT FROM _blamejs_leader WHERE scope =
|
|
172
|
-
|
|
182
|
+
// SELECT ... FROM _blamejs_leader WHERE `scope` = ? (b.sql binds the
|
|
183
|
+
// scope value rather than inlining the 'leader' literal).
|
|
184
|
+
if (/^SELECT[\s\S]+FROM _blamejs_leader WHERE `?scope`? = (?:\?|'leader')/i.test(t)) {
|
|
173
185
|
var rl = rows._blamejs_leader;
|
|
174
186
|
if (!rl) return { rows: [], affectedRows: 0 };
|
|
175
187
|
return { rows: [Object.assign({}, rl)], affectedRows: 1 };
|
|
@@ -135,6 +135,27 @@ var DEFAULTS = {
|
|
|
135
135
|
mitmproxyV6: "http://[::1]:8090",
|
|
136
136
|
mitmproxyWebV6: "http://[::1]:8091",
|
|
137
137
|
squidV6: "http://[::1]:3128",
|
|
138
|
+
|
|
139
|
+
// ---- cloud-storage / telemetry / AWS emulators (test-CA TLS) ----
|
|
140
|
+
azurite: "https://127.0.0.1:10000", // Azure Blob (path-style)
|
|
141
|
+
azuriteV6: "https://[::1]:10000",
|
|
142
|
+
gcs: "https://127.0.0.1:4443", // fake-gcs-server
|
|
143
|
+
gcsV6: "https://[::1]:4443",
|
|
144
|
+
localstack: "https://127.0.0.1:4566", // CloudWatch Logs + SQS (Caddy-terminated)
|
|
145
|
+
localstackV6: "https://[::1]:4566",
|
|
146
|
+
otelOtlp: "https://127.0.0.1:4318", // OTLP/HTTP receiver
|
|
147
|
+
otelOtlpV6: "https://[::1]:4318",
|
|
148
|
+
otelHealth: "http://127.0.0.1:13133/", // OTel health_check extension
|
|
149
|
+
otelHealthV6: "http://[::1]:13133/",
|
|
150
|
+
toxiproxy: "http://127.0.0.1:8474", // fault-injection API
|
|
151
|
+
toxiproxyV6: "http://[::1]:8474",
|
|
152
|
+
toxiproxyRedis: "redis://127.0.0.1:16379", // proxied redis
|
|
153
|
+
toxiproxyPg: "postgres://blamejs:blamejs_test@127.0.0.1:15432/blamejs_test",
|
|
154
|
+
|
|
155
|
+
// ---- postgres streaming replica (hot standby) ----
|
|
156
|
+
postgresReplica: "postgres://blamejs:blamejs_test@127.0.0.1:5433/blamejs_test",
|
|
157
|
+
postgresReplicaV6: "postgres://blamejs:blamejs_test@[::1]:5433/blamejs_test",
|
|
158
|
+
postgresReplicaTls: "postgres://blamejs:blamejs_test@127.0.0.1:5433/blamejs_test?sslmode=require",
|
|
138
159
|
};
|
|
139
160
|
|
|
140
161
|
function _envOverride(name) {
|