@blamejs/blamejs-shop 0.4.30 → 0.4.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/checkout.js +8 -0
- package/lib/order.js +71 -11
- package/lib/vendor/MANIFEST.json +392 -278
- package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
- package/lib/vendor/blamejs/.gitignore +6 -0
- package/lib/vendor/blamejs/CHANGELOG.md +26 -0
- package/lib/vendor/blamejs/MIGRATING.md +43 -0
- package/lib/vendor/blamejs/README.md +8 -6
- package/lib/vendor/blamejs/SECURITY.md +19 -3
- package/lib/vendor/blamejs/api-snapshot.json +2190 -664
- package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
- package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
- package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
- package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
- package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
- package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
- package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
- package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
- package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
- package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
- package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
- package/lib/vendor/blamejs/index.js +4 -0
- package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
- package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
- package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
- package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
- package/lib/vendor/blamejs/lib/api-key.js +158 -77
- package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
- package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
- package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
- package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
- package/lib/vendor/blamejs/lib/audit.js +259 -123
- package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
- package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
- package/lib/vendor/blamejs/lib/backup/index.js +45 -10
- package/lib/vendor/blamejs/lib/break-glass.js +355 -147
- package/lib/vendor/blamejs/lib/cache.js +174 -105
- package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
- package/lib/vendor/blamejs/lib/cli.js +19 -14
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
- package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
- package/lib/vendor/blamejs/lib/cluster.js +119 -71
- package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
- package/lib/vendor/blamejs/lib/compliance.js +206 -4
- package/lib/vendor/blamejs/lib/consent.js +82 -29
- package/lib/vendor/blamejs/lib/constants.js +27 -11
- package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
- package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
- package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
- package/lib/vendor/blamejs/lib/db-query.js +882 -260
- package/lib/vendor/blamejs/lib/db-schema.js +228 -44
- package/lib/vendor/blamejs/lib/db.js +249 -99
- package/lib/vendor/blamejs/lib/dsr.js +385 -55
- package/lib/vendor/blamejs/lib/error-page.js +14 -1
- package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
- package/lib/vendor/blamejs/lib/external-db.js +549 -34
- package/lib/vendor/blamejs/lib/file-upload.js +52 -7
- package/lib/vendor/blamejs/lib/framework-error.js +20 -1
- package/lib/vendor/blamejs/lib/framework-files.js +73 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
- package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
- package/lib/vendor/blamejs/lib/guard-all.js +1 -0
- package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
- package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
- package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
- package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
- package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
- package/lib/vendor/blamejs/lib/guard-email.js +47 -69
- package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
- package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
- package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
- package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
- package/lib/vendor/blamejs/lib/guard-html.js +53 -108
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
- package/lib/vendor/blamejs/lib/guard-image.js +46 -103
- package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
- package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
- package/lib/vendor/blamejs/lib/guard-json.js +38 -108
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
- package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
- package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
- package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
- package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
- package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
- package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
- package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
- package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
- package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
- package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
- package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
- package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
- package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
- package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
- package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
- package/lib/vendor/blamejs/lib/guard-template.js +35 -172
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-time.js +32 -154
- package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
- package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
- package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
- package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
- package/lib/vendor/blamejs/lib/http-client.js +37 -9
- package/lib/vendor/blamejs/lib/inbox.js +120 -107
- package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
- package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
- package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
- package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
- package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
- package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
- package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
- package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
- package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
- package/lib/vendor/blamejs/lib/mail-store.js +293 -154
- package/lib/vendor/blamejs/lib/mail.js +8 -4
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
- package/lib/vendor/blamejs/lib/migrations.js +108 -66
- package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
- package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
- package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
- package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
- package/lib/vendor/blamejs/lib/observability.js +124 -0
- package/lib/vendor/blamejs/lib/otel-export.js +12 -3
- package/lib/vendor/blamejs/lib/outbox.js +184 -83
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
- package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
- package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
- package/lib/vendor/blamejs/lib/queue-local.js +225 -140
- package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
- package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
- package/lib/vendor/blamejs/lib/queue.js +7 -0
- package/lib/vendor/blamejs/lib/redact.js +68 -11
- package/lib/vendor/blamejs/lib/redis-client.js +160 -31
- package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
- package/lib/vendor/blamejs/lib/retention.js +101 -40
- package/lib/vendor/blamejs/lib/router.js +212 -5
- package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
- package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
- package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
- package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
- package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
- package/lib/vendor/blamejs/lib/safe-url.js +170 -3
- package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
- package/lib/vendor/blamejs/lib/scheduler.js +35 -12
- package/lib/vendor/blamejs/lib/seeders.js +122 -74
- package/lib/vendor/blamejs/lib/session-stores.js +42 -14
- package/lib/vendor/blamejs/lib/session.js +175 -77
- package/lib/vendor/blamejs/lib/sql.js +3842 -0
- package/lib/vendor/blamejs/lib/sse.js +26 -0
- package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
- package/lib/vendor/blamejs/lib/static.js +177 -34
- package/lib/vendor/blamejs/lib/subject.js +96 -49
- package/lib/vendor/blamejs/lib/vault/index.js +3 -2
- package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
- package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
- package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
- package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
- package/lib/vendor/blamejs/lib/websocket.js +35 -5
- package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
- package/lib/vendor/blamejs/package.json +2 -2
- package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
- package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
- package/lib/vendor/blamejs/scripts/check-services.js +21 -0
- package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
- package/lib/vendor/blamejs/scripts/release.js +398 -38
- package/lib/vendor/blamejs/test/00-primitives.js +117 -0
- package/lib/vendor/blamejs/test/10-state.js +140 -14
- package/lib/vendor/blamejs/test/20-db.js +65 -2
- package/lib/vendor/blamejs/test/helpers/db.js +9 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
- package/lib/vendor/blamejs/test/helpers/services.js +21 -0
- package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
- package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
- package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
- package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
- package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
- package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
- package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
- package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
- package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
- package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
- package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
- package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
- package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
- package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
- package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
- package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
- package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
- package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
- package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
- package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
- package/lib/vendor/blamejs/test/smoke.js +79 -21
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
- package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
- package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
- package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
- package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
- package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
- package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
- package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
- package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
- package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
- package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
- package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
- package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
- package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.atomicFile temp-file create hardening (CWE-377 insecure temporary
|
|
4
|
+
* file / CWE-59 symlink follow).
|
|
5
|
+
*
|
|
6
|
+
* Every atomic write stages bytes into a sibling temp file
|
|
7
|
+
* (`<filepath>.tmp-<csprng>`) before renaming over the destination. The
|
|
8
|
+
* temp create must be EXCLUSIVE and NO-FOLLOW: an attacker who guesses
|
|
9
|
+
* (or, on a shared dir, races) the temp path and pre-plants a regular
|
|
10
|
+
* file or a symlink there must NOT have that file truncated or written
|
|
11
|
+
* through. O_EXCL makes the open fail with EEXIST when anything already
|
|
12
|
+
* exists at the path; O_NOFOLLOW refuses a symlink in the final
|
|
13
|
+
* component where the platform defines it.
|
|
14
|
+
*
|
|
15
|
+
* Coverage:
|
|
16
|
+
* - write / writeSync round-trip (the atomicity contract is intact)
|
|
17
|
+
* - the exact create flag set the substrate uses refuses a pre-existing
|
|
18
|
+
* regular file (EEXIST) instead of truncating it
|
|
19
|
+
* - the same flag set refuses a pre-existing symlink (does not follow
|
|
20
|
+
* it to a victim path) on platforms where symlinks are creatable
|
|
21
|
+
* - a symlink planted at the DESTINATION is replaced by the rename, not
|
|
22
|
+
* followed — the victim the symlink pointed at is left untouched
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
var fs = require("fs");
|
|
26
|
+
var path = require("path");
|
|
27
|
+
|
|
28
|
+
var helpers = require("../helpers");
|
|
29
|
+
var b = helpers.b;
|
|
30
|
+
var check = helpers.check;
|
|
31
|
+
|
|
32
|
+
// The production temp-create flag set (lib/atomic-file.js _openExclTemp /
|
|
33
|
+
// lib/http-client.js downloadStream). Reproduced here so the test asserts
|
|
34
|
+
// the SAME security property the substrate depends on; O_NOFOLLOW is
|
|
35
|
+
// undefined on Windows, hence the `|| 0` fold both here and in the source.
|
|
36
|
+
var EXCL_FLAGS = fs.constants.O_WRONLY | fs.constants.O_CREAT |
|
|
37
|
+
fs.constants.O_EXCL | (fs.constants.O_NOFOLLOW || 0);
|
|
38
|
+
|
|
39
|
+
function _tmpDir() {
|
|
40
|
+
return b.testing.tempDir("atomicfile-excl");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function testWriteRoundTrips() {
|
|
44
|
+
var dir = _tmpDir();
|
|
45
|
+
try {
|
|
46
|
+
var p = path.join(dir.path, "state.bin");
|
|
47
|
+
var payload = Buffer.from("round-trip payload ✓", "utf8");
|
|
48
|
+
var res = await b.atomicFile.write(p, payload, { computeHash: true });
|
|
49
|
+
check("write: bytesWritten matches", res.bytesWritten === payload.length);
|
|
50
|
+
check("write: dest exists", fs.existsSync(p));
|
|
51
|
+
check("write: content round-trips", fs.readFileSync(p).equals(payload));
|
|
52
|
+
// No temp left behind on the success path.
|
|
53
|
+
var leftovers = fs.readdirSync(dir.path).filter(function (n) {
|
|
54
|
+
return n.indexOf("state.bin.tmp-") === 0;
|
|
55
|
+
});
|
|
56
|
+
check("write: no temp file leaked", leftovers.length === 0);
|
|
57
|
+
|
|
58
|
+
// Overwrite the same destination — the temp create must succeed each
|
|
59
|
+
// time even though `p` already exists (we stage to a fresh temp path,
|
|
60
|
+
// never to `p` itself), and the new bytes must win.
|
|
61
|
+
var payload2 = Buffer.from("second write", "utf8");
|
|
62
|
+
await b.atomicFile.write(p, payload2);
|
|
63
|
+
check("write: overwrite replaces content", fs.readFileSync(p).equals(payload2));
|
|
64
|
+
} finally {
|
|
65
|
+
dir.cleanup();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function testWriteSyncRoundTrips() {
|
|
70
|
+
var dir = _tmpDir();
|
|
71
|
+
try {
|
|
72
|
+
var p = path.join(dir.path, "sync-state.bin");
|
|
73
|
+
var payload = Buffer.from("sync payload", "utf8");
|
|
74
|
+
var res = b.atomicFile.writeSync(p, payload);
|
|
75
|
+
check("writeSync: bytesWritten matches", res.bytesWritten === payload.length);
|
|
76
|
+
check("writeSync: content round-trips", fs.readFileSync(p).equals(payload));
|
|
77
|
+
var leftovers = fs.readdirSync(dir.path).filter(function (n) {
|
|
78
|
+
return n.indexOf("sync-state.bin.tmp-") === 0;
|
|
79
|
+
});
|
|
80
|
+
check("writeSync: no temp file leaked", leftovers.length === 0);
|
|
81
|
+
} finally {
|
|
82
|
+
dir.cleanup();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function testExclusiveRefusesExistingFile() {
|
|
87
|
+
var dir = _tmpDir();
|
|
88
|
+
try {
|
|
89
|
+
var p = path.join(dir.path, "victim.bin");
|
|
90
|
+
fs.writeFileSync(p, "ATTACKER PRE-CREATED THIS", { mode: 0o600 });
|
|
91
|
+
var threw = null;
|
|
92
|
+
var fd = null;
|
|
93
|
+
try {
|
|
94
|
+
fd = fs.openSync(p, EXCL_FLAGS, 0o600);
|
|
95
|
+
} catch (e) { threw = e; }
|
|
96
|
+
finally { if (fd !== null) { try { fs.closeSync(fd); } catch (_c) { /* ignore */ } } }
|
|
97
|
+
check("excl flags: existing regular file refused with EEXIST",
|
|
98
|
+
threw != null && threw.code === "EEXIST");
|
|
99
|
+
// The pre-existing bytes must be intact — O_EXCL refused before any
|
|
100
|
+
// truncation could happen (the old "w"/O_TRUNC flag would have wiped it).
|
|
101
|
+
check("excl flags: pre-existing file NOT truncated",
|
|
102
|
+
fs.readFileSync(p, "utf8") === "ATTACKER PRE-CREATED THIS");
|
|
103
|
+
} finally {
|
|
104
|
+
dir.cleanup();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function testNoFollowRefusesSymlink() {
|
|
109
|
+
var dir = _tmpDir();
|
|
110
|
+
try {
|
|
111
|
+
var victim = path.join(dir.path, "victim-secret.txt");
|
|
112
|
+
fs.writeFileSync(victim, "SECRET", { mode: 0o600 });
|
|
113
|
+
var link = path.join(dir.path, "staged.tmp");
|
|
114
|
+
|
|
115
|
+
var symlinkOk = true;
|
|
116
|
+
try {
|
|
117
|
+
fs.symlinkSync(victim, link);
|
|
118
|
+
} catch (_e) {
|
|
119
|
+
// Windows without the SeCreateSymbolicLink privilege (common on CI
|
|
120
|
+
// and dev boxes) cannot create symlinks. The O_NOFOLLOW property is
|
|
121
|
+
// still exercised by the EEXIST-on-existing-file test above; skip
|
|
122
|
+
// only the symlink-specific assertion here.
|
|
123
|
+
symlinkOk = false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (symlinkOk) {
|
|
127
|
+
var threw = null;
|
|
128
|
+
var fd = null;
|
|
129
|
+
try {
|
|
130
|
+
fd = fs.openSync(link, EXCL_FLAGS, 0o600);
|
|
131
|
+
} catch (e) { threw = e; }
|
|
132
|
+
finally { if (fd !== null) { try { fs.closeSync(fd); } catch (_c) { /* ignore */ } } }
|
|
133
|
+
check("excl flags: pre-existing symlink refused (not followed)",
|
|
134
|
+
threw != null && (threw.code === "EEXIST" || threw.code === "ELOOP"));
|
|
135
|
+
// The victim the symlink pointed at must be untouched — neither
|
|
136
|
+
// truncated nor written through.
|
|
137
|
+
check("excl flags: symlink target (victim) NOT written through",
|
|
138
|
+
fs.readFileSync(victim, "utf8") === "SECRET");
|
|
139
|
+
} else {
|
|
140
|
+
check("excl flags: symlink case skipped (platform lacks symlink privilege)", true);
|
|
141
|
+
}
|
|
142
|
+
} finally {
|
|
143
|
+
dir.cleanup();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function testSymlinkAtDestinationReplacedNotFollowed() {
|
|
148
|
+
// A symlink planted at the DESTINATION path (not the temp path) must be
|
|
149
|
+
// replaced by the atomic rename, not followed to its target. The temp
|
|
150
|
+
// file is created next to the dest, then renamed over the symlink — the
|
|
151
|
+
// rename swaps the directory entry, leaving the symlink's old target
|
|
152
|
+
// (the victim) untouched.
|
|
153
|
+
var dir = _tmpDir();
|
|
154
|
+
try {
|
|
155
|
+
var victim = path.join(dir.path, "outside-victim.bin");
|
|
156
|
+
fs.writeFileSync(victim, "DO NOT OVERWRITE", { mode: 0o600 });
|
|
157
|
+
var dest = path.join(dir.path, "dest-link.bin");
|
|
158
|
+
|
|
159
|
+
var symlinkOk = true;
|
|
160
|
+
try { fs.symlinkSync(victim, dest); }
|
|
161
|
+
catch (_e) { symlinkOk = false; }
|
|
162
|
+
|
|
163
|
+
if (!symlinkOk) {
|
|
164
|
+
check("dest-symlink case skipped (platform lacks symlink privilege)", true);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
var payload = Buffer.from("fresh contents", "utf8");
|
|
169
|
+
await b.atomicFile.write(dest, payload);
|
|
170
|
+
|
|
171
|
+
// dest is now a regular file with the new bytes (the rename replaced
|
|
172
|
+
// the symlink entry). Open ONE no-follow fd and take both the type
|
|
173
|
+
// check (fstat) and the byte read from that same descriptor — no
|
|
174
|
+
// lstat-then-read against the path, which would be a check-then-use
|
|
175
|
+
// file-system race (CWE-367). O_NOFOLLOW makes the open itself fail
|
|
176
|
+
// if dest were still a symlink, so a successful open already proves
|
|
177
|
+
// the rename replaced the link with a regular file.
|
|
178
|
+
var destFd = fs.openSync(dest, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
|
179
|
+
try {
|
|
180
|
+
var fst = fs.fstatSync(destFd);
|
|
181
|
+
check("dest-symlink: destination is a regular file after write",
|
|
182
|
+
fst.isFile() && !fst.isSymbolicLink());
|
|
183
|
+
var destBytes = Buffer.alloc(fst.size);
|
|
184
|
+
var got = 0;
|
|
185
|
+
while (got < fst.size) {
|
|
186
|
+
var n = fs.readSync(destFd, destBytes, got, fst.size - got, null);
|
|
187
|
+
if (n === 0) break;
|
|
188
|
+
got += n;
|
|
189
|
+
}
|
|
190
|
+
check("dest-symlink: destination holds the new bytes",
|
|
191
|
+
got === payload.length && destBytes.equals(payload));
|
|
192
|
+
} finally {
|
|
193
|
+
fs.closeSync(destFd);
|
|
194
|
+
}
|
|
195
|
+
// ... and the victim the symlink pointed at was NOT written through.
|
|
196
|
+
check("dest-symlink: symlink target (victim) untouched",
|
|
197
|
+
fs.readFileSync(victim, "utf8") === "DO NOT OVERWRITE");
|
|
198
|
+
} finally {
|
|
199
|
+
dir.cleanup();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function run() {
|
|
204
|
+
await testWriteRoundTrips();
|
|
205
|
+
testWriteSyncRoundTrips();
|
|
206
|
+
testExclusiveRefusesExistingFile();
|
|
207
|
+
testNoFollowRefusesSymlink();
|
|
208
|
+
await testSymlinkAtDestinationReplacedNotFollowed();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
module.exports = { run: run };
|
|
212
|
+
|
|
213
|
+
if (require.main === module) {
|
|
214
|
+
run().then(function () { console.log("OK"); })
|
|
215
|
+
.catch(function (e) { console.error(e.stack || e); process.exit(1); });
|
|
216
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// SMOKE_RUN_SOLO — the smoke runner (test/smoke.js) runs this file ALONE
|
|
4
|
+
// with the whole machine instead of inside the parallel layer-0 pool.
|
|
5
|
+
// The test drives the encrypted-at-rest durability path end to end:
|
|
6
|
+
// db.enc re-encryption (flushToDisk), the atomicFile.writeSync tip
|
|
7
|
+
// sidecar, an in-place crash corruption of the tmpfs working file, and a
|
|
8
|
+
// decrypt-from-db.enc reboot — every step a blocking fsync on the real
|
|
9
|
+
// data dir. Under SMOKE_PARALLEL=64 on a virtualized filesystem (the
|
|
10
|
+
// Dropbox-backed working tree, Docker-Desktop FS-virt) 64 sibling forks
|
|
11
|
+
// contend for fsync on the same volume and these synchronous writes
|
|
12
|
+
// overrun the per-file watchdog. There is no single async event to poll
|
|
13
|
+
// past — the contention is whole-process I/O, so the file runs solo and
|
|
14
|
+
// finishes in its normal time. Passes alone and at SMOKE_PARALLEL=16.
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* audit.checkpoint() durability vs the boot-time rollback check.
|
|
18
|
+
*
|
|
19
|
+
* In encrypted-at-rest mode the live SQLite copy is a tmpfs working file;
|
|
20
|
+
* durability comes from re-encrypting it into db.enc (flushToDisk /
|
|
21
|
+
* periodic encrypt / process-exit handler / orderly close). The boot-time
|
|
22
|
+
* rollback detector compares MAX(monotonicCounter) in the restored DB
|
|
23
|
+
* against the audit.tip sidecar and REFUSES boot when the DB is below the
|
|
24
|
+
* tip (the snapshot was rolled back to an older state, or rows were
|
|
25
|
+
* deleted).
|
|
26
|
+
*
|
|
27
|
+
* checkpoint() writes the audit.tip sidecar with atomicFile.writeSync — a
|
|
28
|
+
* durable write to a real disk path in dataDir — immediately after
|
|
29
|
+
* inserting the checkpoint row, but WITHOUT first re-encrypting the
|
|
30
|
+
* audit_log rows it anchors into db.enc. So the tip's durability outruns
|
|
31
|
+
* the rows' durability: if the process dies between checkpoint() and the
|
|
32
|
+
* next encrypt, the durable db.enc is BEHIND the durable tip.
|
|
33
|
+
*
|
|
34
|
+
* On reboot the tmpfs working copy is gone / discarded as corrupt (the
|
|
35
|
+
* unclean-shutdown case decryptToTmp falls back from), so the DB is
|
|
36
|
+
* restored from db.enc — below the tip. The rollback detector then refuses
|
|
37
|
+
* boot with db/audit-rollback-detected even though the chain is perfectly
|
|
38
|
+
* intact (it was a normal crash, not a rollback attack or a deletion).
|
|
39
|
+
*
|
|
40
|
+
* This test reproduces that crash window and asserts the CORRECT behavior:
|
|
41
|
+
* a clean reopen with the chain readable, no false rollback refusal. If
|
|
42
|
+
* checkpoint() left the tip ahead of the durable rows (issue #62), the
|
|
43
|
+
* reopen throws db/audit-rollback-detected and this test FAILS.
|
|
44
|
+
*
|
|
45
|
+
* Scratch dir lives under the repo-local .test-output (not os.tmpdir):
|
|
46
|
+
* the crash simulation corrupts the working file in place, which static
|
|
47
|
+
* analysis (CodeQL js/insecure-temporary-file) flags as an insecure
|
|
48
|
+
* OS-temp-dir write. A gitignored per-test dir outside the shared OS temp
|
|
49
|
+
* dir sidesteps that false positive and keeps test isolation clean.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
var helpers = require("../helpers");
|
|
53
|
+
var b = helpers.b;
|
|
54
|
+
var check = helpers.check;
|
|
55
|
+
var fs = helpers.fs;
|
|
56
|
+
var path = helpers.path;
|
|
57
|
+
var setupTestDb = helpers.setupTestDb;
|
|
58
|
+
var teardownTestDb = helpers.teardownTestDb;
|
|
59
|
+
var setTestPassphraseEnv = helpers.setTestPassphraseEnv;
|
|
60
|
+
|
|
61
|
+
var SCHEMA = [{ name: "ckpt_t", columns: { _id: "TEXT PRIMARY KEY", v: "TEXT" } }];
|
|
62
|
+
|
|
63
|
+
function currentMaxCounter() {
|
|
64
|
+
var row = b.db.prepare("SELECT MAX(monotonicCounter) AS m FROM audit_log").get();
|
|
65
|
+
return row && row.m ? Number(row.m) : 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function readTipCounter(tmpDir) {
|
|
69
|
+
var tipPath = path.join(tmpDir, "audit.tip");
|
|
70
|
+
var tip = JSON.parse(fs.readFileSync(tipPath, "utf8"));
|
|
71
|
+
return Number(tip.atMonotonicCounter);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Simulate an unclean crash: close the SQLite handle WITHOUT re-encrypting
|
|
75
|
+
// (so db.enc keeps the last flushed snapshot), then corrupt the tmpfs
|
|
76
|
+
// working copy + stamp it newer than db.enc so the next boot's
|
|
77
|
+
// decryptToTmp discards it and restores the durable db.enc. This is the
|
|
78
|
+
// exact shape produced by an unclean shutdown / full tmpfs — the carrier
|
|
79
|
+
// of the post-checkpoint rows is lost, db.enc is the only durable state.
|
|
80
|
+
function simulateCrashDiscardingTmpfs(tmpDir) {
|
|
81
|
+
b.db._resetForTest(); // drops the handle; leaves the working file on disk
|
|
82
|
+
var workingDir = path.join(tmpDir, "tmpfs");
|
|
83
|
+
var workingFile = fs.readdirSync(workingDir).filter(function (f) {
|
|
84
|
+
return /\.db$/.test(f);
|
|
85
|
+
})[0];
|
|
86
|
+
var workingPath = path.join(workingDir, workingFile);
|
|
87
|
+
var corruptFd = fs.openSync(workingPath, "r+");
|
|
88
|
+
try {
|
|
89
|
+
fs.writeSync(corruptFd,
|
|
90
|
+
Buffer.from("not a sqlite database -- crash\n".repeat(8)), 0, undefined, 0);
|
|
91
|
+
} finally {
|
|
92
|
+
fs.closeSync(corruptFd);
|
|
93
|
+
}
|
|
94
|
+
var future = new Date(Date.now() + 60000);
|
|
95
|
+
fs.utimesSync(workingPath, future, future);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function reinitDbOnly(tmpDir) {
|
|
99
|
+
// Re-wire audit-sign from the same passphrase; do NOT reset the vault so
|
|
100
|
+
// the existing db.enc key still decrypts. Mirrors setupTestDb's pre-init
|
|
101
|
+
// reset minus the vault reset (the boot-recovery path).
|
|
102
|
+
setTestPassphraseEnv();
|
|
103
|
+
b.audit._resetForTest();
|
|
104
|
+
// Same disk-residency posture as setupTestDb: the fixture's scratch dir is
|
|
105
|
+
// the repo-local .test-output (not a real tmpfs mount), so the v0.15.0
|
|
106
|
+
// non-tmpfs tmpDir gate must be opted past explicitly here too — otherwise
|
|
107
|
+
// db.init refuses with db/tmpdir-not-tmpfs on Linux (the gate is
|
|
108
|
+
// platform-specific, so this only surfaces off Windows).
|
|
109
|
+
await b.db.init({
|
|
110
|
+
dataDir: tmpDir,
|
|
111
|
+
tmpDir: path.join(tmpDir, "tmpfs"),
|
|
112
|
+
schema: SCHEMA,
|
|
113
|
+
allowNonTmpfsTmpDir: true,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function run() {
|
|
118
|
+
var scratchBase = path.join(__dirname, "..", ".test-output");
|
|
119
|
+
fs.mkdirSync(scratchBase, { recursive: true });
|
|
120
|
+
var tmpDir = fs.mkdtempSync(path.join(scratchBase, "audit-ckpt-rollback-"));
|
|
121
|
+
try {
|
|
122
|
+
await setupTestDb(tmpDir, SCHEMA);
|
|
123
|
+
check("test runs in encrypted at-rest mode", b.db.getMode() === "encrypted");
|
|
124
|
+
// Register an app namespace so safeEmit rows land in the chain (the
|
|
125
|
+
// async handler drops events under unregistered namespaces).
|
|
126
|
+
b.audit.registerNamespace("orders");
|
|
127
|
+
|
|
128
|
+
// ---- Baseline: a clean checkpoint that IS flushed to durable disk ----
|
|
129
|
+
b.audit.safeEmit({ action: "orders.baseline.one", outcome: "success" });
|
|
130
|
+
b.audit.safeEmit({ action: "orders.baseline.two", outcome: "success" });
|
|
131
|
+
await b.audit.flush();
|
|
132
|
+
var baselineCkpt = await b.audit.checkpoint();
|
|
133
|
+
check("baseline checkpoint anchored", baselineCkpt && baselineCkpt.atMonotonicCounter > 0);
|
|
134
|
+
await b.db.flushToDisk(); // db.enc + tip now agree at the baseline counter
|
|
135
|
+
var baselineCounter = currentMaxCounter();
|
|
136
|
+
check("baseline tip agrees with durable rows",
|
|
137
|
+
readTipCounter(tmpDir) === baselineCounter);
|
|
138
|
+
|
|
139
|
+
// ---- More audit activity, then a checkpoint that is NOT flushed ----
|
|
140
|
+
// These rows + the new checkpoint row live only in the tmpfs working
|
|
141
|
+
// copy; db.enc is unchanged. checkpoint() advances the durable tip.
|
|
142
|
+
b.audit.safeEmit({ action: "orders.post.one", outcome: "success" });
|
|
143
|
+
b.audit.safeEmit({ action: "orders.post.two", outcome: "success" });
|
|
144
|
+
b.audit.safeEmit({ action: "orders.post.three", outcome: "success" });
|
|
145
|
+
await b.audit.flush();
|
|
146
|
+
var advancedCounter = currentMaxCounter();
|
|
147
|
+
check("new audit activity advanced the in-memory counter past baseline",
|
|
148
|
+
advancedCounter > baselineCounter);
|
|
149
|
+
|
|
150
|
+
var postCkpt = await b.audit.checkpoint();
|
|
151
|
+
check("post-batch checkpoint anchored at the advanced counter",
|
|
152
|
+
postCkpt && postCkpt.atMonotonicCounter === advancedCounter);
|
|
153
|
+
|
|
154
|
+
// The tip is now durable and points at the advanced counter — but the
|
|
155
|
+
// rows it anchors were never re-encrypted into db.enc.
|
|
156
|
+
check("durable tip advanced to the post-checkpoint counter",
|
|
157
|
+
readTipCounter(tmpDir) === advancedCounter);
|
|
158
|
+
|
|
159
|
+
// ---- Crash: lose the tmpfs carrier; db.enc stays at the baseline ----
|
|
160
|
+
simulateCrashDiscardingTmpfs(tmpDir);
|
|
161
|
+
|
|
162
|
+
// ---- Reboot: must NOT falsely refuse with a rollback error ----
|
|
163
|
+
// The chain is intact — db.enc holds a valid prefix of the same chain,
|
|
164
|
+
// every row hashes correctly, no row was deleted. A normal crash that
|
|
165
|
+
// lost unflushed rows is not a rollback attack. checkpoint() should
|
|
166
|
+
// have flushed the rows durably before (or with) the tip so the tip
|
|
167
|
+
// never references a counter that isn't on durable disk.
|
|
168
|
+
var rollbackError = null;
|
|
169
|
+
try {
|
|
170
|
+
await reinitDbOnly(tmpDir);
|
|
171
|
+
} catch (e) {
|
|
172
|
+
rollbackError = e;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
check("reopen after crash does NOT falsely refuse with a rollback error" +
|
|
176
|
+
(rollbackError ? " (got: " + rollbackError.code + " — " + rollbackError.message + ")" : ""),
|
|
177
|
+
rollbackError === null);
|
|
178
|
+
|
|
179
|
+
// If the reopen succeeded, the recovered chain must be readable and the
|
|
180
|
+
// app table intact — a real clean recovery, not a degraded boot.
|
|
181
|
+
if (rollbackError === null) {
|
|
182
|
+
var rows = await b.audit.query({ action: "orders.baseline.one" });
|
|
183
|
+
check("recovered audit chain is readable (baseline row present)", rows.length === 1);
|
|
184
|
+
var ckptVerify = await b.audit.verifyCheckpoints();
|
|
185
|
+
check("recovered checkpoints verify clean", ckptVerify.ok === true);
|
|
186
|
+
}
|
|
187
|
+
} finally {
|
|
188
|
+
try { b.db._resetForTest(); } catch (_e) { /* best effort */ }
|
|
189
|
+
await teardownTestDb(tmpDir);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log("OK — audit checkpoint false-rollback tests");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
module.exports = { run: run };
|
|
196
|
+
if (require.main === module) {
|
|
197
|
+
// Rethrow on failure so Node exits non-zero, instead of logging the
|
|
198
|
+
// caught error object — a taint analyzer traces a logged error back to
|
|
199
|
+
// the test passphrase fixture (a non-secret constant) and raises a
|
|
200
|
+
// false clear-text-logging alert.
|
|
201
|
+
run().then(function () { process.exit(0); })
|
|
202
|
+
.catch(function (err) { process.exitCode = 1; throw err; });
|
|
203
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.audit.query self-logging (PCI DSS 10.2.3) — every read of audit_log
|
|
4
|
+
* is itself recorded as an `audit.read` event.
|
|
5
|
+
*
|
|
6
|
+
* The self-log suppression is decided per-invocation from the call's own
|
|
7
|
+
* `criteria.action`, never from shared module state. A prior design used a
|
|
8
|
+
* module-global `_selfLogging` boolean toggled across record()'s await
|
|
9
|
+
* (chain mutex + SQL yield); a CONCURRENT query() racing a mid-flight
|
|
10
|
+
* self-log observed the flag set and silently skipped emitting its own
|
|
11
|
+
* audit.read — under-logging reads exactly when load is highest. These
|
|
12
|
+
* tests pin: two concurrent reads BOTH log, a single read logs exactly
|
|
13
|
+
* once (no double/recursive log), and a query targeting `audit.read`
|
|
14
|
+
* itself does not auto-log.
|
|
15
|
+
*
|
|
16
|
+
* Run standalone: `node test/layer-0-primitives/audit-query-self-log.test.js`
|
|
17
|
+
* Or via smoke: `node test/smoke.js`
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
var helpers = require("../helpers");
|
|
21
|
+
var b = helpers.b;
|
|
22
|
+
var fs = helpers.fs;
|
|
23
|
+
var os = helpers.os;
|
|
24
|
+
var path = helpers.path;
|
|
25
|
+
var check = helpers.check;
|
|
26
|
+
var waitUntil = helpers.waitUntil;
|
|
27
|
+
var setupTestDb = helpers.setupTestDb;
|
|
28
|
+
var teardownTestDb = helpers.teardownTestDb;
|
|
29
|
+
|
|
30
|
+
function _tmp() { return fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-audit-self-")); }
|
|
31
|
+
|
|
32
|
+
// Count audit.read rows directly via the query path that does NOT auto-log
|
|
33
|
+
// (criteria.action === "audit.read"), so counting never perturbs the count.
|
|
34
|
+
async function _readCount() {
|
|
35
|
+
var rows = await b.audit.query({ action: "audit.read" });
|
|
36
|
+
return rows.length;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ---- Concurrent reads each emit their own audit.read ----
|
|
40
|
+
|
|
41
|
+
async function testConcurrentReadsBothLog() {
|
|
42
|
+
var tmpDir = _tmp();
|
|
43
|
+
await setupTestDb(tmpDir);
|
|
44
|
+
try {
|
|
45
|
+
// Seed a non-read row so the concurrent queries return something and,
|
|
46
|
+
// more importantly, so each emits its own self-log.
|
|
47
|
+
await b.audit.record({ action: "consent.granted", outcome: "success" });
|
|
48
|
+
|
|
49
|
+
var before = await _readCount();
|
|
50
|
+
|
|
51
|
+
// Fire two concurrent reads. Pre-fix, the second to enter would see the
|
|
52
|
+
// module-global flag set by the first (mid-record) and skip its self-log,
|
|
53
|
+
// so only ONE audit.read would land. Post-fix, BOTH land.
|
|
54
|
+
var qA = b.audit.query({ action: "consent.granted", actorUserId: "reader-a" });
|
|
55
|
+
var qB = b.audit.query({ action: "consent.granted", actorUserId: "reader-b" });
|
|
56
|
+
await Promise.all([qA, qB]);
|
|
57
|
+
|
|
58
|
+
await waitUntil(async function () {
|
|
59
|
+
return (await _readCount()) >= before + 2;
|
|
60
|
+
}, { timeoutMs: 5000, label: "M3: both concurrent reads emit audit.read" });
|
|
61
|
+
|
|
62
|
+
var after = await _readCount();
|
|
63
|
+
check("two concurrent reads emit two audit.read rows (no under-logging)",
|
|
64
|
+
after === before + 2);
|
|
65
|
+
} finally {
|
|
66
|
+
await teardownTestDb(tmpDir);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ---- A single read emits exactly one audit.read (no double/recursion) ----
|
|
71
|
+
|
|
72
|
+
async function testSingleReadLogsExactlyOnce() {
|
|
73
|
+
var tmpDir = _tmp();
|
|
74
|
+
await setupTestDb(tmpDir);
|
|
75
|
+
try {
|
|
76
|
+
await b.audit.record({ action: "consent.granted", outcome: "success" });
|
|
77
|
+
|
|
78
|
+
var before = await _readCount();
|
|
79
|
+
await b.audit.query({ action: "consent.granted" });
|
|
80
|
+
var after = await _readCount();
|
|
81
|
+
|
|
82
|
+
check("a single read emits exactly one audit.read (no double/recursive log)",
|
|
83
|
+
after === before + 1);
|
|
84
|
+
} finally {
|
|
85
|
+
await teardownTestDb(tmpDir);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ---- Querying audit.read itself does not auto-log (no Russell spiral) ----
|
|
90
|
+
|
|
91
|
+
async function testAuditReadQueryDoesNotSelfLog() {
|
|
92
|
+
var tmpDir = _tmp();
|
|
93
|
+
await setupTestDb(tmpDir);
|
|
94
|
+
try {
|
|
95
|
+
// Generate at least one audit.read so the table is non-empty.
|
|
96
|
+
await b.audit.query({ action: "consent.granted" });
|
|
97
|
+
|
|
98
|
+
var before = await _readCount();
|
|
99
|
+
// Querying for audit.read must NOT emit another audit.read.
|
|
100
|
+
await b.audit.query({ action: "audit.read" });
|
|
101
|
+
var after = await _readCount();
|
|
102
|
+
|
|
103
|
+
check("querying action='audit.read' does not auto-log a new audit.read",
|
|
104
|
+
after === before);
|
|
105
|
+
} finally {
|
|
106
|
+
await teardownTestDb(tmpDir);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function run() {
|
|
111
|
+
await testConcurrentReadsBothLog();
|
|
112
|
+
await testSingleReadLogsExactlyOnce();
|
|
113
|
+
await testAuditReadQueryDoesNotSelfLog();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = { run: run };
|
|
117
|
+
|
|
118
|
+
if (require.main === module) {
|
|
119
|
+
run().then(function () { console.log("OK"); })
|
|
120
|
+
// Re-throw rather than console.error the error object: a DB-setup
|
|
121
|
+
// failure can carry passphrase-derived material on the error, and
|
|
122
|
+
// logging it would be clear-text logging of sensitive data
|
|
123
|
+
// (CWE-312). The non-zero exit + thrown stack still surface the
|
|
124
|
+
// failure to the runner.
|
|
125
|
+
.catch(function (e) { process.exitCode = 1; throw e; });
|
|
126
|
+
}
|