@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,353 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Live test of b.frameworkSchema.ensureSchema against the docker MySQL
|
|
4
|
+
* container. ensureSchema materializes the framework's external-DB surface
|
|
5
|
+
* (audit / consent / checkpoints / tips / scheduler / rate-limit / pubsub /
|
|
6
|
+
* api-key / session / job / cache / seeder / break-glass tables + their
|
|
7
|
+
* indexes + the append-only WORM triggers). It was Postgres/SQLite-only
|
|
8
|
+
* until the MySQL DDL branch landed; this file proves the MySQL branch
|
|
9
|
+
* creates EVERY framework table on a real server AND that a row inserts +
|
|
10
|
+
* reads back through each, with the dialect-specific shapes exercised:
|
|
11
|
+
*
|
|
12
|
+
* - BIGINT for ms-epoch counters/timestamps (a 32-bit INT overflows
|
|
13
|
+
* Date.now()).
|
|
14
|
+
* - LONGBLOB for the binary nonce / signature columns.
|
|
15
|
+
* - VARCHAR(191) for every TEXT column in a PRIMARY KEY or index (MySQL
|
|
16
|
+
* refuses unbounded TEXT/BLOB in a key — the bug #97 surfaced).
|
|
17
|
+
* - BIGINT AUTO_INCREMENT PRIMARY KEY for the pubsub id.
|
|
18
|
+
* - the WORM triggers (SIGNAL SQLSTATE '45000') block DELETE/UPDATE on
|
|
19
|
+
* the append-only tables.
|
|
20
|
+
*
|
|
21
|
+
* The driver is the externalDb-shaped persistent docker-exec mysql client
|
|
22
|
+
* b.externalDb.init wraps; ensureSchema dispatches its DDL through it. A
|
|
23
|
+
* dedicated database (bjfs_test) keeps the run isolated.
|
|
24
|
+
*
|
|
25
|
+
* RUN: node scripts/test-integration.js --skip-service-check framework-schema-mysql
|
|
26
|
+
*/
|
|
27
|
+
var execFileSync = require("node:child_process").execFileSync;
|
|
28
|
+
var helpers = require("../helpers");
|
|
29
|
+
var check = helpers.check;
|
|
30
|
+
var services = require("../helpers/services");
|
|
31
|
+
var b = require("../../");
|
|
32
|
+
|
|
33
|
+
var CONTAINER = "blamejs-test-mysql";
|
|
34
|
+
var DB_NAME = "bjfs_test";
|
|
35
|
+
// ANSI_QUOTES is NOT set: the framework's MySQL DDL emits backtick-quoted
|
|
36
|
+
// identifiers, which parse in either sql_mode — this proves the backtick
|
|
37
|
+
// path, not a double-quote-via-ANSI_QUOTES workaround.
|
|
38
|
+
var _results = [];
|
|
39
|
+
function soft(label, cond) {
|
|
40
|
+
_results.push({ label: label, ok: !!cond });
|
|
41
|
+
console.log((cond ? " ok " : " FAIL ") + label);
|
|
42
|
+
return !!cond;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---- one-shot docker-exec mysql ----
|
|
46
|
+
// SQL on stdin (never argv). Each call is a fresh connection — faithful to
|
|
47
|
+
// a pooled driver where each acquire is a clean session. stderr captured so
|
|
48
|
+
// a MySQL error surfaces with its 4-digit code.
|
|
49
|
+
function _mysqlExec(sql, opts) {
|
|
50
|
+
opts = opts || {};
|
|
51
|
+
var dbArgs = opts.noDb ? [] : [DB_NAME];
|
|
52
|
+
var args = ["exec", "-i", CONTAINER, "mysql", "-uroot", "-pblamejs_test_root",
|
|
53
|
+
"--batch", "--raw"].concat(dbArgs);
|
|
54
|
+
try {
|
|
55
|
+
var out = execFileSync("docker", args,
|
|
56
|
+
{ input: sql + "\n", stdio: ["pipe", "pipe", "pipe"], maxBuffer: 64 * 1024 * 1024 });
|
|
57
|
+
return { ok: true, out: out.toString("utf8") };
|
|
58
|
+
} catch (e) {
|
|
59
|
+
var stderr = e.stderr ? e.stderr.toString("utf8") : "";
|
|
60
|
+
return { ok: false, out: (e.stdout ? e.stdout.toString("utf8") : ""), err: stderr || (e.message || String(e)) };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// One-shot for setup / out-of-band assertions; throws on error.
|
|
65
|
+
function _mysql(sql, opts) {
|
|
66
|
+
var r = _mysqlExec(sql, opts);
|
|
67
|
+
if (!r.ok) throw new Error("mysql setup failed for [" + sql.slice(0, 120) + "]: " + _clean(r.err));
|
|
68
|
+
return r.out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function _clean(s) {
|
|
72
|
+
return String(s || "").split(/\r?\n/)
|
|
73
|
+
.filter(function (l) { return l && l.indexOf("World-writable") === -1 && l.indexOf("Using a password") === -1; })
|
|
74
|
+
.join(" ").slice(0, 220);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// externalDb-shaped driver over the one-shot exec. connect/close are no-ops
|
|
78
|
+
// (each query opens its own connection); query inlines `$N` / `?` params and
|
|
79
|
+
// returns { rows, rowCount }.
|
|
80
|
+
function _makeDockerMysqlDriver() {
|
|
81
|
+
return {
|
|
82
|
+
connect: function () { return Promise.resolve({}); },
|
|
83
|
+
query: function (_client, sql, params) {
|
|
84
|
+
var r = _mysqlExec(_bindParams(sql, params || []));
|
|
85
|
+
if (!r.ok) {
|
|
86
|
+
var em = /ERROR\s+(\d+)[^:]*:\s*(.*)/.exec(r.err);
|
|
87
|
+
var err = new Error("MySQL " + (em ? em[1] + ": " + em[2] : _clean(r.err)));
|
|
88
|
+
if (em) err.code = em[1];
|
|
89
|
+
return Promise.reject(err);
|
|
90
|
+
}
|
|
91
|
+
return Promise.resolve({ rows: _parseRows(r.out), rowCount: 0 });
|
|
92
|
+
},
|
|
93
|
+
close: function () { return Promise.resolve(); },
|
|
94
|
+
dialect: "mysql",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function _parseRows(out) {
|
|
99
|
+
var lines = out.split(/\r?\n/).filter(function (l) {
|
|
100
|
+
return l.length > 0 && l.indexOf("World-writable") === -1 && l.indexOf("Using a password") === -1;
|
|
101
|
+
});
|
|
102
|
+
if (lines.length === 0) return [];
|
|
103
|
+
var headers = lines[0].split("\t");
|
|
104
|
+
var rows = [];
|
|
105
|
+
for (var i = 1; i < lines.length; i++) {
|
|
106
|
+
var cells = lines[i].split("\t");
|
|
107
|
+
var row = {};
|
|
108
|
+
for (var c = 0; c < headers.length; c++) {
|
|
109
|
+
var cell = cells[c];
|
|
110
|
+
row[headers[c]] = (cell === "NULL" || cell === undefined) ? null : cell;
|
|
111
|
+
}
|
|
112
|
+
rows.push(row);
|
|
113
|
+
}
|
|
114
|
+
return rows;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function _bindParams(sql, params) {
|
|
118
|
+
var i = 0;
|
|
119
|
+
return sql.replace(/\$(\d+)|\?/g, function (m) {
|
|
120
|
+
var idx = m.charAt(0) === "$" ? Number(m.slice(1)) - 1 : i++;
|
|
121
|
+
var v = params[idx];
|
|
122
|
+
if (v === null || v === undefined) return "NULL";
|
|
123
|
+
if (Buffer.isBuffer(v)) return "0x" + (v.length ? v.toString("hex") : "00");
|
|
124
|
+
if (typeof v === "number") return String(v);
|
|
125
|
+
if (typeof v === "boolean") return v ? "1" : "0";
|
|
126
|
+
return "'" + String(v).replace(/\\/g, "\\\\").replace(/'/g, "''") + "'";
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Every framework table ensureSchema creates, with a minimal valid row to
|
|
131
|
+
// insert + read back so the per-table column shapes (BIGINT / LONGBLOB /
|
|
132
|
+
// VARCHAR-key) are exercised end-to-end. Buffers exercise LONGBLOB; large
|
|
133
|
+
// ms-epoch numbers exercise BIGINT (a 32-bit INT would overflow).
|
|
134
|
+
var BIG_MS = 1893456000000; // 2030-01-01, well beyond 32-bit INT range
|
|
135
|
+
var NONCE = Buffer.from("0123456789abcdef0123456789abcdef", "hex");
|
|
136
|
+
var FRAMEWORK_TABLES = [
|
|
137
|
+
"_blamejs_audit_log", "_blamejs_consent_log", "_blamejs_audit_checkpoints",
|
|
138
|
+
"_blamejs_audit_tip", "_blamejs_consent_tip", "_blamejs_audit_purge_anchor",
|
|
139
|
+
"_blamejs_scheduler_ticks", "_blamejs_rate_limit_counters",
|
|
140
|
+
"_blamejs_pubsub_messages", "_blamejs_api_encrypt_nonces", "_blamejs_api_keys",
|
|
141
|
+
"_blamejs_sessions", "_blamejs_jobs", "_blamejs_cache", "_blamejs_cache_tags",
|
|
142
|
+
"_blamejs_seeders", "_blamejs_seeders_lock", "_blamejs_break_glass_policies",
|
|
143
|
+
"_blamejs_break_glass_grants",
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
async function run() {
|
|
147
|
+
var mysqlSvc = await services.requireService("mysql");
|
|
148
|
+
if (!mysqlSvc.ok) throw new Error("mysql unreachable: " + mysqlSvc.reason);
|
|
149
|
+
|
|
150
|
+
_mysql("CREATE DATABASE IF NOT EXISTS " + DB_NAME + ";", { noDb: true });
|
|
151
|
+
_mysql(FRAMEWORK_TABLES.map(function (t) { return "DROP TABLE IF EXISTS `" + t + "`;"; }).join("\n"));
|
|
152
|
+
|
|
153
|
+
var driver = _makeDockerMysqlDriver();
|
|
154
|
+
b.externalDb._resetForTest();
|
|
155
|
+
b.externalDb.init({
|
|
156
|
+
backends: {
|
|
157
|
+
ops: { connect: driver.connect, query: driver.query, close: driver.close, dialect: "mysql" },
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
// ---- ensureSchema creates EVERY framework table on real MySQL ----
|
|
163
|
+
var report = await b.frameworkSchema.ensureSchema({ externalDbBackend: "ops", dialect: "mysql" });
|
|
164
|
+
soft("ensureSchema(mysql) returned the created-table report",
|
|
165
|
+
report && Array.isArray(report.tables) && report.tables.length === FRAMEWORK_TABLES.length);
|
|
166
|
+
|
|
167
|
+
var present = _mysql(
|
|
168
|
+
"SELECT count(*) AS n FROM information_schema.tables WHERE table_schema='" + DB_NAME +
|
|
169
|
+
"' AND table_name LIKE '_blamejs_%';");
|
|
170
|
+
var createdCount = Number((_parseRows(present)[0] || {}).n);
|
|
171
|
+
soft("ensureSchema(mysql) materialized all " + FRAMEWORK_TABLES.length +
|
|
172
|
+
" framework tables on the server (got " + createdCount + ")",
|
|
173
|
+
createdCount === FRAMEWORK_TABLES.length);
|
|
174
|
+
|
|
175
|
+
// Confirm a representative key column is VARCHAR (NOT TEXT) — the bug
|
|
176
|
+
// #97 fix (MySQL refuses TEXT in a key). audit_log._id is the PK.
|
|
177
|
+
var colType = _mysql(
|
|
178
|
+
"SELECT DATA_TYPE AS dt FROM information_schema.columns WHERE table_schema='" + DB_NAME +
|
|
179
|
+
"' AND table_name='_blamejs_audit_log' AND column_name='_id';");
|
|
180
|
+
soft("ensureSchema(mysql): a PRIMARY-KEY TEXT column is VARCHAR, not TEXT (key-length fix)",
|
|
181
|
+
/varchar/i.test((_parseRows(colType)[0] || {}).dt || ""));
|
|
182
|
+
// ms-epoch column is BIGINT (not a 32-bit INT).
|
|
183
|
+
var intType = _mysql(
|
|
184
|
+
"SELECT DATA_TYPE AS dt FROM information_schema.columns WHERE table_schema='" + DB_NAME +
|
|
185
|
+
"' AND table_name='_blamejs_audit_log' AND column_name='recordedAt';");
|
|
186
|
+
soft("ensureSchema(mysql): a ms-epoch column is BIGINT (no 32-bit overflow)",
|
|
187
|
+
/bigint/i.test((_parseRows(intType)[0] || {}).dt || ""));
|
|
188
|
+
|
|
189
|
+
// ---- insert + read a row through each table ----
|
|
190
|
+
await _roundTripAuditLog();
|
|
191
|
+
await _roundTripConsentLog();
|
|
192
|
+
await _roundTripCheckpoints();
|
|
193
|
+
await _roundTripSingleRow("_blamejs_audit_tip", { scope: "audit", atMonotonicCounter: 1, rowHash: "h", signedAt: "now", fencingToken: 0 }, "scope", "audit");
|
|
194
|
+
await _roundTripSingleRow("_blamejs_consent_tip", { scope: "consent", atMonotonicCounter: 1, rowHash: "h", signedAt: "now", fencingToken: 0 }, "scope", "consent");
|
|
195
|
+
await _roundTripSingleRow("_blamejs_audit_purge_anchor", { scope: "audit", lastPurgedCounter: 1, lastPurgedRowHash: "h", archiveBundleId: "b1", purgedAt: BIG_MS }, "scope", "audit");
|
|
196
|
+
await _roundTripSingleRow("_blamejs_scheduler_ticks", { tickKey: "job:1", name: "job", scheduledAtUnix: BIG_MS, claimedAtUnix: BIG_MS, claimedBy: "node-1" }, "tickKey", "job:1");
|
|
197
|
+
await _roundTripSingleRow("_blamejs_rate_limit_counters", { key: "ip:1.2.3.4", windowStart: BIG_MS, count: 7 }, "key", "ip:1.2.3.4");
|
|
198
|
+
await _roundTripPubsub();
|
|
199
|
+
await _roundTripSingleRow("_blamejs_api_encrypt_nonces", { nonceHash: "nh-1", expireAt: BIG_MS }, "nonceHash", "nh-1");
|
|
200
|
+
await _roundTripApiKeys();
|
|
201
|
+
await _roundTripSessions();
|
|
202
|
+
await _roundTripJobs();
|
|
203
|
+
await _roundTripSingleRow("_blamejs_cache", { cacheKey: "ns:k1", valueJson: "{\"a\":1}", expiresAt: BIG_MS, updatedAt: BIG_MS }, "cacheKey", "ns:k1");
|
|
204
|
+
await _roundTripSingleRow("_blamejs_cache_tags", { cacheKey: "ns:k1", tag: "t1" }, "cacheKey", "ns:k1");
|
|
205
|
+
await _roundTripSingleRow("_blamejs_seeders", { env: "dev", name: "0001-seed", description: "d", appliedAt: "now", rerunnable: 0 }, "name", "0001-seed");
|
|
206
|
+
await _roundTripSingleRow("_blamejs_seeders_lock", { scope: "lock", lockedAt: BIG_MS, lockedBy: "node-1" }, "scope", "lock");
|
|
207
|
+
await _roundTripBreakGlassPolicies();
|
|
208
|
+
await _roundTripBreakGlassGrants();
|
|
209
|
+
|
|
210
|
+
// ---- WORM trigger blocks DELETE/UPDATE on an append-only table ----
|
|
211
|
+
await _wormBlocks();
|
|
212
|
+
} finally {
|
|
213
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
214
|
+
b.externalDb._resetForTest();
|
|
215
|
+
_mysql(FRAMEWORK_TABLES.map(function (t) { return "DROP TABLE IF EXISTS `" + t + "`;"; }).join("\n"));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
var failed = _results.filter(function (r) { return !r.ok; });
|
|
219
|
+
console.log("\n[framework-schema-mysql] " + (_results.length - failed.length) + "/" +
|
|
220
|
+
_results.length + " checks passed");
|
|
221
|
+
if (failed.length) failed.forEach(function (r) { console.log(" - " + r.label); });
|
|
222
|
+
for (var i = 0; i < _results.length; i++) check(_results[i].label, _results[i].ok);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Insert a row via externalDb then read it back; assert the key column
|
|
226
|
+
// round-trips. Generic single-PK helper.
|
|
227
|
+
async function _roundTripSingleRow(table, row, keyCol, keyVal) {
|
|
228
|
+
var cols = Object.keys(row);
|
|
229
|
+
var placeholders = cols.map(function () { return "?"; }).join(", ");
|
|
230
|
+
var quotedCols = cols.map(function (c) { return "`" + c + "`"; }).join(", ");
|
|
231
|
+
var params = cols.map(function (c) { return row[c]; });
|
|
232
|
+
await b.externalDb.query(
|
|
233
|
+
"INSERT INTO `" + table + "` (" + quotedCols + ") VALUES (" + placeholders + ")",
|
|
234
|
+
params, { backend: "ops", rowResidencyTag: "unrestricted" });
|
|
235
|
+
var res = await b.externalDb.query(
|
|
236
|
+
"SELECT `" + keyCol + "` AS k FROM `" + table + "` WHERE `" + keyCol + "` = ?",
|
|
237
|
+
[keyVal], { backend: "ops" });
|
|
238
|
+
soft(table + ": insert + read a row round-trips through real MySQL",
|
|
239
|
+
res && res.rows && res.rows.length === 1 && res.rows[0].k === keyVal);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function _roundTripAuditLog() {
|
|
243
|
+
await b.externalDb.query(
|
|
244
|
+
"INSERT INTO `_blamejs_audit_log` (`_id`,`recordedAt`,`monotonicCounter`,`action`,`outcome`,`prevHash`,`rowHash`,`nonce`,`fencingToken`) " +
|
|
245
|
+
"VALUES (?,?,?,?,?,?,?,?,?)",
|
|
246
|
+
["a-1", BIG_MS, 1, "login", "success", "p0", "r1", NONCE, 0],
|
|
247
|
+
{ backend: "ops", rowResidencyTag: "unrestricted" });
|
|
248
|
+
var res = await b.externalDb.query(
|
|
249
|
+
"SELECT `_id` AS k, `recordedAt` AS ts FROM `_blamejs_audit_log` WHERE `_id` = ?",
|
|
250
|
+
["a-1"], { backend: "ops" });
|
|
251
|
+
var row = res && res.rows && res.rows[0];
|
|
252
|
+
soft("_blamejs_audit_log: row round-trips (LONGBLOB nonce + BIGINT recordedAt)",
|
|
253
|
+
row && row.k === "a-1" && String(row.ts) === String(BIG_MS));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function _roundTripConsentLog() {
|
|
257
|
+
await b.externalDb.query(
|
|
258
|
+
"INSERT INTO `_blamejs_consent_log` (`_id`,`recordedAt`,`monotonicCounter`,`subjectId`,`subjectIdHash`,`purpose`,`lawfulBasis`,`action`,`channel`,`prevHash`,`rowHash`,`nonce`,`fencingToken`) " +
|
|
259
|
+
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
|
260
|
+
["c-1", BIG_MS, 1, "subj", "subjHash", "marketing", "consent", "grant", "web", "p0", "r1", NONCE, 0],
|
|
261
|
+
{ backend: "ops", rowResidencyTag: "unrestricted" });
|
|
262
|
+
var res = await b.externalDb.query("SELECT `_id` AS k FROM `_blamejs_consent_log` WHERE `_id` = ?", ["c-1"], { backend: "ops" });
|
|
263
|
+
soft("_blamejs_consent_log: row round-trips", res && res.rows && res.rows[0] && res.rows[0].k === "c-1");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function _roundTripCheckpoints() {
|
|
267
|
+
await b.externalDb.query(
|
|
268
|
+
"INSERT INTO `_blamejs_audit_checkpoints` (`_id`,`createdAt`,`atMonotonicCounter`,`atRowHash`,`signature`,`publicKeyFingerprint`,`fencingToken`) VALUES (?,?,?,?,?,?,?)",
|
|
269
|
+
["chk-1", BIG_MS, 5, "r5", NONCE, "fp-1", 0],
|
|
270
|
+
{ backend: "ops", rowResidencyTag: "unrestricted" });
|
|
271
|
+
var res = await b.externalDb.query("SELECT `_id` AS k FROM `_blamejs_audit_checkpoints` WHERE `_id` = ?", ["chk-1"], { backend: "ops" });
|
|
272
|
+
soft("_blamejs_audit_checkpoints: row round-trips (LONGBLOB signature)", res && res.rows && res.rows[0] && res.rows[0].k === "chk-1");
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function _roundTripPubsub() {
|
|
276
|
+
// id is BIGINT AUTO_INCREMENT — omit it so the engine assigns one.
|
|
277
|
+
await b.externalDb.query(
|
|
278
|
+
"INSERT INTO `_blamejs_pubsub_messages` (`topic`,`payload`,`publishedAt`,`publishedBy`) VALUES (?,?,?,?)",
|
|
279
|
+
["chan", "{\"x\":1}", BIG_MS, "node-1"], { backend: "ops", rowResidencyTag: "unrestricted" });
|
|
280
|
+
var res = await b.externalDb.query("SELECT `id` AS id FROM `_blamejs_pubsub_messages` WHERE `topic` = ?", ["chan"], { backend: "ops" });
|
|
281
|
+
soft("_blamejs_pubsub_messages: AUTO_INCREMENT id assigned + row round-trips",
|
|
282
|
+
res && res.rows && res.rows[0] && Number(res.rows[0].id) >= 1);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function _roundTripApiKeys() {
|
|
286
|
+
await b.externalDb.query(
|
|
287
|
+
"INSERT INTO `_blamejs_api_keys` (`id`,`namespace`,`ownerId`,`ownerIdHash`,`secretHash`,`createdAt`,`prefix`) VALUES (?,?,?,?,?,?,?)",
|
|
288
|
+
["ns:idhex", "ns", "owner", "ownerHash", "secretHash", BIG_MS, "pfx"],
|
|
289
|
+
{ backend: "ops", rowResidencyTag: "unrestricted" });
|
|
290
|
+
var res = await b.externalDb.query("SELECT `id` AS k FROM `_blamejs_api_keys` WHERE `id` = ?", ["ns:idhex"], { backend: "ops" });
|
|
291
|
+
soft("_blamejs_api_keys: row round-trips", res && res.rows && res.rows[0] && res.rows[0].k === "ns:idhex");
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async function _roundTripSessions() {
|
|
295
|
+
await b.externalDb.query(
|
|
296
|
+
"INSERT INTO `_blamejs_sessions` (`sidHash`,`userId`,`userIdHash`,`createdAt`,`expiresAt`,`lastActivity`) VALUES (?,?,?,?,?,?)",
|
|
297
|
+
["sid-1", "uid-sealed", "uidHash", BIG_MS, BIG_MS, BIG_MS],
|
|
298
|
+
{ backend: "ops", rowResidencyTag: "unrestricted" });
|
|
299
|
+
var res = await b.externalDb.query("SELECT `sidHash` AS k FROM `_blamejs_sessions` WHERE `sidHash` = ?", ["sid-1"], { backend: "ops" });
|
|
300
|
+
soft("_blamejs_sessions: row round-trips", res && res.rows && res.rows[0] && res.rows[0].k === "sid-1");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function _roundTripJobs() {
|
|
304
|
+
await b.externalDb.query(
|
|
305
|
+
"INSERT INTO `_blamejs_jobs` (`_id`,`queueName`,`status`,`enqueuedAt`,`availableAt`) VALUES (?,?,?,?,?)",
|
|
306
|
+
["job-1", "q1", "pending", BIG_MS, BIG_MS],
|
|
307
|
+
{ backend: "ops", rowResidencyTag: "unrestricted" });
|
|
308
|
+
var res = await b.externalDb.query("SELECT `_id` AS k FROM `_blamejs_jobs` WHERE `_id` = ?", ["job-1"], { backend: "ops" });
|
|
309
|
+
soft("_blamejs_jobs: row round-trips", res && res.rows && res.rows[0] && res.rows[0].k === "job-1");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function _roundTripBreakGlassPolicies() {
|
|
313
|
+
await b.externalDb.query(
|
|
314
|
+
"INSERT INTO `_blamejs_break_glass_policies` (`tableName`,`columnsJson`,`factorsJson`,`grantTtlMs`,`updatedAt`) VALUES (?,?,?,?,?)",
|
|
315
|
+
["secret_table", "[\"ssn\"]", "[\"webauthn\"]", BIG_MS, BIG_MS],
|
|
316
|
+
{ backend: "ops", rowResidencyTag: "unrestricted" });
|
|
317
|
+
var res = await b.externalDb.query("SELECT `tableName` AS k FROM `_blamejs_break_glass_policies` WHERE `tableName` = ?", ["secret_table"], { backend: "ops" });
|
|
318
|
+
soft("_blamejs_break_glass_policies: row round-trips", res && res.rows && res.rows[0] && res.rows[0].k === "secret_table");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function _roundTripBreakGlassGrants() {
|
|
322
|
+
await b.externalDb.query(
|
|
323
|
+
"INSERT INTO `_blamejs_break_glass_grants` (`_id`,`issuedToActorId`,`issuedToActorHash`,`factorType`,`scopeTable`,`scopeColumnsJson`,`issuedAt`,`expiresAt`,`maxRowsPerGrant`) VALUES (?,?,?,?,?,?,?,?,?)",
|
|
324
|
+
["grant-1", "actor", "actorHash", "webauthn", "secret_table", "[\"ssn\"]", BIG_MS, BIG_MS, 1],
|
|
325
|
+
{ backend: "ops", rowResidencyTag: "unrestricted" });
|
|
326
|
+
var res = await b.externalDb.query("SELECT `_id` AS k FROM `_blamejs_break_glass_grants` WHERE `_id` = ?", ["grant-1"], { backend: "ops" });
|
|
327
|
+
soft("_blamejs_break_glass_grants: row round-trips", res && res.rows && res.rows[0] && res.rows[0].k === "grant-1");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// The append-only WORM triggers must block DELETE + UPDATE on the audit
|
|
331
|
+
// tables. ensureSchema installs them via SIGNAL SQLSTATE '45000'.
|
|
332
|
+
async function _wormBlocks() {
|
|
333
|
+
var delBlocked = false;
|
|
334
|
+
try {
|
|
335
|
+
await b.externalDb.query("DELETE FROM `_blamejs_audit_log` WHERE `_id` = ?", ["a-1"], { backend: "ops" });
|
|
336
|
+
} catch (_e) { delBlocked = true; }
|
|
337
|
+
soft("_blamejs_audit_log: WORM trigger blocks DELETE (append-only)", delBlocked);
|
|
338
|
+
|
|
339
|
+
var updBlocked = false;
|
|
340
|
+
try {
|
|
341
|
+
await b.externalDb.query("UPDATE `_blamejs_audit_log` SET `action` = ? WHERE `_id` = ?", ["tampered", "a-1"], { backend: "ops" });
|
|
342
|
+
} catch (_e) { updBlocked = true; }
|
|
343
|
+
soft("_blamejs_audit_log: WORM trigger blocks UPDATE (append-only)", updBlocked);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
module.exports = { run: run };
|
|
347
|
+
|
|
348
|
+
if (require.main === module) {
|
|
349
|
+
run().then(
|
|
350
|
+
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); process.exit(0); },
|
|
351
|
+
function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
|
|
352
|
+
);
|
|
353
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Live CloudWatch Logs sink test — exercises lib/log-stream-cloudwatch.js
|
|
4
|
+
* against LocalStack's CloudWatch Logs API (Logs_20140328) over TLS.
|
|
5
|
+
*
|
|
6
|
+
* What this proves (real, end-to-end):
|
|
7
|
+
* - The sink's autoCreate handshake issues CreateLogGroup ->
|
|
8
|
+
* CreateLogStream before the first PutLogEvents, and treats an
|
|
9
|
+
* already-existing group/stream as success.
|
|
10
|
+
* - The PutLogEvents wire shape is correct: X-Amz-Target
|
|
11
|
+
* Logs_20140328.PutLogEvents, Content-Type application/x-amz-json-1.1,
|
|
12
|
+
* a { logGroupName, logStreamName, logEvents:[{timestamp,message}] }
|
|
13
|
+
* body, events sorted ascending by timestamp.
|
|
14
|
+
* - The endpoint override (cfg.endpoint) is honoured — requests reach
|
|
15
|
+
* 127.0.0.1:4566, not logs.<region>.amazonaws.com.
|
|
16
|
+
* - The sequence-token handshake: the framework picks up
|
|
17
|
+
* nextSequenceToken from each PutLogEvents response and carries it
|
|
18
|
+
* forward (stats().sequenceToken advances).
|
|
19
|
+
* - close()/shutdown() drains buffered records to the wire — the
|
|
20
|
+
* "records queued just before shutdown reach the wire" contract
|
|
21
|
+
* b.logStream advertises.
|
|
22
|
+
*
|
|
23
|
+
* Read-back is a SigV4-signed GetLogEvents / DescribeLogStreams built
|
|
24
|
+
* with the framework's own signer (service "logs"), so the events the
|
|
25
|
+
* sink delivered are confirmed present in CloudWatch with the expected
|
|
26
|
+
* message content + ordering.
|
|
27
|
+
*
|
|
28
|
+
* Scope honesty — what this does NOT prove:
|
|
29
|
+
* LocalStack accepts the test credentials and does NOT verify the
|
|
30
|
+
* SigV4 signature. So this proves request SHAPE, the
|
|
31
|
+
* create-group -> create-stream -> put-events sequence, sequence-token
|
|
32
|
+
* handling, endpoint-override honouring, close-time drain, and
|
|
33
|
+
* read-back content/ordering — NOT signature correctness.
|
|
34
|
+
* (Signature correctness against a server that
|
|
35
|
+
* DOES verify SigV4 is covered by object-store-sigv4.test.js against
|
|
36
|
+
* MinIO.)
|
|
37
|
+
*
|
|
38
|
+
* No security bypass: TLS to LocalStack trusts the test CA via
|
|
39
|
+
* NODE_EXTRA_CA_CERTS (exported by scripts/test-integration.js).
|
|
40
|
+
* rejectUnauthorized stays on; the sink's allowInternal:true only
|
|
41
|
+
* permits the loopback host, it does not disable verification.
|
|
42
|
+
*/
|
|
43
|
+
var nodeCrypto = require("node:crypto");
|
|
44
|
+
var helpers = require("../helpers");
|
|
45
|
+
var check = helpers.check;
|
|
46
|
+
var services = require("../helpers/services");
|
|
47
|
+
|
|
48
|
+
var cloudwatchProto = require("../../lib/log-stream-cloudwatch");
|
|
49
|
+
var sigv4 = require("../../lib/object-store/sigv4");
|
|
50
|
+
var httpClient = require("../../lib/http-client");
|
|
51
|
+
var safeUrl = require("../../lib/safe-url");
|
|
52
|
+
|
|
53
|
+
var REGION = "us-east-1";
|
|
54
|
+
var ACCESS = "test";
|
|
55
|
+
var SECRET = "test";
|
|
56
|
+
|
|
57
|
+
// ---- SigV4-signed CloudWatch Logs read-back helper ----
|
|
58
|
+
// Builds + signs a Logs_20140328 request with the framework's own
|
|
59
|
+
// signer (service "logs") and posts it through httpClient. Returns the
|
|
60
|
+
// parsed JSON body. Rejects (throws) on a non-2xx so the caller sees
|
|
61
|
+
// the AWS exception text.
|
|
62
|
+
function _logsCall(endpoint, target, payload) {
|
|
63
|
+
var body = Buffer.from(JSON.stringify(payload), "utf8");
|
|
64
|
+
var payloadHash = nodeCrypto.createHash("sha256").update(body).digest("hex");
|
|
65
|
+
var signed = sigv4.signRequest({
|
|
66
|
+
method: "POST",
|
|
67
|
+
url: endpoint,
|
|
68
|
+
headers: {
|
|
69
|
+
"Content-Type": "application/x-amz-json-1.1",
|
|
70
|
+
"X-Amz-Target": target,
|
|
71
|
+
},
|
|
72
|
+
payloadHash: payloadHash,
|
|
73
|
+
region: REGION,
|
|
74
|
+
service: "logs",
|
|
75
|
+
accessKeyId: ACCESS,
|
|
76
|
+
secretAccessKey: SECRET,
|
|
77
|
+
allowedProtocols: safeUrl.ALLOW_HTTP_TLS,
|
|
78
|
+
});
|
|
79
|
+
return httpClient.request({
|
|
80
|
+
method: "POST",
|
|
81
|
+
url: endpoint,
|
|
82
|
+
headers: signed.headers,
|
|
83
|
+
body: body,
|
|
84
|
+
allowInternal: true,
|
|
85
|
+
}).then(function (res) {
|
|
86
|
+
return JSON.parse(res.body.toString("utf8"));
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function _readEvents(endpoint, logGroupName, logStreamName) {
|
|
91
|
+
return _logsCall(endpoint, "Logs_20140328.GetLogEvents", {
|
|
92
|
+
logGroupName: logGroupName,
|
|
93
|
+
logStreamName: logStreamName,
|
|
94
|
+
startFromHead: true,
|
|
95
|
+
}).then(function (parsed) {
|
|
96
|
+
return (parsed && parsed.events) || [];
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function run() {
|
|
101
|
+
var ls = await services.requireService("localstack");
|
|
102
|
+
if (!ls.ok) throw new Error("localstack unreachable: " + ls.reason);
|
|
103
|
+
|
|
104
|
+
var endpoint = services.URLS.localstack; // https://127.0.0.1:4566
|
|
105
|
+
|
|
106
|
+
// ---- 1) endpoint override is honoured ----
|
|
107
|
+
// _resolveEndpoint must reflect cfg.endpoint, not the AWS regional host.
|
|
108
|
+
var resolved = cloudwatchProto._resolveEndpoint({
|
|
109
|
+
endpoint: endpoint, region: REGION,
|
|
110
|
+
});
|
|
111
|
+
check("endpoint override honoured (not logs.<region>.amazonaws.com)",
|
|
112
|
+
resolved === endpoint.replace(/\/+$/, "") + "/" &&
|
|
113
|
+
resolved.indexOf("127.0.0.1:4566") !== -1);
|
|
114
|
+
|
|
115
|
+
// ---- 2) autoCreate is load-bearing: PutLogEvents to a never-created
|
|
116
|
+
// group/stream is a permanent ResourceNotFoundException. (Establishes
|
|
117
|
+
// that the create handshake below is doing real work, not a no-op.) ----
|
|
118
|
+
var stamp = Date.now() + "-" + Math.floor(Math.random() * 1e6);
|
|
119
|
+
var missingGroup = "blamejs-cw-missing-" + stamp;
|
|
120
|
+
var missingStream = "missing-stream-" + stamp;
|
|
121
|
+
var rnfErr = null;
|
|
122
|
+
try {
|
|
123
|
+
await _logsCall(endpoint, "Logs_20140328.PutLogEvents", {
|
|
124
|
+
logGroupName: missingGroup,
|
|
125
|
+
logStreamName: missingStream,
|
|
126
|
+
logEvents: [{ timestamp: Date.now(), message: "should-not-land" }],
|
|
127
|
+
});
|
|
128
|
+
} catch (e) { rnfErr = e; }
|
|
129
|
+
check("PutLogEvents to an uncreated group fails (ResourceNotFoundException) — autoCreate is load-bearing",
|
|
130
|
+
rnfErr && /ResourceNotFoundException/.test(String(rnfErr.message || "")));
|
|
131
|
+
check("classifier treats ResourceNotFoundException as permanent",
|
|
132
|
+
cloudwatchProto._isPermanentAwsError(new Error("ResourceNotFoundException: no such group")) === true);
|
|
133
|
+
|
|
134
|
+
// ---- 3) sink delivers events via autoCreate + close() drain + read-back ----
|
|
135
|
+
// This is the core proof: emit a few records (under batchSize so close()
|
|
136
|
+
// is the thing that has to drain them), close, then read them back out
|
|
137
|
+
// of CloudWatch. Records are pushed out of timestamp order to confirm
|
|
138
|
+
// the sink sorts ascending before PutLogEvents (AWS hard requirement).
|
|
139
|
+
var logGroupName = "blamejs-cw-test-" + stamp;
|
|
140
|
+
var logStreamName = "stream-" + stamp;
|
|
141
|
+
|
|
142
|
+
var drops = [];
|
|
143
|
+
var sink = cloudwatchProto.create({
|
|
144
|
+
region: REGION,
|
|
145
|
+
accessKeyId: ACCESS,
|
|
146
|
+
secretAccessKey: SECRET,
|
|
147
|
+
endpoint: endpoint,
|
|
148
|
+
logGroupName: logGroupName,
|
|
149
|
+
logStreamName: logStreamName,
|
|
150
|
+
autoCreate: true, // exercise CreateLogGroup -> CreateLogStream
|
|
151
|
+
allowInternal: true, // permit the loopback host (TLS stays verified)
|
|
152
|
+
allowedProtocols: safeUrl.ALLOW_HTTP_TLS,
|
|
153
|
+
batchSize: 10, // larger than the record count → close() drains
|
|
154
|
+
onDrop: function (d) { drops.push(d); },
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
var base = Date.now();
|
|
158
|
+
var messages = [
|
|
159
|
+
{ ts: base + 30, message: "cw-event-three" },
|
|
160
|
+
{ ts: base + 10, message: "cw-event-one" },
|
|
161
|
+
{ ts: base + 20, message: "cw-event-two" },
|
|
162
|
+
];
|
|
163
|
+
for (var i = 0; i < messages.length; i += 1) {
|
|
164
|
+
var rv = await sink.emit(messages[i]);
|
|
165
|
+
check("emit accepted record '" + messages[i].message + "'", rv && rv.accepted === true);
|
|
166
|
+
}
|
|
167
|
+
// close() MUST drain the buffered records to CloudWatch — this is the
|
|
168
|
+
// documented shutdown contract. (If close() flips closed=true before
|
|
169
|
+
// draining, _flush()'s `while (... && !closed)` loop strands them.)
|
|
170
|
+
await sink.close();
|
|
171
|
+
|
|
172
|
+
check("no drops during delivery", drops.length === 0);
|
|
173
|
+
|
|
174
|
+
// Read the events back out of CloudWatch — the real end-to-end proof.
|
|
175
|
+
var events = await helpers.waitUntil(async function () {
|
|
176
|
+
var ev = await _readEvents(endpoint, logGroupName, logStreamName);
|
|
177
|
+
return ev.length >= 3 ? ev : false;
|
|
178
|
+
}, { timeoutMs: 8000, label: "cloudwatch: GetLogEvents returns the 3 records close() should have drained" });
|
|
179
|
+
|
|
180
|
+
check("read-back: all three events present", events.length === 3);
|
|
181
|
+
|
|
182
|
+
var readMessages = events.map(function (e) { return e.message; });
|
|
183
|
+
check("read-back: 'cw-event-one' present", readMessages.indexOf("cw-event-one") !== -1);
|
|
184
|
+
check("read-back: 'cw-event-two' present", readMessages.indexOf("cw-event-two") !== -1);
|
|
185
|
+
check("read-back: 'cw-event-three' present", readMessages.indexOf("cw-event-three") !== -1);
|
|
186
|
+
|
|
187
|
+
// GetLogEvents with startFromHead returns events oldest-first. The
|
|
188
|
+
// sink sorts by timestamp ascending, so the read-back order must be
|
|
189
|
+
// one -> two -> three regardless of emit order.
|
|
190
|
+
check("read-back: events ordered ascending by timestamp (sink sorted them)",
|
|
191
|
+
readMessages[0] === "cw-event-one" &&
|
|
192
|
+
readMessages[1] === "cw-event-two" &&
|
|
193
|
+
readMessages[2] === "cw-event-three");
|
|
194
|
+
|
|
195
|
+
var st = sink.stats();
|
|
196
|
+
check("after delivery: nothing left queued", st.queued === 0);
|
|
197
|
+
check("sink picked up a sequenceToken from PutLogEvents response",
|
|
198
|
+
typeof st.sequenceToken === "string" && st.sequenceToken.length > 0);
|
|
199
|
+
|
|
200
|
+
// ---- 4) DescribeLogStreams confirms the stream the sink created ----
|
|
201
|
+
var describe = await _logsCall(endpoint, "Logs_20140328.DescribeLogStreams", {
|
|
202
|
+
logGroupName: logGroupName,
|
|
203
|
+
});
|
|
204
|
+
var streamNames = ((describe && describe.logStreams) || []).map(function (s) { return s.logStreamName; });
|
|
205
|
+
check("DescribeLogStreams: autoCreate created the stream",
|
|
206
|
+
streamNames.indexOf(logStreamName) !== -1);
|
|
207
|
+
|
|
208
|
+
// NOTE: a redaction-before-sink leg was intentionally not included. The
|
|
209
|
+
// CloudWatch sink serializes only record.message (record.meta is dropped),
|
|
210
|
+
// and log records are not redacted before egress, so neither a
|
|
211
|
+
// meta-redaction nor a meta-preservation guarantee holds for this sink —
|
|
212
|
+
// asserting one would be vacuous. That structured-field / DLP gap is tracked
|
|
213
|
+
// separately; this file proves the wire shape, autoCreate, sequence-token
|
|
214
|
+
// handling, close-time drain, and read-back.
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = { run: run };
|
|
218
|
+
|
|
219
|
+
if (require.main === module) {
|
|
220
|
+
run().then(
|
|
221
|
+
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); process.exit(0); },
|
|
222
|
+
function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
|
|
223
|
+
);
|
|
224
|
+
}
|