@blamejs/blamejs-shop 0.4.31 → 0.4.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +392 -278
- package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
- package/lib/vendor/blamejs/.gitignore +6 -0
- package/lib/vendor/blamejs/CHANGELOG.md +26 -0
- package/lib/vendor/blamejs/MIGRATING.md +43 -0
- package/lib/vendor/blamejs/README.md +8 -6
- package/lib/vendor/blamejs/SECURITY.md +19 -3
- package/lib/vendor/blamejs/api-snapshot.json +2190 -664
- package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
- package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
- package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
- package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
- package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
- package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
- package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
- package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
- package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
- package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
- package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
- package/lib/vendor/blamejs/index.js +4 -0
- package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
- package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
- package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
- package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
- package/lib/vendor/blamejs/lib/api-key.js +158 -77
- package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
- package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
- package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
- package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
- package/lib/vendor/blamejs/lib/audit.js +259 -123
- package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
- package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
- package/lib/vendor/blamejs/lib/backup/index.js +45 -10
- package/lib/vendor/blamejs/lib/break-glass.js +355 -147
- package/lib/vendor/blamejs/lib/cache.js +174 -105
- package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
- package/lib/vendor/blamejs/lib/cli.js +19 -14
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
- package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
- package/lib/vendor/blamejs/lib/cluster.js +119 -71
- package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
- package/lib/vendor/blamejs/lib/compliance.js +206 -4
- package/lib/vendor/blamejs/lib/consent.js +82 -29
- package/lib/vendor/blamejs/lib/constants.js +27 -11
- package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
- package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
- package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
- package/lib/vendor/blamejs/lib/db-query.js +882 -260
- package/lib/vendor/blamejs/lib/db-schema.js +228 -44
- package/lib/vendor/blamejs/lib/db.js +249 -99
- package/lib/vendor/blamejs/lib/dsr.js +385 -55
- package/lib/vendor/blamejs/lib/error-page.js +14 -1
- package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
- package/lib/vendor/blamejs/lib/external-db.js +549 -34
- package/lib/vendor/blamejs/lib/file-upload.js +52 -7
- package/lib/vendor/blamejs/lib/framework-error.js +20 -1
- package/lib/vendor/blamejs/lib/framework-files.js +73 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
- package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
- package/lib/vendor/blamejs/lib/guard-all.js +1 -0
- package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
- package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
- package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
- package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
- package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
- package/lib/vendor/blamejs/lib/guard-email.js +47 -69
- package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
- package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
- package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
- package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
- package/lib/vendor/blamejs/lib/guard-html.js +53 -108
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
- package/lib/vendor/blamejs/lib/guard-image.js +46 -103
- package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
- package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
- package/lib/vendor/blamejs/lib/guard-json.js +38 -108
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
- package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
- package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
- package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
- package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
- package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
- package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
- package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
- package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
- package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
- package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
- package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
- package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
- package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
- package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
- package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
- package/lib/vendor/blamejs/lib/guard-template.js +35 -172
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-time.js +32 -154
- package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
- package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
- package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
- package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
- package/lib/vendor/blamejs/lib/http-client.js +37 -9
- package/lib/vendor/blamejs/lib/inbox.js +120 -107
- package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
- package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
- package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
- package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
- package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
- package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
- package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
- package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
- package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
- package/lib/vendor/blamejs/lib/mail-store.js +293 -154
- package/lib/vendor/blamejs/lib/mail.js +8 -4
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
- package/lib/vendor/blamejs/lib/migrations.js +108 -66
- package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
- package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
- package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
- package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
- package/lib/vendor/blamejs/lib/observability.js +124 -0
- package/lib/vendor/blamejs/lib/otel-export.js +12 -3
- package/lib/vendor/blamejs/lib/outbox.js +184 -83
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
- package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
- package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
- package/lib/vendor/blamejs/lib/queue-local.js +225 -140
- package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
- package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
- package/lib/vendor/blamejs/lib/queue.js +7 -0
- package/lib/vendor/blamejs/lib/redact.js +68 -11
- package/lib/vendor/blamejs/lib/redis-client.js +160 -31
- package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
- package/lib/vendor/blamejs/lib/retention.js +101 -40
- package/lib/vendor/blamejs/lib/router.js +212 -5
- package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
- package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
- package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
- package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
- package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
- package/lib/vendor/blamejs/lib/safe-url.js +170 -3
- package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
- package/lib/vendor/blamejs/lib/scheduler.js +35 -12
- package/lib/vendor/blamejs/lib/seeders.js +122 -74
- package/lib/vendor/blamejs/lib/session-stores.js +42 -14
- package/lib/vendor/blamejs/lib/session.js +175 -77
- package/lib/vendor/blamejs/lib/sql.js +3842 -0
- package/lib/vendor/blamejs/lib/sse.js +26 -0
- package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
- package/lib/vendor/blamejs/lib/static.js +177 -34
- package/lib/vendor/blamejs/lib/subject.js +96 -49
- package/lib/vendor/blamejs/lib/vault/index.js +3 -2
- package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
- package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
- package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
- package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
- package/lib/vendor/blamejs/lib/websocket.js +35 -5
- package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
- package/lib/vendor/blamejs/package.json +2 -2
- package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
- package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
- package/lib/vendor/blamejs/scripts/check-services.js +21 -0
- package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
- package/lib/vendor/blamejs/scripts/release.js +398 -38
- package/lib/vendor/blamejs/test/00-primitives.js +117 -0
- package/lib/vendor/blamejs/test/10-state.js +140 -14
- package/lib/vendor/blamejs/test/20-db.js +65 -2
- package/lib/vendor/blamejs/test/helpers/db.js +9 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
- package/lib/vendor/blamejs/test/helpers/services.js +21 -0
- package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
- package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
- package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
- package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
- package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
- package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
- package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
- package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
- package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
- package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
- package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
- package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
- package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
- package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
- package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
- package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
- package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
- package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
- package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
- package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
- package/lib/vendor/blamejs/test/smoke.js +79 -21
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
- package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
- package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
- package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
- package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
- package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
- package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
- package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
- package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
- package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
- package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
- package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
- package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
- package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
|
@@ -57,13 +57,63 @@
|
|
|
57
57
|
* Audit: every limit hit emits system.ratelimit.block with the key + path.
|
|
58
58
|
*/
|
|
59
59
|
var C = require("../constants");
|
|
60
|
+
var frameworkSchema = require("../framework-schema");
|
|
60
61
|
var lazyRequire = require("../lazy-require");
|
|
61
62
|
var requestHelpers = require("../request-helpers");
|
|
62
63
|
var safeAsync = require("../safe-async");
|
|
64
|
+
var sql = require("../sql");
|
|
63
65
|
var validateOpts = require("../validate-opts");
|
|
64
66
|
var clusterStorage = require("../cluster-storage");
|
|
65
67
|
var denyResponse = require("./deny-response").denyResponse;
|
|
66
68
|
|
|
69
|
+
// Cluster-backend table — resolved through frameworkSchema.tableName so a
|
|
70
|
+
// configured table prefix (b.frameworkSchema.setTablePrefix) is honored.
|
|
71
|
+
// The name is identity-mapped in LOCAL_TO_EXTERNAL, so clusterStorage's
|
|
72
|
+
// resolveTables leaves it untouched at dispatch and the resolved name is
|
|
73
|
+
// what reaches the backend on both single-node + cluster sides.
|
|
74
|
+
var RATE_LIMIT_TABLE = "_blamejs_rate_limit_counters"; // allow:hand-rolled-sql — canonical logical table-name declaration
|
|
75
|
+
function _rateLimitSqlTable() { return frameworkSchema.tableName(RATE_LIMIT_TABLE); }
|
|
76
|
+
|
|
77
|
+
// b.sql opts for every cluster-backend statement: thread the ACTIVE backend
|
|
78
|
+
// dialect (clusterStorage.dialect() — "sqlite" single-node, "postgres" |
|
|
79
|
+
// "mysql" in cluster mode) so the emitted identifier quoting and dialect
|
|
80
|
+
// idioms (ON CONFLICT ... DO UPDATE vs ON DUPLICATE KEY UPDATE) match the
|
|
81
|
+
// backend the SQL dispatches to. b.sql defaults to "sqlite", which works on
|
|
82
|
+
// Postgres only by accident (both double-quote identifiers) and emits the
|
|
83
|
+
// wrong quoting + ON CONFLICT (which MySQL rejects) on MySQL.
|
|
84
|
+
// clusterStorage.execute still rewrites table names + translates `?`
|
|
85
|
+
// placeholders at dispatch; this controls only the builder-side quoting +
|
|
86
|
+
// idiom selection.
|
|
87
|
+
function _rateLimitSqlOpts() { return { dialect: clusterStorage.dialect() }; }
|
|
88
|
+
|
|
89
|
+
// Dialect-aware references for the conflict-action CASE expressions in
|
|
90
|
+
// take(). The fixed-window counter's update is per-column conditional (a new
|
|
91
|
+
// window resets count to 1; the same window increments), so it can't reduce
|
|
92
|
+
// to doUpdateFromExcluded — it needs a CASE that reads BOTH the proposed row
|
|
93
|
+
// and the existing row. Those two references are spelled differently per
|
|
94
|
+
// dialect and b.sql passes a doUpdate({col: rawExpr}) expression through
|
|
95
|
+
// verbatim (it is NOT EXCLUDED->VALUES translated on MySQL), so the caller
|
|
96
|
+
// must emit the dialect-correct tokens itself:
|
|
97
|
+
// - proposed-row column: EXCLUDED."<col>" (Postgres/SQLite) vs
|
|
98
|
+
// VALUES(`<col>`) (MySQL ON DUPLICATE KEY UPDATE)
|
|
99
|
+
// - existing-row column: "<table>"."<col>" (Postgres/SQLite) vs
|
|
100
|
+
// `<table>`.`<col>` (MySQL)
|
|
101
|
+
// Identifiers here are framework-controlled constants (the table name + the
|
|
102
|
+
// three counter columns), never operator input, so the inline quoting is
|
|
103
|
+
// closed over a fixed set of names.
|
|
104
|
+
function _conflictRefs(dialect, table) {
|
|
105
|
+
if (dialect === "mysql") {
|
|
106
|
+
return {
|
|
107
|
+
proposed: function (col) { return "VALUES(`" + col + "`)"; },
|
|
108
|
+
existing: function (col) { return "`" + table + "`.`" + col + "`"; },
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
proposed: function (col) { return "EXCLUDED.\"" + col + "\""; },
|
|
113
|
+
existing: function (col) { return "\"" + table + "\".\"" + col + "\""; },
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
67
117
|
var audit = lazyRequire(function () { return require("../audit"); });
|
|
68
118
|
var logger = lazyRequire(function () { return require("../log").boot("rate-limit"); });
|
|
69
119
|
|
|
@@ -260,10 +310,10 @@ function _clusterBackend(opts) {
|
|
|
260
310
|
if (now - lastPruneAt < pruneIntervalMs) return;
|
|
261
311
|
lastPruneAt = now;
|
|
262
312
|
var cutoff = now - windowMs;
|
|
263
|
-
|
|
264
|
-
"
|
|
265
|
-
|
|
266
|
-
).catch(function (e) {
|
|
313
|
+
var built = sql.delete(_rateLimitSqlTable(), _rateLimitSqlOpts())
|
|
314
|
+
.where("windowStart", "<", cutoff)
|
|
315
|
+
.toSql();
|
|
316
|
+
clusterStorage.execute(built.sql, built.params).catch(function (e) {
|
|
267
317
|
try {
|
|
268
318
|
logger().warn("rate-limit prune failed: " + ((e && e.message) || String(e)));
|
|
269
319
|
} catch (_e) { /* logger best-effort */ }
|
|
@@ -274,29 +324,49 @@ function _clusterBackend(opts) {
|
|
|
274
324
|
var now = Date.now();
|
|
275
325
|
var windowStart = Math.floor(now / windowMs) * windowMs;
|
|
276
326
|
|
|
277
|
-
// Atomic increment: a fresh window resets count to 1; an existing
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
"
|
|
293
|
-
|
|
294
|
-
"
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
327
|
+
// Atomic increment: a fresh window resets count to 1; an existing row in
|
|
328
|
+
// the same window gets count + 1. The per-column conflict action is a
|
|
329
|
+
// CASE that reads the proposed row AND the existing row, so it goes
|
|
330
|
+
// through the STRUCTURED upsert().doUpdate({...}) form with the dialect
|
|
331
|
+
// threaded — b.sql then renders ON CONFLICT...DO UPDATE...RETURNING
|
|
332
|
+
// (Postgres/SQLite) or ON DUPLICATE KEY UPDATE + a readback SELECT
|
|
333
|
+
// (MySQL). The CASE bodies spell the proposed-row (EXCLUDED / VALUES())
|
|
334
|
+
// and existing-row (table self-reference) tokens per dialect via
|
|
335
|
+
// _conflictRefs so the same logic compiles on every backend. No `?` in
|
|
336
|
+
// the CASE bodies; the count seed of 1 binds as the third inserted value.
|
|
337
|
+
var t = _rateLimitSqlTable();
|
|
338
|
+
var dialect = clusterStorage.dialect();
|
|
339
|
+
var refs = _conflictRefs(dialect, t);
|
|
340
|
+
var newerWindow = refs.proposed("windowStart") + " > " + refs.existing("windowStart");
|
|
341
|
+
var countExpr = "CASE WHEN " + newerWindow + " THEN 1 ELSE " +
|
|
342
|
+
refs.existing("count") + " + 1 END";
|
|
343
|
+
var windowExpr = "CASE WHEN " + newerWindow + " THEN " + refs.proposed("windowStart") +
|
|
344
|
+
" ELSE " + refs.existing("windowStart") + " END";
|
|
345
|
+
var built = sql.upsert(t, _rateLimitSqlOpts())
|
|
346
|
+
.columns(["key", "windowStart", "count"])
|
|
347
|
+
.values({ key: key, windowStart: windowStart, count: 1 })
|
|
348
|
+
.onConflict(["key"])
|
|
349
|
+
.doUpdate({ count: countExpr, windowStart: windowExpr })
|
|
350
|
+
.returning(["count", "windowStart"])
|
|
351
|
+
.toSql();
|
|
352
|
+
var row;
|
|
353
|
+
if (built.readbackSql) {
|
|
354
|
+
// MySQL: ON DUPLICATE KEY UPDATE has no RETURNING. Run the upsert,
|
|
355
|
+
// then the readback SELECT b.sql emits (keyed on the conflict key) to
|
|
356
|
+
// learn the post-upsert count/windowStart. clusterStorage.execute
|
|
357
|
+
// coerces the framework int columns (count/windowStart) back to JS
|
|
358
|
+
// numbers on both reads.
|
|
359
|
+
await clusterStorage.execute(built.sql, built.params);
|
|
360
|
+
var readback = await clusterStorage.execute(built.readbackSql.sql, built.readbackSql.params);
|
|
361
|
+
row = readback.rows && readback.rows[0];
|
|
362
|
+
} else {
|
|
363
|
+
var result = await clusterStorage.execute(built.sql, built.params);
|
|
364
|
+
row = result.rows && result.rows[0];
|
|
365
|
+
}
|
|
366
|
+
// count/windowStart are framework int columns coerced to JS numbers by
|
|
367
|
+
// clusterStorage; the absent-row fall-back keeps the verdict math finite.
|
|
368
|
+
var count = row ? Number(row.count) : 1;
|
|
369
|
+
var rowWindow = row ? Number(row.windowStart) : windowStart;
|
|
300
370
|
|
|
301
371
|
_maybePrune();
|
|
302
372
|
|
|
@@ -318,10 +388,10 @@ function _clusterBackend(opts) {
|
|
|
318
388
|
}
|
|
319
389
|
|
|
320
390
|
async function reset(key) {
|
|
321
|
-
|
|
322
|
-
"
|
|
323
|
-
|
|
324
|
-
);
|
|
391
|
+
var built = sql.delete(_rateLimitSqlTable(), _rateLimitSqlOpts())
|
|
392
|
+
.where("key", key)
|
|
393
|
+
.toSql();
|
|
394
|
+
await clusterStorage.execute(built.sql, built.params);
|
|
325
395
|
}
|
|
326
396
|
|
|
327
397
|
function close() { /* no resources to release */ }
|
|
@@ -417,7 +487,7 @@ function create(opts) {
|
|
|
417
487
|
// pass "RateLimit-", or a gateway's own prefix. Kept as a matched pair.
|
|
418
488
|
var headerPrefix = (typeof opts.headerPrefix === "string" && opts.headerPrefix.length > 0)
|
|
419
489
|
? opts.headerPrefix : "X-RateLimit-";
|
|
420
|
-
var limitHeader = headerPrefix + "Limit";
|
|
490
|
+
var limitHeader = headerPrefix + "Limit"; // allow:hand-rolled-sql — HTTP response-header name (X-RateLimit-Limit), not a SQL LIMIT clause
|
|
421
491
|
var remainingHeader = headerPrefix + "Remaining";
|
|
422
492
|
var skipPaths = opts.skipPaths || [];
|
|
423
493
|
// Throw at create(): each entry must be a string prefix or a RegExp.
|
|
@@ -10,8 +10,12 @@
|
|
|
10
10
|
* Referrer-Policy: no-referrer — don't leak full URL to outbound links
|
|
11
11
|
* Permissions-Policy — disable common-attack APIs (camera, geolocation, payment, etc.)
|
|
12
12
|
* Cross-Origin-Opener-Policy: same-origin
|
|
13
|
-
* Cross-Origin-Embedder-Policy:
|
|
14
|
-
*
|
|
13
|
+
* Cross-Origin-Embedder-Policy: credentialless (default-on — with COOP
|
|
14
|
+
* same-origin this yields cross-origin isolation; credentialless is the
|
|
15
|
+
* relaxed enforcing mode that lets cross-origin no-cors requests load
|
|
16
|
+
* without CORP markers as long as they don't carry credentials, so CDN
|
|
17
|
+
* images/fonts keep working. Pass coep: "require-corp" to tighten, or
|
|
18
|
+
* coep: false to disable.)
|
|
15
19
|
* Cross-Origin-Resource-Policy: same-origin
|
|
16
20
|
* Origin-Agent-Cluster: ?1 — origin-keyed agent cluster; extra process isolation
|
|
17
21
|
* X-DNS-Prefetch-Control: off — don't pre-resolve DNS for off-page links
|
|
@@ -173,8 +177,9 @@ function _validatePermissionsPolicy(value) {
|
|
|
173
177
|
* nosniff, X-Frame-Options DENY, Referrer-Policy no-referrer, an
|
|
174
178
|
* extensive Permissions-Policy denylist (camera / geolocation /
|
|
175
179
|
* payment / Privacy-Sandbox attribution-reporting / bluetooth /
|
|
176
|
-
* etc.), COOP same-origin,
|
|
177
|
-
*
|
|
180
|
+
* etc.), COOP same-origin, COEP credentialless (cross-origin isolation
|
|
181
|
+
* on by default; pass `coep: false` to disable), CORP same-origin,
|
|
182
|
+
* Origin-Agent-Cluster `?1`, and a strict default CSP with `require-trusted-types-for
|
|
178
183
|
* 'script'`. Each header can be softened by passing the option
|
|
179
184
|
* value or disabled by passing `false`. Mount FIRST (after
|
|
180
185
|
* `requestId`) so headers are set before any response could be
|
|
@@ -233,7 +238,18 @@ function create(opts) {
|
|
|
233
238
|
var refPolicy = opts.referrerPolicy === undefined ? "no-referrer" : opts.referrerPolicy;
|
|
234
239
|
var permPolicy = opts.permissionsPolicy === undefined ? DEFAULT_PERMISSIONS.join(", ") : opts.permissionsPolicy;
|
|
235
240
|
var coop = opts.coop === undefined ? "same-origin" : opts.coop;
|
|
236
|
-
|
|
241
|
+
// COEP default-on (v0.15.0): emit Cross-Origin-Embedder-Policy:
|
|
242
|
+
// credentialless. With COOP same-origin this completes cross-origin
|
|
243
|
+
// isolation (crossOriginIsolated === true), re-enabling SharedArrayBuffer
|
|
244
|
+
// / high-resolution timers while closing the Spectre-class cross-origin
|
|
245
|
+
// read surface. `credentialless` (HTML spec, shipped Chrome 110+) is the
|
|
246
|
+
// least-breaking enforcing mode: cross-origin no-cors subresources (CDN
|
|
247
|
+
// images, fonts) still load — they're fetched WITHOUT credentials rather
|
|
248
|
+
// than requiring an explicit CORP/CORS opt-in, so existing pages keep
|
|
249
|
+
// working where `require-corp` would have broken them. Operators serving
|
|
250
|
+
// credentialed cross-origin subresources pass coep: "require-corp" (and
|
|
251
|
+
// add CORP/CORS headers), or coep: false to opt out of COEP entirely.
|
|
252
|
+
var coep = opts.coep === undefined ? "credentialless" : opts.coep;
|
|
237
253
|
var corp = opts.corp === undefined ? "same-origin" : opts.corp;
|
|
238
254
|
var oac = opts.originAgentCluster === undefined ? "?1" : opts.originAgentCluster;
|
|
239
255
|
var dpc = opts.dnsPrefetchControl === undefined ? "off" : opts.dnsPrefetchControl;
|
|
@@ -41,10 +41,13 @@
|
|
|
41
41
|
var nodePath = require("node:path");
|
|
42
42
|
var atomicFile = require("./atomic-file");
|
|
43
43
|
var dbSchema = require("./db-schema");
|
|
44
|
+
var frameworkSchema = require("./framework-schema");
|
|
44
45
|
var lazyRequire = require("./lazy-require");
|
|
45
46
|
var { boot } = require("./log");
|
|
46
47
|
var migrationFiles = require("./migration-files");
|
|
47
48
|
var numericBounds = require("./numeric-bounds");
|
|
49
|
+
var safeSql = require("./safe-sql");
|
|
50
|
+
var sql = require("./sql");
|
|
48
51
|
var db = lazyRequire(function () { return require("./db"); });
|
|
49
52
|
var validateOpts = require("./validate-opts");
|
|
50
53
|
var { FrameworkError } = require("./framework-error");
|
|
@@ -60,11 +63,29 @@ class MigrationError extends FrameworkError {
|
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
|
|
66
|
+
// Logical names; the physical names resolve through
|
|
67
|
+
// frameworkSchema.tableName so a configured table prefix flows here too.
|
|
68
|
+
// SQL is composed with b.sql (quoteName: true) so the resolved name is
|
|
69
|
+
// quoted by construction — a reserved-word / whitespace-bearing name
|
|
70
|
+
// still emits a valid `"..."` identifier.
|
|
71
|
+
var MIGRATIONS_TABLE = "_blamejs_migrations"; // allow:hand-rolled-sql — logical name declaration; physical name + prefix resolve via frameworkSchema.tableName below
|
|
72
|
+
function _migrationsTable() { return frameworkSchema.tableName(MIGRATIONS_TABLE); }
|
|
73
|
+
// b.sql opts for the migration bookkeeping statements. db.prepare /
|
|
74
|
+
// runSqlOnHandle run these directly against the handle (never
|
|
75
|
+
// clusterStorage), so the dialect must match the handle: db.from()'s local
|
|
76
|
+
// node:sqlite default, or an operator's own Postgres / MySQL handle (which
|
|
77
|
+
// declares `handle.dialect`). The handle-dialect / opts / key-text-type
|
|
78
|
+
// resolution is shared with db-schema's reconciler + seeders.js, so it is
|
|
79
|
+
// composed from db-schema rather than re-derived here. The historical
|
|
80
|
+
// default (sqlite) is byte-identical for every existing local-handle caller.
|
|
81
|
+
var _handleDialect = dbSchema.handleDialect;
|
|
82
|
+
var _sqlOpts = dbSchema.sqlOpts;
|
|
83
|
+
var _keyTextType = dbSchema.keyTextType;
|
|
84
|
+
// A ms-epoch column type. Date.now() exceeds a 32-bit INTEGER, so the
|
|
85
|
+
// lock timestamp needs a 64-bit type on Postgres + MySQL (BIGINT) — b.sql's
|
|
86
|
+
// logical "int" resolves to BIGINT on both and INTEGER on SQLite, so passing
|
|
87
|
+
// the logical name through the handle dialect is enough.
|
|
88
|
+
var _MS_EPOCH_TYPE = "int";
|
|
68
89
|
// Filename grammar: leading numeric prefix (any width), then '-', then a
|
|
69
90
|
// non-empty body, then '.js'. Numeric prefix orders execution. Letters
|
|
70
91
|
// in the body include hyphens, underscores, and alphanumerics; anything
|
|
@@ -87,31 +108,36 @@ function _isMigrationFile(name) {
|
|
|
87
108
|
var _runSql = dbSchema.runSqlOnHandle;
|
|
88
109
|
|
|
89
110
|
function _ensureTable(db) {
|
|
90
|
-
_runSql(db,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"
|
|
94
|
-
|
|
95
|
-
")"
|
|
96
|
-
);
|
|
111
|
+
_runSql(db, sql.createTable(_migrationsTable(), [
|
|
112
|
+
{ name: "name", type: _keyTextType(db), primaryKey: true },
|
|
113
|
+
{ name: "description", type: "text" },
|
|
114
|
+
{ name: "appliedAt", type: "text", notNull: true },
|
|
115
|
+
], _sqlOpts(db)).sql);
|
|
97
116
|
}
|
|
98
117
|
|
|
99
118
|
// Single-row advisory-lock table. Two processes running `migrate up`
|
|
100
119
|
// concurrently against the same DB race on this table: the winner of
|
|
101
120
|
// the INSERT acquires the lock; the loser sees a UNIQUE violation and
|
|
102
121
|
// the operator gets a clear "lock held by other process" error.
|
|
103
|
-
var LOCK_TABLE
|
|
104
|
-
|
|
122
|
+
var LOCK_TABLE = "_blamejs_migrations_lock"; // allow:hand-rolled-sql — logical name declaration; physical name + prefix resolve via frameworkSchema.tableName below
|
|
123
|
+
function _lockTable() { return frameworkSchema.tableName(LOCK_TABLE); }
|
|
105
124
|
|
|
106
125
|
function _ensureLockTable(db) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
126
|
+
// The single-row invariant (CHECK scope = 'lock') is a static,
|
|
127
|
+
// framework-controlled column constraint — b.sql guards the verbatim
|
|
128
|
+
// fragment (allowLiterals) and quotes the column by construction. The
|
|
129
|
+
// CHECK references `scope` with the handle's identifier quoting (backtick
|
|
130
|
+
// on mysql) so the constraint parses on every dialect. lockedAt is an
|
|
131
|
+
// ms-epoch value (`int` → BIGINT on Postgres/MySQL, INTEGER on SQLite);
|
|
132
|
+
// a 32-bit INTEGER would overflow Date.now() and make the lock
|
|
133
|
+
// unacquirable on Postgres.
|
|
134
|
+
var dialect = _handleDialect(db);
|
|
135
|
+
var scopeCheck = "CHECK (" + safeSql.quoteIdentifier("scope", dialect, { allowReserved: true }) + " = 'lock')";
|
|
136
|
+
_runSql(db, sql.createTable(_lockTable(), [
|
|
137
|
+
{ name: "scope", type: _keyTextType(db), primaryKey: true, constraints: scopeCheck },
|
|
138
|
+
{ name: "lockedAt", type: _MS_EPOCH_TYPE, notNull: true },
|
|
139
|
+
{ name: "lockedBy", type: "text", notNull: true },
|
|
140
|
+
], _sqlOpts(db)).sql);
|
|
115
141
|
}
|
|
116
142
|
|
|
117
143
|
function _lockHolderId() {
|
|
@@ -139,23 +165,24 @@ function _acquireLock(db, opts) {
|
|
|
139
165
|
} else {
|
|
140
166
|
staleAfterMs = opts.staleAfterMs;
|
|
141
167
|
}
|
|
168
|
+
var insertLock = sql.insert(_lockTable(), _sqlOpts(db))
|
|
169
|
+
.values({ scope: "lock", lockedAt: nowMs, lockedBy: holder }).toSql();
|
|
142
170
|
// Try to insert; if there's a stale lock, optionally force-replace it.
|
|
143
171
|
try {
|
|
144
|
-
db.prepare(
|
|
145
|
-
|
|
146
|
-
).run(nowMs, holder);
|
|
172
|
+
var insStmt = db.prepare(insertLock.sql);
|
|
173
|
+
insStmt.run.apply(insStmt, insertLock.params);
|
|
147
174
|
return holder;
|
|
148
175
|
} catch {
|
|
149
176
|
// PRIMARY KEY conflict → existing lock. Inspect it.
|
|
150
|
-
var
|
|
151
|
-
"
|
|
152
|
-
|
|
177
|
+
var selExisting = sql.select(_lockTable(), _sqlOpts(db))
|
|
178
|
+
.columns(["lockedAt", "lockedBy"]).where("scope", "lock").toSql();
|
|
179
|
+
var selStmt = db.prepare(selExisting.sql);
|
|
180
|
+
var existing = selStmt.get.apply(selStmt, selExisting.params);
|
|
153
181
|
if (!existing) {
|
|
154
182
|
// Race window between INSERT failure and SELECT — try once more.
|
|
155
183
|
try {
|
|
156
|
-
db.prepare(
|
|
157
|
-
|
|
158
|
-
).run(nowMs, holder);
|
|
184
|
+
var retryStmt = db.prepare(insertLock.sql);
|
|
185
|
+
retryStmt.run.apply(retryStmt, insertLock.params);
|
|
159
186
|
return holder;
|
|
160
187
|
} catch (e2) {
|
|
161
188
|
throw new MigrationError("migrations/lock-busy",
|
|
@@ -165,25 +192,32 @@ function _acquireLock(db, opts) {
|
|
|
165
192
|
}
|
|
166
193
|
var ageMs = nowMs - Number(existing.lockedAt);
|
|
167
194
|
if (staleAfterMs > 0 && ageMs > staleAfterMs) {
|
|
168
|
-
// Force-replace the stale lock.
|
|
169
|
-
//
|
|
170
|
-
|
|
195
|
+
// Force-replace the stale lock. The DELETE + INSERT run in a single
|
|
196
|
+
// transaction so the next process can't slip in between. The
|
|
197
|
+
// transaction boundary is dialect-aware: only SQLite has the
|
|
198
|
+
// `BEGIN IMMEDIATE` write-lock-up-front form — Postgres + MySQL
|
|
199
|
+
// reject the `IMMEDIATE` keyword, so the shared runInTransaction
|
|
200
|
+
// helper emits a plain portable `BEGIN`/`COMMIT`/`ROLLBACK` there.
|
|
201
|
+
var lockMode = _handleDialect(db) === "sqlite" ? "IMMEDIATE" : null;
|
|
171
202
|
try {
|
|
172
|
-
|
|
173
|
-
.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
203
|
+
return dbSchema.runInTransaction(db, function () {
|
|
204
|
+
var delStale = sql.delete(_lockTable(), _sqlOpts(db))
|
|
205
|
+
.where("scope", "lock").where("lockedAt", existing.lockedAt).toSql();
|
|
206
|
+
var delStaleStmt = db.prepare(delStale.sql);
|
|
207
|
+
delStaleStmt.run.apply(delStaleStmt, delStale.params);
|
|
208
|
+
var replStmt = db.prepare(insertLock.sql);
|
|
209
|
+
replStmt.run.apply(replStmt, insertLock.params);
|
|
210
|
+
return holder;
|
|
211
|
+
}, {
|
|
212
|
+
lockMode: lockMode,
|
|
213
|
+
onRollbackFail: function (rollbackErr) {
|
|
214
|
+
log.debug("rollback-failed", {
|
|
215
|
+
op: "lock-stale-replace",
|
|
216
|
+
error: rollbackErr && rollbackErr.message,
|
|
217
|
+
});
|
|
218
|
+
},
|
|
219
|
+
});
|
|
179
220
|
} catch (forceErr) {
|
|
180
|
-
try { _runSql(db, "ROLLBACK"); }
|
|
181
|
-
catch (rollbackErr) {
|
|
182
|
-
log.debug("rollback-failed", {
|
|
183
|
-
op: "lock-stale-replace",
|
|
184
|
-
error: rollbackErr && rollbackErr.message,
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
221
|
throw new MigrationError("migrations/lock-stale-replace-failed",
|
|
188
222
|
"could not replace stale lock: " + ((forceErr && forceErr.message) || String(forceErr)),
|
|
189
223
|
true);
|
|
@@ -202,9 +236,10 @@ function _releaseLock(db, holder) {
|
|
|
202
236
|
// shouldn't have its lock cleared by an unrelated next deploy unless
|
|
203
237
|
// the operator explicitly used the staleAfterMs nodePath.
|
|
204
238
|
try {
|
|
205
|
-
|
|
206
|
-
"
|
|
207
|
-
|
|
239
|
+
var rel = sql.delete(_lockTable(), _sqlOpts(db))
|
|
240
|
+
.where("scope", "lock").where("lockedBy", holder).toSql();
|
|
241
|
+
var relStmt = db.prepare(rel.sql);
|
|
242
|
+
relStmt.run.apply(relStmt, rel.params);
|
|
208
243
|
} catch (_e) { /* best-effort release; operator can DELETE manually */ }
|
|
209
244
|
}
|
|
210
245
|
|
|
@@ -271,10 +306,11 @@ function create(opts) {
|
|
|
271
306
|
function _appliedRows() {
|
|
272
307
|
var db = _resolveDb(opts);
|
|
273
308
|
_ensureTable(db);
|
|
274
|
-
|
|
275
|
-
"
|
|
276
|
-
"
|
|
277
|
-
|
|
309
|
+
var q = sql.select(_migrationsTable(), _sqlOpts(db))
|
|
310
|
+
.columns(["name", "description", "appliedAt"])
|
|
311
|
+
.orderBy("appliedAt", "asc").orderBy("name", "asc").toSql();
|
|
312
|
+
var stmt = db.prepare(q.sql);
|
|
313
|
+
return stmt.all.apply(stmt, q.params);
|
|
278
314
|
}
|
|
279
315
|
|
|
280
316
|
function status() {
|
|
@@ -293,8 +329,10 @@ function create(opts) {
|
|
|
293
329
|
var db = _resolveDb(opts);
|
|
294
330
|
_ensureTable(db);
|
|
295
331
|
return _withLock(db, opts, function () {
|
|
332
|
+
var namesQ = sql.select(_migrationsTable(), _sqlOpts(db)).columns(["name"]).toSql();
|
|
333
|
+
var namesStmt = db.prepare(namesQ.sql);
|
|
296
334
|
var appliedSet = new Set(
|
|
297
|
-
|
|
335
|
+
namesStmt.all.apply(namesStmt, namesQ.params)
|
|
298
336
|
.map(function (r) { return r.name; })
|
|
299
337
|
);
|
|
300
338
|
var files = _list(dir);
|
|
@@ -307,10 +345,11 @@ function create(opts) {
|
|
|
307
345
|
try {
|
|
308
346
|
_txn(db, function () {
|
|
309
347
|
mod.up(db);
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
348
|
+
var insQ = sql.insert(_migrationsTable(), _sqlOpts(db))
|
|
349
|
+
.values({ name: file, description: mod.description || "",
|
|
350
|
+
appliedAt: new Date().toISOString() }).toSql();
|
|
351
|
+
var insStmt = db.prepare(insQ.sql);
|
|
352
|
+
insStmt.run.apply(insStmt, insQ.params);
|
|
314
353
|
});
|
|
315
354
|
} catch (e) {
|
|
316
355
|
throw new MigrationError("migrations/up-failed",
|
|
@@ -336,11 +375,12 @@ function create(opts) {
|
|
|
336
375
|
return _withLock(db, opts, function () {
|
|
337
376
|
// Most-recent applied first (reverse chronological by appliedAt
|
|
338
377
|
// then by name as a stable tiebreaker for fixtures with identical
|
|
339
|
-
// timestamps).
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
"
|
|
343
|
-
|
|
378
|
+
// timestamps). steps is a validated positive integer, so b.sql
|
|
379
|
+
// inlines the LIMIT.
|
|
380
|
+
var downQ = sql.select(_migrationsTable(), _sqlOpts(db)).columns(["name"])
|
|
381
|
+
.orderBy("appliedAt", "desc").orderBy("name", "desc").limit(steps).toSql();
|
|
382
|
+
var downStmt = db.prepare(downQ.sql);
|
|
383
|
+
var rows = downStmt.all.apply(downStmt, downQ.params);
|
|
344
384
|
|
|
345
385
|
var reverted = [];
|
|
346
386
|
for (var i = 0; i < rows.length; i++) {
|
|
@@ -355,7 +395,9 @@ function create(opts) {
|
|
|
355
395
|
try {
|
|
356
396
|
_txn(db, function () {
|
|
357
397
|
mod.down(db);
|
|
358
|
-
|
|
398
|
+
var delQ = sql.delete(_migrationsTable(), _sqlOpts(db)).where("name", file).toSql();
|
|
399
|
+
var delStmt = db.prepare(delQ.sql);
|
|
400
|
+
delStmt.run.apply(delStmt, delQ.params);
|
|
359
401
|
});
|
|
360
402
|
} catch (e) {
|
|
361
403
|
throw new MigrationError("migrations/down-failed",
|
|
@@ -72,6 +72,13 @@ function _probeHttp(target, timeoutMs) {
|
|
|
72
72
|
url: target.url,
|
|
73
73
|
method: target.method || "GET",
|
|
74
74
|
timeoutMs: timeoutMs,
|
|
75
|
+
// Forward the target's protocol/host allowlists so an operator who
|
|
76
|
+
// opts a cleartext http:// heartbeat in (allowedProtocols:
|
|
77
|
+
// b.safeUrl.ALLOW_HTTP_ALL) is honoured. Left undefined, httpClient
|
|
78
|
+
// applies its https-only default (ALLOW_HTTP_TLS) — so an http://
|
|
79
|
+
// target with no opt-in is still rejected, not silently probed.
|
|
80
|
+
allowedProtocols: target.allowedProtocols,
|
|
81
|
+
allowedHosts: target.allowedHosts,
|
|
75
82
|
allowInternal: target.allowInternal === true ? true : target.allowInternal,
|
|
76
83
|
});
|
|
77
84
|
p.then(function (res) {
|
|
@@ -20,6 +20,11 @@ var DEFAULT_HTTPS_PORT = 443; // RFC 9110 §4.2.2
|
|
|
20
20
|
var DEFAULT_HTTP_PORT = C.BYTES.bytes(80); // RFC 9110 §4.2.1
|
|
21
21
|
|
|
22
22
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
23
|
+
// Lazy so pqc-agent's TLS/audit graph isn't pulled into every process that
|
|
24
|
+
// imports network-proxy but never proxies an https upstream. Used only to audit
|
|
25
|
+
// a classical-group fallback on a proxy-tunneled TLS handshake (the direct path
|
|
26
|
+
// audits in pqc-agent.create()).
|
|
27
|
+
var pqcAgent = lazyRequire(function () { return require("./pqc-agent"); });
|
|
23
28
|
|
|
24
29
|
var STATE = {
|
|
25
30
|
http: null,
|
|
@@ -167,6 +172,13 @@ function _connectThroughTunnel(proxyUrl, targetHost, targetPort, callback) {
|
|
|
167
172
|
function done(err, sock) { if (settled) return; settled = true; callback(err, sock); }
|
|
168
173
|
proxySocket.on("error", function (e) { done(e); });
|
|
169
174
|
proxySocket.on(proxyUrl.protocol === "https:" ? "secureConnect" : "connect", function () {
|
|
175
|
+
if (proxyUrl.protocol === "https:") {
|
|
176
|
+
// The CONNECT-tunnel leg to an https proxy is itself a TLS handshake;
|
|
177
|
+
// audit a classical fallback to the proxy too, not only to the upstream.
|
|
178
|
+
pqcAgent()._auditClassicalDowngrade(proxySocket, {
|
|
179
|
+
host: proxyUrl.hostname, port: proxyPort,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
170
182
|
var lines = [
|
|
171
183
|
"CONNECT " + targetHost + ":" + targetPort + " HTTP/1.1",
|
|
172
184
|
"Host: " + targetHost + ":" + targetPort,
|
|
@@ -218,7 +230,18 @@ function agentFor(targetUrl) {
|
|
|
218
230
|
minVersion: "TLSv1.3",
|
|
219
231
|
ecdhCurve: C.TLS_GROUP_CURVE_STR,
|
|
220
232
|
ALPNProtocols: options.ALPNProtocols,
|
|
221
|
-
}, function () {
|
|
233
|
+
}, function () {
|
|
234
|
+
// Audit a classical-group fallback on the upstream (target) handshake
|
|
235
|
+
// reached through the proxy tunnel, so the "every outbound TLS path
|
|
236
|
+
// emits tls.classical_downgrade" guarantee holds for proxied requests
|
|
237
|
+
// too (the direct path audits in pqc-agent.create). Drop-silent; the
|
|
238
|
+
// handshake itself is unchanged (still hybrid-preferred TLSv1.3).
|
|
239
|
+
pqcAgent()._auditClassicalDowngrade(secure, {
|
|
240
|
+
host: options.servername || options.host,
|
|
241
|
+
port: options.port,
|
|
242
|
+
});
|
|
243
|
+
cb(null, secure);
|
|
244
|
+
});
|
|
222
245
|
secure.on("error", function (e) { cb(e); });
|
|
223
246
|
});
|
|
224
247
|
};
|
|
@@ -43,10 +43,30 @@
|
|
|
43
43
|
|
|
44
44
|
var clusterStorage = require("./cluster-storage");
|
|
45
45
|
var C = require("./constants");
|
|
46
|
+
var frameworkSchema = require("./framework-schema");
|
|
46
47
|
var safeAsync = require("./safe-async");
|
|
48
|
+
var sql = require("./sql");
|
|
47
49
|
var { defineClass } = require("./framework-error");
|
|
48
50
|
var { boundedMap } = require("./bounded-map");
|
|
49
51
|
|
|
52
|
+
// Cluster-backend table — resolved through frameworkSchema.tableName so a
|
|
53
|
+
// configured table prefix (b.frameworkSchema.setTablePrefix) is honored.
|
|
54
|
+
// The name is identity-mapped in LOCAL_TO_EXTERNAL, so clusterStorage's
|
|
55
|
+
// resolveTables leaves it untouched at dispatch and the resolved name is
|
|
56
|
+
// what reaches the backend on both sides.
|
|
57
|
+
var NONCE_TABLE = "_blamejs_api_encrypt_nonces"; // allow:hand-rolled-sql — canonical logical table-name declaration
|
|
58
|
+
|
|
59
|
+
// b.sql opts for every cluster-backend statement: thread the ACTIVE backend
|
|
60
|
+
// dialect (clusterStorage.dialect() — "sqlite" single-node, "postgres" |
|
|
61
|
+
// "mysql" in cluster mode) so the emitted identifier quoting and dialect
|
|
62
|
+
// idioms (ON CONFLICT vs ON DUPLICATE KEY) match the backend the SQL
|
|
63
|
+
// dispatches to. Defaulting to "sqlite" works on Postgres only by accident
|
|
64
|
+
// (both double-quote identifiers) and emits invalid quoting + ON CONFLICT on
|
|
65
|
+
// MySQL. clusterStorage.execute still rewrites table names + translates `?`
|
|
66
|
+
// placeholders at dispatch; this controls only the builder-side quoting +
|
|
67
|
+
// idiom selection.
|
|
68
|
+
function _nonceSqlOpts() { return { dialect: clusterStorage.dialect() }; }
|
|
69
|
+
|
|
50
70
|
var NonceStoreError = defineClass("NonceStoreError");
|
|
51
71
|
|
|
52
72
|
var DEFAULT_SWEEP_INTERVAL_MS = C.TIME.minutes(5);
|
|
@@ -148,19 +168,21 @@ function _clusterBackend(_opts) {
|
|
|
148
168
|
// (someone else already inserted the same nonce, i.e. replay).
|
|
149
169
|
// The middleware hashes the raw nonce before passing it here so
|
|
150
170
|
// the table only ever sees hashes, not the originals.
|
|
151
|
-
var
|
|
152
|
-
"
|
|
153
|
-
|
|
154
|
-
[
|
|
155
|
-
|
|
171
|
+
var built = sql.upsert(frameworkSchema.tableName(NONCE_TABLE), _nonceSqlOpts())
|
|
172
|
+
.columns(["nonceHash", "expireAt"])
|
|
173
|
+
.values({ nonceHash: nonce, expireAt: expireAt })
|
|
174
|
+
.onConflict(["nonceHash"])
|
|
175
|
+
.doNothing()
|
|
176
|
+
.toSql();
|
|
177
|
+
var result = await clusterStorage.execute(built.sql, built.params);
|
|
156
178
|
return (result && result.rowCount > 0);
|
|
157
179
|
}
|
|
158
180
|
|
|
159
181
|
async function purgeExpired() {
|
|
160
|
-
var
|
|
161
|
-
"
|
|
162
|
-
|
|
163
|
-
);
|
|
182
|
+
var built = sql.delete(frameworkSchema.tableName(NONCE_TABLE), _nonceSqlOpts())
|
|
183
|
+
.where("expireAt", "<=", Date.now())
|
|
184
|
+
.toSql();
|
|
185
|
+
var result = await clusterStorage.execute(built.sql, built.params);
|
|
164
186
|
return (result && result.rowCount) || 0;
|
|
165
187
|
}
|
|
166
188
|
|