@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
|
@@ -52,12 +52,13 @@ var nodeFs = require("node:fs");
|
|
|
52
52
|
var nodePath = require("node:path");
|
|
53
53
|
var { DatabaseSync } = require("node:sqlite");
|
|
54
54
|
var atomicFile = require("../atomic-file");
|
|
55
|
-
var
|
|
55
|
+
var sql = require("../sql");
|
|
56
56
|
var C = require("../constants");
|
|
57
57
|
var cryptoField = require("../crypto-field");
|
|
58
58
|
var bCrypto = require("../crypto");
|
|
59
59
|
var vaultAad = require("../vault-aad");
|
|
60
60
|
var dbSchema = require("../db-schema");
|
|
61
|
+
var frameworkFiles = require("../framework-files");
|
|
61
62
|
var lazyRequire = require("../lazy-require");
|
|
62
63
|
var { boot } = require("../log");
|
|
63
64
|
var numericBounds = require("../numeric-bounds");
|
|
@@ -81,6 +82,11 @@ var agentSnapshotLazy = lazyRequire(function () { return require("../agent-snaps
|
|
|
81
82
|
// rotation pipeline never walks, so archive-wrap exports the same external
|
|
82
83
|
// AAD_ROTATION descriptor and must be gated here too.
|
|
83
84
|
var archiveWrapLazy = lazyRequire(function () { return require("../archive-wrap"); });
|
|
85
|
+
// The DSR ticket store, when backed by an operator-supplied database, holds
|
|
86
|
+
// {aad:true} sealed cells (subject identifiers + request payload) keyed off the
|
|
87
|
+
// vault root that this pipeline never walks, so dsr exports the same external
|
|
88
|
+
// AAD_ROTATION descriptor and must be gated here too.
|
|
89
|
+
var dsrLazy = lazyRequire(function () { return require("../dsr"); });
|
|
84
90
|
var { defineClass } = require("../framework-error");
|
|
85
91
|
|
|
86
92
|
var rotateLog = boot("vault-rotate");
|
|
@@ -92,18 +98,30 @@ var DEFAULT_DRIFT_SAMPLE_LIMIT = 100;
|
|
|
92
98
|
var DEFAULT_VERIFY_SAMPLE_MIN = 5;
|
|
93
99
|
var DEFAULT_VERIFY_SAMPLE_FRAC = 0.01;
|
|
94
100
|
|
|
101
|
+
// The catalog/PRAGMA statements all compose through b.sql's narrow audited
|
|
102
|
+
// catalog sub-API (b.sql.catalog / b.sql.pragma) - the only path that emits
|
|
103
|
+
// an sqlite_master reference or a PRAGMA verb, allowlisting exactly the
|
|
104
|
+
// statements the key-rotation walk needs and refusing every other internal
|
|
105
|
+
// identifier / PRAGMA verb. Each returns { sql, params }; the node:sqlite
|
|
106
|
+
// handle takes the params positionally.
|
|
107
|
+
function _all(db, built) {
|
|
108
|
+
var stmt = db.prepare(built.sql);
|
|
109
|
+
return built.params.length > 0 ? stmt.all.apply(stmt, built.params) : stmt.all();
|
|
110
|
+
}
|
|
111
|
+
function _get(db, built) {
|
|
112
|
+
var stmt = db.prepare(built.sql);
|
|
113
|
+
return built.params.length > 0 ? stmt.get.apply(stmt, built.params) : stmt.get();
|
|
114
|
+
}
|
|
115
|
+
|
|
95
116
|
function _listLiveTables(db) {
|
|
96
|
-
return db.
|
|
97
|
-
"SELECT name FROM sqlite_master " +
|
|
98
|
-
"WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
|
99
|
-
).all().map(function (r) { return r.name; });
|
|
117
|
+
return _all(db, sql.catalog.listTables()).map(function (r) { return r.name; });
|
|
100
118
|
}
|
|
101
119
|
|
|
102
120
|
function _listLiveColumns(db, table) {
|
|
103
121
|
// PRAGMA table_info — table name comes from sqlite_master so it's
|
|
104
|
-
// already validated as an existing identifier.
|
|
105
|
-
|
|
106
|
-
|
|
122
|
+
// already validated as an existing identifier; b.sql.catalog.tableInfo
|
|
123
|
+
// quotes it by construction.
|
|
124
|
+
return _all(db, sql.catalog.tableInfo(table)).map(function (c) { return c.name; });
|
|
107
125
|
}
|
|
108
126
|
|
|
109
127
|
function _knownColumnsFor(schema, infraColumns) {
|
|
@@ -196,12 +214,13 @@ function validateSchemaMatch(db, opts) {
|
|
|
196
214
|
}
|
|
197
215
|
if (unknown.length === 0) continue;
|
|
198
216
|
|
|
199
|
-
var
|
|
200
|
-
|
|
201
|
-
|
|
217
|
+
var sampleBuilt = sql.select(table, { dialect: "sqlite", quoteName: true })
|
|
218
|
+
.columns(unknown)
|
|
219
|
+
.limit(sampleLimit)
|
|
220
|
+
.toSql();
|
|
202
221
|
var sampled;
|
|
203
222
|
try {
|
|
204
|
-
sampled = db
|
|
223
|
+
sampled = _all(db, sampleBuilt);
|
|
205
224
|
} catch (e) {
|
|
206
225
|
warnings.push({
|
|
207
226
|
kind: "sample_failed",
|
|
@@ -307,7 +326,8 @@ function verify(opts) {
|
|
|
307
326
|
var schema = cryptoField.getSchema(table);
|
|
308
327
|
if (!schema || !Array.isArray(schema.sealedFields) || schema.sealedFields.length === 0) continue;
|
|
309
328
|
|
|
310
|
-
var totalRow = db.
|
|
329
|
+
var totalRow = _get(db, sql.select(table, { dialect: "sqlite", quoteName: true })
|
|
330
|
+
.count("*", "n").toSql());
|
|
311
331
|
var total = totalRow ? totalRow.n : 0;
|
|
312
332
|
if (total === 0) continue;
|
|
313
333
|
|
|
@@ -315,10 +335,10 @@ function verify(opts) {
|
|
|
315
335
|
if (sampleN > total) sampleN = total;
|
|
316
336
|
|
|
317
337
|
// RANDOM() is fine for a sampler — we're picking representative rows,
|
|
318
|
-
// not building cryptographic randomness.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
338
|
+
// not building cryptographic randomness. b.sql.catalog.sampleRandom is
|
|
339
|
+
// the audited ORDER BY RANDOM() form (the general builder has no random-
|
|
340
|
+
// order clause); columns omitted -> `*`.
|
|
341
|
+
var sampled = _all(db, sql.catalog.sampleRandom(table, null, { limit: sampleN }));
|
|
322
342
|
|
|
323
343
|
var foundOldFail = !oldKeys; // when no oldKeys supplied, this check is N/A
|
|
324
344
|
var verifiedRows = 0;
|
|
@@ -439,7 +459,7 @@ var VAULT_PREFIX_LEN = C.VAULT_PREFIX.length;
|
|
|
439
459
|
// so loading rotate.js doesn't eagerly pull the agent modules.
|
|
440
460
|
var EXTERNAL_AAD_MODULE_LOADERS = [
|
|
441
461
|
agentIdempotencyLazy, agentOrchestratorLazy, agentTenantLazy, agentSnapshotLazy,
|
|
442
|
-
archiveWrapLazy,
|
|
462
|
+
archiveWrapLazy, dsrLazy,
|
|
443
463
|
];
|
|
444
464
|
|
|
445
465
|
function _externalAadTables() {
|
|
@@ -464,22 +484,25 @@ function _emit(cb, ev) {
|
|
|
464
484
|
}
|
|
465
485
|
}
|
|
466
486
|
|
|
467
|
-
//
|
|
468
|
-
//
|
|
469
|
-
//
|
|
470
|
-
// the
|
|
471
|
-
//
|
|
472
|
-
//
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
487
|
+
// Create a fresh file in the owner-only staging dir with exclusive,
|
|
488
|
+
// no-follow semantics, then fsync it. O_EXCL turns a pre-planted file or
|
|
489
|
+
// symlink into a hard failure instead of a followed write; O_NOFOLLOW
|
|
490
|
+
// refuses a symlinked final component; the explicit 0o600 keeps the bytes
|
|
491
|
+
// owner-only regardless of umask. Any leftover from an aborted prior
|
|
492
|
+
// rotation is cleared first so the exclusive create can proceed. The
|
|
493
|
+
// staging dir is already 0o700 owner-only, so this is defense in depth
|
|
494
|
+
// against a same-user pre-plant / symlink swap (CWE-377 / CWE-379 / CWE-59).
|
|
495
|
+
function _writeStagedFileExclusive(p, data) {
|
|
496
|
+
try { nodeFs.unlinkSync(p); } catch (_e) { /* no stale entry to clear */ }
|
|
497
|
+
var fd = nodeFs.openSync(p,
|
|
498
|
+
nodeFs.constants.O_WRONLY | nodeFs.constants.O_CREAT |
|
|
499
|
+
nodeFs.constants.O_EXCL | (nodeFs.constants.O_NOFOLLOW || 0), 0o600);
|
|
479
500
|
try {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
501
|
+
nodeFs.writeFileSync(fd, data);
|
|
502
|
+
nodeFs.fsyncSync(fd);
|
|
503
|
+
} finally {
|
|
504
|
+
nodeFs.closeSync(fd);
|
|
505
|
+
}
|
|
483
506
|
}
|
|
484
507
|
|
|
485
508
|
function _reSealValue(sealedValue, oldKeys, newKeys) {
|
|
@@ -521,15 +544,19 @@ function _walkAndReSeal(node, oldKeys, newKeys) {
|
|
|
521
544
|
return { value: node, changed: false };
|
|
522
545
|
}
|
|
523
546
|
|
|
524
|
-
|
|
547
|
+
// Transaction-control statements only (BEGIN / COMMIT / ROLLBACK) - fixed
|
|
548
|
+
// keywords, no identifier / value, so they stay verbatim rather than route
|
|
549
|
+
// through b.sql (the builder has no transaction-control verb). The param is
|
|
550
|
+
// named `stmtText` so it does not shadow the module-level `sql` builder.
|
|
551
|
+
function _runStmt(db, stmtText) { db.prepare(stmtText).run(); }
|
|
525
552
|
|
|
526
553
|
function _rotateColumn(db, table, column, schema, roots, batchSize, progress) {
|
|
527
|
-
//
|
|
528
|
-
//
|
|
529
|
-
//
|
|
530
|
-
|
|
531
|
-
var
|
|
532
|
-
|
|
554
|
+
// Every statement composes through b.sql (sqlite dialect, quoteName so
|
|
555
|
+
// the concrete handle's table is quoted, not left bare for a cluster
|
|
556
|
+
// rewrite that does not apply here). Identifiers are validated + quoted
|
|
557
|
+
// by construction; the cursor bound (_id) + LIMIT bind as ? placeholders.
|
|
558
|
+
var total = _get(db, sql.select(table, { dialect: "sqlite", quoteName: true })
|
|
559
|
+
.count("*", "n").whereNotNull(column).toSql()).n;
|
|
533
560
|
if (total === 0) return 0;
|
|
534
561
|
|
|
535
562
|
// AAD-bound tables (registerTable({aad:true})) seal each cell under a
|
|
@@ -540,36 +567,54 @@ function _rotateColumn(db, table, column, schema, roots, batchSize, progress) {
|
|
|
540
567
|
var aadMode = !!(schema && schema.aad);
|
|
541
568
|
var rowIdField = aadMode ? schema.rowIdField : null;
|
|
542
569
|
var needRid = aadMode && rowIdField && rowIdField !== "_id";
|
|
543
|
-
var qrid = needRid ? safeSql.quoteIdentifier(rowIdField, "sqlite") : null;
|
|
544
570
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
571
|
+
// Keyset-cursor page over (_id) ascending. The projected columns are read
|
|
572
|
+
// by their REAL names off the result row (no AS alias) - the column value
|
|
573
|
+
// is row[column], the row-id value is row[rowIdField]. The SQL text is
|
|
574
|
+
// constant across the loop (only the bound _id-cursor changes; LIMIT is a
|
|
575
|
+
// builder-inlined integer literal, validated non-negative), so prepare
|
|
576
|
+
// once + re-run with the fresh cursor param positionally. The SELECT
|
|
577
|
+
// carries exactly one `?` (the _id cursor); the UPDATE carries two (the
|
|
578
|
+
// resealed value + the _id).
|
|
579
|
+
var selCols = ["_id", column];
|
|
580
|
+
if (needRid) selCols.push(rowIdField);
|
|
581
|
+
var selBuilt = sql.select(table, { dialect: "sqlite", quoteName: true })
|
|
582
|
+
.columns(selCols)
|
|
583
|
+
.whereNotNull(column)
|
|
584
|
+
.whereOp("_id", ">", "")
|
|
585
|
+
.orderBy("_id")
|
|
586
|
+
.limit(batchSize)
|
|
587
|
+
.toSql();
|
|
588
|
+
var sel = db.prepare(selBuilt.sql);
|
|
589
|
+
var updBuilt = sql.update(table, { dialect: "sqlite", quoteName: true })
|
|
590
|
+
.set(column, "")
|
|
591
|
+
.where("_id", "")
|
|
592
|
+
.toSql();
|
|
593
|
+
var upd = db.prepare(updBuilt.sql);
|
|
550
594
|
|
|
551
595
|
var processed = 0;
|
|
552
596
|
var lastId = "";
|
|
553
597
|
while (true) {
|
|
554
|
-
var rows = sel.all(lastId
|
|
598
|
+
var rows = sel.all(lastId);
|
|
555
599
|
if (rows.length === 0) break;
|
|
556
600
|
|
|
557
601
|
dbSchema.runInTransaction(db, function () {
|
|
558
602
|
for (var i = 0; i < rows.length; i++) {
|
|
559
603
|
var row = rows[i];
|
|
560
|
-
|
|
561
|
-
if (
|
|
604
|
+
var cellVal = row[column];
|
|
605
|
+
if (typeof cellVal !== "string") continue;
|
|
606
|
+
if (aadMode && vaultAad.isAadSealed(cellVal)) {
|
|
562
607
|
// Rebuild the exact AAD the seal side used. cryptoField._aadParts
|
|
563
608
|
// reads row[schema.rowIdField]; feed it the rowIdField value we
|
|
564
|
-
// selected (
|
|
609
|
+
// selected (row[rowIdField], or _id when rowIdField IS _id).
|
|
565
610
|
var rowForAad = {};
|
|
566
|
-
rowForAad[rowIdField] = needRid ? row
|
|
611
|
+
rowForAad[rowIdField] = needRid ? row[rowIdField] : row._id;
|
|
567
612
|
var aad = cryptoField._aadParts(schema, table, column, rowForAad);
|
|
568
|
-
upd.run(vaultAad.resealRoot(
|
|
569
|
-
} else if (
|
|
613
|
+
upd.run(vaultAad.resealRoot(cellVal, aad, roots.oldRootJson, roots.newRootJson), row._id);
|
|
614
|
+
} else if (cellVal.indexOf(C.VAULT_PREFIX) === 0) {
|
|
570
615
|
// Plain vault: cell (non-AAD table, or a legacy pre-AAD cell in
|
|
571
616
|
// an AAD table that the next sealRow upgrades).
|
|
572
|
-
upd.run(_reSealValue(
|
|
617
|
+
upd.run(_reSealValue(cellVal, roots.oldKeys, roots.newKeys), row._id);
|
|
573
618
|
}
|
|
574
619
|
}
|
|
575
620
|
});
|
|
@@ -581,23 +626,33 @@ function _rotateColumn(db, table, column, schema, roots, batchSize, progress) {
|
|
|
581
626
|
}
|
|
582
627
|
|
|
583
628
|
function _rotateOverflow(db, table, oldKeys, newKeys, batchSize, progress, warnings) {
|
|
584
|
-
var
|
|
585
|
-
var cols = db.prepare("PRAGMA table_info(" + qt + ")").all();
|
|
629
|
+
var cols = _all(db, sql.catalog.tableInfo(table));
|
|
586
630
|
if (!cols.some(function (c) { return c.name === "data"; })) return 0;
|
|
587
631
|
|
|
588
|
-
var total = db.
|
|
632
|
+
var total = _get(db, sql.select(table, { dialect: "sqlite", quoteName: true })
|
|
633
|
+
.count("*", "n").whereNotNull("data").toSql()).n;
|
|
589
634
|
if (total === 0) return 0;
|
|
590
635
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
636
|
+
// Same keyset cursor as _rotateColumn over the overflow `data` JSON
|
|
637
|
+
// column: one bound `?` (the _id cursor), builder-inlined LIMIT literal.
|
|
638
|
+
var selBuilt = sql.select(table, { dialect: "sqlite", quoteName: true })
|
|
639
|
+
.columns(["_id", "data"])
|
|
640
|
+
.whereNotNull("data")
|
|
641
|
+
.whereOp("_id", ">", "")
|
|
642
|
+
.orderBy("_id")
|
|
643
|
+
.limit(batchSize)
|
|
644
|
+
.toSql();
|
|
645
|
+
var sel = db.prepare(selBuilt.sql);
|
|
646
|
+
var updBuilt = sql.update(table, { dialect: "sqlite", quoteName: true })
|
|
647
|
+
.set("data", "")
|
|
648
|
+
.where("_id", "")
|
|
649
|
+
.toSql();
|
|
650
|
+
var upd = db.prepare(updBuilt.sql);
|
|
596
651
|
|
|
597
652
|
var processed = 0;
|
|
598
653
|
var lastId = "";
|
|
599
654
|
while (true) {
|
|
600
|
-
var rows = sel.all(lastId
|
|
655
|
+
var rows = sel.all(lastId);
|
|
601
656
|
if (rows.length === 0) break;
|
|
602
657
|
|
|
603
658
|
_runStmt(db, "BEGIN");
|
|
@@ -670,7 +725,8 @@ async function rotate(opts) {
|
|
|
670
725
|
"pipeline and would be orphaned under the retired keypair: " + externalAad.join(", ") +
|
|
671
726
|
". Re-seal each via its module hook (b.agent.idempotency.reseal / " +
|
|
672
727
|
"b.agent.orchestrator.reseal / b.agent.tenant AAD_ROTATION reseal / " +
|
|
673
|
-
"b.agent.snapshot.reseal / b.archive.rewrapTenant for archive-wrap:tenant-blobs
|
|
728
|
+
"b.agent.snapshot.reseal / b.archive.rewrapTenant for archive-wrap:tenant-blobs / " +
|
|
729
|
+
"b.dsr.reseal for the dsr_tickets store) " +
|
|
674
730
|
"BEFORE retiring the old keypair, then pass " +
|
|
675
731
|
"opts.externalAadResealed: [" + externalAad.map(function (t) { return JSON.stringify(t); }).join(", ") +
|
|
676
732
|
"] to acknowledge. If you do not use these features, pass opts.externalAadResealed: true.");
|
|
@@ -680,10 +736,10 @@ async function rotate(opts) {
|
|
|
680
736
|
var progress = opts.progressCallback;
|
|
681
737
|
var warnings = [];
|
|
682
738
|
var paths = Object.assign({
|
|
683
|
-
encryptedDb:
|
|
684
|
-
dbKeySealed:
|
|
685
|
-
vaultKeyPlain:
|
|
686
|
-
vaultKeySealed: "
|
|
739
|
+
encryptedDb: frameworkFiles.fileName("dbEnc"),
|
|
740
|
+
dbKeySealed: frameworkFiles.fileName("dbKeyEnc"),
|
|
741
|
+
vaultKeyPlain: frameworkFiles.fileName("vaultKey"),
|
|
742
|
+
vaultKeySealed: frameworkFiles.fileName("vaultKey") + ".sealed",
|
|
687
743
|
additionalSealed: [],
|
|
688
744
|
verbatimFiles: [],
|
|
689
745
|
verbatimDirs: [],
|
|
@@ -709,7 +765,10 @@ async function rotate(opts) {
|
|
|
709
765
|
}
|
|
710
766
|
var dest = nodePath.join(stagingDir, entry.relativePath);
|
|
711
767
|
atomicFile.ensureDir(nodePath.dirname(dest));
|
|
712
|
-
|
|
768
|
+
// Stage via the exclusive-create + fsync helper rather than a plain copy,
|
|
769
|
+
// so the verbatim file is durable at write time (no later by-path fsync)
|
|
770
|
+
// and a pre-planted file/symlink at the staging path hard-fails.
|
|
771
|
+
_writeStagedFileExclusive(dest, nodeFs.readFileSync(src));
|
|
713
772
|
}
|
|
714
773
|
for (var vd = 0; vd < paths.verbatimDirs.length; vd++) {
|
|
715
774
|
var dent = paths.verbatimDirs[vd];
|
|
@@ -737,9 +796,9 @@ async function rotate(opts) {
|
|
|
737
796
|
var newRootJson = keysJson;
|
|
738
797
|
if (mode === "wrapped") {
|
|
739
798
|
var sealed = await vaultWrap().wrap(keysJson, opts.newPassphrase);
|
|
740
|
-
|
|
799
|
+
_writeStagedFileExclusive(nodePath.join(stagingDir, paths.vaultKeySealed), sealed);
|
|
741
800
|
} else {
|
|
742
|
-
|
|
801
|
+
_writeStagedFileExclusive(nodePath.join(stagingDir, paths.vaultKeyPlain), keysJson);
|
|
743
802
|
}
|
|
744
803
|
|
|
745
804
|
// 3. re-seal db.key.enc + any operator-supplied additionalSealed files
|
|
@@ -759,14 +818,14 @@ async function rotate(opts) {
|
|
|
759
818
|
var dbKeyB64Aad = vaultAad.unsealRoot(sealedKey, dbKeyAad, oldRootJson);
|
|
760
819
|
dbKey = Buffer.from(dbKeyB64Aad, "base64");
|
|
761
820
|
var resealedAad = vaultAad.sealRoot(dbKeyB64Aad, dbKeyAad, newRootJson);
|
|
762
|
-
|
|
821
|
+
_writeStagedFileExclusive(nodePath.join(stagingDir, paths.dbKeySealed), resealedAad);
|
|
763
822
|
} else if (sealedKey.indexOf(C.VAULT_PREFIX) === 0) {
|
|
764
823
|
// Legacy plain-sealed db.key.enc (pre-AAD). Re-key in place; db.init
|
|
765
824
|
// read-migrates plain -> AAD on the next boot.
|
|
766
825
|
var dbKeyB64 = bCrypto.decrypt(sealedKey.substring(VAULT_PREFIX_LEN), oldKeys);
|
|
767
826
|
dbKey = Buffer.from(dbKeyB64, "base64");
|
|
768
827
|
var resealedKey = C.VAULT_PREFIX + bCrypto.encrypt(dbKeyB64, newKeys);
|
|
769
|
-
|
|
828
|
+
_writeStagedFileExclusive(nodePath.join(stagingDir, paths.dbKeySealed), resealedKey);
|
|
770
829
|
} else {
|
|
771
830
|
throw new VaultRotateError("vault-rotate/bad-dbkey",
|
|
772
831
|
"rotate: db.key.enc does not start with a vault prefix (vault: or vault.aad:)");
|
|
@@ -789,8 +848,8 @@ async function rotate(opts) {
|
|
|
789
848
|
}
|
|
790
849
|
var asDestDir = nodePath.join(stagingDir, nodePath.dirname(ase.relativePath));
|
|
791
850
|
if (!nodeFs.existsSync(asDestDir)) atomicFile.ensureDir(asDestDir);
|
|
792
|
-
|
|
793
|
-
_reSealValue(current, oldKeys, newKeys)
|
|
851
|
+
_writeStagedFileExclusive(nodePath.join(stagingDir, ase.relativePath),
|
|
852
|
+
_reSealValue(current, oldKeys, newKeys));
|
|
794
853
|
}
|
|
795
854
|
|
|
796
855
|
// 3b. Framework-managed crypto-field derived-hash files — always
|
|
@@ -801,14 +860,17 @@ async function rotate(opts) {
|
|
|
801
860
|
// re-seals to the same value since the keypair is unchanged).
|
|
802
861
|
var saltSrc = nodePath.join(dataDir, "vault.derived-hash-salt");
|
|
803
862
|
if (nodeFs.existsSync(saltSrc)) {
|
|
804
|
-
|
|
863
|
+
// Stage via the exclusive-create + fsync helper (not a plain copy) so the
|
|
864
|
+
// salt is durable at write time and no later by-path fsync is needed.
|
|
865
|
+
_writeStagedFileExclusive(nodePath.join(stagingDir, "vault.derived-hash-salt"),
|
|
866
|
+
nodeFs.readFileSync(saltSrc));
|
|
805
867
|
}
|
|
806
868
|
var macSrc = nodePath.join(dataDir, "vault.derived-hash-mac.sealed");
|
|
807
869
|
if (nodeFs.existsSync(macSrc)) {
|
|
808
870
|
var macCurrent = nodeFs.readFileSync(macSrc, "utf8").trim();
|
|
809
871
|
if (macCurrent.indexOf(C.VAULT_PREFIX) === 0) {
|
|
810
|
-
|
|
811
|
-
_reSealValue(macCurrent, oldKeys, newKeys)
|
|
872
|
+
_writeStagedFileExclusive(nodePath.join(stagingDir, "vault.derived-hash-mac.sealed"),
|
|
873
|
+
_reSealValue(macCurrent, oldKeys, newKeys));
|
|
812
874
|
}
|
|
813
875
|
}
|
|
814
876
|
|
|
@@ -830,22 +892,19 @@ async function rotate(opts) {
|
|
|
830
892
|
try { plainBytes = bCrypto.decryptPacked(packed, dbKey, dbEncAad); }
|
|
831
893
|
catch (_eAad) { plainBytes = bCrypto.decryptPacked(packed, dbKey); }
|
|
832
894
|
var tmpDbPath = nodePath.join(stagingDir, "_blamejs_rotate.tmp.db");
|
|
833
|
-
|
|
895
|
+
_writeStagedFileExclusive(tmpDbPath, plainBytes);
|
|
834
896
|
|
|
835
897
|
var db = new DatabaseSync(tmpDbPath);
|
|
836
898
|
try {
|
|
837
|
-
|
|
838
|
-
|
|
899
|
+
db.prepare(sql.pragma("journal_mode", "WAL").sql).run();
|
|
900
|
+
db.prepare(sql.pragma("synchronous", "NORMAL").sql).run();
|
|
839
901
|
|
|
840
902
|
// Walk tables. For each, re-seal every column declared sealed
|
|
841
903
|
// by the field-crypto registry, plus the overflow `data` JSON
|
|
842
904
|
// column if present.
|
|
843
905
|
var tablesToRotate = Array.isArray(opts.tables) && opts.tables.length > 0
|
|
844
906
|
? opts.tables.slice()
|
|
845
|
-
: db
|
|
846
|
-
"SELECT name FROM sqlite_master " +
|
|
847
|
-
"WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
|
848
|
-
).all().map(function (r) { return r.name; });
|
|
907
|
+
: _listLiveTables(db);
|
|
849
908
|
|
|
850
909
|
// Serialized roots threaded to the AAD reseal path; oldRootJson /
|
|
851
910
|
// newRootJson match b.vault.getKeysJson() so rotated AAD cells unseal
|
|
@@ -854,15 +913,11 @@ async function rotate(opts) {
|
|
|
854
913
|
|
|
855
914
|
for (var ti = 0; ti < tablesToRotate.length; ti++) {
|
|
856
915
|
var table = tablesToRotate[ti];
|
|
857
|
-
var tableExists = db.
|
|
858
|
-
"SELECT name FROM sqlite_master WHERE type='table' AND name = ?"
|
|
859
|
-
).get(table);
|
|
916
|
+
var tableExists = _get(db, sql.catalog.tableExists(table));
|
|
860
917
|
if (!tableExists) continue;
|
|
861
918
|
|
|
862
919
|
var schema = cryptoField.getSchema(table);
|
|
863
|
-
var liveCols = db
|
|
864
|
-
'PRAGMA table_info("' + table.replace(/"/g, '""') + '")'
|
|
865
|
-
).all().map(function (c) { return c.name; });
|
|
920
|
+
var liveCols = _listLiveColumns(db, table);
|
|
866
921
|
var liveColSet = Object.create(null);
|
|
867
922
|
for (var lc = 0; lc < liveCols.length; lc++) liveColSet[liveCols[lc]] = true;
|
|
868
923
|
|
|
@@ -879,7 +934,7 @@ async function rotate(opts) {
|
|
|
879
934
|
if (tableRows > 0) { tablesProcessed++; totalRowsProcessed += tableRows; }
|
|
880
935
|
}
|
|
881
936
|
|
|
882
|
-
|
|
937
|
+
db.prepare(sql.pragma("wal_checkpoint", "TRUNCATE").sql).run();
|
|
883
938
|
} finally {
|
|
884
939
|
db.close();
|
|
885
940
|
}
|
|
@@ -893,25 +948,23 @@ async function rotate(opts) {
|
|
|
893
948
|
try { nodeFs.unlinkSync(tmpDbPath + "-shm"); }
|
|
894
949
|
catch (e) { rotateLog.debug("cleanup-failed", { op: "fs.unlinkSync", path: tmpDbPath + "-shm", error: e.message }); }
|
|
895
950
|
|
|
896
|
-
//
|
|
897
|
-
//
|
|
898
|
-
//
|
|
899
|
-
//
|
|
900
|
-
//
|
|
901
|
-
//
|
|
902
|
-
// inside it. Files are written 0o600 implicitly via the dir's umask
|
|
903
|
-
// and removed before the rotation completes.
|
|
951
|
+
// Every staged path lives inside opts.stagingDir (operator-supplied,
|
|
952
|
+
// ensureDir'd 0o700 owner-only, never under os.tmpdir()) and carries a
|
|
953
|
+
// framework-internal marker name. The staged writes go through
|
|
954
|
+
// _writeStagedFileExclusive — exclusive + no-follow create, owner-only
|
|
955
|
+
// 0o600 — so a same-user pre-plant or symlink swap is a hard failure
|
|
956
|
+
// rather than a followed write, and the bytes never inherit a wider mode.
|
|
904
957
|
var rotatedBytes = nodeFs.readFileSync(tmpDbPath);
|
|
905
958
|
// Re-encrypt under the SAME dataDir AAD so db.init's AAD-first open
|
|
906
959
|
// succeeds after the staged dir is swapped over dataDir in place.
|
|
907
|
-
|
|
960
|
+
_writeStagedFileExclusive(nodePath.join(stagingDir, paths.encryptedDb),
|
|
908
961
|
bCrypto.encryptPacked(rotatedBytes, dbKey, dbEncAad));
|
|
909
962
|
nodeFs.unlinkSync(tmpDbPath);
|
|
910
963
|
|
|
911
964
|
// Round-trip verify on the staged DB
|
|
912
965
|
_emit(progress, { phase: "verify" });
|
|
913
966
|
var verifyTmp = nodePath.join(stagingDir, "_blamejs_verify.tmp.db");
|
|
914
|
-
|
|
967
|
+
_writeStagedFileExclusive(verifyTmp,
|
|
915
968
|
bCrypto.decryptPacked(nodeFs.readFileSync(nodePath.join(stagingDir, paths.encryptedDb)), dbKey, dbEncAad));
|
|
916
969
|
var vdb = new DatabaseSync(verifyTmp);
|
|
917
970
|
try {
|
|
@@ -934,19 +987,26 @@ async function rotate(opts) {
|
|
|
934
987
|
}
|
|
935
988
|
}
|
|
936
989
|
|
|
937
|
-
// 5. fsync staging for durability before caller
|
|
990
|
+
// 5. fsync staging directory entries for durability before the caller swaps.
|
|
991
|
+
// Every staged FILE is already fsync'd at write time by
|
|
992
|
+
// _writeStagedFileExclusive (the re-encrypted db, the resealed vault/db keys,
|
|
993
|
+
// sealed files, the derived-hash salt, and verbatim files), so re-opening
|
|
994
|
+
// each by path here is redundant — and opening a staged file by path is the
|
|
995
|
+
// os-temp-dir open the static analyzer refuses (CWE-377 heuristic). Only the
|
|
996
|
+
// optional verbatimDirs are copied with copyFileSync (no per-file fsync);
|
|
997
|
+
// their directory entries + the rename are made durable by fsyncDir and their
|
|
998
|
+
// source files in dataDir remain intact, so a crash in that narrow window is
|
|
999
|
+
// recoverable.
|
|
938
1000
|
_emit(progress, { phase: "fsync" });
|
|
939
|
-
function
|
|
1001
|
+
function fsyncDirTree(dir) {
|
|
940
1002
|
var entries = nodeFs.readdirSync(dir);
|
|
941
1003
|
for (var i = 0; i < entries.length; i++) {
|
|
942
1004
|
var p = nodePath.join(dir, entries[i]);
|
|
943
|
-
|
|
944
|
-
if (st.isFile()) _fsyncFileByPath(p);
|
|
945
|
-
else if (st.isDirectory()) fsyncTree(p);
|
|
1005
|
+
if (nodeFs.statSync(p).isDirectory()) fsyncDirTree(p);
|
|
946
1006
|
}
|
|
947
1007
|
atomicFile.fsyncDir(dir);
|
|
948
1008
|
}
|
|
949
|
-
|
|
1009
|
+
fsyncDirTree(stagingDir);
|
|
950
1010
|
|
|
951
1011
|
var durationMs = Date.now() - startedAt;
|
|
952
1012
|
_emit(progress, {
|
|
@@ -304,6 +304,12 @@ module.exports = {
|
|
|
304
304
|
isAadSealed: isAadSealed,
|
|
305
305
|
buildColumnAad: buildColumnAad,
|
|
306
306
|
buildContextAad: buildContextAad,
|
|
307
|
+
// canonicalizeAad — the length-prefixed, sorted-keys AAD-bytes
|
|
308
|
+
// encoder. Exported (internal) so a sibling primitive that runs its
|
|
309
|
+
// own AEAD (crypto-field's per-row K_row cells) threads byte-identical
|
|
310
|
+
// AAD into encryptPacked/decryptPacked as this module does for its
|
|
311
|
+
// own seal/unseal — one canonical encoder, no drift.
|
|
312
|
+
canonicalizeAad: _canonicalize,
|
|
307
313
|
AAD_PREFIX: AAD_PREFIX,
|
|
308
314
|
AAD_VERSION: AAD_VERSION,
|
|
309
315
|
VaultAadError: VaultAadError,
|
|
@@ -130,6 +130,7 @@ function _timingSafeHexEqual(a, b) {
|
|
|
130
130
|
var KNOWN_VENDOR_DATA = Object.freeze({
|
|
131
131
|
"public-suffix-list": {
|
|
132
132
|
module: "./vendor/public-suffix-list.data",
|
|
133
|
+
// allow:hand-rolled-sql — `_blamejs_canary_*` is an in-payload tamper-canary token, not a SQL table name (no DB sink in this file)
|
|
133
134
|
canary: "_blamejs_canary_v0_9_8_.local",
|
|
134
135
|
// Canary parse check — operator-side `b.publicSuffix.isPublicSuffix(canary)`
|
|
135
136
|
// MUST return true after the PSL parser ingests the data. The check
|
|
@@ -138,6 +139,7 @@ var KNOWN_VENDOR_DATA = Object.freeze({
|
|
|
138
139
|
},
|
|
139
140
|
"common-passwords-top-10000": {
|
|
140
141
|
module: "./vendor/common-passwords-top-10000.data",
|
|
142
|
+
// allow:hand-rolled-sql — `_blamejs_canary_*` is an in-payload tamper-canary token, not a SQL table name (no DB sink in this file)
|
|
141
143
|
canary: "_blamejs_canary_password_2026_05_13_blamejs_internal_",
|
|
142
144
|
description: "Top-10000 most common passwords (SecLists). Used by b.auth.password to refuse known-breached credentials.",
|
|
143
145
|
},
|
|
@@ -530,22 +530,32 @@ function _parseExtensionHeader(header) {
|
|
|
530
530
|
for (var i = 0; i < entries.length; i++) {
|
|
531
531
|
var parts = structuredFields.splitTopLevel(entries[i], ";").map(function (s) { return s.trim(); });
|
|
532
532
|
if (!parts[0]) continue;
|
|
533
|
-
//
|
|
534
|
-
//
|
|
535
|
-
//
|
|
536
|
-
|
|
533
|
+
// Collect [name, value] pairs, then materialize the params map via
|
|
534
|
+
// Object.fromEntries onto a null-prototype object. The extension-
|
|
535
|
+
// parameter name is taken from the client-supplied Sec-WebSocket-
|
|
536
|
+
// Extensions header, so it is never used as a computed-write key
|
|
537
|
+
// (`params[name] = value`) — that is the CWE-915 unsafe-reflection /
|
|
538
|
+
// CWE-1321 prototype-pollution sink. POISONED params (`__proto__` /
|
|
539
|
+
// `constructor` / `prototype`) are dropped, and the null-prototype
|
|
540
|
+
// accumulator means even a slipped name cannot reach Object.prototype.
|
|
541
|
+
var paramPairs = [];
|
|
537
542
|
for (var j = 1; j < parts.length; j++) {
|
|
538
543
|
var kv = parts[j].split("=");
|
|
539
544
|
var k = kv[0].trim().toLowerCase();
|
|
540
545
|
if (!k) continue;
|
|
546
|
+
if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
|
|
541
547
|
var v = kv.length > 1 ? kv.slice(1).join("=").trim() : true;
|
|
542
548
|
// Strip surrounding quotes per the token-or-quoted-string grammar.
|
|
543
549
|
if (typeof v === "string") {
|
|
544
550
|
var _unq = structuredFields.unquoteSfString(v);
|
|
545
551
|
if (_unq !== null) v = _unq;
|
|
546
552
|
}
|
|
547
|
-
|
|
553
|
+
paramPairs.push([k, v]);
|
|
548
554
|
}
|
|
555
|
+
var ext = {
|
|
556
|
+
name: parts[0].toLowerCase(),
|
|
557
|
+
params: Object.assign(Object.create(null), Object.fromEntries(paramPairs)),
|
|
558
|
+
};
|
|
549
559
|
out.push(ext);
|
|
550
560
|
}
|
|
551
561
|
return out;
|
|
@@ -961,6 +971,22 @@ class WebSocketConnection extends EventEmitter {
|
|
|
961
971
|
self._transitionToClosed(1006, "abnormal closure", false, null);
|
|
962
972
|
}
|
|
963
973
|
});
|
|
974
|
+
socket.on("end", function () {
|
|
975
|
+
// Peer half-closed (TCP FIN) without sending a Close frame. HTTP
|
|
976
|
+
// 'upgrade' sockets default to allowHalfOpen=true, so this arrives
|
|
977
|
+
// as 'end' (readable side ended) while the writable side stays
|
|
978
|
+
// open — the 'close' handler above never fires and the connection
|
|
979
|
+
// would otherwise wedge open (ping timer running, no 'close' event,
|
|
980
|
+
// peer's socket never destroyed). RFC 6455 §7.1.1 treats a TCP
|
|
981
|
+
// close without a prior Close frame as abnormal closure: surface
|
|
982
|
+
// the lifecycle event and end our writable side so the socket
|
|
983
|
+
// actually tears down. _transitionToClosed is idempotent, so the
|
|
984
|
+
// native 'close' that follows is a no-op.
|
|
985
|
+
if (self._state !== STATE_CLOSED) {
|
|
986
|
+
self._transitionToClosed(1006, "abnormal closure", false, null);
|
|
987
|
+
}
|
|
988
|
+
try { socket.end(); } catch (_e) { /* socket already closing */ }
|
|
989
|
+
});
|
|
964
990
|
}
|
|
965
991
|
|
|
966
992
|
// Single state-transition method. Idempotent — repeat calls after
|
|
@@ -1541,6 +1567,10 @@ module.exports = {
|
|
|
1541
1567
|
// Server-side entrypoints
|
|
1542
1568
|
handleUpgrade: handleUpgrade, // h1 — RFC 6455 HTTP upgrade
|
|
1543
1569
|
handleExtendedConnect: handleExtendedConnect, // h2 — RFC 8441 Extended CONNECT
|
|
1570
|
+
// Internal helper exposed for tests — the Sec-WebSocket-Extensions
|
|
1571
|
+
// parser (RFC 7692 negotiation feeds off this). Underscore-prefixed so
|
|
1572
|
+
// it is not part of the public primitive surface.
|
|
1573
|
+
_parseExtensionHeader: _parseExtensionHeader,
|
|
1544
1574
|
// Constants
|
|
1545
1575
|
GUID: GUID,
|
|
1546
1576
|
REFUSED_AUTH_QUERY_PARAMS: REFUSED_AUTH_QUERY_PARAMS,
|
|
@@ -287,6 +287,11 @@ function create(scriptPath, opts) {
|
|
|
287
287
|
reason: "workerpool/worker-error",
|
|
288
288
|
message: (err && err.message) || String(err),
|
|
289
289
|
});
|
|
290
|
+
// Mark the slot dying BEFORE _finishTask. _finishTask sets slot.busy =
|
|
291
|
+
// false and drains the queue, so an unmarked slot would be handed a
|
|
292
|
+
// freshly-queued task that then dies with this same worker. _recycleWorker
|
|
293
|
+
// re-asserts the flag idempotently.
|
|
294
|
+
slot.recycling = true;
|
|
290
295
|
if (failingTask) {
|
|
291
296
|
_finishTask(slot, true,
|
|
292
297
|
new WorkerPoolError("workerpool/worker-error",
|
|
@@ -331,6 +336,12 @@ function create(scriptPath, opts) {
|
|
|
331
336
|
workerId: slot.id, taskId: taskId, taskTimeoutMs: taskTimeoutMs,
|
|
332
337
|
});
|
|
333
338
|
var failingTask = slot.currentTask;
|
|
339
|
+
// Mark the slot dying BEFORE _finishTask drains the queue, so the drain
|
|
340
|
+
// skips this about-to-be-terminated slot instead of dispatching a queued
|
|
341
|
+
// task onto it (which would die with the worker on terminate, surfacing as
|
|
342
|
+
// workerpool/worker-exit on a task that never ran). _recycleWorker
|
|
343
|
+
// re-asserts the flag idempotently.
|
|
344
|
+
slot.recycling = true;
|
|
334
345
|
if (failingTask) {
|
|
335
346
|
_finishTask(slot, true,
|
|
336
347
|
new WorkerPoolError("workerpool/timeout",
|