@blamejs/blamejs-shop 0.4.30 → 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 +4 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/checkout.js +8 -0
- package/lib/order.js +71 -11
- 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,267 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.db.stream OOM-safety + streamLimit cap, and sealed-column byte-
|
|
4
|
+
* fidelity across hostile payload shapes.
|
|
5
|
+
*
|
|
6
|
+
* Two guarantees under test, both on the production encrypted-at-rest
|
|
7
|
+
* path (real wrapped vault + sealed db.enc + audit chain via
|
|
8
|
+
* setupTestDb):
|
|
9
|
+
*
|
|
10
|
+
* (a) b.db.stream yields rows from node:sqlite's iterate() WITHOUT
|
|
11
|
+
* materializing the whole result set (the documented win over
|
|
12
|
+
* .all()), preserves row order, and DESTROYS the stream with a
|
|
13
|
+
* db/stream-limit-exceeded error once the per-call streamLimit
|
|
14
|
+
* cap is crossed rather than accumulating unboundedly.
|
|
15
|
+
*
|
|
16
|
+
* (b) A sealed column round-trips seal -> store -> read -> unseal
|
|
17
|
+
* byte-identical for the payload shapes a real column carries.
|
|
18
|
+
* This is where a silent value-corruption bug would hide: the
|
|
19
|
+
* seal layer must not lose bytes on the way through the AEAD
|
|
20
|
+
* envelope.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
var helpers = require("../helpers");
|
|
24
|
+
var b = helpers.b;
|
|
25
|
+
var check = helpers.check;
|
|
26
|
+
var fs = helpers.fs;
|
|
27
|
+
var os = helpers.os;
|
|
28
|
+
var path = helpers.path;
|
|
29
|
+
var setupTestDb = helpers.setupTestDb;
|
|
30
|
+
var waitUntil = helpers.waitUntil;
|
|
31
|
+
|
|
32
|
+
// Stream-test table: a sealed column so the stream's auto-unseal path
|
|
33
|
+
// (opts.table) is exercised, plus an ordering column.
|
|
34
|
+
// Payload-fidelity table: one sealed TEXT column (seal layer) and one
|
|
35
|
+
// NON-sealed BLOB column (raw node:sqlite storage), so the two
|
|
36
|
+
// round-trip paths are isolated from each other.
|
|
37
|
+
var SCHEMA = [
|
|
38
|
+
{
|
|
39
|
+
name: "events",
|
|
40
|
+
columns: {
|
|
41
|
+
_id: "TEXT PRIMARY KEY",
|
|
42
|
+
seq: "INTEGER NOT NULL",
|
|
43
|
+
payload: "TEXT",
|
|
44
|
+
},
|
|
45
|
+
indexes: ["seq"],
|
|
46
|
+
sealedFields: ["payload"],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "blobs",
|
|
50
|
+
columns: {
|
|
51
|
+
_id: "TEXT PRIMARY KEY",
|
|
52
|
+
sealedText: "TEXT", // routed through the seal layer
|
|
53
|
+
rawBlob: "BLOB", // stored as a raw node:sqlite BLOB, not sealed
|
|
54
|
+
},
|
|
55
|
+
sealedFields: ["sealedText"],
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// Drain a Readable into an array of rows, honoring backpressure-free
|
|
60
|
+
// flowing mode but capturing order. Rejects on stream error.
|
|
61
|
+
function collectStream(stream) {
|
|
62
|
+
return new Promise(function (resolve, reject) {
|
|
63
|
+
var rows = [];
|
|
64
|
+
stream.on("data", function (row) { rows.push(row); });
|
|
65
|
+
stream.on("end", function () { resolve(rows); });
|
|
66
|
+
stream.on("error", function (err) { reject(err); });
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Capture the error a stream destroys with (the cap path). Resolves
|
|
71
|
+
// with { errored, err, rowsBeforeError }.
|
|
72
|
+
function collectStreamError(stream) {
|
|
73
|
+
return new Promise(function (resolve) {
|
|
74
|
+
var rows = [];
|
|
75
|
+
stream.on("data", function (row) { rows.push(row); });
|
|
76
|
+
stream.on("end", function () { resolve({ errored: false, err: null, rowsBeforeError: rows }); });
|
|
77
|
+
stream.on("error", function (err) { resolve({ errored: true, err: err, rowsBeforeError: rows }); });
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function bufEqual(a, exp) {
|
|
82
|
+
return Buffer.isBuffer(a) || a instanceof Uint8Array
|
|
83
|
+
? Buffer.compare(Buffer.from(a), exp) === 0
|
|
84
|
+
: false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function run() {
|
|
88
|
+
var tmp = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-db-stream-shape-"));
|
|
89
|
+
await setupTestDb(tmp, SCHEMA);
|
|
90
|
+
|
|
91
|
+
// ====================================================================
|
|
92
|
+
// (a) b.db.stream — non-materializing, ordered, capped
|
|
93
|
+
// ====================================================================
|
|
94
|
+
|
|
95
|
+
// Insert a few thousand rows so the stream has real work to do and
|
|
96
|
+
// the cap can fire well before the end of the set.
|
|
97
|
+
var N = 4000;
|
|
98
|
+
var events = b.db.from("events");
|
|
99
|
+
for (var i = 0; i < N; i++) {
|
|
100
|
+
events.insertOne({ seq: i, payload: "payload-row-" + i });
|
|
101
|
+
}
|
|
102
|
+
check("inserted N rows", b.db.from("events").count() === N);
|
|
103
|
+
|
|
104
|
+
// ---- order + completeness: every row arrives, in seq order, unsealed
|
|
105
|
+
var streamed = await collectStream(
|
|
106
|
+
b.db.stream("SELECT * FROM events ORDER BY seq ASC", { table: "events" })
|
|
107
|
+
);
|
|
108
|
+
check("stream yielded every row", streamed.length === N);
|
|
109
|
+
var ordered = true;
|
|
110
|
+
var unsealedOk = true;
|
|
111
|
+
for (var j = 0; j < streamed.length; j++) {
|
|
112
|
+
if (streamed[j].seq !== j) ordered = false;
|
|
113
|
+
if (streamed[j].payload !== "payload-row-" + j) unsealedOk = false;
|
|
114
|
+
}
|
|
115
|
+
check("stream preserved row order", ordered);
|
|
116
|
+
check("stream auto-unsealed the sealed column", unsealedOk);
|
|
117
|
+
|
|
118
|
+
// ---- non-materialization: the stream is pull-based. Pause it after
|
|
119
|
+
// the first row and prove no further rows are delivered while paused
|
|
120
|
+
// (a .all()-style eager materialization could not honor this), then
|
|
121
|
+
// resume and confirm the rest flow. This demonstrates the iterate()
|
|
122
|
+
// generator is pulled lazily, not collected up-front.
|
|
123
|
+
var pullStream = b.db.stream("SELECT * FROM events ORDER BY seq ASC", { table: "events" });
|
|
124
|
+
var seen = [];
|
|
125
|
+
var pausedAtCount = -1;
|
|
126
|
+
pullStream.on("data", function (row) {
|
|
127
|
+
seen.push(row);
|
|
128
|
+
if (seen.length === 1) {
|
|
129
|
+
pullStream.pause();
|
|
130
|
+
pausedAtCount = seen.length;
|
|
131
|
+
// Resume on the next tick; while paused, no more 'data' must fire.
|
|
132
|
+
setImmediate(function () {
|
|
133
|
+
// Confirm the stream did NOT race ahead and buffer the whole
|
|
134
|
+
// set while we were paused: at most a tiny highWaterMark of
|
|
135
|
+
// rows can be in flight, never the full N.
|
|
136
|
+
check("paused stream did not materialize the full set",
|
|
137
|
+
seen.length < N);
|
|
138
|
+
pullStream.resume();
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
var pulled = await new Promise(function (resolve, reject) {
|
|
143
|
+
pullStream.on("end", function () { resolve(seen); });
|
|
144
|
+
pullStream.on("error", reject);
|
|
145
|
+
});
|
|
146
|
+
check("paused-then-resumed stream still delivered every row", pulled.length === N);
|
|
147
|
+
check("pause point was the first row", pausedAtCount === 1);
|
|
148
|
+
|
|
149
|
+
// ---- cap: a per-call streamLimit BELOW the row count must destroy
|
|
150
|
+
// the stream with db/stream-limit-exceeded, not silently truncate or
|
|
151
|
+
// run to completion.
|
|
152
|
+
var CAP = 100;
|
|
153
|
+
var capResult = await collectStreamError(
|
|
154
|
+
b.db.stream("SELECT * FROM events ORDER BY seq ASC", { table: "events", streamLimit: CAP })
|
|
155
|
+
);
|
|
156
|
+
check("over-cap stream errored (did not run to completion)", capResult.errored === true);
|
|
157
|
+
check("over-cap stream error carries db/stream-limit-exceeded code",
|
|
158
|
+
!!capResult.err && capResult.err.code === "db/stream-limit-exceeded");
|
|
159
|
+
check("over-cap stream stopped at the cap (no unbounded accumulation)",
|
|
160
|
+
capResult.rowsBeforeError.length <= CAP);
|
|
161
|
+
|
|
162
|
+
// ---- a streamLimit AT OR ABOVE the row count runs clean to the end.
|
|
163
|
+
var underCap = await collectStream(
|
|
164
|
+
b.db.stream("SELECT * FROM events ORDER BY seq ASC", { table: "events", streamLimit: N + 10 })
|
|
165
|
+
);
|
|
166
|
+
check("within-cap stream completes without error", underCap.length === N);
|
|
167
|
+
|
|
168
|
+
// ---- a bad streamLimit shape throws at call time (config-time tier).
|
|
169
|
+
var badLimitThrew = false;
|
|
170
|
+
try { b.db.stream("SELECT * FROM events", { table: "events", streamLimit: -1 }); }
|
|
171
|
+
catch (e) { badLimitThrew = e && e.code === "db/bad-stream-limit"; }
|
|
172
|
+
check("negative streamLimit throws db/bad-stream-limit at call time", badLimitThrew);
|
|
173
|
+
|
|
174
|
+
// ====================================================================
|
|
175
|
+
// (b) sealed-column byte-fidelity across hostile payload shapes
|
|
176
|
+
// ====================================================================
|
|
177
|
+
//
|
|
178
|
+
// For each shape, drive the PRODUCTION seal path
|
|
179
|
+
// (cryptoField.sealRow -> store via db -> read -> auto-unseal) and
|
|
180
|
+
// assert byte-identical recovery. Any shape that does not round-trip
|
|
181
|
+
// byte-identical is a real data-integrity bug — the column was
|
|
182
|
+
// declared sealed by the operator and silently corrupting its value
|
|
183
|
+
// defeats the at-rest-encryption guarantee.
|
|
184
|
+
|
|
185
|
+
// --- Shape 1: astral-plane Unicode (emoji + CJK Extension B) ---
|
|
186
|
+
var astral = "rocket\u{1F680} cjkB\u{20000}\u{2A6B2} family\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}";
|
|
187
|
+
b.db.from("blobs").insertOne({ _id: "astral", sealedText: astral });
|
|
188
|
+
var gotAstral = b.db.from("blobs").where({ _id: "astral" }).first();
|
|
189
|
+
check("astral-plane Unicode round-trips byte-identical through sealed column",
|
|
190
|
+
gotAstral.sealedText === astral);
|
|
191
|
+
|
|
192
|
+
// --- Shape 2: a string containing embedded NUL bytes ---
|
|
193
|
+
var withNul = "beforemiddleafter";
|
|
194
|
+
b.db.from("blobs").insertOne({ _id: "nul", sealedText: withNul });
|
|
195
|
+
var gotNul = b.db.from("blobs").where({ _id: "nul" }).first();
|
|
196
|
+
check("string with embedded NUL bytes round-trips byte-identical through sealed column",
|
|
197
|
+
gotNul.sealedText === withNul);
|
|
198
|
+
|
|
199
|
+
// --- Shape 3: a large (~256 KiB) value ---
|
|
200
|
+
var large = "";
|
|
201
|
+
var chunk = "0123456789abcdef"; // 16 bytes
|
|
202
|
+
while (large.length < 256 * 1024) large += chunk; // ~256 KiB
|
|
203
|
+
b.db.from("blobs").insertOne({ _id: "large", sealedText: large });
|
|
204
|
+
var gotLarge = b.db.from("blobs").where({ _id: "large" }).first();
|
|
205
|
+
check("large (~256 KiB) value round-trips byte-identical through sealed column",
|
|
206
|
+
gotLarge.sealedText === large && gotLarge.sealedText.length === large.length);
|
|
207
|
+
|
|
208
|
+
// --- Shape 4: a binary Buffer/BLOB carrying all 256 byte values ---
|
|
209
|
+
// First prove the DB BLOB column itself preserves bytes (the storage
|
|
210
|
+
// floor), so any seal-layer corruption can't be blamed on sqlite.
|
|
211
|
+
var allBytes = Buffer.alloc(256);
|
|
212
|
+
for (var bb = 0; bb < 256; bb++) allBytes[bb] = bb;
|
|
213
|
+
b.db.from("blobs").insertOne({ _id: "rawblob", rawBlob: allBytes });
|
|
214
|
+
var gotRawBlob = b.db.from("blobs").where({ _id: "rawblob" }).first();
|
|
215
|
+
check("raw (non-sealed) BLOB column preserves all 256 byte values byte-identical",
|
|
216
|
+
bufEqual(gotRawBlob.rawBlob, allBytes));
|
|
217
|
+
|
|
218
|
+
// Now the same all-256-bytes Buffer through a SEALED column. A sealed
|
|
219
|
+
// column is the operator's declaration that the value holds protected
|
|
220
|
+
// data; a binary column (e.g. a sealed encryption sub-key, a packed
|
|
221
|
+
// protobuf, a thumbnail) is a legitimate sealed payload. The seal
|
|
222
|
+
// path MUST either preserve the bytes or refuse — silently mangling
|
|
223
|
+
// them is the bug.
|
|
224
|
+
b.db.from("blobs").insertOne({ _id: "sealedblob", sealedText: allBytes });
|
|
225
|
+
var gotSealedBlob = b.db.from("blobs").where({ _id: "sealedblob" }).first();
|
|
226
|
+
var sealedBlobBytes = Buffer.isBuffer(gotSealedBlob.sealedText) || gotSealedBlob.sealedText instanceof Uint8Array
|
|
227
|
+
? Buffer.from(gotSealedBlob.sealedText)
|
|
228
|
+
: Buffer.from(String(gotSealedBlob.sealedText), "utf8");
|
|
229
|
+
check("all-256-byte Buffer round-trips byte-identical through a SEALED column",
|
|
230
|
+
Buffer.compare(sealedBlobBytes, allBytes) === 0);
|
|
231
|
+
|
|
232
|
+
// --- Shape 5: a deeply-nested JSON object ---
|
|
233
|
+
// A sealed column declared to hold a structured value (the framework
|
|
234
|
+
// even JSON.stringify's containment values elsewhere) must recover the
|
|
235
|
+
// SAME object — not a coerced "[object Object]" / lossy string.
|
|
236
|
+
var nested = { a: { b: { c: { d: [1, 2, { e: "deep", f: [null, true, "xy"] }] } } }, n: 42 };
|
|
237
|
+
b.db.from("blobs").insertOne({ _id: "nested", sealedText: nested });
|
|
238
|
+
var gotNested = b.db.from("blobs").where({ _id: "nested" }).first();
|
|
239
|
+
var recovered;
|
|
240
|
+
try {
|
|
241
|
+
recovered = typeof gotNested.sealedText === "string"
|
|
242
|
+
? JSON.parse(gotNested.sealedText)
|
|
243
|
+
: gotNested.sealedText;
|
|
244
|
+
} catch (_e) { recovered = gotNested.sealedText; }
|
|
245
|
+
check("deeply-nested object round-trips structurally through a SEALED column",
|
|
246
|
+
JSON.stringify(recovered) === JSON.stringify(nested));
|
|
247
|
+
|
|
248
|
+
// Touch waitUntil so the import is exercised even though every assert
|
|
249
|
+
// above is synchronous against the local sqlite path (keeps the
|
|
250
|
+
// helper dependency honest for future async growth).
|
|
251
|
+
await waitUntil(function () { return true; }, { timeoutMs: 1000, label: "db-stream-shape: trivial settle" });
|
|
252
|
+
|
|
253
|
+
b.db.close();
|
|
254
|
+
b.audit._resetForTest();
|
|
255
|
+
b.db._resetForTest();
|
|
256
|
+
b.vault._resetForTest();
|
|
257
|
+
b.cluster._resetForTest();
|
|
258
|
+
try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (_e) {}
|
|
259
|
+
|
|
260
|
+
console.log("OK — db.stream OOM-safety + cap, sealed-column byte-fidelity");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
module.exports = { run: run };
|
|
264
|
+
if (require.main === module) {
|
|
265
|
+
run().then(function () { process.exit(0); })
|
|
266
|
+
.catch(function (err) { process.exitCode = 1; throw err; });
|
|
267
|
+
}
|
|
@@ -25,6 +25,27 @@ async function run() {
|
|
|
25
25
|
},
|
|
26
26
|
indexes: ["userId"],
|
|
27
27
|
},
|
|
28
|
+
{
|
|
29
|
+
// Erasure target for the eraseHard happy-path proof. NOT a WORM
|
|
30
|
+
// table (eraseHard's raw DELETE would trip a WORM BEFORE-DELETE
|
|
31
|
+
// trigger), but gated by dual-control so the destructive op only
|
|
32
|
+
// runs against a consumed m-of-n grant.
|
|
33
|
+
// ssn — sealed at rest (exercises cryptoField.eraseRow)
|
|
34
|
+
// tag — non-sealed, INDEXED plaintext column. This is the
|
|
35
|
+
// forensic target: its plaintext lands in the SQLite
|
|
36
|
+
// table-leaf AND index B-tree pages, so DELETE + REINDEX
|
|
37
|
+
// (+ the advertised vacuumAfterErase companion) must
|
|
38
|
+
// leave no on-disk residual.
|
|
39
|
+
name: "stale_pii",
|
|
40
|
+
columns: {
|
|
41
|
+
_id: "TEXT PRIMARY KEY",
|
|
42
|
+
ssn: "TEXT",
|
|
43
|
+
tag: "TEXT",
|
|
44
|
+
keepId: "TEXT",
|
|
45
|
+
},
|
|
46
|
+
indexes: ["tag"],
|
|
47
|
+
sealedFields: ["ssn"],
|
|
48
|
+
},
|
|
28
49
|
]);
|
|
29
50
|
|
|
30
51
|
// Insert a row before WORM declaration to exercise the trigger gate.
|
|
@@ -83,6 +104,135 @@ async function run() {
|
|
|
83
104
|
catch (e) { noGrantRefused = /dual-control/i.test(e.message); }
|
|
84
105
|
check("eraseHard: dual-control gate refuses without grant", noGrantRefused);
|
|
85
106
|
|
|
107
|
+
// ---------------------------------------------------------------------
|
|
108
|
+
// Happy path — eraseHard ACTUALLY destroys data (DELETE + crypto-erase
|
|
109
|
+
// + REINDEX) under a real consumed dual-control grant, and leaves no
|
|
110
|
+
// forensic residual on disk.
|
|
111
|
+
// ---------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
// A unique, high-entropy sentinel so a byte-scan of the on-disk files
|
|
114
|
+
// can't false-positive on incidental bytes. Stored in the non-sealed,
|
|
115
|
+
// INDEXED `tag` column → its plaintext is in both the table-leaf and
|
|
116
|
+
// the index B-tree pages of the working SQLite file.
|
|
117
|
+
var SENTINEL = "ERASE-SENTINEL-" +
|
|
118
|
+
b.crypto.generateToken(b.constants.BYTES.bytes(24)) + "-ZZ";
|
|
119
|
+
var SSN_PLAIN = "987-65-4321";
|
|
120
|
+
|
|
121
|
+
b.db.from("stale_pii").insertOne({ _id: "doomed", ssn: SSN_PLAIN, tag: SENTINEL, keepId: "k1" });
|
|
122
|
+
// A second row that must SURVIVE the erase — proves eraseHard scopes
|
|
123
|
+
// to the named rowId and doesn't wipe the table.
|
|
124
|
+
var SURVIVOR = "SURVIVOR-SENTINEL-" +
|
|
125
|
+
b.crypto.generateToken(b.constants.BYTES.bytes(16)) + "-YY";
|
|
126
|
+
b.db.from("stale_pii").insertOne({ _id: "keeper", ssn: "111-22-3333", tag: SURVIVOR, keepId: "k2" });
|
|
127
|
+
|
|
128
|
+
// Gate the erase behind dual-control (m=2, n=3).
|
|
129
|
+
b.db.declareRequireDualControl({ tables: ["stale_pii"], m: 2, n: 3, posture: "gdpr" });
|
|
130
|
+
|
|
131
|
+
// Sealed round-trip: the row reads back with plaintext ssn (proves the
|
|
132
|
+
// value was genuinely sealed-then-unsealed, i.e. encryption is live).
|
|
133
|
+
var before = b.db.from("stale_pii").where({ _id: "doomed" }).first();
|
|
134
|
+
check("eraseHard happy: sealed row reads back before erase", !!before && before.ssn === SSN_PLAIN);
|
|
135
|
+
check("eraseHard happy: tag present in-row before erase", !!before && before.tag === SENTINEL);
|
|
136
|
+
|
|
137
|
+
// Locate the on-disk files. In encrypted-at-rest mode the working
|
|
138
|
+
// SQLite file lives in tmpfs (getDbPath); the durable sealed copy is
|
|
139
|
+
// <dataDir>/db.enc. Force a checkpoint + WAL flush so committed pages
|
|
140
|
+
// are visible in the main file for the forensic scan.
|
|
141
|
+
var dbFile = b.db.getDbPath();
|
|
142
|
+
var walFile = dbFile + "-wal";
|
|
143
|
+
var encFile = path.join(tmpDir, "db.enc");
|
|
144
|
+
b.db.flushToDisk(); // checkpoint(TRUNCATE) + re-seal db.enc
|
|
145
|
+
|
|
146
|
+
function fileHasSentinel(p, needle) {
|
|
147
|
+
if (!fs.existsSync(p)) return false;
|
|
148
|
+
return fs.readFileSync(p).includes(Buffer.from(needle, "utf8"));
|
|
149
|
+
}
|
|
150
|
+
function diskHasSentinel(needle) {
|
|
151
|
+
return fileHasSentinel(dbFile, needle) ||
|
|
152
|
+
fileHasSentinel(walFile, needle);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// CONTROL — before erase the sentinel plaintext IS recoverable from the
|
|
156
|
+
// working SQLite file (table-leaf and/or index pages). Without this the
|
|
157
|
+
// "absent after" assertion below would be vacuous.
|
|
158
|
+
check("eraseHard FORENSIC control: sentinel plaintext present on disk before erase",
|
|
159
|
+
diskHasSentinel(SENTINEL));
|
|
160
|
+
// db.enc is an encrypted envelope — the sentinel must NOT be plaintext-
|
|
161
|
+
// visible there at any point (sanity check on at-rest encryption).
|
|
162
|
+
check("eraseHard FORENSIC: db.enc never exposes sentinel plaintext (encrypted at rest)",
|
|
163
|
+
!fileHasSentinel(encFile, SENTINEL));
|
|
164
|
+
|
|
165
|
+
// Build a REAL dual-control grant: request → a different actor approves
|
|
166
|
+
// → quorum → consume. consume() returns { ready: true } which eraseHard
|
|
167
|
+
// requires. No stub — the production dual-control workflow.
|
|
168
|
+
var approvals = b.dualControl.create({
|
|
169
|
+
namespace: "stale_pii.eraseHard",
|
|
170
|
+
cache: b.cache.create({ namespace: "dc-erase", backend: "memory" }),
|
|
171
|
+
audit: b.audit,
|
|
172
|
+
minApprovers: 2,
|
|
173
|
+
});
|
|
174
|
+
var opened = await approvals.request({
|
|
175
|
+
action: "stale_pii.eraseHard",
|
|
176
|
+
resource: { kind: "stale_pii", id: "doomed" },
|
|
177
|
+
requestedBy: { id: "alice" },
|
|
178
|
+
reason: "GDPR Art. 17 erasure request, ticket SUP-9001",
|
|
179
|
+
});
|
|
180
|
+
check("eraseHard happy: grant opened pending", opened && opened.status === "pending");
|
|
181
|
+
await approvals.approve({ grantId: opened.grantId, approver: { id: "bob" }, reason: "verified subject identity" });
|
|
182
|
+
var quorum = await approvals.approve({ grantId: opened.grantId, approver: { id: "carol" }, reason: "second approval" });
|
|
183
|
+
check("eraseHard happy: grant reached quorum", quorum && quorum.status === "approved");
|
|
184
|
+
var grant = await approvals.consume(opened.grantId, {});
|
|
185
|
+
check("eraseHard happy: grant consumed (ready)", grant && grant.ready === true);
|
|
186
|
+
|
|
187
|
+
// The destructive op under the consumed grant.
|
|
188
|
+
var result = await b.db.eraseHard("stale_pii", "doomed", {
|
|
189
|
+
reason: "subject erasure under GDPR Art 17, ticket SUP-9001",
|
|
190
|
+
dualControlGrant: grant,
|
|
191
|
+
});
|
|
192
|
+
check("eraseHard happy: reports rowsDeleted === 1", result && result.rowsDeleted === 1);
|
|
193
|
+
|
|
194
|
+
// (a) Row is gone from the table.
|
|
195
|
+
var afterRow = b.db.from("stale_pii").where({ _id: "doomed" }).first();
|
|
196
|
+
check("eraseHard happy: erased row absent from table", afterRow === null || afterRow === undefined);
|
|
197
|
+
// The unrelated row survives — erase is row-scoped, not a table wipe.
|
|
198
|
+
var keeper = b.db.from("stale_pii").where({ _id: "keeper" }).first();
|
|
199
|
+
check("eraseHard happy: unrelated row survives", !!keeper && keeper.tag === SURVIVOR);
|
|
200
|
+
|
|
201
|
+
// (b) Audit chain still verifies end-to-end (the erase emitted a signed
|
|
202
|
+
// db.erase_hard row; the hash chain must remain intact).
|
|
203
|
+
await b.audit.flush();
|
|
204
|
+
var chain = await b.audit.verify();
|
|
205
|
+
check("eraseHard happy: audit chain verifies intact after erase", chain && chain.ok === true);
|
|
206
|
+
|
|
207
|
+
// (c) FORENSIC — the core guarantee, attributed to eraseHard ITSELF
|
|
208
|
+
// (DELETE + REINDEX), measured BEFORE any later vacuum so the scrub
|
|
209
|
+
// can't be credited to a separate VACUUM pass. Re-checkpoint + re-seal
|
|
210
|
+
// so the on-disk files reflect post-erase state, then assert the
|
|
211
|
+
// deleted row's plaintext sentinel is gone from the working SQLite +
|
|
212
|
+
// WAL. The CONTROL above proved it WAS recoverable before the erase, so
|
|
213
|
+
// this is a real before/after delta, not a vacuous read.
|
|
214
|
+
b.db.flushToDisk();
|
|
215
|
+
check("eraseHard FORENSIC: erased sentinel plaintext ABSENT from working SQLite + WAL after eraseHard+REINDEX",
|
|
216
|
+
!diskHasSentinel(SENTINEL));
|
|
217
|
+
// The survivor's plaintext is still on disk — confirms the scan is live
|
|
218
|
+
// (it isn't reporting "absent" because the whole file was wiped).
|
|
219
|
+
check("eraseHard FORENSIC: survivor sentinel still present on disk (scan is live, not whole-file wipe)",
|
|
220
|
+
diskHasSentinel(SURVIVOR));
|
|
221
|
+
// db.enc is an encrypted envelope — never plaintext-visible at any point.
|
|
222
|
+
check("eraseHard FORENSIC: db.enc holds no sentinel plaintext after erase",
|
|
223
|
+
!fileHasSentinel(encFile, SENTINEL));
|
|
224
|
+
|
|
225
|
+
// Belt-and-suspenders: the advertised vacuumAfterErase companion (full
|
|
226
|
+
// VACUUM rewrites every page) is the operator's defense against any
|
|
227
|
+
// freed-page residual a later large-scale erase batch could leave. Run
|
|
228
|
+
// it and re-confirm the sentinel stays absent and the survivor stays.
|
|
229
|
+
b.db.vacuumAfterErase({ mode: "full" });
|
|
230
|
+
b.db.flushToDisk();
|
|
231
|
+
check("eraseHard FORENSIC: sentinel still absent after vacuumAfterErase(full)",
|
|
232
|
+
!diskHasSentinel(SENTINEL));
|
|
233
|
+
check("eraseHard FORENSIC: survivor still present after vacuumAfterErase(full)",
|
|
234
|
+
diskHasSentinel(SURVIVOR));
|
|
235
|
+
|
|
86
236
|
await dbHelper.teardownTestDb(tmpDir);
|
|
87
237
|
}
|
|
88
238
|
|
package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// defineGuard's default gate must resolve the profile + posture BEFORE building
|
|
3
|
+
// the gate, so a guard gated with { compliancePosture } honors that posture's
|
|
4
|
+
// forensicSnippetBytes (and the profile's maxRuntimeMs). The default gate passed
|
|
5
|
+
// RAW opts straight to buildGuardGate, so the posture's forensic cap was dropped
|
|
6
|
+
// to 0 (forensic snapshots disabled) — a regulated-posture refusal carried no
|
|
7
|
+
// evidence, defeating the forensic-capture the posture promises.
|
|
8
|
+
|
|
9
|
+
var helpers = require("../helpers");
|
|
10
|
+
var b = helpers.b;
|
|
11
|
+
var check = helpers.check;
|
|
12
|
+
|
|
13
|
+
async function run() {
|
|
14
|
+
// guard-cidr is built via gateContract.defineGuard with the STANDARD default
|
|
15
|
+
// gate (no bespoke spec.gate), so .gate() exercises the default-gate path.
|
|
16
|
+
// Its hipaa posture sets forensicSnippetBytes = 128.
|
|
17
|
+
var bad = "999.999.999.999/99";
|
|
18
|
+
var g = b.guardCidr.gate({ compliancePosture: "hipaa" });
|
|
19
|
+
var decision = await g.check({ identifier: bad, bytes: Buffer.from(bad, "utf8") });
|
|
20
|
+
check("hipaa gate refuses a malformed CIDR", decision.action === "refuse");
|
|
21
|
+
check("hipaa posture forensicSnippetBytes is applied → refusal carries a forensic snapshot",
|
|
22
|
+
decision.forensicSnapshot != null);
|
|
23
|
+
check("the forensic snapshot is bounded by the posture cap (<= 128 bytes)",
|
|
24
|
+
decision.forensicSnapshot != null && decision.forensicSnapshot.length <= 128);
|
|
25
|
+
|
|
26
|
+
process.stdout.write("OK — defineGuard default-gate posture-cap tests\n");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
run().then(function () { process.exit(0); })
|
|
30
|
+
.catch(function (e) { process.stderr.write((e && e.stack ? e.stack : String(e)) + "\n"); process.exit(1); });
|
package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// b.middleware.dpop must REQUIRE a replayStore. The store IS the jti-replay
|
|
3
|
+
// defense: without it a captured DPoP proof can be replayed indefinitely — the
|
|
4
|
+
// exact attack proof-of-possession exists to stop (RFC 9449 §11.1). The doc has
|
|
5
|
+
// always listed replayStore as "required", but create() read it optionally and
|
|
6
|
+
// mounted a gate that performed no replay check when it was omitted. Mounting
|
|
7
|
+
// must fail closed at config time, never silently disable the defense.
|
|
8
|
+
|
|
9
|
+
var helpers = require("../helpers");
|
|
10
|
+
var b = helpers.b;
|
|
11
|
+
var check = helpers.check;
|
|
12
|
+
|
|
13
|
+
function expectThrow(label, fn, codeNeedle) {
|
|
14
|
+
var threw = null;
|
|
15
|
+
try { fn(); } catch (e) { threw = e; }
|
|
16
|
+
check(label + " — throws at create()", threw !== null);
|
|
17
|
+
if (threw && codeNeedle) {
|
|
18
|
+
check(label + " — error names the replay store",
|
|
19
|
+
String((threw.code || threw.message) || "").toLowerCase().indexOf(codeNeedle) !== -1);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function run() {
|
|
24
|
+
// 1. Omitting replayStore must throw at create() — no silent replay-off mount.
|
|
25
|
+
expectThrow("dpop middleware without replayStore", function () {
|
|
26
|
+
b.middleware.dpop({ getAccessToken: function () { return null; } });
|
|
27
|
+
}, "replay");
|
|
28
|
+
|
|
29
|
+
// 2. A replayStore lacking checkAndInsert must throw at create() (fail fast on
|
|
30
|
+
// config, not at the first request).
|
|
31
|
+
expectThrow("dpop middleware with a malformed replayStore", function () {
|
|
32
|
+
b.middleware.dpop({ replayStore: { notCheckAndInsert: true } });
|
|
33
|
+
}, "replay");
|
|
34
|
+
|
|
35
|
+
// 3. A valid replayStore mounts cleanly (control — proves the gate is not
|
|
36
|
+
// over-tightened into refusing legitimate config).
|
|
37
|
+
var mounted = null;
|
|
38
|
+
try {
|
|
39
|
+
mounted = b.middleware.dpop({ replayStore: b.nonceStore.create({ backend: "memory" }) });
|
|
40
|
+
} catch (e) { mounted = e; }
|
|
41
|
+
check("dpop middleware with a valid replayStore mounts", typeof mounted === "function");
|
|
42
|
+
|
|
43
|
+
process.stdout.write("OK — dpop middleware replayStore-required tests\n");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
run();
|