@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
|
@@ -25,6 +25,62 @@ var nodeCrypto = require("node:crypto");
|
|
|
25
25
|
var helpers = require("../helpers");
|
|
26
26
|
var check = helpers.check;
|
|
27
27
|
var b = require("../../");
|
|
28
|
+
var asn1 = require("../../lib/asn1-der");
|
|
29
|
+
var cms = require("../../lib/cms-codec");
|
|
30
|
+
|
|
31
|
+
// The S/MIME signer is pure-PQC (ML-DSA-65). Trust-chain validation in
|
|
32
|
+
// b.mail.crypto.smime.verify (since v0.13.42) binds the chain leaf to the
|
|
33
|
+
// key that actually verified the signature: it refuses unless a cert in
|
|
34
|
+
// SignedData.certificates carries that exact public key. b.mtlsCa issues
|
|
35
|
+
// classical (RSA/EC) leaf certs, whose key can never equal the ML-DSA
|
|
36
|
+
// signer key — so the chain leaf has to be an X.509 cert that embeds the
|
|
37
|
+
// ML-DSA-65 public key in its SubjectPublicKeyInfo. The framework has no
|
|
38
|
+
// ML-DSA X.509 issuer (ML-DSA in X.509 is still draft), so this builds a
|
|
39
|
+
// minimal RFC 5280 cert over the b.asn1Der writer + b.cms ML-DSA OID and
|
|
40
|
+
// signs the TBS with ML-DSA-65. node:crypto.X509Certificate parses it,
|
|
41
|
+
// exports the SPKI, and verifies the ML-DSA signature natively.
|
|
42
|
+
function _x509Name(cn) {
|
|
43
|
+
var atv = asn1.writeSequence([asn1.writeOid("2.5.4.3"), asn1.writeUtf8String(cn)]);
|
|
44
|
+
return asn1.writeSequence([asn1.writeSet([atv])]);
|
|
45
|
+
}
|
|
46
|
+
function _x509GeneralizedTime(d) {
|
|
47
|
+
function pad2(n) { return String(n).length >= 2 ? String(n) : "0" + n; }
|
|
48
|
+
var s = d.getUTCFullYear() +
|
|
49
|
+
pad2(d.getUTCMonth() + 1) + pad2(d.getUTCDate()) +
|
|
50
|
+
pad2(d.getUTCHours()) + pad2(d.getUTCMinutes()) + pad2(d.getUTCSeconds()) + "Z";
|
|
51
|
+
return asn1.writeNode(0x18, Buffer.from(s, "ascii")); // 0x18 = GeneralizedTime
|
|
52
|
+
}
|
|
53
|
+
function _buildMlDsaCert(opts) {
|
|
54
|
+
// opts: { subjectCn, subjectPubKey, issuerCn, issuerSecretKey, serial,
|
|
55
|
+
// notBefore, notAfter }. signatureAlgorithm == subject key alg
|
|
56
|
+
// (ML-DSA-65) for both self-signed (CA) and issued (leaf) shapes.
|
|
57
|
+
var algId = asn1.writeSequence([asn1.writeOid(cms.OID.mldsa65)]);
|
|
58
|
+
var spki = asn1.writeSequence([algId, asn1.writeBitString(Buffer.from(opts.subjectPubKey), 0)]);
|
|
59
|
+
var tbs = asn1.writeSequence([
|
|
60
|
+
asn1.writeContextExplicit(0, asn1.writeInteger(Buffer.from([2]))), // version v3
|
|
61
|
+
asn1.writeInteger(Buffer.from([opts.serial])), // serialNumber
|
|
62
|
+
algId, // signature AlgorithmIdentifier
|
|
63
|
+
_x509Name(opts.issuerCn), // issuer
|
|
64
|
+
asn1.writeSequence([_x509GeneralizedTime(opts.notBefore),
|
|
65
|
+
_x509GeneralizedTime(opts.notAfter)]), // validity
|
|
66
|
+
_x509Name(opts.subjectCn), // subject
|
|
67
|
+
spki, // SubjectPublicKeyInfo
|
|
68
|
+
]);
|
|
69
|
+
var sig = b.pqcSoftware.ml_dsa_65.sign(new Uint8Array(tbs), opts.issuerSecretKey);
|
|
70
|
+
return asn1.writeSequence([tbs, algId, asn1.writeBitString(Buffer.from(sig), 0)]);
|
|
71
|
+
}
|
|
72
|
+
function _derToPem(der) {
|
|
73
|
+
// 64-char line wrap. A regex replace adds a trailing newline when the
|
|
74
|
+
// base64 length is an exact multiple of 64, which (combined with the
|
|
75
|
+
// closing newline) yields a blank line mid-block that OpenSSL's PEM
|
|
76
|
+
// reader treats as end-of-data — silently truncating the cert. Slice
|
|
77
|
+
// explicitly so every line is full except the last.
|
|
78
|
+
var b64 = Buffer.from(der).toString("base64");
|
|
79
|
+
var lines = [];
|
|
80
|
+
for (var i = 0; i < b64.length; i += 64) { lines.push(b64.slice(i, i + 64)); }
|
|
81
|
+
return "-----BEGIN CERTIFICATE-----\n" + lines.join("\n") +
|
|
82
|
+
"\n-----END CERTIFICATE-----\n";
|
|
83
|
+
}
|
|
28
84
|
|
|
29
85
|
async function run() {
|
|
30
86
|
// ---- Spin up an isolated mTLS CA (caKeySealedMode=disabled keeps
|
|
@@ -117,21 +173,46 @@ async function run() {
|
|
|
117
173
|
threw === "mail-crypto/smime/signature-mismatch");
|
|
118
174
|
|
|
119
175
|
// ---- Trust-anchor chain validation — opts.trustAnchorCertsPem.
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
176
|
+
// verify() walks leaf → CA against the operator's trust anchor
|
|
177
|
+
// AND binds the leaf to the key that verified the signature
|
|
178
|
+
// (since v0.13.42): the chain leaf must be the cert carrying
|
|
179
|
+
// signerPublicKey, else the chain-validated result would assert
|
|
180
|
+
// a cert↔signer binding the code never made. The signer is
|
|
181
|
+
// ML-DSA-65, so the chain leaf must embed that ML-DSA key in its
|
|
182
|
+
// SubjectPublicKeyInfo — build a real ML-DSA cert chain (CA →
|
|
183
|
+
// leaf) carrying signerKp.publicKey, with the CA PEM as the
|
|
184
|
+
// trust anchor. The leaf goes into SignedData.certificates so
|
|
185
|
+
// verify() can locate + bind it.
|
|
186
|
+
var caKp = b.pqcSoftware.ml_dsa_65.keygen();
|
|
187
|
+
var nowD = new Date();
|
|
188
|
+
var notBefore = new Date(nowD.getTime() - 60 * 60 * 1000);
|
|
189
|
+
var notAfter = new Date(nowD.getTime() + 7 * 24 * 60 * 60 * 1000);
|
|
190
|
+
var caCertDer = _buildMlDsaCert({
|
|
191
|
+
subjectCn: "smime-pqc-ca.blamejs-test.example",
|
|
192
|
+
subjectPubKey: caKp.publicKey,
|
|
193
|
+
issuerCn: "smime-pqc-ca.blamejs-test.example",
|
|
194
|
+
issuerSecretKey: caKp.secretKey, // self-signed
|
|
195
|
+
serial: 1,
|
|
196
|
+
notBefore: notBefore,
|
|
197
|
+
notAfter: notAfter,
|
|
198
|
+
});
|
|
199
|
+
var pqcLeafCertDer = _buildMlDsaCert({
|
|
200
|
+
subjectCn: "smime-signer.blamejs-test.example",
|
|
201
|
+
subjectPubKey: signerKp.publicKey, // == the verified signer key
|
|
202
|
+
issuerCn: "smime-pqc-ca.blamejs-test.example",
|
|
203
|
+
issuerSecretKey: caKp.secretKey, // issued by the CA
|
|
204
|
+
serial: 2,
|
|
205
|
+
notBefore: notBefore,
|
|
206
|
+
notAfter: notAfter,
|
|
207
|
+
});
|
|
208
|
+
var caCertPem = _derToPem(caCertDer);
|
|
209
|
+
|
|
129
210
|
var signedWithCerts = b.cms.encodeSignedData({
|
|
130
211
|
encapContent: Buffer.from(message, "utf8"),
|
|
131
212
|
digestAlg: "sha3-512",
|
|
132
|
-
certificates: [
|
|
213
|
+
certificates: [pqcLeafCertDer],
|
|
133
214
|
signers: [{
|
|
134
|
-
certificate:
|
|
215
|
+
certificate: pqcLeafCertDer,
|
|
135
216
|
secretKey: signerKp.secretKey,
|
|
136
217
|
sigAlg: "ML-DSA-65",
|
|
137
218
|
}],
|
|
@@ -140,30 +221,74 @@ async function run() {
|
|
|
140
221
|
message: Buffer.from(message, "utf8"),
|
|
141
222
|
signature: signedWithCerts,
|
|
142
223
|
signerPublicKey: signerKp.publicKey,
|
|
143
|
-
trustAnchorCertsPem: [
|
|
224
|
+
trustAnchorCertsPem: [caCertPem],
|
|
144
225
|
});
|
|
145
226
|
check("smime.verify: chain validates against trust anchor",
|
|
146
227
|
verifiedChain && verifiedChain.valid === true &&
|
|
147
228
|
verifiedChain.chainVerified === true);
|
|
148
229
|
|
|
149
230
|
// ---- Untrusted-chain refusal: supply an UNRELATED trust anchor.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
231
|
+
// A second, independent ML-DSA CA did not issue the leaf, so no
|
|
232
|
+
// chain link reaches it.
|
|
233
|
+
var otherCaKp = b.pqcSoftware.ml_dsa_65.keygen();
|
|
234
|
+
var unrelatedCaDer = _buildMlDsaCert({
|
|
235
|
+
subjectCn: "smime-pqc-other-ca.blamejs-test.example",
|
|
236
|
+
subjectPubKey: otherCaKp.publicKey,
|
|
237
|
+
issuerCn: "smime-pqc-other-ca.blamejs-test.example",
|
|
238
|
+
issuerSecretKey: otherCaKp.secretKey,
|
|
239
|
+
serial: 1,
|
|
240
|
+
notBefore: notBefore,
|
|
241
|
+
notAfter: notAfter,
|
|
153
242
|
});
|
|
154
|
-
var unrelatedBundle = await unrelatedCa.initCA();
|
|
155
243
|
threw = null;
|
|
156
244
|
try {
|
|
157
245
|
b.mail.crypto.smime.verify({
|
|
158
246
|
message: Buffer.from(message, "utf8"),
|
|
159
247
|
signature: signedWithCerts,
|
|
160
248
|
signerPublicKey: signerKp.publicKey,
|
|
161
|
-
trustAnchorCertsPem: [
|
|
249
|
+
trustAnchorCertsPem: [_derToPem(unrelatedCaDer)],
|
|
162
250
|
});
|
|
163
251
|
} catch (eC) { threw = eC.code; }
|
|
164
252
|
check("smime.verify: refuses unrelated trust anchor",
|
|
165
253
|
threw === "mail-crypto/smime/untrusted-chain");
|
|
166
254
|
|
|
255
|
+
// ---- Binding refusal: a validly-chained cert for a DIFFERENT key
|
|
256
|
+
// must NOT pass chain validation when the signature was verified
|
|
257
|
+
// under signerKp. Build a leaf carrying an UNRELATED ML-DSA key
|
|
258
|
+
// but properly issued by the trusted CA; verify() must refuse
|
|
259
|
+
// because no cert in the chain carries the verified signer key.
|
|
260
|
+
var strangerKp = b.pqcSoftware.ml_dsa_65.keygen();
|
|
261
|
+
var strangerLeafDer = _buildMlDsaCert({
|
|
262
|
+
subjectCn: "smime-stranger.blamejs-test.example",
|
|
263
|
+
subjectPubKey: strangerKp.publicKey,
|
|
264
|
+
issuerCn: "smime-pqc-ca.blamejs-test.example",
|
|
265
|
+
issuerSecretKey: caKp.secretKey,
|
|
266
|
+
serial: 3,
|
|
267
|
+
notBefore: notBefore,
|
|
268
|
+
notAfter: notAfter,
|
|
269
|
+
});
|
|
270
|
+
var signedWithStrangerCert = b.cms.encodeSignedData({
|
|
271
|
+
encapContent: Buffer.from(message, "utf8"),
|
|
272
|
+
digestAlg: "sha3-512",
|
|
273
|
+
certificates: [strangerLeafDer],
|
|
274
|
+
signers: [{
|
|
275
|
+
certificate: pqcLeafCertDer,
|
|
276
|
+
secretKey: signerKp.secretKey,
|
|
277
|
+
sigAlg: "ML-DSA-65",
|
|
278
|
+
}],
|
|
279
|
+
});
|
|
280
|
+
threw = null;
|
|
281
|
+
try {
|
|
282
|
+
b.mail.crypto.smime.verify({
|
|
283
|
+
message: Buffer.from(message, "utf8"),
|
|
284
|
+
signature: signedWithStrangerCert,
|
|
285
|
+
signerPublicKey: signerKp.publicKey,
|
|
286
|
+
trustAnchorCertsPem: [caCertPem],
|
|
287
|
+
});
|
|
288
|
+
} catch (eB) { threw = eB.code; }
|
|
289
|
+
check("smime.verify: refuses chain leaf bound to a different key",
|
|
290
|
+
threw === "mail-crypto/smime/signer-not-in-chain");
|
|
291
|
+
|
|
167
292
|
// ---- X.509 sanity — confirm node:crypto can parse the leaf and
|
|
168
293
|
// verify its issuer matches the CA subject.
|
|
169
294
|
var leafX509 = new nodeCrypto.X509Certificate(leaf.cert);
|
|
@@ -58,33 +58,48 @@ async function run() {
|
|
|
58
58
|
],
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
//
|
|
61
|
+
// start() registers every target in the status table synchronously,
|
|
62
|
+
// before any probe has run — so a bare `statuses().length >= 3` is
|
|
63
|
+
// satisfied while every entry is still in its initial state with no
|
|
64
|
+
// probe recorded. Wait until each target has actually completed a
|
|
65
|
+
// probe (lastProbeAt set) so the assertions below read real outcomes.
|
|
62
66
|
var statuses = await helpers.waitUntil(function () {
|
|
63
67
|
var s = b.network.heartbeat.statuses();
|
|
64
|
-
var
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
var arr = Array.isArray(s) ? s : Object.keys(s).map(function (k) { return s[k]; });
|
|
69
|
+
if (arr.length < 3) return false;
|
|
70
|
+
return arr.every(function (e) { return e && e.lastProbeAt !== null; }) ? s : false;
|
|
71
|
+
}, { label: "heartbeat: all 3 targets completed a probe" });
|
|
67
72
|
check("statuses: returns at least one entry",
|
|
68
73
|
Array.isArray(statuses) ? statuses.length >= 3 : Object.keys(statuses).length >= 3);
|
|
69
74
|
|
|
75
|
+
// Assert the reported `state` field directly. Matching a keyword
|
|
76
|
+
// against JSON.stringify(status) is meaningless here — the status
|
|
77
|
+
// object carries `name` ("caddy-up" / "caddy-down"), so the blob
|
|
78
|
+
// always contains "up"/"down" regardless of the real probe outcome.
|
|
70
79
|
var caddyUp = b.network.heartbeat.status("caddy-up");
|
|
71
80
|
check("caddy-up: status object returned",
|
|
72
81
|
typeof caddyUp === "object" && caddyUp !== null);
|
|
73
|
-
check("caddy-up: marked as
|
|
74
|
-
|
|
82
|
+
check("caddy-up: marked as healthy (200 from /healthz)",
|
|
83
|
+
caddyUp.state === "healthy");
|
|
75
84
|
|
|
76
85
|
var caddyDown = b.network.heartbeat.status("caddy-down");
|
|
77
86
|
check("caddy-down: status object returned",
|
|
78
87
|
typeof caddyDown === "object" && caddyDown !== null);
|
|
79
|
-
check("caddy-down: marked as down
|
|
80
|
-
|
|
88
|
+
check("caddy-down: marked as down (connection refused)",
|
|
89
|
+
caddyDown.state === "down");
|
|
81
90
|
|
|
82
91
|
var squidTcp = b.network.heartbeat.status("squid-tcp");
|
|
83
92
|
check("squid-tcp: TCP target probe returned status",
|
|
84
93
|
typeof squidTcp === "object" && squidTcp !== null);
|
|
85
|
-
check("squid-tcp: marked as
|
|
86
|
-
|
|
94
|
+
check("squid-tcp: marked as healthy (port 3128 open)",
|
|
95
|
+
squidTcp.state === "healthy");
|
|
87
96
|
|
|
97
|
+
// caddy-down probes a dead port and crosses healthy → down on its
|
|
98
|
+
// first failing probe (threshold 1), which fires onStateChange. Poll
|
|
99
|
+
// rather than read synchronously — the transition lands on the probe
|
|
100
|
+
// callback, which may settle a tick after lastProbeAt is recorded.
|
|
101
|
+
await helpers.waitUntil(function () { return stateChanges.length >= 1; },
|
|
102
|
+
{ label: "heartbeat: onStateChange fired for at least one target" });
|
|
88
103
|
check("onStateChange: fired for at least one target",
|
|
89
104
|
stateChanges.length >= 1);
|
|
90
105
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Live Azure Blob (Shared Key) round-trip against the Azurite emulator in
|
|
4
|
+
* docker-compose.test.yml, over TLS with the test CA (NODE_EXTRA_CA_CERTS set
|
|
5
|
+
* by scripts/test-integration.js — no rejectUnauthorized:false).
|
|
6
|
+
*
|
|
7
|
+
* Azurite is PATH-STYLE: the account is the first URL path segment
|
|
8
|
+
* (https://127.0.0.1:10000/devstoreaccount1/<container>/<blob>), unlike
|
|
9
|
+
* production Azure's host-based <account>.blob.core.windows.net. A successful
|
|
10
|
+
* authenticated round-trip is itself the proof that the Shared-Key canonical
|
|
11
|
+
* resource carries the account exactly once — a doubled account (the prior
|
|
12
|
+
* behavior) returns 403 AuthenticationFailed and put() would throw. Also a
|
|
13
|
+
* deterministic buildStringToSign check that path-style does not double it.
|
|
14
|
+
*/
|
|
15
|
+
var helpers = require("../helpers");
|
|
16
|
+
var check = helpers.check;
|
|
17
|
+
var services = require("../helpers/services");
|
|
18
|
+
var b = require("../../");
|
|
19
|
+
var azureBlob = require("../../lib/object-store/azure-blob");
|
|
20
|
+
|
|
21
|
+
// Azurite's well-known emulator account + key (public, documented).
|
|
22
|
+
var ENDPOINT = "https://127.0.0.1:10000";
|
|
23
|
+
var ACCOUNT = "devstoreaccount1";
|
|
24
|
+
var KEY = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
|
|
25
|
+
|
|
26
|
+
async function run() {
|
|
27
|
+
var az = await services.requireService("azurite");
|
|
28
|
+
if (!az.ok) throw new Error("azurite unreachable: " + az.reason);
|
|
29
|
+
|
|
30
|
+
// --- deterministic: canonical resource is "/" + account + the request's
|
|
31
|
+
// absolute path. For a PATH-STYLE URL (account already the first path
|
|
32
|
+
// segment, as with Azurite) that yields the account twice — the form a
|
|
33
|
+
// path-style server expects (verified live below). For a HOST-BASED URL
|
|
34
|
+
// (account only in the host) it appears once. ---
|
|
35
|
+
var HDRS = { "x-ms-date": "Mon, 01 Jan 2024 00:00:00 GMT", "x-ms-version": azureBlob.DEFAULT_API_VERSION };
|
|
36
|
+
var stsPath = azureBlob.buildStringToSign({
|
|
37
|
+
method: "GET", accountName: ACCOUNT,
|
|
38
|
+
url: new URL(ENDPOINT + "/" + ACCOUNT + "/c1/blob.txt"), headers: HDRS,
|
|
39
|
+
});
|
|
40
|
+
check("path-style canonical resource is the doubled-account form a path-style server expects",
|
|
41
|
+
stsPath.indexOf("/" + ACCOUNT + "/" + ACCOUNT + "/c1/blob.txt") !== -1);
|
|
42
|
+
var stsHost = azureBlob.buildStringToSign({
|
|
43
|
+
method: "GET", accountName: ACCOUNT,
|
|
44
|
+
url: new URL("https://" + ACCOUNT + ".blob.core.windows.net/c1/blob.txt"), headers: HDRS,
|
|
45
|
+
});
|
|
46
|
+
check("host-based canonical resource carries the account exactly once",
|
|
47
|
+
stsHost.indexOf("/" + ACCOUNT + "/c1/blob.txt") !== -1 &&
|
|
48
|
+
stsHost.indexOf("/" + ACCOUNT + "/" + ACCOUNT + "/") === -1);
|
|
49
|
+
|
|
50
|
+
var container = "blamejs-az-" + process.pid;
|
|
51
|
+
var commonCfg = {
|
|
52
|
+
protocol: "azure-blob", accountName: ACCOUNT, accountKey: KEY,
|
|
53
|
+
endpoint: ENDPOINT, allowInternal: true,
|
|
54
|
+
pathStyle: true, // Azurite addresses the account as the first path segment
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// --- container lifecycle (path-style auto-detected from the IP host) ---
|
|
58
|
+
var ops = b.objectStore.bucketOps.create(commonCfg);
|
|
59
|
+
await ops.create(container);
|
|
60
|
+
check("createContainer accepted (signature verified by Azurite)", true);
|
|
61
|
+
var containers = await ops.list();
|
|
62
|
+
check("listContainers includes the new container",
|
|
63
|
+
containers.some(function (c) { return c.name === container; }));
|
|
64
|
+
|
|
65
|
+
// --- blob round-trip ---
|
|
66
|
+
var be = b.objectStore.buildBackend(Object.assign({ container: container }, commonCfg));
|
|
67
|
+
var key = "folder/object.bin";
|
|
68
|
+
var payload = Buffer.from("azure-roundtrip-" + process.pid + "-" + "Z".repeat(64), "utf8");
|
|
69
|
+
|
|
70
|
+
var putRes = await be.put(key, payload, { contentType: "application/octet-stream" });
|
|
71
|
+
check("put returns the byte length", putRes.size === payload.length);
|
|
72
|
+
|
|
73
|
+
var got = await be.get(key);
|
|
74
|
+
check("get returns byte-identical content", Buffer.isBuffer(got) && Buffer.compare(got, payload) === 0);
|
|
75
|
+
|
|
76
|
+
var h = await be.head(key);
|
|
77
|
+
check("head reports the correct size", h.size === payload.length);
|
|
78
|
+
|
|
79
|
+
var listed = await be.list("");
|
|
80
|
+
check("list surfaces the uploaded key",
|
|
81
|
+
listed.items.some(function (it) { return it.key === key; }));
|
|
82
|
+
|
|
83
|
+
var deleted = await be.delete(key);
|
|
84
|
+
check("delete returns true for an existing blob", deleted === true);
|
|
85
|
+
|
|
86
|
+
var afterDelete = await be.list("");
|
|
87
|
+
check("list no longer surfaces the deleted key",
|
|
88
|
+
!afterDelete.items.some(function (it) { return it.key === key; }));
|
|
89
|
+
|
|
90
|
+
await ops.delete(container);
|
|
91
|
+
check("deleteContainer accepted", true);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = { run: run };
|
|
95
|
+
|
|
96
|
+
if (require.main === module) {
|
|
97
|
+
run().then(
|
|
98
|
+
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); process.exit(0); },
|
|
99
|
+
function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Live GCS object-store round-trip against the docker-compose fake-gcs
|
|
4
|
+
* fixture (fsouza/fake-gcs-server, TLS-terminated with the test CA).
|
|
5
|
+
*
|
|
6
|
+
* What this proves: the framework's GCS JSON-API client
|
|
7
|
+
* (lib/object-store/gcs.js) marshals create-bucket / upload / download /
|
|
8
|
+
* list / head / delete onto the real GCS wire shape, routes them to a
|
|
9
|
+
* configurable endpoint, parses the real JSON responses, and round-trips
|
|
10
|
+
* bytes unchanged — over TLS, trusting the test CA with no
|
|
11
|
+
* rejectUnauthorized override anywhere.
|
|
12
|
+
*
|
|
13
|
+
* The OAuth2 hop. The framework ALWAYS exchanges the service-account
|
|
14
|
+
* RSA-SHA256-signed JWT for an access token before any storage call
|
|
15
|
+
* (gcs.js _ensureToken). fake-gcs has no token endpoint (POST /token →
|
|
16
|
+
* 404), so it cannot be targeted on its own — the client would fail at
|
|
17
|
+
* AUTH_FAILED before reaching the storage API. The framework exposes
|
|
18
|
+
* `config.tokenEndpoint` to point the OAuth2 leg elsewhere; this test
|
|
19
|
+
* stands up a tiny in-process HTTPS token issuer using the test CA's
|
|
20
|
+
* own gcs.crt / gcs.key (valid for 127.0.0.1, CA-signed, so the
|
|
21
|
+
* framework's http-client trusts it via NODE_EXTRA_CA_CERTS — no
|
|
22
|
+
* bypass). The framework's real OAuth2 wire exchange (JWT assertion
|
|
23
|
+
* POST, x-www-form-urlencoded, parse access_token) runs end-to-end over
|
|
24
|
+
* TLS against that issuer; every storage call then carries the issued
|
|
25
|
+
* bearer to fake-gcs.
|
|
26
|
+
*
|
|
27
|
+
* Honest scope — what is NOT proven here:
|
|
28
|
+
* - fake-gcs does NOT verify the bearer token or any signature; the
|
|
29
|
+
* in-test issuer does NOT verify the JWT assertion. So this is a
|
|
30
|
+
* wire / URL / JSON-marshalling / TLS-trust / endpoint-routing /
|
|
31
|
+
* OAuth2-exchange-shape proof, NOT a token- or signature-CORRECTNESS
|
|
32
|
+
* proof (RSA-SHA256 JWT signing + V4 presign signing stay covered by
|
|
33
|
+
* unit vectors in the layer-0 suite).
|
|
34
|
+
* - bucket DELETE: real GCS returns 204; fake-gcs returns 200 on an
|
|
35
|
+
* existing bucket (an emulator quirk). The framework correctly
|
|
36
|
+
* accepts 204 / 404 and refuses the unexpected 200, so we assert the
|
|
37
|
+
* correct delete-missing path (404 → false) against fake-gcs and
|
|
38
|
+
* leave the created bucket for the container reset to sweep rather
|
|
39
|
+
* than assert against the emulator's non-spec status.
|
|
40
|
+
*/
|
|
41
|
+
var fs = require("node:fs");
|
|
42
|
+
var os = require("node:os");
|
|
43
|
+
var path = require("node:path");
|
|
44
|
+
var https = require("node:https");
|
|
45
|
+
var nodeCrypto = require("node:crypto");
|
|
46
|
+
var child = require("node:child_process");
|
|
47
|
+
var helpers = require("../helpers");
|
|
48
|
+
var check = helpers.check;
|
|
49
|
+
var services = require("../helpers/services");
|
|
50
|
+
var b = require("../../");
|
|
51
|
+
|
|
52
|
+
// A service account whose RSA keypair is generated fresh per run — the
|
|
53
|
+
// JWT signing path in gcs.js is exercised for real even though neither
|
|
54
|
+
// the local issuer nor fake-gcs verifies the assertion.
|
|
55
|
+
function _serviceAccount() {
|
|
56
|
+
var pair = nodeCrypto.generateKeyPairSync("rsa", {
|
|
57
|
+
modulusLength: 2048,
|
|
58
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
59
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
type: "service_account",
|
|
63
|
+
project_id: "blamejs-emu-project",
|
|
64
|
+
client_email: "emu-sa@blamejs-emu-project.iam.gserviceaccount.com",
|
|
65
|
+
private_key: pair.privateKey,
|
|
66
|
+
private_key_id: "emu-key-001",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Copy a file out of the certs volume into the host tmpdir via a running
|
|
71
|
+
// container (dev tooling — same mechanism services.exportCaCert uses).
|
|
72
|
+
function _exportCert(name, dest) {
|
|
73
|
+
return new Promise(function (resolve, reject) {
|
|
74
|
+
var p = child.spawn("docker", ["cp", "blamejs-test-gcs:/certs/" + name, dest], {
|
|
75
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
76
|
+
});
|
|
77
|
+
var err = "";
|
|
78
|
+
p.stderr.on("data", function (d) { err += d.toString(); });
|
|
79
|
+
p.on("close", function (code) {
|
|
80
|
+
if (code !== 0) return reject(new Error("docker cp " + name + " failed (exit " + code + "): " + err.trim()));
|
|
81
|
+
resolve(dest);
|
|
82
|
+
});
|
|
83
|
+
p.on("error", reject);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// In-process HTTPS token issuer using the CA-signed gcs cert (valid for
|
|
88
|
+
// 127.0.0.1). Returns { url, hits(), close() }. The framework's
|
|
89
|
+
// http-client trusts it via NODE_EXTRA_CA_CERTS; no rejectUnauthorized
|
|
90
|
+
// override is set anywhere.
|
|
91
|
+
async function _startTokenIssuer() {
|
|
92
|
+
var tmp = os.tmpdir();
|
|
93
|
+
var crtPath = path.join(tmp, "blamejs-gcs-issuer.crt");
|
|
94
|
+
var keyPath = path.join(tmp, "blamejs-gcs-issuer.key");
|
|
95
|
+
await _exportCert("gcs.crt", crtPath);
|
|
96
|
+
await _exportCert("gcs.key", keyPath);
|
|
97
|
+
var hits = 0;
|
|
98
|
+
var lastAssertionSeen = false;
|
|
99
|
+
var srv = https.createServer({
|
|
100
|
+
cert: fs.readFileSync(crtPath),
|
|
101
|
+
key: fs.readFileSync(keyPath),
|
|
102
|
+
}, function (req, res) {
|
|
103
|
+
var chunks = [];
|
|
104
|
+
req.on("data", function (c) { chunks.push(c); });
|
|
105
|
+
req.on("end", function () {
|
|
106
|
+
var body = Buffer.concat(chunks).toString("utf8");
|
|
107
|
+
// Confirm the framework actually sent the JWT-bearer assertion the
|
|
108
|
+
// real OAuth2 service-account flow requires.
|
|
109
|
+
if (/grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer/.test(body) &&
|
|
110
|
+
/assertion=/.test(body)) {
|
|
111
|
+
lastAssertionSeen = true;
|
|
112
|
+
}
|
|
113
|
+
hits++;
|
|
114
|
+
res.statusCode = 200;
|
|
115
|
+
res.setHeader("Content-Type", "application/json");
|
|
116
|
+
res.end(JSON.stringify({ access_token: "emu-access-token", expires_in: 3600 }));
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
await new Promise(function (resolve) { srv.listen(0, "127.0.0.1", resolve); });
|
|
120
|
+
var port = srv.address().port;
|
|
121
|
+
return {
|
|
122
|
+
url: "https://127.0.0.1:" + port + "/token",
|
|
123
|
+
hits: function () { return hits; },
|
|
124
|
+
sawJwtAssertion: function () { return lastAssertionSeen; },
|
|
125
|
+
close: function () { return new Promise(function (r) { srv.close(r); }); },
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function _runRoundTrip(issuer) {
|
|
130
|
+
var sa = _serviceAccount();
|
|
131
|
+
var endpoint = services.URLS.gcs; // https://127.0.0.1:4443
|
|
132
|
+
var bucket = "blamejs-gcs-live-" + Date.now();
|
|
133
|
+
var common = {
|
|
134
|
+
serviceAccount: sa,
|
|
135
|
+
endpoint: endpoint,
|
|
136
|
+
tokenEndpoint: issuer.url,
|
|
137
|
+
allowInternal: true,
|
|
138
|
+
timeoutMs: 8000,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// ---- bucket create ----
|
|
142
|
+
var ops = b.objectStore.bucketOps.create(Object.assign({
|
|
143
|
+
protocol: "gcs",
|
|
144
|
+
projectId: sa.project_id,
|
|
145
|
+
}, common));
|
|
146
|
+
var created = await ops.create(bucket, { location: "US" });
|
|
147
|
+
check("bucketOps.create returns the bucket name", created.name === bucket);
|
|
148
|
+
check("OAuth2 token exchange happened before the storage call",
|
|
149
|
+
issuer.hits() >= 1);
|
|
150
|
+
check("framework sent the JWT-bearer assertion on the OAuth2 leg",
|
|
151
|
+
issuer.sawJwtAssertion() === true);
|
|
152
|
+
|
|
153
|
+
// ---- backend put / get / list / head / delete ----
|
|
154
|
+
var backend = b.objectStore.buildBackend(Object.assign({
|
|
155
|
+
name: "gcs-live",
|
|
156
|
+
protocol: "gcs",
|
|
157
|
+
bucket: bucket,
|
|
158
|
+
classifications: ["operational"],
|
|
159
|
+
residencyTag: "unrestricted",
|
|
160
|
+
}, common));
|
|
161
|
+
|
|
162
|
+
var key = "obj-" + Math.floor(Math.random() * 1e6) + ".bin";
|
|
163
|
+
var payload = nodeCrypto.randomBytes(4096);
|
|
164
|
+
|
|
165
|
+
var putRv = await backend.put(key, payload, { contentType: "application/octet-stream" });
|
|
166
|
+
check("put: returned a size", putRv && putRv.size === payload.length);
|
|
167
|
+
|
|
168
|
+
var got = await backend.get(key);
|
|
169
|
+
var gotBuf = Buffer.isBuffer(got) ? got : (got && got.body);
|
|
170
|
+
check("get: bytes round-trip exactly (byte-identical)",
|
|
171
|
+
Buffer.isBuffer(gotBuf) && Buffer.compare(gotBuf, payload) === 0);
|
|
172
|
+
|
|
173
|
+
var listing = await backend.list("obj-");
|
|
174
|
+
check("list: returns { items } shape",
|
|
175
|
+
listing && Array.isArray(listing.items));
|
|
176
|
+
check("list: surfaces the just-put object key",
|
|
177
|
+
listing.items.some(function (it) { return it.key === key; }));
|
|
178
|
+
check("list: item size matches the payload length",
|
|
179
|
+
listing.items.some(function (it) { return it.key === key && it.size === payload.length; }));
|
|
180
|
+
|
|
181
|
+
var emptyListing = await backend.list("does-not-exist-prefix-");
|
|
182
|
+
check("list: non-matching prefix returns empty items",
|
|
183
|
+
emptyListing && Array.isArray(emptyListing.items) && emptyListing.items.length === 0);
|
|
184
|
+
|
|
185
|
+
var meta = await backend.head(key);
|
|
186
|
+
check("head: size matches the payload length", meta && meta.size === payload.length);
|
|
187
|
+
|
|
188
|
+
var del = await backend.delete(key);
|
|
189
|
+
check("delete: returned true", del === true);
|
|
190
|
+
|
|
191
|
+
var afterDelete = await backend.list("obj-");
|
|
192
|
+
check("list after delete: object is gone",
|
|
193
|
+
!afterDelete.items.some(function (it) { return it.key === key; }));
|
|
194
|
+
|
|
195
|
+
// get on the deleted key surfaces a 404 — the framework rejects with
|
|
196
|
+
// an ObjectStoreError carrying statusCode 404 (not a silent empty).
|
|
197
|
+
var notFound = null;
|
|
198
|
+
try { await backend.get(key); } catch (e) { notFound = e; }
|
|
199
|
+
check("get on deleted key throws (404 surfaced, not swallowed)",
|
|
200
|
+
notFound && (notFound.statusCode === 404 ||
|
|
201
|
+
/404|not.?found/i.test(String(notFound.message || notFound.code || ""))));
|
|
202
|
+
|
|
203
|
+
// ---- bucket delete-missing path (correct against fake-gcs: 404 → false) ----
|
|
204
|
+
var delMissing = await ops.delete("blamejs-gcs-never-existed-" + Date.now());
|
|
205
|
+
check("bucketOps.delete on a missing bucket returns false (404 path)",
|
|
206
|
+
delMissing === false);
|
|
207
|
+
|
|
208
|
+
// Best-effort cleanup of the created bucket. Real GCS returns 204 on
|
|
209
|
+
// bucket DELETE; fake-gcs returns 200 (an emulator quirk the framework
|
|
210
|
+
// correctly refuses as UNEXPECTED_STATUS), so this throw is expected
|
|
211
|
+
// against the emulator and is NOT a framework defect. The bucket name
|
|
212
|
+
// carries Date.now() so re-runs don't collide; the container reset
|
|
213
|
+
// sweeps it.
|
|
214
|
+
try { await ops.delete(bucket); } catch (_e) { /* fake-gcs 200-on-delete quirk */ }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function run() {
|
|
218
|
+
var svc = await services.requireService("gcs");
|
|
219
|
+
if (!svc.ok) throw new Error("fake-gcs unreachable: " + svc.reason);
|
|
220
|
+
// exportCaCert is what the runner already relies on for NODE_EXTRA_CA_CERTS;
|
|
221
|
+
// calling it keeps this test self-contained if run directly.
|
|
222
|
+
await services.exportCaCert();
|
|
223
|
+
|
|
224
|
+
var issuer = await _startTokenIssuer();
|
|
225
|
+
try {
|
|
226
|
+
await _runRoundTrip(issuer);
|
|
227
|
+
} finally {
|
|
228
|
+
await issuer.close();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
module.exports = { run: run };
|
|
233
|
+
|
|
234
|
+
if (require.main === module) {
|
|
235
|
+
run().then(
|
|
236
|
+
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); process.exit(0); },
|
|
237
|
+
function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
|
|
238
|
+
);
|
|
239
|
+
}
|