@blamejs/blamejs-shop 0.4.31 → 0.4.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +392 -278
- package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
- package/lib/vendor/blamejs/.gitignore +6 -0
- package/lib/vendor/blamejs/CHANGELOG.md +26 -0
- package/lib/vendor/blamejs/MIGRATING.md +43 -0
- package/lib/vendor/blamejs/README.md +8 -6
- package/lib/vendor/blamejs/SECURITY.md +19 -3
- package/lib/vendor/blamejs/api-snapshot.json +2190 -664
- package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
- package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
- package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
- package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
- package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
- package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
- package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
- package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
- package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
- package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
- package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
- package/lib/vendor/blamejs/index.js +4 -0
- package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
- package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
- package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
- package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
- package/lib/vendor/blamejs/lib/api-key.js +158 -77
- package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
- package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
- package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
- package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
- package/lib/vendor/blamejs/lib/audit.js +259 -123
- package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
- package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
- package/lib/vendor/blamejs/lib/backup/index.js +45 -10
- package/lib/vendor/blamejs/lib/break-glass.js +355 -147
- package/lib/vendor/blamejs/lib/cache.js +174 -105
- package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
- package/lib/vendor/blamejs/lib/cli.js +19 -14
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
- package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
- package/lib/vendor/blamejs/lib/cluster.js +119 -71
- package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
- package/lib/vendor/blamejs/lib/compliance.js +206 -4
- package/lib/vendor/blamejs/lib/consent.js +82 -29
- package/lib/vendor/blamejs/lib/constants.js +27 -11
- package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
- package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
- package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
- package/lib/vendor/blamejs/lib/db-query.js +882 -260
- package/lib/vendor/blamejs/lib/db-schema.js +228 -44
- package/lib/vendor/blamejs/lib/db.js +249 -99
- package/lib/vendor/blamejs/lib/dsr.js +385 -55
- package/lib/vendor/blamejs/lib/error-page.js +14 -1
- package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
- package/lib/vendor/blamejs/lib/external-db.js +549 -34
- package/lib/vendor/blamejs/lib/file-upload.js +52 -7
- package/lib/vendor/blamejs/lib/framework-error.js +20 -1
- package/lib/vendor/blamejs/lib/framework-files.js +73 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
- package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
- package/lib/vendor/blamejs/lib/guard-all.js +1 -0
- package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
- package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
- package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
- package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
- package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
- package/lib/vendor/blamejs/lib/guard-email.js +47 -69
- package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
- package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
- package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
- package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
- package/lib/vendor/blamejs/lib/guard-html.js +53 -108
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
- package/lib/vendor/blamejs/lib/guard-image.js +46 -103
- package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
- package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
- package/lib/vendor/blamejs/lib/guard-json.js +38 -108
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
- package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
- package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
- package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
- package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
- package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
- package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
- package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
- package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
- package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
- package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
- package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
- package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
- package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
- package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
- package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
- package/lib/vendor/blamejs/lib/guard-template.js +35 -172
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-time.js +32 -154
- package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
- package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
- package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
- package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
- package/lib/vendor/blamejs/lib/http-client.js +37 -9
- package/lib/vendor/blamejs/lib/inbox.js +120 -107
- package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
- package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
- package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
- package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
- package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
- package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
- package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
- package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
- package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
- package/lib/vendor/blamejs/lib/mail-store.js +293 -154
- package/lib/vendor/blamejs/lib/mail.js +8 -4
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
- package/lib/vendor/blamejs/lib/migrations.js +108 -66
- package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
- package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
- package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
- package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
- package/lib/vendor/blamejs/lib/observability.js +124 -0
- package/lib/vendor/blamejs/lib/otel-export.js +12 -3
- package/lib/vendor/blamejs/lib/outbox.js +184 -83
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
- package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
- package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
- package/lib/vendor/blamejs/lib/queue-local.js +225 -140
- package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
- package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
- package/lib/vendor/blamejs/lib/queue.js +7 -0
- package/lib/vendor/blamejs/lib/redact.js +68 -11
- package/lib/vendor/blamejs/lib/redis-client.js +160 -31
- package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
- package/lib/vendor/blamejs/lib/retention.js +101 -40
- package/lib/vendor/blamejs/lib/router.js +212 -5
- package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
- package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
- package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
- package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
- package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
- package/lib/vendor/blamejs/lib/safe-url.js +170 -3
- package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
- package/lib/vendor/blamejs/lib/scheduler.js +35 -12
- package/lib/vendor/blamejs/lib/seeders.js +122 -74
- package/lib/vendor/blamejs/lib/session-stores.js +42 -14
- package/lib/vendor/blamejs/lib/session.js +175 -77
- package/lib/vendor/blamejs/lib/sql.js +3842 -0
- package/lib/vendor/blamejs/lib/sse.js +26 -0
- package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
- package/lib/vendor/blamejs/lib/static.js +177 -34
- package/lib/vendor/blamejs/lib/subject.js +96 -49
- package/lib/vendor/blamejs/lib/vault/index.js +3 -2
- package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
- package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
- package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
- package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
- package/lib/vendor/blamejs/lib/websocket.js +35 -5
- package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
- package/lib/vendor/blamejs/package.json +2 -2
- package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
- package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
- package/lib/vendor/blamejs/scripts/check-services.js +21 -0
- package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
- package/lib/vendor/blamejs/scripts/release.js +398 -38
- package/lib/vendor/blamejs/test/00-primitives.js +117 -0
- package/lib/vendor/blamejs/test/10-state.js +140 -14
- package/lib/vendor/blamejs/test/20-db.js +65 -2
- package/lib/vendor/blamejs/test/helpers/db.js +9 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
- package/lib/vendor/blamejs/test/helpers/services.js +21 -0
- package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
- package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
- package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
- package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
- package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
- package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
- package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
- package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
- package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
- package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
- package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
- package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
- package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
- package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
- package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
- package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
- package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
- package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
- package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
- package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
- package/lib/vendor/blamejs/test/smoke.js +79 -21
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
- package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
- package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
- package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
- package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
- package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
- package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
- package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
- package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
- package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
- package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
- package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
- package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
- package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
|
@@ -57,9 +57,11 @@ var { generateToken, generateBytes, encryptPacked, decryptPacked, sha3Hash } = r
|
|
|
57
57
|
var cryptoField = require("./crypto-field");
|
|
58
58
|
var dbDeclareRowPolicy = require("./db-declare-row-policy");
|
|
59
59
|
var dbDeclareView = require("./db-declare-view");
|
|
60
|
-
var { Query } = require("./db-query");
|
|
60
|
+
var { Query, _isRawWriteToResidencyTable, _assertRawWriteResidency } = require("./db-query");
|
|
61
61
|
var dbSchema = require("./db-schema");
|
|
62
62
|
var { defineClass } = require("./framework-error");
|
|
63
|
+
var frameworkFiles = require("./framework-files");
|
|
64
|
+
var frameworkSchema = require("./framework-schema");
|
|
63
65
|
var { boot } = require("./log");
|
|
64
66
|
var lazyRequire = require("./lazy-require");
|
|
65
67
|
var observability = require("./observability");
|
|
@@ -68,10 +70,18 @@ var safeAsync = require("./safe-async");
|
|
|
68
70
|
var safeEnv = require("./parsers/safe-env");
|
|
69
71
|
var safeJson = require("./safe-json");
|
|
70
72
|
var safeSql = require("./safe-sql");
|
|
73
|
+
var sql = require("./sql");
|
|
71
74
|
var validateOpts = require("./validate-opts");
|
|
72
75
|
var vault = require("./vault");
|
|
73
76
|
var vaultAad = require("./vault-aad");
|
|
74
77
|
|
|
78
|
+
// b.sql opts for the local single-node sqlite handle (database.prepare,
|
|
79
|
+
// never clusterStorage): "sqlite" dialect + quoteName so the resolved
|
|
80
|
+
// framework table name quotes by construction. The few DML sites here that
|
|
81
|
+
// target a framework state table resolve its name through
|
|
82
|
+
// frameworkSchema.tableName so a configured table prefix flows through.
|
|
83
|
+
var _SQL_OPTS = { dialect: "sqlite", quoteName: true };
|
|
84
|
+
|
|
75
85
|
var DbError = defineClass("DbError", { alwaysPermanent: true });
|
|
76
86
|
var WormViolationError = require("./framework-error").WormViolationError;
|
|
77
87
|
var _wormErr = WormViolationError.factory;
|
|
@@ -173,27 +183,30 @@ var columnGateMode = "reject";
|
|
|
173
183
|
// are provisioned by the framework before app schema reconciles. Apps cannot
|
|
174
184
|
// opt out, override, or rename them. An app schema entry colliding with any of
|
|
175
185
|
// these names is refused at init.
|
|
186
|
+
// These are the canonical LOCAL reserved table-name declarations (the
|
|
187
|
+
// guard set an operator schema may not collide with), NOT query SQL — the
|
|
188
|
+
// literal names ARE the contract. allow:hand-rolled-sql markers below.
|
|
176
189
|
var RESERVED_TABLE_NAMES = new Set([
|
|
177
190
|
"audit_log",
|
|
178
191
|
"audit_checkpoints",
|
|
179
192
|
"consent_log",
|
|
180
|
-
"_blamejs_subject_restrictions",
|
|
181
|
-
"_blamejs_subject_erasures",
|
|
182
|
-
"_blamejs_sessions",
|
|
183
|
-
"_blamejs_jobs",
|
|
184
|
-
"_blamejs_migrations",
|
|
185
|
-
"_blamejs_counters",
|
|
186
|
-
"_blamejs_audit_purge_anchor",
|
|
187
|
-
"_blamejs_scheduler_ticks",
|
|
188
|
-
"_blamejs_rate_limit_counters",
|
|
189
|
-
"_blamejs_pubsub_messages",
|
|
190
|
-
"_blamejs_api_encrypt_nonces",
|
|
191
|
-
"_blamejs_api_keys",
|
|
192
|
-
"_blamejs_cache",
|
|
193
|
-
"_blamejs_seeders",
|
|
194
|
-
"_blamejs_seeders_lock",
|
|
195
|
-
"_blamejs_break_glass_policies",
|
|
196
|
-
"_blamejs_break_glass_grants",
|
|
193
|
+
"_blamejs_subject_restrictions", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
194
|
+
"_blamejs_subject_erasures", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
195
|
+
"_blamejs_sessions", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
196
|
+
"_blamejs_jobs", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
197
|
+
"_blamejs_migrations", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
198
|
+
"_blamejs_counters", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
199
|
+
"_blamejs_audit_purge_anchor", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
200
|
+
"_blamejs_scheduler_ticks", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
201
|
+
"_blamejs_rate_limit_counters", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
202
|
+
"_blamejs_pubsub_messages", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
203
|
+
"_blamejs_api_encrypt_nonces", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
204
|
+
"_blamejs_api_keys", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
205
|
+
"_blamejs_cache", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
206
|
+
"_blamejs_seeders", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
207
|
+
"_blamejs_seeders_lock", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
208
|
+
"_blamejs_break_glass_policies", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
209
|
+
"_blamejs_break_glass_grants", // allow:hand-rolled-sql — canonical reserved local table-name declaration
|
|
197
210
|
]);
|
|
198
211
|
|
|
199
212
|
var FRAMEWORK_SCHEMA = [
|
|
@@ -260,7 +273,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
260
273
|
},
|
|
261
274
|
},
|
|
262
275
|
{
|
|
263
|
-
name: "_blamejs_subject_restrictions",
|
|
276
|
+
name: "_blamejs_subject_restrictions", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
264
277
|
columns: {
|
|
265
278
|
subjectIdHash: "TEXT PRIMARY KEY",
|
|
266
279
|
since: "INTEGER NOT NULL",
|
|
@@ -269,7 +282,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
269
282
|
sealedFields: ["reason"],
|
|
270
283
|
},
|
|
271
284
|
{
|
|
272
|
-
name: "_blamejs_subject_erasures",
|
|
285
|
+
name: "_blamejs_subject_erasures", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
273
286
|
columns: {
|
|
274
287
|
subjectIdHash: "TEXT PRIMARY KEY",
|
|
275
288
|
erasedAt: "INTEGER NOT NULL",
|
|
@@ -281,7 +294,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
281
294
|
// b.retention consult b.legalHold.isHeld(subjectId) before
|
|
282
295
|
// accepting any deletion. Per FRCP Rule 26/37(e), GDPR Art
|
|
283
296
|
// 17(3)(e), SEC Rule 17a-4, HIPAA §164.530(j)(2).
|
|
284
|
-
name: "_blamejs_legal_hold",
|
|
297
|
+
name: "_blamejs_legal_hold", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
285
298
|
columns: {
|
|
286
299
|
subjectIdHash: "TEXT PRIMARY KEY",
|
|
287
300
|
placedAt: "INTEGER NOT NULL",
|
|
@@ -291,15 +304,29 @@ var FRAMEWORK_SCHEMA = [
|
|
|
291
304
|
citation: "TEXT",
|
|
292
305
|
retainUntil: "INTEGER",
|
|
293
306
|
},
|
|
307
|
+
// The legal-basis / custodian / ticket-citation free text links a data
|
|
308
|
+
// subject to a legal matter — PII at rest. Sealed like the DSR ticket store
|
|
309
|
+
// (b.legalHold seals on write + unseals on read through cryptoField).
|
|
310
|
+
sealedFields: ["reason", "placedBy", "custodian", "citation"],
|
|
294
311
|
indexes: ["placedAt"],
|
|
295
312
|
},
|
|
296
313
|
{
|
|
297
314
|
// Per-row crypto-erasure key registry — per-row keys.
|
|
298
|
-
// Each entry holds
|
|
299
|
-
// rowId)
|
|
300
|
-
//
|
|
301
|
-
|
|
315
|
+
// Each entry holds the AAD-sealed random row-secret keyed by
|
|
316
|
+
// (tableName, rowId); the row-scoped K_row is derived from it.
|
|
317
|
+
// b.subject.eraseHard / b.retention destroy the entry, leaving WAL /
|
|
318
|
+
// replica residuals undecryptable. wrappedKey is registered as an
|
|
319
|
+
// AAD-bound sealed field (aad:true, rowIdField:"rowId") so a vault
|
|
320
|
+
// keypair rotation auto-reseals it old-root -> new-root via
|
|
321
|
+
// rotate._rotateColumn — without this a rotation would orphan every
|
|
322
|
+
// wrapped secret and brick every keyed row.
|
|
323
|
+
name: "_blamejs_per_row_keys", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
302
324
|
columns: {
|
|
325
|
+
// _id is the rotation pipeline's keyset-pagination + UPDATE key
|
|
326
|
+
// (rotate._rotateColumn SELECTs _id and orders by it); the natural
|
|
327
|
+
// identity stays the composite (tableName, rowId). materialize
|
|
328
|
+
// populates _id with a fresh token.
|
|
329
|
+
_id: "TEXT",
|
|
303
330
|
tableName: "TEXT NOT NULL",
|
|
304
331
|
rowId: "TEXT NOT NULL",
|
|
305
332
|
wrappedKey: "BLOB NOT NULL",
|
|
@@ -307,6 +334,10 @@ var FRAMEWORK_SCHEMA = [
|
|
|
307
334
|
},
|
|
308
335
|
primaryKey: ["tableName", "rowId"],
|
|
309
336
|
indexes: [],
|
|
337
|
+
sealedFields: ["wrappedKey"],
|
|
338
|
+
aad: true,
|
|
339
|
+
rowIdField: "rowId",
|
|
340
|
+
schemaVersion: "1",
|
|
310
341
|
},
|
|
311
342
|
{
|
|
312
343
|
// Operator-declared WORM (write-once-read-many) registry. Each
|
|
@@ -314,7 +345,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
314
345
|
// demanded the WORM declaration; boot-time assertions iterate
|
|
315
346
|
// this registry to verify triggers are installed under the
|
|
316
347
|
// current b.compliance.current() posture.
|
|
317
|
-
name: "_blamejs_worm_tables",
|
|
348
|
+
name: "_blamejs_worm_tables", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
318
349
|
columns: {
|
|
319
350
|
tableName: "TEXT PRIMARY KEY",
|
|
320
351
|
posture: "TEXT",
|
|
@@ -327,7 +358,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
327
358
|
// destructive ops; under the named posture the framework refuses
|
|
328
359
|
// execution unless the caller passes a consumed dual-control
|
|
329
360
|
// grant.
|
|
330
|
-
name: "_blamejs_dual_control_gates",
|
|
361
|
+
name: "_blamejs_dual_control_gates", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
331
362
|
columns: {
|
|
332
363
|
tableName: "TEXT PRIMARY KEY",
|
|
333
364
|
posture: "TEXT",
|
|
@@ -354,7 +385,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
354
385
|
sealedFields: [],
|
|
355
386
|
},
|
|
356
387
|
{
|
|
357
|
-
name: "_blamejs_audit_purge_anchor",
|
|
388
|
+
name: "_blamejs_audit_purge_anchor", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
358
389
|
columns: {
|
|
359
390
|
// CHECK constraint: scope is one of the framework's audit-
|
|
360
391
|
// chain anchor scopes (`audit` / `consent`). Pre-v0.8.37 a
|
|
@@ -376,7 +407,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
376
407
|
// leader's INSERT loses with a constraint violation, and that node
|
|
377
408
|
// skips the tick. Closes the once-globally gap during cluster
|
|
378
409
|
// leader hand-offs where two leaders briefly coexist.
|
|
379
|
-
name: "_blamejs_scheduler_ticks",
|
|
410
|
+
name: "_blamejs_scheduler_ticks", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
380
411
|
columns: {
|
|
381
412
|
tickKey: "TEXT PRIMARY KEY",
|
|
382
413
|
name: "TEXT NOT NULL",
|
|
@@ -392,7 +423,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
392
423
|
// the cluster-shared rate-limit backend. One row per (key); the
|
|
393
424
|
// count rolls over atomically when the windowStart advances. Used
|
|
394
425
|
// by lib/middleware/rate-limit.js when scope: 'cluster' is set.
|
|
395
|
-
name: "_blamejs_rate_limit_counters",
|
|
426
|
+
name: "_blamejs_rate_limit_counters", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
396
427
|
columns: {
|
|
397
428
|
key: "TEXT PRIMARY KEY",
|
|
398
429
|
windowStart: "INTEGER NOT NULL",
|
|
@@ -408,7 +439,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
408
439
|
// publish; other nodes poll for new ids and dispatch to their
|
|
409
440
|
// local subscribers. Rows older than the configured retention
|
|
410
441
|
// window are pruned by the backend on a rate-limited basis.
|
|
411
|
-
name: "_blamejs_pubsub_messages",
|
|
442
|
+
name: "_blamejs_pubsub_messages", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
412
443
|
columns: {
|
|
413
444
|
id: "INTEGER PRIMARY KEY AUTOINCREMENT",
|
|
414
445
|
topic: "TEXT NOT NULL",
|
|
@@ -426,7 +457,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
426
457
|
// exposes the original 16-byte client nonces. Hashing is
|
|
427
458
|
// deterministic so the PRIMARY KEY conflict is what catches a
|
|
428
459
|
// replay attempt within the replay window.
|
|
429
|
-
name: "_blamejs_api_encrypt_nonces",
|
|
460
|
+
name: "_blamejs_api_encrypt_nonces", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
430
461
|
columns: {
|
|
431
462
|
nonceHash: "TEXT PRIMARY KEY",
|
|
432
463
|
expireAt: "INTEGER NOT NULL",
|
|
@@ -435,7 +466,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
435
466
|
sealedFields: [],
|
|
436
467
|
},
|
|
437
468
|
{
|
|
438
|
-
name: "_blamejs_sessions",
|
|
469
|
+
name: "_blamejs_sessions", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
439
470
|
columns: {
|
|
440
471
|
sidHash: "TEXT PRIMARY KEY",
|
|
441
472
|
userId: "TEXT NOT NULL",
|
|
@@ -456,7 +487,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
456
487
|
// verify. Same dual-storage pattern as sessions: this row mirrors
|
|
457
488
|
// the cluster-mode DDL in framework-schema.js so cluster-storage
|
|
458
489
|
// can route to either backend transparently.
|
|
459
|
-
name: "_blamejs_api_keys",
|
|
490
|
+
name: "_blamejs_api_keys", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
460
491
|
columns: {
|
|
461
492
|
id: "TEXT PRIMARY KEY",
|
|
462
493
|
namespace: "TEXT NOT NULL",
|
|
@@ -487,7 +518,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
487
518
|
derivedHashes: { ownerIdHash: { from: "ownerId" } },
|
|
488
519
|
},
|
|
489
520
|
{
|
|
490
|
-
name: "_blamejs_jobs",
|
|
521
|
+
name: "_blamejs_jobs", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
491
522
|
columns: {
|
|
492
523
|
_id: "TEXT PRIMARY KEY",
|
|
493
524
|
queueName: "TEXT NOT NULL",
|
|
@@ -532,7 +563,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
532
563
|
// JSON-serialized; expiresAt is unix-ms (Number.MAX_SAFE_INTEGER for
|
|
533
564
|
// never-expiring entries). Not sealed: cache values are operator-
|
|
534
565
|
// chosen application data, the operator decides what's worth storing.
|
|
535
|
-
name: "_blamejs_cache",
|
|
566
|
+
name: "_blamejs_cache", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
536
567
|
columns: {
|
|
537
568
|
cacheKey: "TEXT PRIMARY KEY",
|
|
538
569
|
valueJson: "TEXT NOT NULL",
|
|
@@ -548,7 +579,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
548
579
|
// PK (cacheKey, tag) lets one cacheKey carry many tags; index on
|
|
549
580
|
// tag makes invalidation a single indexed scan. Cleared together
|
|
550
581
|
// with the matching _blamejs_cache rows on del / clear / sweep.
|
|
551
|
-
name: "_blamejs_cache_tags",
|
|
582
|
+
name: "_blamejs_cache_tags", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
552
583
|
columns: {
|
|
553
584
|
cacheKey: "TEXT NOT NULL",
|
|
554
585
|
tag: "TEXT NOT NULL",
|
|
@@ -564,7 +595,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
564
595
|
// collide with prod fixtures by name). rerunnable=1 entries get
|
|
565
596
|
// their appliedAt updated in place on every run; non-rerunnable
|
|
566
597
|
// entries are insert-once.
|
|
567
|
-
name: "_blamejs_seeders",
|
|
598
|
+
name: "_blamejs_seeders", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
568
599
|
columns: {
|
|
569
600
|
env: "TEXT NOT NULL",
|
|
570
601
|
name: "TEXT NOT NULL",
|
|
@@ -582,7 +613,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
582
613
|
// on scope='lock' enforces single row). Two processes calling
|
|
583
614
|
// `seed run` against the same DB race on this PK; loser sees a
|
|
584
615
|
// clear "lock held" error.
|
|
585
|
-
name: "_blamejs_seeders_lock",
|
|
616
|
+
name: "_blamejs_seeders_lock", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
586
617
|
columns: {
|
|
587
618
|
scope: "TEXT PRIMARY KEY CHECK (scope = 'lock')",
|
|
588
619
|
lockedAt: "INTEGER NOT NULL",
|
|
@@ -596,7 +627,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
596
627
|
// glass-locked and what the operator's grant rules are. Sealed
|
|
597
628
|
// columns hold the column-list, factor-list, and bypass config so
|
|
598
629
|
// policy contents aren't browsable in cleartext.
|
|
599
|
-
name: "_blamejs_break_glass_policies",
|
|
630
|
+
name: "_blamejs_break_glass_policies", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
600
631
|
columns: {
|
|
601
632
|
tableName: "TEXT PRIMARY KEY",
|
|
602
633
|
columnsJson: "TEXT NOT NULL",
|
|
@@ -625,7 +656,7 @@ var FRAMEWORK_SCHEMA = [
|
|
|
625
656
|
// (each row access = its own grant).
|
|
626
657
|
// Sealed columns hold reason + scopeColumns so audit-readable
|
|
627
658
|
// metadata doesn't leak in cleartext.
|
|
628
|
-
name: "_blamejs_break_glass_grants",
|
|
659
|
+
name: "_blamejs_break_glass_grants", // allow:hand-rolled-sql — canonical local-schema table-name declaration
|
|
629
660
|
columns: {
|
|
630
661
|
_id: "TEXT PRIMARY KEY",
|
|
631
662
|
issuedToActorId: "TEXT NOT NULL",
|
|
@@ -688,7 +719,7 @@ function loadOrCreateDbKey(dataDirPath, keyPathOverride) {
|
|
|
688
719
|
// needs to live outside `dataDir` (e.g. a separate volume mounted
|
|
689
720
|
// from a KMS-fronted secret store). Default places it next to the
|
|
690
721
|
// encrypted DB so backup capture is one-tarball.
|
|
691
|
-
var keyPath = keyPathOverride || nodePath.join(dataDirPath,
|
|
722
|
+
var keyPath = keyPathOverride || nodePath.join(dataDirPath, frameworkFiles.fileName("dbKeyEnc"));
|
|
692
723
|
var aad = _dbKeyAad(dataDirPath, keyPath);
|
|
693
724
|
if (nodeFs.existsSync(keyPath)) {
|
|
694
725
|
var sealed = atomicFile.readSync(keyPath, { encoding: "utf8" }).trim();
|
|
@@ -956,6 +987,7 @@ function cleanStaleTmpDbs(tmpDir) {
|
|
|
956
987
|
* schema: Array, // required — [{ name, columns, indexes, sealedFields, derivedHashes, foreignKeys, primaryKey, subjectField, personalDataCategories }, ...]
|
|
957
988
|
* atRest: "encrypted"|"plain", // default "encrypted"
|
|
958
989
|
* tmpDir: string, // override the encrypted-mode tmpfs path (default /dev/shm or BLAMEJS_TMPDIR)
|
|
990
|
+
* allowNonTmpfsTmpDir: boolean, // default false — encrypted mode THROWS when tmpDir is not a recognized tmpfs mount (plaintext-on-disk leak); pass true to downgrade to a warning when the mount is verified in-memory out-of-band
|
|
959
991
|
* migrationDir: string, // optional — path to ./migrations/ (run-once each)
|
|
960
992
|
* streamLimit: number, // default 1_000_000 — db.stream row ceiling
|
|
961
993
|
* columnGate: "reject"|"warn"|"off", // default "reject" — refuse queries on columns not declared in the table schema
|
|
@@ -1028,6 +1060,15 @@ async function init(opts) {
|
|
|
1028
1060
|
JSON.stringify(opts.columnGate));
|
|
1029
1061
|
}
|
|
1030
1062
|
columnGateMode = opts.columnGate || "reject";
|
|
1063
|
+
// Configurable framework-table prefix. Config-time: setTablePrefix
|
|
1064
|
+
// throws a FrameworkSchemaError on a non-identifier prefix so a typo
|
|
1065
|
+
// surfaces at boot rather than as a silently-misnamed table. Applied
|
|
1066
|
+
// BEFORE any schema creation below so framework DDL + the cluster
|
|
1067
|
+
// tableName resolver honor it. Default ("_blamejs_") is byte-identical
|
|
1068
|
+
// to the historical names, so omitting the option is a no-op.
|
|
1069
|
+
if (opts.tablePrefix !== undefined) {
|
|
1070
|
+
frameworkSchema.setTablePrefix(opts.tablePrefix);
|
|
1071
|
+
}
|
|
1031
1072
|
dataDir = opts.dataDir;
|
|
1032
1073
|
if (!nodeFs.existsSync(dataDir)) nodeFs.mkdirSync(dataDir, { recursive: true });
|
|
1033
1074
|
|
|
@@ -1040,21 +1081,42 @@ async function init(opts) {
|
|
|
1040
1081
|
}
|
|
1041
1082
|
if (!nodeFs.existsSync(tmpDir)) nodeFs.mkdirSync(tmpDir, { recursive: true });
|
|
1042
1083
|
|
|
1043
|
-
// If the resolved tmpDir is NOT actually tmpfs, the
|
|
1044
|
-
//
|
|
1045
|
-
//
|
|
1046
|
-
//
|
|
1047
|
-
//
|
|
1048
|
-
//
|
|
1084
|
+
// If the resolved tmpDir is NOT actually tmpfs, the plaintext working
|
|
1085
|
+
// copy lives on persistent storage and leaks into backup snapshots,
|
|
1086
|
+
// replication, and forensic disk images — defeating the whole point of
|
|
1087
|
+
// encrypted-at-rest mode. On Linux we verify the resolved path lands
|
|
1088
|
+
// under a known in-memory mount (/dev/shm /run/shm /run/user, plus
|
|
1089
|
+
// /tmp which is tmpfs on systemd-default + most container images).
|
|
1090
|
+
//
|
|
1091
|
+
// Fail-closed default (v0.15.0): a tmpDir that resolves OUTSIDE those
|
|
1092
|
+
// mounts THROWS db/tmpdir-not-tmpfs at boot rather than logging a
|
|
1093
|
+
// warning the operator never reads — the prior warn-only path silently
|
|
1094
|
+
// shipped plaintext to disk under the encrypted-mode default. The
|
|
1095
|
+
// documented opt-out is opts.allowNonTmpfsTmpDir: true, for the operator
|
|
1096
|
+
// who has verified the mount is in-memory out-of-band (e.g. a ramfs /
|
|
1097
|
+
// a tmpfs bind-mounted at a non-standard path the heuristic can't see)
|
|
1098
|
+
// or who has accepted the disk-residency tradeoff. The opt-out downgrades
|
|
1099
|
+
// to the prior warning. (Free-space headroom is enforced separately via
|
|
1100
|
+
// fs.statfsSync in the storage guard below.) The heuristic is Linux-only;
|
|
1101
|
+
// other platforms can't be probed by path and emit the warning unchanged.
|
|
1049
1102
|
if (process.platform === "linux") {
|
|
1050
1103
|
var realTmp = "";
|
|
1051
1104
|
try { realTmp = nodeFs.realpathSync(tmpDir); } catch (_e) { /* stat best-effort */ }
|
|
1052
1105
|
if (realTmp.indexOf("/dev/shm") !== 0 && realTmp.indexOf("/run/shm") !== 0 &&
|
|
1053
1106
|
realTmp.indexOf("/run/user/") !== 0 && realTmp.indexOf("/tmp") !== 0) {
|
|
1054
|
-
|
|
1055
|
-
"') does not resolve under /dev/shm /run/shm /run/user /tmp —
|
|
1056
|
-
"
|
|
1057
|
-
"snapshots, replication, and forensic disk images."
|
|
1107
|
+
var tmpfsMsg = "db.init: tmpDir '" + tmpDir + "' (real: '" + realTmp +
|
|
1108
|
+
"') does not resolve under /dev/shm /run/shm /run/user /tmp — it is not a " +
|
|
1109
|
+
"recognized tmpfs mount. A persistent-disk tmpDir leaks the decrypted " +
|
|
1110
|
+
"working copy into backup snapshots, replication, and forensic disk images.";
|
|
1111
|
+
if (opts.allowNonTmpfsTmpDir === true) {
|
|
1112
|
+
log.warn("WARNING: " + tmpfsMsg + " (allowNonTmpfsTmpDir:true — verify the " +
|
|
1113
|
+
"mount is in-memory out-of-band.)");
|
|
1114
|
+
} else {
|
|
1115
|
+
throw _dbErr("db/tmpdir-not-tmpfs", "FATAL: " + tmpfsMsg +
|
|
1116
|
+
" Mount a tmpfs at the path (or set BLAMEJS_TMPDIR / opts.tmpDir to one), " +
|
|
1117
|
+
"or pass opts.allowNonTmpfsTmpDir: true to accept the disk-residency tradeoff, " +
|
|
1118
|
+
"or pass atRest: 'plain' if encryption-at-rest is not required.");
|
|
1119
|
+
}
|
|
1058
1120
|
}
|
|
1059
1121
|
}
|
|
1060
1122
|
|
|
@@ -1063,7 +1125,7 @@ async function init(opts) {
|
|
|
1063
1125
|
// just the basename under `dataDir` (default "db.enc"). Helps when
|
|
1064
1126
|
// multiple framework-shaped instances share a dataDir.
|
|
1065
1127
|
encPath = opts.encryptedDbPath ||
|
|
1066
|
-
nodePath.join(dataDir, opts.encryptedDbName ||
|
|
1128
|
+
nodePath.join(dataDir, opts.encryptedDbName || frameworkFiles.fileName("dbEnc"));
|
|
1067
1129
|
dbPath = nodePath.join(tmpDir, "blamejs-" + generateToken(C.BYTES.bytes(16)) + ".db");
|
|
1068
1130
|
encKey = loadOrCreateDbKey(dataDir, opts.dbKeyPath);
|
|
1069
1131
|
|
|
@@ -1624,6 +1686,27 @@ var _prepareCache = new Map();
|
|
|
1624
1686
|
* typeof row.total;
|
|
1625
1687
|
* // → "object"
|
|
1626
1688
|
*/
|
|
1689
|
+
// Wrap a prepared statement that writes to a per-row-residency table so its
|
|
1690
|
+
// execution (run / get / all / iterate) validates the residency tag of the
|
|
1691
|
+
// bound row through the same gate the structured builder uses. Only residency
|
|
1692
|
+
// writes are wrapped (cheap prepare-time pre-check) so the common path is
|
|
1693
|
+
// untouched.
|
|
1694
|
+
function _gatedResidencyStmt(stmt, sql) {
|
|
1695
|
+
var EXEC = { run: true, get: true, all: true, iterate: true };
|
|
1696
|
+
return new Proxy(stmt, {
|
|
1697
|
+
get: function (target, prop) {
|
|
1698
|
+
var v = target[prop];
|
|
1699
|
+
if (typeof prop === "string" && EXEC[prop] && typeof v === "function") {
|
|
1700
|
+
return function () {
|
|
1701
|
+
_assertRawWriteResidency(sql, Array.prototype.slice.call(arguments));
|
|
1702
|
+
return v.apply(target, arguments);
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
return typeof v === "function" ? v.bind(target) : v;
|
|
1706
|
+
},
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1627
1710
|
function prepare(sql) {
|
|
1628
1711
|
_requireInit();
|
|
1629
1712
|
if (_prepareCache.has(sql)) {
|
|
@@ -1634,6 +1717,7 @@ function prepare(sql) {
|
|
|
1634
1717
|
return hit;
|
|
1635
1718
|
}
|
|
1636
1719
|
var stmt = database.prepare(sql);
|
|
1720
|
+
if (_isRawWriteToResidencyTable(sql)) stmt = _gatedResidencyStmt(stmt, sql);
|
|
1637
1721
|
_prepareCache.set(sql, stmt);
|
|
1638
1722
|
if (_prepareCache.size > PREPARE_CACHE_MAX) {
|
|
1639
1723
|
var oldestKey = _prepareCache.keys().next().value;
|
|
@@ -1796,6 +1880,9 @@ function _reportSlowSqlite(durationMs, statement) {
|
|
|
1796
1880
|
|
|
1797
1881
|
function execRaw(sql) {
|
|
1798
1882
|
_requireInit();
|
|
1883
|
+
// Raw writes bypass the structured builder's residency gate; validate the
|
|
1884
|
+
// residency tag of an INSERT / UPDATE to a per-row-residency table here too.
|
|
1885
|
+
_assertRawWriteResidency(sql);
|
|
1799
1886
|
var startedAt = Date.now();
|
|
1800
1887
|
var auditMod = (function () { try { return require("./audit"); } catch (_e) { return null; } })(); // allow:inline-require — circular-load defense (audit imports db)
|
|
1801
1888
|
// DDL_RE only matches the leading keyword — bounded by `/\s*(KEYWORD)\b/`
|
|
@@ -1926,6 +2013,31 @@ function hashFor(table, field, value) {
|
|
|
1926
2013
|
return lookup ? lookup.value : null;
|
|
1927
2014
|
}
|
|
1928
2015
|
|
|
2016
|
+
/**
|
|
2017
|
+
* @primitive b.db.hashCandidatesFor
|
|
2018
|
+
* @signature b.db.hashCandidatesFor(table, field, value)
|
|
2019
|
+
* @since 0.15.1
|
|
2020
|
+
* @status stable
|
|
2021
|
+
* @related b.db.hashFor, b.db.from
|
|
2022
|
+
*
|
|
2023
|
+
* Dual-read sibling of `hashFor`. Returns `{ field, values }` where `values`
|
|
2024
|
+
* holds the active derived-hash digest AND — across the v0.15.0 keyed-MAC
|
|
2025
|
+
* default flip — the legacy salted-sha3 digest a row written before the flip
|
|
2026
|
+
* carries. A `WHERE <hashColumn> IN (...)` lookup over `values` matches both
|
|
2027
|
+
* keyed-indexed and legacy-indexed rows, so the flip never silently drops an
|
|
2028
|
+
* un-migrated row. Returns `null` when the field has no derived-hash
|
|
2029
|
+
* declaration on the table.
|
|
2030
|
+
*
|
|
2031
|
+
* @example
|
|
2032
|
+
* var c = b.db.hashCandidatesFor("users", "email", "alice@example.com");
|
|
2033
|
+
* b.db.from("users").whereIn(c.field, c.values).all();
|
|
2034
|
+
* // → rows matching either the keyed-MAC or the legacy digest
|
|
2035
|
+
*/
|
|
2036
|
+
function hashCandidatesFor(table, field, value) {
|
|
2037
|
+
_requireInit();
|
|
2038
|
+
return cryptoField.lookupHashCandidates(table, field, value);
|
|
2039
|
+
}
|
|
2040
|
+
|
|
1929
2041
|
// _ddlToJsonSchemaType — best-effort SQL→JSON Schema type mapping.
|
|
1930
2042
|
// SQLite is dynamically typed but the framework's DDL syntax pins
|
|
1931
2043
|
// concrete types; we map them here. Operator-supplied custom types
|
|
@@ -2250,21 +2362,24 @@ function _normalizePk(tableSpec) {
|
|
|
2250
2362
|
// that RAISE(ABORT) the operation. INSERT remains permitted (that's what
|
|
2251
2363
|
// audit.record / consent.grant do).
|
|
2252
2364
|
function _installAppendOnlyTriggers(database) {
|
|
2365
|
+
// b.sql has no CREATE TRIGGER builder — these append-only WORM triggers
|
|
2366
|
+
// are SQLite-specific (RAISE(ABORT) trigger bodies) over framework-
|
|
2367
|
+
// controlled, fixed table names. Identifiers are quoted by construction.
|
|
2253
2368
|
var tables = ["audit_log", "consent_log", "audit_checkpoints"];
|
|
2254
2369
|
for (var i = 0; i < tables.length; i++) {
|
|
2255
2370
|
var t = tables[i];
|
|
2256
2371
|
runSql(database,
|
|
2257
|
-
'CREATE TRIGGER IF NOT EXISTS "no_delete_' + t + '" ' +
|
|
2372
|
+
'CREATE TRIGGER IF NOT EXISTS "no_delete_' + t + '" ' + // allow:hand-rolled-sql — b.sql has no CREATE TRIGGER builder; SQLite append-only WORM trigger, fixed framework table
|
|
2258
2373
|
'BEFORE DELETE ON "' + t + '" ' +
|
|
2259
2374
|
'BEGIN ' +
|
|
2260
|
-
" SELECT RAISE(ABORT, '" + t + " is append-only — DELETE prohibited'); " +
|
|
2375
|
+
" SELECT RAISE(ABORT, '" + t + " is append-only — DELETE prohibited'); " + // allow:hand-rolled-sql — RAISE(ABORT) trigger body, not a query
|
|
2261
2376
|
'END'
|
|
2262
2377
|
);
|
|
2263
2378
|
runSql(database,
|
|
2264
|
-
'CREATE TRIGGER IF NOT EXISTS "no_update_' + t + '" ' +
|
|
2379
|
+
'CREATE TRIGGER IF NOT EXISTS "no_update_' + t + '" ' + // allow:hand-rolled-sql — b.sql has no CREATE TRIGGER builder; SQLite append-only WORM trigger, fixed framework table
|
|
2265
2380
|
'BEFORE UPDATE ON "' + t + '" ' +
|
|
2266
2381
|
'BEGIN ' +
|
|
2267
|
-
" SELECT RAISE(ABORT, '" + t + " is append-only — UPDATE prohibited'); " +
|
|
2382
|
+
" SELECT RAISE(ABORT, '" + t + " is append-only — UPDATE prohibited'); " + // allow:hand-rolled-sql — RAISE(ABORT) trigger body, not a query
|
|
2268
2383
|
'END'
|
|
2269
2384
|
);
|
|
2270
2385
|
}
|
|
@@ -2277,19 +2392,22 @@ function _installAppendOnlyTriggers(database) {
|
|
|
2277
2392
|
// boot-time assertion under WORM_POSTURES catches operators who
|
|
2278
2393
|
// set the posture without declaring tables.
|
|
2279
2394
|
function _installWormTriggers(database, tableName) {
|
|
2395
|
+
// b.sql has no CREATE TRIGGER builder — operator-table WORM triggers are
|
|
2396
|
+
// SQLite-specific RAISE(ABORT) trigger bodies; tableName is validated
|
|
2397
|
+
// (validateIdentifier) and quoted by construction.
|
|
2280
2398
|
safeSql.validateIdentifier(tableName);
|
|
2281
2399
|
runSql(database,
|
|
2282
|
-
'CREATE TRIGGER IF NOT EXISTS "worm_no_delete_' + tableName + '" ' +
|
|
2400
|
+
'CREATE TRIGGER IF NOT EXISTS "worm_no_delete_' + tableName + '" ' + // allow:hand-rolled-sql — b.sql has no CREATE TRIGGER builder; SQLite WORM trigger over validated operator table
|
|
2283
2401
|
'BEFORE DELETE ON "' + tableName + '" ' +
|
|
2284
2402
|
'BEGIN ' +
|
|
2285
|
-
" SELECT RAISE(ABORT, '" + tableName + " is WORM (write-once-read-many) - DELETE prohibited'); " +
|
|
2403
|
+
" SELECT RAISE(ABORT, '" + tableName + " is WORM (write-once-read-many) - DELETE prohibited'); " + // allow:hand-rolled-sql — RAISE(ABORT) trigger body, not a query
|
|
2286
2404
|
'END'
|
|
2287
2405
|
);
|
|
2288
2406
|
runSql(database,
|
|
2289
|
-
'CREATE TRIGGER IF NOT EXISTS "worm_no_update_' + tableName + '" ' +
|
|
2407
|
+
'CREATE TRIGGER IF NOT EXISTS "worm_no_update_' + tableName + '" ' + // allow:hand-rolled-sql — b.sql has no CREATE TRIGGER builder; SQLite WORM trigger over validated operator table
|
|
2290
2408
|
'BEFORE UPDATE ON "' + tableName + '" ' +
|
|
2291
2409
|
'BEGIN ' +
|
|
2292
|
-
" SELECT RAISE(ABORT, '" + tableName + " is WORM (write-once-read-many) - UPDATE prohibited'); " +
|
|
2410
|
+
" SELECT RAISE(ABORT, '" + tableName + " is WORM (write-once-read-many) - UPDATE prohibited'); " + // allow:hand-rolled-sql — RAISE(ABORT) trigger body, not a query
|
|
2293
2411
|
'END'
|
|
2294
2412
|
);
|
|
2295
2413
|
}
|
|
@@ -2355,9 +2473,8 @@ function declareWorm(args) {
|
|
|
2355
2473
|
"the SQLite trigger primitive is single-node only");
|
|
2356
2474
|
}
|
|
2357
2475
|
var nowMs = Date.now();
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
);
|
|
2476
|
+
// allow:hand-rolled-sql — logical name resolved through frameworkSchema.tableName (the prescribed prefix-aware indirection)
|
|
2477
|
+
var wormTable = frameworkSchema.tableName("_blamejs_worm_tables");
|
|
2361
2478
|
for (var j = 0; j < args.tables.length; j++) {
|
|
2362
2479
|
var t = args.tables[j];
|
|
2363
2480
|
if (t === "audit_log" || t === "consent_log" || t === "audit_checkpoints") {
|
|
@@ -2366,7 +2483,14 @@ function declareWorm(args) {
|
|
|
2366
2483
|
"use audit-tools.purge for sanctioned deletions");
|
|
2367
2484
|
}
|
|
2368
2485
|
_installWormTriggers(database, t);
|
|
2369
|
-
|
|
2486
|
+
// INSERT-or-replace on the tableName PK: b.sql upsert emits the
|
|
2487
|
+
// portable ON CONFLICT DO UPDATE form (same replace-on-PK semantics
|
|
2488
|
+
// as the prior INSERT OR REPLACE).
|
|
2489
|
+
var wormUp = sql.upsert(wormTable, _SQL_OPTS)
|
|
2490
|
+
.values({ tableName: t, posture: args.posture || null, declaredAt: nowMs })
|
|
2491
|
+
.onConflict(["tableName"]).doUpdateFromExcluded(["posture", "declaredAt"]).toSql();
|
|
2492
|
+
var wormStmt = database.prepare(wormUp.sql);
|
|
2493
|
+
wormStmt.run.apply(wormStmt, wormUp.params);
|
|
2370
2494
|
audit.safeEmit({
|
|
2371
2495
|
action: "db.worm.declared",
|
|
2372
2496
|
outcome: "success",
|
|
@@ -2383,9 +2507,11 @@ function _assertWormUnderPosture() {
|
|
|
2383
2507
|
if (cluster.isClusterMode()) return;
|
|
2384
2508
|
var rows;
|
|
2385
2509
|
try {
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2510
|
+
// allow:hand-rolled-sql — logical name resolved through frameworkSchema.tableName (prefix-aware), composed via b.sql
|
|
2511
|
+
var wormSel = sql.select(frameworkSchema.tableName("_blamejs_worm_tables"), _SQL_OPTS)
|
|
2512
|
+
.columns(["tableName"]).toSql();
|
|
2513
|
+
var wormSelStmt = database.prepare(wormSel.sql);
|
|
2514
|
+
rows = wormSelStmt.all.apply(wormSelStmt, wormSel.params);
|
|
2389
2515
|
} catch (_e) { rows = []; }
|
|
2390
2516
|
if (!rows || rows.length === 0) {
|
|
2391
2517
|
throw _wormErr("POSTURE_VIOLATION",
|
|
@@ -2459,12 +2585,17 @@ function declareRequireDualControl(args) {
|
|
|
2459
2585
|
"declareRequireDualControl: args.posture must be a non-empty string or null");
|
|
2460
2586
|
}
|
|
2461
2587
|
var nowMs = Date.now();
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
'(tableName, posture, m, n, declaredAt) VALUES (?, ?, ?, ?, ?)'
|
|
2465
|
-
);
|
|
2588
|
+
// allow:hand-rolled-sql — logical name resolved through frameworkSchema.tableName (the prescribed prefix-aware indirection)
|
|
2589
|
+
var gatesTable = frameworkSchema.tableName("_blamejs_dual_control_gates");
|
|
2466
2590
|
for (var j = 0; j < args.tables.length; j++) {
|
|
2467
|
-
|
|
2591
|
+
// INSERT-or-replace on the tableName PK via b.sql upsert (same
|
|
2592
|
+
// replace-on-PK semantics as the prior INSERT OR REPLACE).
|
|
2593
|
+
var gateUp = sql.upsert(gatesTable, _SQL_OPTS)
|
|
2594
|
+
.values({ tableName: args.tables[j], posture: args.posture || null,
|
|
2595
|
+
m: m, n: n, declaredAt: nowMs })
|
|
2596
|
+
.onConflict(["tableName"]).doUpdateFromExcluded(["posture", "m", "n", "declaredAt"]).toSql();
|
|
2597
|
+
var gateStmt = database.prepare(gateUp.sql);
|
|
2598
|
+
gateStmt.run.apply(gateStmt, gateUp.params);
|
|
2468
2599
|
audit.safeEmit({
|
|
2469
2600
|
action: "db.dual_control.declared",
|
|
2470
2601
|
outcome: "success",
|
|
@@ -2479,9 +2610,11 @@ function _checkDualControlGate(tableName) {
|
|
|
2479
2610
|
if (cluster.isClusterMode()) return null;
|
|
2480
2611
|
var row;
|
|
2481
2612
|
try {
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2613
|
+
// allow:hand-rolled-sql — logical name resolved through frameworkSchema.tableName (prefix-aware), composed via b.sql
|
|
2614
|
+
var gateSel = sql.select(frameworkSchema.tableName("_blamejs_dual_control_gates"), _SQL_OPTS)
|
|
2615
|
+
.columns(["tableName", "posture", "m", "n"]).where("tableName", tableName).toSql();
|
|
2616
|
+
var gateSelStmt = database.prepare(gateSel.sql);
|
|
2617
|
+
row = gateSelStmt.get.apply(gateSelStmt, gateSel.params);
|
|
2485
2618
|
} catch (_e) { return null; }
|
|
2486
2619
|
return row || null;
|
|
2487
2620
|
}
|
|
@@ -2568,16 +2701,18 @@ function eraseHard(tableName, rowId, opts) {
|
|
|
2568
2701
|
var t0 = Date.now();
|
|
2569
2702
|
var deleted = 0;
|
|
2570
2703
|
transaction(function () {
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
).
|
|
2704
|
+
// tableName is an operator app table (validateIdentifier'd above), not
|
|
2705
|
+
// a framework table, so it is NOT routed through tableName(); b.sql
|
|
2706
|
+
// quotes it by construction (quoteName) on the local sqlite handle.
|
|
2707
|
+
var rowSel = sql.select(tableName, _SQL_OPTS).where("_id", rowId).toSql();
|
|
2708
|
+
var rowSelStmt = database.prepare(rowSel.sql);
|
|
2709
|
+
var row = rowSelStmt.get.apply(rowSelStmt, rowSel.params);
|
|
2574
2710
|
if (row) {
|
|
2575
2711
|
try { cryptoField.eraseRow(tableName, row); } catch (_e) { /* table may have no sealed cols */ }
|
|
2576
2712
|
}
|
|
2577
|
-
var
|
|
2578
|
-
|
|
2579
|
-
);
|
|
2580
|
-
var result = del.run(rowId);
|
|
2713
|
+
var rowDel = sql.delete(tableName, _SQL_OPTS).where("_id", rowId).toSql();
|
|
2714
|
+
var rowDelStmt = database.prepare(rowDel.sql);
|
|
2715
|
+
var result = rowDelStmt.run.apply(rowDelStmt, rowDel.params);
|
|
2581
2716
|
deleted = (result && result.changes) || 0;
|
|
2582
2717
|
// REINDEX rebuilds every index on the table from scratch,
|
|
2583
2718
|
// dropping the B-tree pages that held the deleted row's index
|
|
@@ -2603,7 +2738,7 @@ function eraseHard(tableName, rowId, opts) {
|
|
|
2603
2738
|
// Read the audit.tip sidecar file in dataDir and compare to the current
|
|
2604
2739
|
// audit_log MAX(monotonicCounter). Refuse boot on rollback (current < tip).
|
|
2605
2740
|
function _checkRollback(dataDirPath) {
|
|
2606
|
-
var tipPath = nodePath.join(dataDirPath,
|
|
2741
|
+
var tipPath = nodePath.join(dataDirPath, frameworkFiles.fileName("auditTip"));
|
|
2607
2742
|
if (!nodeFs.existsSync(tipPath)) {
|
|
2608
2743
|
log("no audit.tip sidecar — skipping rollback check (first boot or operator-cleared)");
|
|
2609
2744
|
return;
|
|
@@ -2617,7 +2752,12 @@ function _checkRollback(dataDirPath) {
|
|
|
2617
2752
|
". Either delete it (forfeits rollback protection until next checkpoint) " +
|
|
2618
2753
|
"or restore from operator backup.");
|
|
2619
2754
|
}
|
|
2620
|
-
|
|
2755
|
+
// The local-SQLite chain table is named "audit_log" (its external-db
|
|
2756
|
+
// counterpart _blamejs_audit_log is the cluster path, handled elsewhere),
|
|
2757
|
+
// so the local read uses that literal name; b.sql quotes it (quoteName).
|
|
2758
|
+
var maxQ = sql.select("audit_log", _SQL_OPTS).max("monotonicCounter", "m").toSql();
|
|
2759
|
+
var maxStmt = database.prepare(maxQ.sql);
|
|
2760
|
+
var current = maxStmt.get.apply(maxStmt, maxQ.params);
|
|
2621
2761
|
var currentMax = current && current.m ? current.m : 0;
|
|
2622
2762
|
if (currentMax < tip.atMonotonicCounter) {
|
|
2623
2763
|
events.emit(events.EVENTS.AUDIT_ROLLBACK_DETECTED, {
|
|
@@ -3168,6 +3308,7 @@ module.exports = {
|
|
|
3168
3308
|
["e" + "xec"]: execRaw,
|
|
3169
3309
|
transaction: transaction,
|
|
3170
3310
|
hashFor: hashFor,
|
|
3311
|
+
hashCandidatesFor: hashCandidatesFor,
|
|
3171
3312
|
close: close,
|
|
3172
3313
|
// flushToDisk — force the live tmpfs SQLite to be re-encrypted to
|
|
3173
3314
|
// <dataDir>/db.enc immediately. In encrypted-at-rest mode the
|
|
@@ -3276,30 +3417,39 @@ module.exports = {
|
|
|
3276
3417
|
}
|
|
3277
3418
|
if (cluster.isClusterMode()) {
|
|
3278
3419
|
// External-db has no append-only triggers; ordinary DELETE works.
|
|
3420
|
+
// clusterStorage.execute rewrites the BARE logical table name to its
|
|
3421
|
+
// cluster-prefixed form and placeholderizes ?→$N, so b.sql emits the
|
|
3422
|
+
// bare name (no quoteName) here.
|
|
3279
3423
|
var cs = clusterStorage();
|
|
3280
|
-
var
|
|
3281
|
-
"
|
|
3282
|
-
);
|
|
3283
|
-
var
|
|
3284
|
-
"
|
|
3285
|
-
);
|
|
3424
|
+
var clusterLogDel = sql.delete("audit_log")
|
|
3425
|
+
.where("monotonicCounter", "<=", lastPurgedCounter).toSql();
|
|
3426
|
+
var d = await cs.execute(clusterLogDel.sql, clusterLogDel.params);
|
|
3427
|
+
var clusterChkDel = sql.delete("audit_checkpoints")
|
|
3428
|
+
.where("atMonotonicCounter", "<=", lastPurgedCounter).toSql();
|
|
3429
|
+
var dc = await cs.execute(clusterChkDel.sql, clusterChkDel.params);
|
|
3286
3430
|
return { rowsDeleted: d.rowCount || 0, checkpointsDeleted: dc.rowCount || 0 };
|
|
3287
3431
|
}
|
|
3288
3432
|
// Single-node: drop triggers, delete, recreate triggers — all in
|
|
3289
3433
|
// one transaction so a crash mid-operation doesn't leave the
|
|
3290
|
-
// table writable to general code.
|
|
3434
|
+
// table writable to general code. The local chain tables are named
|
|
3435
|
+
// "audit_log" / "audit_checkpoints" (no _blamejs_ prefix locally);
|
|
3436
|
+
// b.sql quotes them (quoteName) on the local handle.
|
|
3291
3437
|
var rowsDeleted = 0;
|
|
3292
3438
|
var checkpointsDeleted = 0;
|
|
3293
3439
|
transaction(function () {
|
|
3440
|
+
// allow:hand-rolled-sql — b.sql has no DROP TRIGGER builder; framework-controlled trigger name, append-only re-installed below
|
|
3294
3441
|
runSql(database, 'DROP TRIGGER IF EXISTS "no_delete_audit_log"');
|
|
3442
|
+
// allow:hand-rolled-sql — b.sql has no DROP TRIGGER builder; framework-controlled trigger name, append-only re-installed below
|
|
3295
3443
|
runSql(database, 'DROP TRIGGER IF EXISTS "no_delete_audit_checkpoints"');
|
|
3296
|
-
var
|
|
3297
|
-
"
|
|
3298
|
-
|
|
3444
|
+
var logDel = sql.delete("audit_log", _SQL_OPTS)
|
|
3445
|
+
.where("monotonicCounter", "<=", lastPurgedCounter).toSql();
|
|
3446
|
+
var logDelStmt = database.prepare(logDel.sql);
|
|
3447
|
+
var d = logDelStmt.run.apply(logDelStmt, logDel.params);
|
|
3299
3448
|
rowsDeleted = (d && d.changes) || 0;
|
|
3300
|
-
var
|
|
3301
|
-
"
|
|
3302
|
-
|
|
3449
|
+
var chkDel = sql.delete("audit_checkpoints", _SQL_OPTS)
|
|
3450
|
+
.where("atMonotonicCounter", "<=", lastPurgedCounter).toSql();
|
|
3451
|
+
var chkDelStmt = database.prepare(chkDel.sql);
|
|
3452
|
+
var dc = chkDelStmt.run.apply(chkDelStmt, chkDel.params);
|
|
3303
3453
|
checkpointsDeleted = (dc && dc.changes) || 0;
|
|
3304
3454
|
_installAppendOnlyTriggers(database);
|
|
3305
3455
|
});
|
|
@@ -3401,7 +3551,7 @@ module.exports = {
|
|
|
3401
3551
|
// Helper for audit.checkpoint to write the rollback-detection sidecar
|
|
3402
3552
|
_writeAuditTip: function (tip) {
|
|
3403
3553
|
if (!dataDir) return;
|
|
3404
|
-
var tipPath = nodePath.join(dataDir,
|
|
3554
|
+
var tipPath = nodePath.join(dataDir, frameworkFiles.fileName("auditTip"));
|
|
3405
3555
|
atomicFile.writeSync(tipPath, JSON.stringify(tip, null, 2), { fileMode: 0o600 });
|
|
3406
3556
|
},
|
|
3407
3557
|
};
|