@blamejs/blamejs-shop 0.4.31 → 0.4.33
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/README.md +1 -1
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +400 -282
- 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 +28 -0
- package/lib/vendor/blamejs/MIGRATING.md +55 -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/elevation-grant.js +6 -2
- package/lib/vendor/blamejs/lib/auth/oauth.js +66 -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 +36 -7
- 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 +210 -4
- package/lib/vendor/blamejs/lib/consent.js +82 -29
- package/lib/vendor/blamejs/lib/constants.js +27 -11
- package/lib/vendor/blamejs/lib/credential-hash.js +9 -0
- 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 +117 -42
- 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 +47 -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 +169 -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/release-notes/v0.15.7.json +43 -0
- package/lib/vendor/blamejs/scripts/check-services.js +21 -0
- package/lib/vendor/blamejs/scripts/gen-migrating.js +67 -0
- package/lib/vendor/blamejs/scripts/release.js +398 -38
- package/lib/vendor/blamejs/test/00-primitives.js +168 -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 +1196 -14
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/credential-hash.test.js +18 -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/retention-floor.test.js +59 -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 +362 -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/scheduler-watchdog-stale-settle.test.js +71 -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 +2 -2
- 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,453 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Live disaster-recovery proof: b.backup.create -> run -> restore against a
|
|
4
|
+
* REAL object store (MinIO/S3 over the docker-compose test fixtures).
|
|
5
|
+
*
|
|
6
|
+
* The README advertises S3/MinIO/Azure/GCS backup bundles
|
|
7
|
+
* (b.backup.bundleAdapterStorage.objectStoreAdapter wrapping a sigv4
|
|
8
|
+
* b.objectStore client). Until now that round-trip was only exercised
|
|
9
|
+
* against local diskStorage / an fs-backed objectStore. This drives the
|
|
10
|
+
* whole pipeline end-to-end:
|
|
11
|
+
*
|
|
12
|
+
* 1. setupTestDb — real wrapped vault + encrypted-at-rest sealed DB.
|
|
13
|
+
* Insert rows whose `email` / `name` columns are sealed (vault:
|
|
14
|
+
* ciphertext on disk; plaintext never bare).
|
|
15
|
+
* 2. flushToDisk — seal the working copy to db.enc / db.key.enc.
|
|
16
|
+
* 3. backup engine bound to a bundleAdapterStorage(objectStoreAdapter(
|
|
17
|
+
* sigv4 client)) — RUN the backup; the bundle (an encrypted bundle
|
|
18
|
+
* directory packed into a tar.gz) lands as ONE object key in a real
|
|
19
|
+
* MinIO bucket. Verify the object exists via the raw client (proves
|
|
20
|
+
* the SigV4 PUT signed correctly — MinIO verifies SigV4).
|
|
21
|
+
* 4. RESTORE: engine.read() pulls the tar.gz back from MinIO and
|
|
22
|
+
* extracts the encrypted bundle directory into a fresh location;
|
|
23
|
+
* restoreBundle.extract() decrypts each per-file blob under the
|
|
24
|
+
* backup passphrase into db.enc / db.key.enc / vault.key.sealed /
|
|
25
|
+
* audit-sign.key.sealed.
|
|
26
|
+
* 5. Assert the restored db.enc is CIPHERTEXT (no bare PII), then
|
|
27
|
+
* re-open vault + db at the restored location and read the sealed
|
|
28
|
+
* columns back — proving the restored sealed data decrypts under the
|
|
29
|
+
* vault and the disaster-recovery claim holds.
|
|
30
|
+
* 6. Negative paths: wrong-passphrase restore FAILS (AEAD tag); a
|
|
31
|
+
* tampered bundle object in the bucket FAILS to restore.
|
|
32
|
+
*
|
|
33
|
+
* What MinIO does and does NOT verify (proof scope, stated honestly):
|
|
34
|
+
* - MinIO is a REAL S3 server: it DOES verify SigV4 signatures, so a
|
|
35
|
+
* successful PUT/GET/HEAD/LIST round-trip proves the framework's
|
|
36
|
+
* sigv4 signer + object-store client are correct on the wire.
|
|
37
|
+
* - The TLS leg (https://127.0.0.1:9443) is the test-CA endpoint; the
|
|
38
|
+
* integration runner exports the CA and sets NODE_EXTRA_CA_CERTS, so
|
|
39
|
+
* TLS is validated with NO rejectUnauthorized:false anywhere.
|
|
40
|
+
* - MinIO does NOT inspect the bundle bytes — the encryption of the
|
|
41
|
+
* bundle is the framework's job; we verify that on disk (db.enc is
|
|
42
|
+
* ciphertext, the bundle object carries no bare PII).
|
|
43
|
+
*
|
|
44
|
+
* No security bypass: full wrapped vault, encrypted at-rest, real AEAD.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
var fs = require("node:fs");
|
|
48
|
+
var os = require("node:os");
|
|
49
|
+
var path = require("node:path");
|
|
50
|
+
var b = require("../../");
|
|
51
|
+
var helpers = require("../helpers");
|
|
52
|
+
var check = helpers.check;
|
|
53
|
+
var services = require("../helpers/services");
|
|
54
|
+
var dbh = require("../helpers/db");
|
|
55
|
+
|
|
56
|
+
var REGION = "us-east-1";
|
|
57
|
+
var ACCESS = "blamejs";
|
|
58
|
+
var SECRET = "blamejs_test_password";
|
|
59
|
+
var BACKUP_PASSPHRASE = "disaster-recovery-passphrase-not-secret-but-strong";
|
|
60
|
+
|
|
61
|
+
// PII payloads that MUST end up sealed (never bare) anywhere — on disk,
|
|
62
|
+
// in the bundle object in the bucket, in the restored db.enc.
|
|
63
|
+
var ROWS = [
|
|
64
|
+
{ _id: "u1", email: "alice@secret-domain.example", name: "Alice Liddell" },
|
|
65
|
+
{ _id: "u2", email: "bob@private-clinic.example", name: "Robert Tables" },
|
|
66
|
+
{ _id: "u3", email: "carol@confidential.example", name: "Carol Danvers" },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
function _tmp(prefix) {
|
|
70
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), prefix + "-"));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function _rmrf(p) {
|
|
74
|
+
try { fs.rmSync(p, { recursive: true, force: true }); } catch (_e) { /* ignore */ }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Walk a directory tree and return every regular file's bytes concatenated
|
|
78
|
+
// — used to scan a restored bundle / data dir for bare-PII leaks.
|
|
79
|
+
function _scanForBare(dir, needles) {
|
|
80
|
+
var hits = [];
|
|
81
|
+
(function walk(d) {
|
|
82
|
+
var entries = fs.readdirSync(d, { withFileTypes: true });
|
|
83
|
+
for (var i = 0; i < entries.length; i++) {
|
|
84
|
+
var full = path.join(d, entries[i].name);
|
|
85
|
+
if (entries[i].isDirectory()) { walk(full); continue; }
|
|
86
|
+
if (!entries[i].isFile()) continue;
|
|
87
|
+
var bytes = fs.readFileSync(full);
|
|
88
|
+
for (var j = 0; j < needles.length; j++) {
|
|
89
|
+
if (bytes.includes(Buffer.from(needles[j], "utf8"))) {
|
|
90
|
+
hits.push({ file: full, needle: needles[j] });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
})(dir);
|
|
95
|
+
return hits;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function run() {
|
|
99
|
+
var svc = await services.requireService("minioTls");
|
|
100
|
+
if (!svc.ok) {
|
|
101
|
+
console.log("[skip] backup-restore-objectstore: minio-tls unreachable: " + svc.reason);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
var endpoint = "https://127.0.0.1:9443";
|
|
105
|
+
|
|
106
|
+
// Unique bucket per run so re-runs don't collide.
|
|
107
|
+
var bucket = "blamejs-backup-dr-" + Date.now();
|
|
108
|
+
var prefix = "dr-bundles";
|
|
109
|
+
|
|
110
|
+
var dataDir = null; // the live deployment data dir
|
|
111
|
+
var restoredBundleDir = null;
|
|
112
|
+
var restoreStaging = null;
|
|
113
|
+
var reopenDataDir = null;
|
|
114
|
+
|
|
115
|
+
// sigv4 ops client (bucket lifecycle) + backend client (object I/O).
|
|
116
|
+
var opsCfg = {
|
|
117
|
+
protocol: "sigv4",
|
|
118
|
+
endpoint: endpoint,
|
|
119
|
+
region: REGION,
|
|
120
|
+
accessKeyId: ACCESS,
|
|
121
|
+
secretAccessKey: SECRET,
|
|
122
|
+
allowInternal: true,
|
|
123
|
+
forcePathStyle: true,
|
|
124
|
+
};
|
|
125
|
+
var ops = b.objectStore.bucketOps.create(opsCfg);
|
|
126
|
+
await ops.create(bucket);
|
|
127
|
+
check("minio: backup bucket created (SigV4 create signed + accepted)", true);
|
|
128
|
+
|
|
129
|
+
var beCfg = {
|
|
130
|
+
name: "minio-backup-dr",
|
|
131
|
+
protocol: "sigv4",
|
|
132
|
+
endpoint: endpoint,
|
|
133
|
+
region: REGION,
|
|
134
|
+
bucket: bucket,
|
|
135
|
+
accessKeyId: ACCESS,
|
|
136
|
+
secretAccessKey: SECRET,
|
|
137
|
+
allowInternal: true,
|
|
138
|
+
forcePathStyle: true,
|
|
139
|
+
classifications: ["operational"],
|
|
140
|
+
residencyTag: "unrestricted",
|
|
141
|
+
};
|
|
142
|
+
var client = b.objectStore.buildBackend(beCfg);
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
// ---- 1. Live deployment: real vault + encrypted-at-rest sealed DB ----
|
|
146
|
+
dataDir = _tmp("blamejs-dr-data");
|
|
147
|
+
await dbh.setupTestDb(dataDir);
|
|
148
|
+
for (var i = 0; i < ROWS.length; i++) {
|
|
149
|
+
b.db.from("users").insertOne({
|
|
150
|
+
_id: ROWS[i]._id,
|
|
151
|
+
email: ROWS[i].email,
|
|
152
|
+
name: ROWS[i].name,
|
|
153
|
+
createdAt: new Date().toISOString(),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Confirm the sealed columns are sealed on disk before we back up:
|
|
158
|
+
// the email / name cells are vault: ciphertext, the bare PII never
|
|
159
|
+
// appears in the cell. This is the property the restore must preserve.
|
|
160
|
+
var rawU1 = b.db.prepare('SELECT email AS e, name AS n FROM users WHERE _id = ?').get("u1");
|
|
161
|
+
var emailSealed = typeof rawU1.e === "string" &&
|
|
162
|
+
(rawU1.e.indexOf("vault:") === 0 || b.vault.aad.isAadSealed(rawU1.e) ||
|
|
163
|
+
b.cryptoField.isRowSealed(rawU1.e));
|
|
164
|
+
check("pre-backup: email column is sealed ciphertext on disk (not bare)",
|
|
165
|
+
emailSealed && rawU1.e.indexOf(ROWS[0].email) === -1);
|
|
166
|
+
check("pre-backup: name column is sealed ciphertext on disk (not bare)",
|
|
167
|
+
typeof rawU1.n === "string" && rawU1.n.indexOf(ROWS[0].name) === -1);
|
|
168
|
+
|
|
169
|
+
// Capture the vault keypair JSON the bundle encrypts into its
|
|
170
|
+
// manifest (so a cold restorer can unseal the recovered vault).
|
|
171
|
+
var vaultKeyJson = b.vault.getKeysJson();
|
|
172
|
+
|
|
173
|
+
// Seal the working copy to durable db.enc / db.key.enc.
|
|
174
|
+
await b.db.flushToDisk();
|
|
175
|
+
check("pre-backup: db.enc exists (sealed at-rest snapshot)",
|
|
176
|
+
fs.existsSync(path.join(dataDir, "db.enc")));
|
|
177
|
+
check("pre-backup: db.key.enc exists (sealed DEK)",
|
|
178
|
+
fs.existsSync(path.join(dataDir, "db.key.enc")));
|
|
179
|
+
check("pre-backup: vault.key.sealed exists (wrapped vault keypair)",
|
|
180
|
+
fs.existsSync(path.join(dataDir, "vault.key.sealed")));
|
|
181
|
+
|
|
182
|
+
// db.enc must not carry bare PII (it's the AEAD-sealed at-rest image).
|
|
183
|
+
var preDbEnc = fs.readFileSync(path.join(dataDir, "db.enc"));
|
|
184
|
+
var preBareHits = ROWS.some(function (r) {
|
|
185
|
+
return preDbEnc.includes(Buffer.from(r.email)) || preDbEnc.includes(Buffer.from(r.name));
|
|
186
|
+
});
|
|
187
|
+
check("pre-backup: db.enc carries NO bare PII (ciphertext at rest)", !preBareHits);
|
|
188
|
+
|
|
189
|
+
// The framework strips the vault passphrase from env after reading it
|
|
190
|
+
// (security feature). Re-supply it so the post-restore re-open can
|
|
191
|
+
// unseal vault.key.sealed under the same passphrase.
|
|
192
|
+
dbh.setTestPassphraseEnv();
|
|
193
|
+
|
|
194
|
+
// ---- 2. Backup engine -> MinIO via the sigv4 objectStore adapter ----
|
|
195
|
+
// bundleAdapterStorage(objectStoreAdapter(sigv4 client)) is the exact
|
|
196
|
+
// S3/MinIO backup path the README advertises. tar.gz format so the
|
|
197
|
+
// whole encrypted bundle dir lands as ONE object key.
|
|
198
|
+
var storage = b.backup.bundleAdapterStorage({
|
|
199
|
+
adapter: b.backup.bundleAdapterStorage.objectStoreAdapter(client, { prefix: prefix }),
|
|
200
|
+
format: "tar.gz",
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
var includeFiles = b.backup.recommendedFiles({
|
|
204
|
+
atRest: "encrypted",
|
|
205
|
+
vaultMode: "wrapped",
|
|
206
|
+
additionalSealed: ["vault.derived-hash-salt"],
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
var engine = b.backup.create({
|
|
210
|
+
dataDir: dataDir,
|
|
211
|
+
storage: storage,
|
|
212
|
+
passphrase: Buffer.from(BACKUP_PASSPHRASE, "utf8"),
|
|
213
|
+
files: includeFiles,
|
|
214
|
+
vaultKeyJson: vaultKeyJson,
|
|
215
|
+
audit: false,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// RUN the backup against the real bucket. This is the exact path the
|
|
219
|
+
// README advertises; it must succeed. (If the objectStoreAdapter's
|
|
220
|
+
// not-found translation is broken for the sigv4 backend, writeBundle's
|
|
221
|
+
// pre-write hasKey existence probe surfaces the bucket's HTTP 404 as a
|
|
222
|
+
// hard error here instead of treating a missing fresh bundle id as
|
|
223
|
+
// "not present" — see the FAIL message below.)
|
|
224
|
+
var summary = null;
|
|
225
|
+
var runErr = null;
|
|
226
|
+
try {
|
|
227
|
+
summary = await engine.run();
|
|
228
|
+
} catch (e) { runErr = e; }
|
|
229
|
+
check("backup.run: bundle written to the real MinIO bucket (no spurious 404 from the " +
|
|
230
|
+
"objectStoreAdapter not-found probe)" +
|
|
231
|
+
(runErr ? " — FAILED: " + (runErr.code || "") + " " + (runErr.message || "") : ""),
|
|
232
|
+
runErr === null && summary !== null);
|
|
233
|
+
if (runErr || !summary) {
|
|
234
|
+
// The advertised S3/MinIO backup path is broken — stop here; every
|
|
235
|
+
// downstream restore assertion depends on a successful write and
|
|
236
|
+
// would only cascade misleading failures. The single failed check
|
|
237
|
+
// above is the precise, honest bug signal.
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
check("backup.run: produced a bundle id", typeof summary.bundleId === "string" && summary.bundleId.length > 0);
|
|
241
|
+
check("backup.run: storage name reports adapter", summary.storage === "adapter");
|
|
242
|
+
var bundleId = summary.bundleId;
|
|
243
|
+
|
|
244
|
+
// ---- 3. Verify the bundle object actually exists in the REAL bucket ----
|
|
245
|
+
// Raw client HEAD on the exact key the adapter writes:
|
|
246
|
+
// <prefix>/<bundleId>/bundle.tar.gz
|
|
247
|
+
var objectKey = prefix + "/" + bundleId + "/bundle.tar.gz";
|
|
248
|
+
var head = await client.head(objectKey);
|
|
249
|
+
check("minio: bundle object exists in the bucket (SigV4 HEAD signed + 200)",
|
|
250
|
+
head && typeof head.size === "number" && head.size > 0);
|
|
251
|
+
|
|
252
|
+
// engine.list() round-trips through SigV4 LIST against the bucket.
|
|
253
|
+
var listed = await engine.list();
|
|
254
|
+
check("backup.list: enumerates the just-written bundle from the real bucket",
|
|
255
|
+
Array.isArray(listed) && listed.some(function (e) { return e.bundleId === bundleId; }));
|
|
256
|
+
|
|
257
|
+
// engine.storage.hasBundle composes a SigV4 HEAD.
|
|
258
|
+
check("backup.hasBundle: true against the real bucket",
|
|
259
|
+
(await storage.hasBundle(bundleId)) === true);
|
|
260
|
+
|
|
261
|
+
// The object bytes pulled straight from MinIO must NOT contain bare
|
|
262
|
+
// PII — the bundle is the framework's encrypted-at-rest payload, and
|
|
263
|
+
// even at format "tar.gz" with cryptoStrategy "none" the per-file
|
|
264
|
+
// blobs inside are passphrase-AEAD-sealed by the bundle builder.
|
|
265
|
+
var bundleObjBytes = await client.get(objectKey);
|
|
266
|
+
var bundleObjBuf = Buffer.isBuffer(bundleObjBytes) ? bundleObjBytes : Buffer.from(bundleObjBytes);
|
|
267
|
+
var objBareHits = ROWS.some(function (r) {
|
|
268
|
+
return bundleObjBuf.includes(Buffer.from(r.email)) || bundleObjBuf.includes(Buffer.from(r.name));
|
|
269
|
+
});
|
|
270
|
+
check("minio: bundle object in the bucket carries NO bare PII", !objBareHits);
|
|
271
|
+
|
|
272
|
+
// ---- 4. RESTORE from the REAL bucket into a fresh location ----
|
|
273
|
+
// engine.read pulls the tar.gz back via SigV4 GET + extracts the
|
|
274
|
+
// encrypted bundle directory.
|
|
275
|
+
restoredBundleDir = path.join(_tmp("blamejs-dr-restored"), "bundle");
|
|
276
|
+
await engine.read(bundleId, restoredBundleDir);
|
|
277
|
+
check("restore: bundle directory extracted from the real bucket",
|
|
278
|
+
fs.existsSync(path.join(restoredBundleDir, "manifest.json")));
|
|
279
|
+
|
|
280
|
+
// The extracted bundle dir holds per-file .enc blobs; scan it for any
|
|
281
|
+
// bare PII leak (there must be none — every blob is passphrase-sealed).
|
|
282
|
+
var bundleBareHits = _scanForBare(restoredBundleDir,
|
|
283
|
+
ROWS.map(function (r) { return r.email; }).concat(ROWS.map(function (r) { return r.name; })));
|
|
284
|
+
check("restore: extracted bundle directory carries NO bare PII", bundleBareHits.length === 0);
|
|
285
|
+
|
|
286
|
+
// Verify the manifest signature survived the object-store round-trip.
|
|
287
|
+
var sigCheck = b.backup.verifyManifestSignature(restoredBundleDir);
|
|
288
|
+
check("restore: restored manifest signature verifies (SLH-DSA, tamper-evident)",
|
|
289
|
+
sigCheck && sigCheck.ok === true);
|
|
290
|
+
|
|
291
|
+
// restoreBundle.extract decrypts each blob under the backup passphrase
|
|
292
|
+
// into the staging dir = the recovered data files.
|
|
293
|
+
restoreStaging = path.join(_tmp("blamejs-dr-staging"), "data");
|
|
294
|
+
var extractRv = await b.restoreBundle.extract({
|
|
295
|
+
bundleDir: restoredBundleDir,
|
|
296
|
+
stagingDir: restoreStaging,
|
|
297
|
+
passphrase: Buffer.from(BACKUP_PASSPHRASE, "utf8"),
|
|
298
|
+
});
|
|
299
|
+
check("restore: extract recovered the expected file count",
|
|
300
|
+
extractRv.fileCount >= 3);
|
|
301
|
+
check("restore: db.enc recovered into staging",
|
|
302
|
+
fs.existsSync(path.join(restoreStaging, "db.enc")));
|
|
303
|
+
check("restore: db.key.enc recovered into staging",
|
|
304
|
+
fs.existsSync(path.join(restoreStaging, "db.key.enc")));
|
|
305
|
+
check("restore: vault.key.sealed recovered into staging",
|
|
306
|
+
fs.existsSync(path.join(restoreStaging, "vault.key.sealed")));
|
|
307
|
+
|
|
308
|
+
// The recovered db.enc must be ciphertext (no bare PII at rest). We do
|
|
309
|
+
// NOT assert byte-identity to the live db.enc: encrypted-at-rest
|
|
310
|
+
// re-seals with a fresh nonce on each flush, so the bytes legitimately
|
|
311
|
+
// differ. Integrity is proven by the re-open + decrypt below (and the
|
|
312
|
+
// manifest's per-file SHA3-512 checksum, which restoreBundle.extract
|
|
313
|
+
// already verified against the recovered plaintext).
|
|
314
|
+
var restoredBareHits = _scanForBare(restoreStaging,
|
|
315
|
+
ROWS.map(function (r) { return r.email; }).concat(ROWS.map(function (r) { return r.name; })));
|
|
316
|
+
check("restore: recovered data files carry NO bare PII (still sealed)",
|
|
317
|
+
restoredBareHits.length === 0);
|
|
318
|
+
|
|
319
|
+
// ---- 5. Re-open vault + db from the recovered files; read sealed cols ----
|
|
320
|
+
// db.key.enc (the sealed DEK) is AAD-bound to the absolute dataDir +
|
|
321
|
+
// keyPath of the deployment that sealed it, so a same-host restore
|
|
322
|
+
// recovers into the ORIGINAL data dir path. Tear the live deployment
|
|
323
|
+
// down (frees the working copy + handles) without deleting the data
|
|
324
|
+
// dir, then lay the recovered files back into that same path.
|
|
325
|
+
reopenDataDir = dataDir;
|
|
326
|
+
dataDir = null; // ownership transfers; finally{} won't double-teardown
|
|
327
|
+
await dbh.teardownTestDb(reopenDataDir); // closes db/vault; rm -rf's the dir
|
|
328
|
+
fs.mkdirSync(reopenDataDir, { recursive: true });
|
|
329
|
+
["db.enc", "db.key.enc", "vault.key.sealed", "audit-sign.key.sealed", "vault.derived-hash-salt"]
|
|
330
|
+
.forEach(function (f) {
|
|
331
|
+
var src = path.join(restoreStaging, f);
|
|
332
|
+
if (fs.existsSync(src)) fs.copyFileSync(src, path.join(reopenDataDir, f));
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
dbh.setTestPassphraseEnv();
|
|
336
|
+
process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
|
|
337
|
+
b.cluster._resetForTest();
|
|
338
|
+
b.audit._resetForTest();
|
|
339
|
+
b.vault._resetForTest();
|
|
340
|
+
b.db._resetForTest();
|
|
341
|
+
await b.vault.init({ dataDir: reopenDataDir });
|
|
342
|
+
check("restore: vault re-opened from recovered vault.key.sealed",
|
|
343
|
+
b.vault.getMode() === "wrapped");
|
|
344
|
+
await b.db.init({
|
|
345
|
+
dataDir: reopenDataDir,
|
|
346
|
+
tmpDir: path.join(reopenDataDir, "tmpfs"),
|
|
347
|
+
schema: [
|
|
348
|
+
{
|
|
349
|
+
name: "users",
|
|
350
|
+
columns: {
|
|
351
|
+
_id: "TEXT PRIMARY KEY",
|
|
352
|
+
email: "TEXT",
|
|
353
|
+
emailHash: "TEXT",
|
|
354
|
+
name: "TEXT",
|
|
355
|
+
status: "TEXT DEFAULT 'active'",
|
|
356
|
+
createdAt: "TEXT",
|
|
357
|
+
},
|
|
358
|
+
indexes: ["emailHash", "status"],
|
|
359
|
+
sealedFields: ["email", "name"],
|
|
360
|
+
derivedHashes: { emailHash: { from: "email", normalize: function (v) { return String(v).toLowerCase(); } } },
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// On the re-opened DB, the on-disk cells are STILL sealed ciphertext.
|
|
366
|
+
var rawReopen = b.db.prepare('SELECT email AS e, name AS n FROM users WHERE _id = ?').get("u2");
|
|
367
|
+
check("restore: re-opened DB cell is still sealed ciphertext (not bare)",
|
|
368
|
+
typeof rawReopen.e === "string" && rawReopen.e.indexOf("vault:") === 0 &&
|
|
369
|
+
rawReopen.e.indexOf(ROWS[1].email) === -1);
|
|
370
|
+
|
|
371
|
+
// ...and decrypt correctly under the restored vault.
|
|
372
|
+
var allOk = true;
|
|
373
|
+
for (var k = 0; k < ROWS.length; k++) {
|
|
374
|
+
var got = b.db.from("users").where({ _id: ROWS[k]._id }).first();
|
|
375
|
+
if (!got || got.email !== ROWS[k].email || got.name !== ROWS[k].name) { allOk = false; }
|
|
376
|
+
}
|
|
377
|
+
check("restore: ALL sealed rows decrypt under the restored vault (data intact)", allOk);
|
|
378
|
+
|
|
379
|
+
// ---- 6a. Negative: wrong-passphrase restore FAILS ----
|
|
380
|
+
// The restored deployment's vault + auditSign stay LIVE here (a real
|
|
381
|
+
// restorer has them initialized) so the bundle's manifest-signature verify
|
|
382
|
+
// passes and the WRONG PASSPHRASE is rejected at AEAD decrypt — proving the
|
|
383
|
+
// passphrase actually gates the data, not an incidental uninit error. The
|
|
384
|
+
// finally block does the teardown.
|
|
385
|
+
var wrongStaging = path.join(_tmp("blamejs-dr-wrong"), "data");
|
|
386
|
+
var wrongErr = null;
|
|
387
|
+
try {
|
|
388
|
+
await b.restoreBundle.extract({
|
|
389
|
+
bundleDir: restoredBundleDir,
|
|
390
|
+
stagingDir: wrongStaging,
|
|
391
|
+
passphrase: Buffer.from("the-WRONG-passphrase", "utf8"),
|
|
392
|
+
});
|
|
393
|
+
} catch (e) { wrongErr = e; }
|
|
394
|
+
check("negative: wrong-passphrase restore FAILS with decrypt-failed (AEAD tag)",
|
|
395
|
+
wrongErr && /decrypt-failed|passphrase rejected/i.test((wrongErr.code || "") + " " + (wrongErr.message || "")));
|
|
396
|
+
_rmrf(path.dirname(wrongStaging));
|
|
397
|
+
|
|
398
|
+
// ---- 6b. Negative: tampered bundle object in the bucket FAILS ----
|
|
399
|
+
// Flip bytes in the middle of the stored tar.gz object and re-PUT it,
|
|
400
|
+
// then a fresh read+extract must fail (gunzip/tar/AEAD all reject).
|
|
401
|
+
var tampered = Buffer.from(bundleObjBuf);
|
|
402
|
+
var mid = Math.floor(tampered.length / 2);
|
|
403
|
+
tampered[mid] = tampered[mid] ^ 0xff;
|
|
404
|
+
tampered[mid + 1] = tampered[mid + 1] ^ 0xff;
|
|
405
|
+
// Force a single PUT overwrite of the same key.
|
|
406
|
+
await client.put(objectKey, tampered, { multipart: false });
|
|
407
|
+
|
|
408
|
+
var tamperedDir = path.join(_tmp("blamejs-dr-tampered"), "bundle");
|
|
409
|
+
var tamperErr = null;
|
|
410
|
+
try {
|
|
411
|
+
await engine.read(bundleId, tamperedDir);
|
|
412
|
+
// If extraction somehow produced a manifest, attempt a decrypt —
|
|
413
|
+
// that must fail. If read() already threw, we never reach here.
|
|
414
|
+
await b.restoreBundle.extract({
|
|
415
|
+
bundleDir: tamperedDir,
|
|
416
|
+
stagingDir: path.join(_tmp("blamejs-dr-tampered-stg"), "data"),
|
|
417
|
+
passphrase: Buffer.from(BACKUP_PASSPHRASE, "utf8"),
|
|
418
|
+
});
|
|
419
|
+
} catch (e) { tamperErr = e; }
|
|
420
|
+
check("negative: tampered bundle object in the bucket FAILS to restore",
|
|
421
|
+
tamperErr !== null);
|
|
422
|
+
_rmrf(path.dirname(tamperedDir));
|
|
423
|
+
|
|
424
|
+
} finally {
|
|
425
|
+
// Teardown: live DB (if still open), bucket + every object, temp dirs.
|
|
426
|
+
try { if (dataDir) await dbh.teardownTestDb(dataDir); } catch (_e) { /* ignore */ }
|
|
427
|
+
try { b.db._resetForTest(); } catch (_e) { /* ignore */ }
|
|
428
|
+
try { b.vault._resetForTest(); } catch (_e) { /* ignore */ }
|
|
429
|
+
try { b.audit._resetForTest(); } catch (_e) { /* ignore */ }
|
|
430
|
+
try { b.cluster._resetForTest(); } catch (_e) { /* ignore */ }
|
|
431
|
+
// Drain the bucket then drop it.
|
|
432
|
+
try {
|
|
433
|
+
var leftover = await client.list(prefix + "/");
|
|
434
|
+
var items = (leftover && leftover.items) || [];
|
|
435
|
+
for (var d = 0; d < items.length; d++) {
|
|
436
|
+
try { await client.delete(items[d].key); } catch (_e) { /* ignore */ }
|
|
437
|
+
}
|
|
438
|
+
await ops.delete(bucket);
|
|
439
|
+
} catch (_e) { /* best-effort bucket cleanup */ }
|
|
440
|
+
if (restoredBundleDir) _rmrf(path.dirname(restoredBundleDir));
|
|
441
|
+
if (restoreStaging) _rmrf(path.dirname(restoreStaging));
|
|
442
|
+
if (reopenDataDir) _rmrf(reopenDataDir);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
module.exports = { run: run };
|
|
447
|
+
|
|
448
|
+
if (require.main === module) {
|
|
449
|
+
run().then(
|
|
450
|
+
function () { console.log("[backup-restore-objectstore] OK — " + helpers.getChecks() + " checks passed"); },
|
|
451
|
+
function (e) { console.error("FAIL:", (e && e.stack) || e); process.exit(1); }
|
|
452
|
+
);
|
|
453
|
+
}
|