@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
|
@@ -245,6 +245,139 @@ async function run() {
|
|
|
245
245
|
check("record: bad name dropped", ex3.bufferedCounters === 0);
|
|
246
246
|
check("record: bad value dropped", ex3.bufferedObservations === 0);
|
|
247
247
|
await ex3.close();
|
|
248
|
+
|
|
249
|
+
// ---- Telemetry-attribute redaction (CWE-532) ----
|
|
250
|
+
// Span/metric attribute VALUES are a first-class egress sink — a secret
|
|
251
|
+
// in an attribute must not reach the OTLP collector verbatim. Redaction
|
|
252
|
+
// is ON by default (composes b.redact via b.observability.getRedactor);
|
|
253
|
+
// benign attributes pass through unchanged.
|
|
254
|
+
await _testRedaction();
|
|
255
|
+
|
|
256
|
+
// setRedactor surface validation
|
|
257
|
+
check("setRedactor rejects non-function/non-null", (function () {
|
|
258
|
+
var t = null;
|
|
259
|
+
try { b.observability.setRedactor(42); } catch (e) { t = e; }
|
|
260
|
+
return t instanceof TypeError && /must be a function or null/.test(t.message);
|
|
261
|
+
})());
|
|
262
|
+
check("getRedactor returns a function", typeof b.observability.getRedactor() === "function");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function _findAttr(list, key) {
|
|
266
|
+
for (var i = 0; i < list.length; i++) if (list[i].key === key) return list[i];
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function _testRedaction() {
|
|
271
|
+
// Always restore the default redactor so this block can't pollute the
|
|
272
|
+
// shared b.observability singleton used by sibling tests.
|
|
273
|
+
try {
|
|
274
|
+
// Default-on: secret-shaped attrs scrubbed, benign attrs untouched —
|
|
275
|
+
// asserted on the real OTLP payload captured by the test exporter.
|
|
276
|
+
var captured = null;
|
|
277
|
+
var hc = b.testing.fakeHttpClient(function (req) {
|
|
278
|
+
captured = JSON.parse(req.body);
|
|
279
|
+
return { statusCode: 200, headers: {}, body: Buffer.from("") };
|
|
280
|
+
});
|
|
281
|
+
var ex = b.otelExport.create({
|
|
282
|
+
endpoint: "https://otel.example/v1/metrics",
|
|
283
|
+
serviceName: "wiki",
|
|
284
|
+
intervalMs: 0,
|
|
285
|
+
httpClient: hc,
|
|
286
|
+
});
|
|
287
|
+
ex.recordCounter("http.requests", 1, {
|
|
288
|
+
"http.route": "/checkout", // benign — passes through
|
|
289
|
+
"authorization": "Bearer eyJabc.eyJdef.sigsigsig", // sensitive field name
|
|
290
|
+
"card": "4111 1111 1111 1111", // credit-card value-shape
|
|
291
|
+
"status": 200, // benign number
|
|
292
|
+
});
|
|
293
|
+
await ex.flush();
|
|
294
|
+
|
|
295
|
+
var dp = captured.resourceMetrics[0].scopeMetrics[0].metrics
|
|
296
|
+
.find(function (m) { return m.name === "http.requests" && m.sum; })
|
|
297
|
+
.sum.dataPoints[0];
|
|
298
|
+
var attrs = dp.attributes;
|
|
299
|
+
check("redact: benign route attr passes through",
|
|
300
|
+
_findAttr(attrs, "http.route").value.stringValue === "/checkout");
|
|
301
|
+
check("redact: benign number attr passes through",
|
|
302
|
+
_findAttr(attrs, "status").value.intValue === "200");
|
|
303
|
+
check("redact: sensitive field-name attr redacted",
|
|
304
|
+
_findAttr(attrs, "authorization").value.stringValue === b.redact.MARKER);
|
|
305
|
+
check("redact: credit-card value-shape attr redacted",
|
|
306
|
+
_findAttr(attrs, "card").value.stringValue === "[REDACTED-CC]");
|
|
307
|
+
await ex.close();
|
|
308
|
+
|
|
309
|
+
// Resource attributes also flow through the redactor — a secret in a
|
|
310
|
+
// resource attribute (operator misconfiguration) is scrubbed too.
|
|
311
|
+
var capturedRes = null;
|
|
312
|
+
var hcRes = b.testing.fakeHttpClient(function (req) {
|
|
313
|
+
capturedRes = JSON.parse(req.body);
|
|
314
|
+
return { statusCode: 200, headers: {}, body: Buffer.from("") };
|
|
315
|
+
});
|
|
316
|
+
var exRes = b.otelExport.create({
|
|
317
|
+
endpoint: "https://otel.example/v1/metrics",
|
|
318
|
+
serviceName: "wiki",
|
|
319
|
+
intervalMs: 0,
|
|
320
|
+
httpClient: hcRes,
|
|
321
|
+
resourceAttributes: { "deployment.environment": "production", "api_key": "AKIAABCDEFGHIJKLMNOP" },
|
|
322
|
+
});
|
|
323
|
+
exRes.recordCounter("x", 1);
|
|
324
|
+
await exRes.flush();
|
|
325
|
+
var resAttrs = capturedRes.resourceMetrics[0].resource.attributes;
|
|
326
|
+
check("redact: benign resource attr passes through",
|
|
327
|
+
_findAttr(resAttrs, "deployment.environment").value.stringValue === "production");
|
|
328
|
+
check("redact: sensitive resource attr redacted",
|
|
329
|
+
_findAttr(resAttrs, "api_key").value.stringValue === b.redact.MARKER);
|
|
330
|
+
await exRes.close();
|
|
331
|
+
|
|
332
|
+
// Operator override via setRedactor — a stricter scrubber is honored
|
|
333
|
+
// without re-creating the exporter.
|
|
334
|
+
b.observability.setRedactor(function (value, key) {
|
|
335
|
+
if (key === "enduser.id") return "[CUSTOM]";
|
|
336
|
+
return b.redact.redact(value, { parentKey: key });
|
|
337
|
+
});
|
|
338
|
+
var enc = b.otelExport._attrsToOtlpForTest({ "enduser.id": "u-42", "http.route": "/p" });
|
|
339
|
+
check("redact: custom redactor applied",
|
|
340
|
+
_findAttr(enc, "enduser.id").value.stringValue === "[CUSTOM]");
|
|
341
|
+
check("redact: custom redactor leaves benign attr",
|
|
342
|
+
_findAttr(enc, "http.route").value.stringValue === "/p");
|
|
343
|
+
|
|
344
|
+
// A throwing redactor must DROP the attribute (fail toward dropping,
|
|
345
|
+
// not leaking) and must NOT crash the export.
|
|
346
|
+
b.observability.setRedactor(function () { throw new Error("redactor-boom"); });
|
|
347
|
+
var threw = null;
|
|
348
|
+
var encThrow;
|
|
349
|
+
try { encThrow = b.otelExport._attrsToOtlpForTest({ secret_token: "shhh", keep: "x" }); }
|
|
350
|
+
catch (e) { threw = e; }
|
|
351
|
+
check("redact: throwing redactor does not crash export", threw === null);
|
|
352
|
+
check("redact: throwing redactor drops all attrs (no leak)", encThrow.length === 0);
|
|
353
|
+
|
|
354
|
+
// A throwing redactor through the full flush path still ships an
|
|
355
|
+
// empty-attribute datapoint rather than the raw secret.
|
|
356
|
+
var capturedThrow = null;
|
|
357
|
+
var hcThrow = b.testing.fakeHttpClient(function (req) {
|
|
358
|
+
capturedThrow = JSON.parse(req.body);
|
|
359
|
+
return { statusCode: 200, headers: {}, body: Buffer.from("") };
|
|
360
|
+
});
|
|
361
|
+
var exThrow = b.otelExport.create({
|
|
362
|
+
endpoint: "https://otel.example/v1/metrics",
|
|
363
|
+
serviceName: "wiki",
|
|
364
|
+
intervalMs: 0,
|
|
365
|
+
httpClient: hcThrow,
|
|
366
|
+
});
|
|
367
|
+
exThrow.recordCounter("http.requests", 1, { secret_token: "shhh" });
|
|
368
|
+
await exThrow.flush();
|
|
369
|
+
var dpThrow = capturedThrow.resourceMetrics[0].scopeMetrics[0].metrics
|
|
370
|
+
.find(function (m) { return m.name === "http.requests" && m.sum; })
|
|
371
|
+
.sum.dataPoints[0];
|
|
372
|
+
check("redact: flush with throwing redactor drops the secret attr",
|
|
373
|
+
(dpThrow.attributes || []).length === 0);
|
|
374
|
+
var bodyStr = JSON.stringify(capturedThrow);
|
|
375
|
+
check("redact: secret never appears in exported payload",
|
|
376
|
+
bodyStr.indexOf("shhh") === -1);
|
|
377
|
+
await exThrow.close();
|
|
378
|
+
} finally {
|
|
379
|
+
b.observability.setRedactor(null); // restore default for sibling tests
|
|
380
|
+
}
|
|
248
381
|
}
|
|
249
382
|
|
|
250
383
|
module.exports = { run: run };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The OTLP span exporter must run every attribute VALUE through the telemetry
|
|
3
|
+
// redactor before serialization. Telemetry is a first-class EGRESS sink — a
|
|
4
|
+
// span attribute holding a bearer token, password, or API key would otherwise
|
|
5
|
+
// be shipped verbatim onto the OTLP wire (CWE-532: insertion of sensitive
|
|
6
|
+
// information into an externally-shipped sink), reaching whatever collector the
|
|
7
|
+
// operator points at.
|
|
8
|
+
//
|
|
9
|
+
// Drives the real consumer path: create() an exporter with a capturing
|
|
10
|
+
// fetchImpl, queue a span (and a span EVENT) carrying sensitive attributes,
|
|
11
|
+
// flush, and inspect the outgoing body — for BOTH json and protobuf encodings.
|
|
12
|
+
// The default redactor (b.redact.redact) scrubs by sensitive field name.
|
|
13
|
+
|
|
14
|
+
var helpers = require("../helpers");
|
|
15
|
+
var b = helpers.b;
|
|
16
|
+
var check = helpers.check;
|
|
17
|
+
|
|
18
|
+
var otlp = require("../../lib/observability-otlp-exporter");
|
|
19
|
+
|
|
20
|
+
var SECRET_TOKEN = "Bearer eyJSECRETtokenABCdef.ghi.jkl";
|
|
21
|
+
var SECRET_PW = "hunter2-PLAINTEXT-PASSWORD";
|
|
22
|
+
var SECRET_API = "sk_live_PLAINTEXT_APIKEY_0001";
|
|
23
|
+
var CONTROL_VAL = "GET-control-value-keep-me";
|
|
24
|
+
|
|
25
|
+
function makeSpan() {
|
|
26
|
+
return {
|
|
27
|
+
traceId: "0123456789abcdef0123456789abcdef",
|
|
28
|
+
spanId: "0123456789abcdef",
|
|
29
|
+
parentSpanId: "",
|
|
30
|
+
name: "GET /x",
|
|
31
|
+
kind: "server",
|
|
32
|
+
startTimeUnixNano: "1700000000000000000",
|
|
33
|
+
endTimeUnixNano: "1700000000100000000",
|
|
34
|
+
attributes: {
|
|
35
|
+
"http.method": CONTROL_VAL, // non-sensitive control — must survive
|
|
36
|
+
"authorization": SECRET_TOKEN,
|
|
37
|
+
"password": SECRET_PW,
|
|
38
|
+
"api_key": SECRET_API,
|
|
39
|
+
},
|
|
40
|
+
events: [
|
|
41
|
+
{ timeUnixNano: "1700000000050000000", name: "log",
|
|
42
|
+
attributes: { "password": SECRET_PW } },
|
|
43
|
+
],
|
|
44
|
+
status: { code: 1, message: "" },
|
|
45
|
+
resource: { "service.name": "svc" },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function captureBody(encoding) {
|
|
50
|
+
var captured = [];
|
|
51
|
+
var ex = otlp.create({
|
|
52
|
+
endpoint: "https://collector.invalid/v1/traces",
|
|
53
|
+
encoding: encoding,
|
|
54
|
+
batchSize: 100, // don't auto-flush; flush explicitly
|
|
55
|
+
audit: false,
|
|
56
|
+
fetchImpl: async function (url, init) {
|
|
57
|
+
captured.push(init && init.body);
|
|
58
|
+
return { ok: true, status: 200, text: async function () { return ""; } };
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
ex.queue(makeSpan());
|
|
62
|
+
await ex.flush();
|
|
63
|
+
await ex.shutdown();
|
|
64
|
+
var body = captured[0];
|
|
65
|
+
return Buffer.isBuffer(body) ? body.toString("latin1") : String(body);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function run() {
|
|
69
|
+
b.observability.setRedactor(null); // default redactor active
|
|
70
|
+
|
|
71
|
+
// The shared primitive both exporters route through — assert it directly so
|
|
72
|
+
// the redaction contract is covered at the seam, not only end-to-end.
|
|
73
|
+
var ra = b.observability.redactAttrs({
|
|
74
|
+
"http.method": CONTROL_VAL, authorization: SECRET_TOKEN, password: SECRET_PW,
|
|
75
|
+
});
|
|
76
|
+
check("redactAttrs scrubs sensitive keys", ra.authorization === "[REDACTED]" && ra.password === "[REDACTED]");
|
|
77
|
+
check("redactAttrs keeps non-sensitive keys", ra["http.method"] === CONTROL_VAL);
|
|
78
|
+
|
|
79
|
+
var jsonWire = await captureBody("json");
|
|
80
|
+
check("json: bearer token redacted on the OTLP wire", jsonWire.indexOf(SECRET_TOKEN) === -1);
|
|
81
|
+
check("json: password redacted on the OTLP wire", jsonWire.indexOf(SECRET_PW) === -1);
|
|
82
|
+
check("json: api key redacted on the OTLP wire", jsonWire.indexOf(SECRET_API) === -1);
|
|
83
|
+
check("json: span-EVENT attribute password redacted", jsonWire.split(SECRET_PW).length === 1);
|
|
84
|
+
check("json: non-sensitive control attribute survives", jsonWire.indexOf(CONTROL_VAL) !== -1);
|
|
85
|
+
|
|
86
|
+
var protoWire = await captureBody("protobuf");
|
|
87
|
+
check("protobuf: bearer token redacted on the OTLP wire", protoWire.indexOf(SECRET_TOKEN) === -1);
|
|
88
|
+
check("protobuf: password redacted on the OTLP wire", protoWire.indexOf(SECRET_PW) === -1);
|
|
89
|
+
check("protobuf: api key redacted on the OTLP wire", protoWire.indexOf(SECRET_API) === -1);
|
|
90
|
+
check("protobuf: non-sensitive control attribute survives", protoWire.indexOf(CONTROL_VAL) !== -1);
|
|
91
|
+
|
|
92
|
+
b.observability.setRedactor(null); // restore default for other tests
|
|
93
|
+
process.stdout.write("OK — otlp attribute redaction tests\n");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
run().then(function () {
|
|
97
|
+
process.exit(0);
|
|
98
|
+
}).catch(function (e) {
|
|
99
|
+
process.stderr.write((e && e.stack ? e.stack : String(e)) + "\n");
|
|
100
|
+
process.exit(1);
|
|
101
|
+
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #128: the outbox must reclaim a row stranded 'in-flight' by a crashed
|
|
3
|
+
// publisher. A claim flips status pending → in-flight and stamps claimed_at;
|
|
4
|
+
// the claim path then only SELECTs status='pending', so if the process dies
|
|
5
|
+
// between the claim and _markPublished/_markRetry/_markDead, the row sits
|
|
6
|
+
// in-flight forever — silently dropped, violating the at-least-once delivery
|
|
7
|
+
// the module header advertises (b.queue has a sweepExpired reaper; outbox had
|
|
8
|
+
// none). The fix reaps any in-flight row whose claim is older than the lease
|
|
9
|
+
// (or predates the claimed_at column) back to 'pending' at the top of every
|
|
10
|
+
// poll.
|
|
11
|
+
//
|
|
12
|
+
// Driven against a REAL node:sqlite backend (a faithful externalDb), so the
|
|
13
|
+
// claim / reap / publish SQL actually executes. RED on the buggy tree: the
|
|
14
|
+
// stranded row is never published. GREEN after the fix: the reaper returns it
|
|
15
|
+
// to the pending pool and it publishes on the next poll.
|
|
16
|
+
|
|
17
|
+
var { DatabaseSync } = require("node:sqlite");
|
|
18
|
+
var helpers = require("../helpers");
|
|
19
|
+
var b = helpers.b;
|
|
20
|
+
var check = helpers.check;
|
|
21
|
+
var C = b.constants;
|
|
22
|
+
|
|
23
|
+
// Minimal faithful externalDb over an in-memory node:sqlite db: implements
|
|
24
|
+
// query(sql, params) + transaction(fn) + dialect, converting JS Date params
|
|
25
|
+
// to ISO strings the way the framework's real sqlite provider does.
|
|
26
|
+
function _sqliteExternalDb() {
|
|
27
|
+
var db = new DatabaseSync(":memory:");
|
|
28
|
+
function _bind(params) {
|
|
29
|
+
return (params || []).map(function (v) {
|
|
30
|
+
if (v instanceof Date) return v.toISOString();
|
|
31
|
+
return v;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function _query(sqlText, params) {
|
|
35
|
+
var stmt = db.prepare(sqlText);
|
|
36
|
+
var args = _bind(params);
|
|
37
|
+
if (/^\s*select/i.test(sqlText)) {
|
|
38
|
+
return { rows: stmt.all.apply(stmt, args) };
|
|
39
|
+
}
|
|
40
|
+
var info = stmt.run.apply(stmt, args);
|
|
41
|
+
return { rows: [], changes: info.changes };
|
|
42
|
+
}
|
|
43
|
+
var xdb = {
|
|
44
|
+
dialect: "sqlite",
|
|
45
|
+
query: async function (s, p) { return _query(s, p); },
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
dialect: "sqlite",
|
|
49
|
+
query: async function (s, p) { return _query(s, p); },
|
|
50
|
+
transaction: async function (fn) {
|
|
51
|
+
db.exec("BEGIN");
|
|
52
|
+
try { var r = await fn(xdb); db.exec("COMMIT"); return r; }
|
|
53
|
+
catch (e) { try { db.exec("ROLLBACK"); } catch (_e) {} throw e; }
|
|
54
|
+
},
|
|
55
|
+
_raw: db,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function run() {
|
|
60
|
+
var xdb = _sqliteExternalDb();
|
|
61
|
+
var published = [];
|
|
62
|
+
var outbox = b.outbox.create({
|
|
63
|
+
externalDb: xdb,
|
|
64
|
+
table: "test_outbox",
|
|
65
|
+
publisher: async function (event) { published.push(event); },
|
|
66
|
+
pollIntervalMs: C.TIME.seconds(1),
|
|
67
|
+
claimReclaimMs: C.TIME.seconds(1), // short lease so a 10s-old claim is stale
|
|
68
|
+
audit: false,
|
|
69
|
+
});
|
|
70
|
+
await outbox.declareSchema();
|
|
71
|
+
|
|
72
|
+
// enqueue is transactional (the outbox pattern — written in the same txn as
|
|
73
|
+
// the domain row); wrap each in a transaction and hand it the txClient.
|
|
74
|
+
function _enqueue(event) {
|
|
75
|
+
return xdb.transaction(async function (tx) { await outbox.enqueue(event, tx); });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---- the crash: a row claimed (in-flight) but never published ----
|
|
79
|
+
await _enqueue({ topic: "orders", payload: { id: "o-1" } });
|
|
80
|
+
check("#128 the enqueued row starts pending", (await outbox.pendingCount()) === 1);
|
|
81
|
+
|
|
82
|
+
// Simulate a publisher that claimed the row then died: flip it to in-flight
|
|
83
|
+
// with a claim timestamp older than the lease.
|
|
84
|
+
var staleClaim = new Date(Date.now() - C.TIME.seconds(10)).toISOString();
|
|
85
|
+
xdb._raw.prepare(
|
|
86
|
+
"UPDATE \"test_outbox\" SET status = 'in-flight', claimed_at = ? WHERE topic = 'orders'"
|
|
87
|
+
).run(staleClaim);
|
|
88
|
+
check("#128 the stranded row is no longer in the pending pool",
|
|
89
|
+
(await outbox.pendingCount()) === 0);
|
|
90
|
+
check("#128 nothing has been published yet", published.length === 0);
|
|
91
|
+
|
|
92
|
+
// ---- one poll: the reaper must reclaim + the row must publish ----
|
|
93
|
+
await outbox._processOnce();
|
|
94
|
+
|
|
95
|
+
check("#128 the stranded in-flight row is reclaimed and published",
|
|
96
|
+
published.length === 1 && published[0].topic === "orders");
|
|
97
|
+
check("#128 no row is left stranded in-flight",
|
|
98
|
+
Number(xdb._raw.prepare(
|
|
99
|
+
"SELECT COUNT(*) AS n FROM \"test_outbox\" WHERE status = 'in-flight'").get().n) === 0);
|
|
100
|
+
check("#128 the published row is marked published",
|
|
101
|
+
Number(xdb._raw.prepare(
|
|
102
|
+
"SELECT COUNT(*) AS n FROM \"test_outbox\" WHERE status = 'published'").get().n) === 1);
|
|
103
|
+
check("#128 deadCount stays 0 (the row was delivered, not dead-lettered)",
|
|
104
|
+
(await outbox.deadCount()) === 0);
|
|
105
|
+
|
|
106
|
+
// ---- a FRESH in-flight claim (within the lease) must NOT be reclaimed ----
|
|
107
|
+
await _enqueue({ topic: "fresh", payload: { id: "f-1" } });
|
|
108
|
+
xdb._raw.prepare(
|
|
109
|
+
"UPDATE \"test_outbox\" SET status = 'in-flight', claimed_at = ? WHERE topic = 'fresh'"
|
|
110
|
+
).run(new Date().toISOString());
|
|
111
|
+
var publishedBefore = published.length;
|
|
112
|
+
await outbox._processOnce();
|
|
113
|
+
check("#128 a fresh in-flight claim (within the lease) is NOT reclaimed",
|
|
114
|
+
Number(xdb._raw.prepare(
|
|
115
|
+
"SELECT COUNT(*) AS n FROM \"test_outbox\" WHERE topic = 'fresh' AND status = 'in-flight'").get().n) === 1);
|
|
116
|
+
check("#128 the fresh in-flight row was not re-published",
|
|
117
|
+
published.length === publishedBefore);
|
|
118
|
+
|
|
119
|
+
// ---- a legacy in-flight row (NULL claimed_at) is reclaimed ----
|
|
120
|
+
await _enqueue({ topic: "legacy", payload: { id: "l-1" } });
|
|
121
|
+
xdb._raw.prepare(
|
|
122
|
+
"UPDATE \"test_outbox\" SET status = 'in-flight', claimed_at = NULL WHERE topic = 'legacy'"
|
|
123
|
+
).run();
|
|
124
|
+
await outbox._processOnce();
|
|
125
|
+
check("#128 a legacy in-flight row (NULL claimed_at) is reclaimed and published",
|
|
126
|
+
published.some(function (e) { return e.topic === "legacy"; }));
|
|
127
|
+
|
|
128
|
+
await outbox.stop();
|
|
129
|
+
console.log("OK — outbox stale-in-flight reaper tests");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = { run: run };
|
|
133
|
+
if (require.main === module) {
|
|
134
|
+
run().then(function () { process.exit(0); })
|
|
135
|
+
.catch(function (err) { process.stderr.write(String(err && err.stack || err) + "\n"); process.exit(1); });
|
|
136
|
+
}
|
|
@@ -12,6 +12,9 @@ var EventEmitter = require("events");
|
|
|
12
12
|
var helpers = require("../helpers");
|
|
13
13
|
var b = helpers.b;
|
|
14
14
|
var check = helpers.check;
|
|
15
|
+
// _contentType is the internal Content-Type parameter parser the
|
|
16
|
+
// middleware + standalone helpers share; exposed for tests.
|
|
17
|
+
var _contentType = require("../../lib/middleware/body-parser")._contentType;
|
|
15
18
|
|
|
16
19
|
function _streamReq(opts) {
|
|
17
20
|
opts = opts || {};
|
|
@@ -201,6 +204,83 @@ async function testMiddlewareAndStandaloneShareImpl() {
|
|
|
201
204
|
JSON.stringify(standalone) === JSON.stringify(middlewareParsed));
|
|
202
205
|
}
|
|
203
206
|
|
|
207
|
+
// ---- Prototype-pollution defense (CWE-915 / CWE-1321) ----
|
|
208
|
+
//
|
|
209
|
+
// Header/parameter/field names are request-controlled. The parsers build
|
|
210
|
+
// their maps from [key, value] pairs through Object.fromEntries instead
|
|
211
|
+
// of a computed-write (`target[key] = value`) sink, dropping the
|
|
212
|
+
// __proto__ / constructor / prototype names so a hostile name can never
|
|
213
|
+
// reach Object.prototype.
|
|
214
|
+
|
|
215
|
+
function testContentTypeIgnoresPoisonedParam() {
|
|
216
|
+
// A Content-Type parameter literally named `__proto__` must not pollute
|
|
217
|
+
// the prototype chain, and the legitimate boundary/charset must still
|
|
218
|
+
// parse to the same shape.
|
|
219
|
+
var ct = _contentType({
|
|
220
|
+
headers: {
|
|
221
|
+
"content-type":
|
|
222
|
+
'multipart/form-data; boundary=----abc; __proto__=evil; charset=utf-8',
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
check("content-type: type parsed", ct.type === "multipart/form-data");
|
|
226
|
+
check("content-type: boundary parsed", ct.params.boundary === "----abc");
|
|
227
|
+
check("content-type: charset parsed", ct.params.charset === "utf-8");
|
|
228
|
+
check("content-type: __proto__ param dropped (no own prop)",
|
|
229
|
+
!Object.prototype.hasOwnProperty.call(ct.params, "__proto__"));
|
|
230
|
+
check("content-type: Object.prototype not polluted",
|
|
231
|
+
({}).evil === undefined && Object.prototype.evil === undefined);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function testMultipartRefusesPoisonedFieldName() {
|
|
235
|
+
// A multipart field whose Content-Disposition name is `__proto__` is
|
|
236
|
+
// refused at the parse boundary (400) and never reaches the field map.
|
|
237
|
+
var boundary = "----blamejstest" + Date.now();
|
|
238
|
+
var body = _multipartBody(boundary, [
|
|
239
|
+
{ name: "__proto__", value: '{"polluted":true}' },
|
|
240
|
+
]);
|
|
241
|
+
var req = _streamReq({
|
|
242
|
+
headers: {
|
|
243
|
+
"content-type": "multipart/form-data; boundary=" + boundary,
|
|
244
|
+
"content-length": String(Buffer.byteLength(body)),
|
|
245
|
+
},
|
|
246
|
+
body: body,
|
|
247
|
+
});
|
|
248
|
+
var threw = false;
|
|
249
|
+
try { await b.parsers.multipart(req, { maxBytes: b.constants.BYTES.mib(1), maxFiles: 5 }); }
|
|
250
|
+
catch (e) { threw = e.code === "body-parser/multipart-poisoned-field"; }
|
|
251
|
+
check("multipart: __proto__ field name refused with 400", threw);
|
|
252
|
+
check("multipart: Object.prototype not polluted by field name",
|
|
253
|
+
({}).polluted === undefined && Object.prototype.polluted === undefined);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function testMultipartRepeatedFieldShapeUnchanged() {
|
|
257
|
+
// Success path: repeated field name → array; single → scalar. The
|
|
258
|
+
// entries-merge accumulation must produce the same shape the prior
|
|
259
|
+
// computed-write accumulation did.
|
|
260
|
+
var boundary = "----blamejstest" + Date.now();
|
|
261
|
+
var body = _multipartBody(boundary, [
|
|
262
|
+
{ name: "tag", value: "a" },
|
|
263
|
+
{ name: "tag", value: "b" },
|
|
264
|
+
{ name: "tag", value: "c" },
|
|
265
|
+
{ name: "title", value: "solo" },
|
|
266
|
+
]);
|
|
267
|
+
var req = _streamReq({
|
|
268
|
+
headers: {
|
|
269
|
+
"content-type": "multipart/form-data; boundary=" + boundary,
|
|
270
|
+
"content-length": String(Buffer.byteLength(body)),
|
|
271
|
+
},
|
|
272
|
+
body: body,
|
|
273
|
+
});
|
|
274
|
+
var result = await b.parsers.multipart(req, { maxBytes: b.constants.BYTES.mib(1), maxFiles: 5 });
|
|
275
|
+
check("multipart: repeated field collapses to array in order",
|
|
276
|
+
Array.isArray(result.fields.tag) &&
|
|
277
|
+
result.fields.tag.length === 3 &&
|
|
278
|
+
result.fields.tag[0] === "a" &&
|
|
279
|
+
result.fields.tag[1] === "b" &&
|
|
280
|
+
result.fields.tag[2] === "c");
|
|
281
|
+
check("multipart: single field stays scalar", result.fields.title === "solo");
|
|
282
|
+
}
|
|
283
|
+
|
|
204
284
|
(async function run() {
|
|
205
285
|
await testJsonParsesValidObject();
|
|
206
286
|
await testJsonRefusesOverMaxBytes();
|
|
@@ -212,5 +292,8 @@ async function testMiddlewareAndStandaloneShareImpl() {
|
|
|
212
292
|
await testMultipartEnforcesMaxFiles();
|
|
213
293
|
await testMultipartRefusesBadOpts();
|
|
214
294
|
await testMiddlewareAndStandaloneShareImpl();
|
|
295
|
+
testContentTypeIgnoresPoisonedParam();
|
|
296
|
+
await testMultipartRefusesPoisonedFieldName();
|
|
297
|
+
await testMultipartRepeatedFieldShapeUnchanged();
|
|
215
298
|
console.log("OK — parsers-standalone tests");
|
|
216
299
|
})().catch(function (e) { console.error(e); process.exit(1); });
|