@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
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
|
|
78
78
|
var C = require("./constants");
|
|
79
79
|
var { defineClass } = require("./framework-error");
|
|
80
|
+
var gateContract = require("./gate-contract");
|
|
80
81
|
|
|
81
82
|
var SafeIcapError = defineClass("SafeIcapError", { alwaysPermanent: true });
|
|
82
83
|
|
|
@@ -131,11 +132,15 @@ var PROFILES = Object.freeze({
|
|
|
131
132
|
},
|
|
132
133
|
});
|
|
133
134
|
|
|
134
|
-
var COMPLIANCE_POSTURES =
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
|
|
136
|
+
|
|
137
|
+
var _resolveProfile = gateContract.makeProfileResolver({
|
|
138
|
+
profiles: PROFILES,
|
|
139
|
+
postures: COMPLIANCE_POSTURES,
|
|
140
|
+
defaults: DEFAULT_PROFILE,
|
|
141
|
+
errorClass: SafeIcapError,
|
|
142
|
+
codePrefix: "safe-icap",
|
|
143
|
+
byObject: true,
|
|
139
144
|
});
|
|
140
145
|
|
|
141
146
|
/**
|
|
@@ -257,22 +262,6 @@ function parse(buf, opts) {
|
|
|
257
262
|
};
|
|
258
263
|
}
|
|
259
264
|
|
|
260
|
-
/**
|
|
261
|
-
* @primitive b.safeIcap.compliancePosture
|
|
262
|
-
* @signature b.safeIcap.compliancePosture(posture)
|
|
263
|
-
* @since 0.9.81
|
|
264
|
-
* @status stable
|
|
265
|
-
*
|
|
266
|
-
* Return the effective profile name for a compliance posture, or
|
|
267
|
-
* `null` for unknown posture names.
|
|
268
|
-
*
|
|
269
|
-
* @example
|
|
270
|
-
* b.safeIcap.compliancePosture("hipaa"); // → "strict"
|
|
271
|
-
*/
|
|
272
|
-
function compliancePosture(posture) {
|
|
273
|
-
return COMPLIANCE_POSTURES[posture] || null;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
265
|
// ---- internals ----
|
|
277
266
|
|
|
278
267
|
function _findHeaderEnd(buf, maxHeaderBytes) {
|
|
@@ -479,25 +468,20 @@ function _detectThreat(statusCode, headers) {
|
|
|
479
468
|
return { found: found, name: name };
|
|
480
469
|
}
|
|
481
470
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
ALLOWED_STATUS: ALLOWED_STATUS,
|
|
500
|
-
SafeIcapError: SafeIcapError,
|
|
501
|
-
NAME: "icap",
|
|
502
|
-
KIND: "icap-response",
|
|
503
|
-
};
|
|
471
|
+
// compliancePosture is assembled by gateContract.defineParser below; its
|
|
472
|
+
// wiki section renders from the single-sourced @abiTemplate (defineParser)
|
|
473
|
+
// block in gate-contract.js, instantiated for this guard by the page
|
|
474
|
+
// generator.
|
|
475
|
+
module.exports = gateContract.defineParser({
|
|
476
|
+
name: "icap",
|
|
477
|
+
entry: parse,
|
|
478
|
+
entryName: "parse",
|
|
479
|
+
errorClass: SafeIcapError,
|
|
480
|
+
profiles: PROFILES,
|
|
481
|
+
postures: COMPLIANCE_POSTURES,
|
|
482
|
+
extra: {
|
|
483
|
+
ALLOWED_STATUS: ALLOWED_STATUS,
|
|
484
|
+
NAME: "icap",
|
|
485
|
+
KIND: "icap-response",
|
|
486
|
+
},
|
|
487
|
+
});
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
*/
|
|
50
50
|
|
|
51
51
|
var { defineClass } = require("./framework-error");
|
|
52
|
+
var gateContract = require("./gate-contract");
|
|
52
53
|
|
|
53
54
|
var SafeSieveError = defineClass("SafeSieveError", { alwaysPermanent: true });
|
|
54
55
|
|
|
@@ -82,12 +83,7 @@ var PROFILES = Object.freeze({
|
|
|
82
83
|
}),
|
|
83
84
|
});
|
|
84
85
|
|
|
85
|
-
var COMPLIANCE_POSTURES =
|
|
86
|
-
hipaa: "strict",
|
|
87
|
-
"pci-dss": "strict",
|
|
88
|
-
gdpr: "strict",
|
|
89
|
-
soc2: "strict",
|
|
90
|
-
});
|
|
86
|
+
var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
|
|
91
87
|
|
|
92
88
|
// RFC 5228 §1.2 capability identifiers. Each entry lists whether the
|
|
93
89
|
// framework's v0.9.55 interpreter implements the capability. Unknown
|
|
@@ -648,37 +644,22 @@ function validate(script, opts) {
|
|
|
648
644
|
}
|
|
649
645
|
}
|
|
650
646
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
return COMPLIANCE_POSTURES[name] || null;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
module.exports = {
|
|
674
|
-
parse: parse,
|
|
675
|
-
validate: validate,
|
|
676
|
-
compliancePosture: compliancePosture,
|
|
677
|
-
KNOWN_CAPABILITIES: KNOWN_CAPABILITIES,
|
|
678
|
-
PROFILES: PROFILES,
|
|
679
|
-
COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
|
|
680
|
-
SafeSieveError: SafeSieveError,
|
|
681
|
-
// Internal exports for the interpreter at lib/mail-sieve.js.
|
|
682
|
-
_tokenize: _tokenize,
|
|
683
|
-
_resolveCaps: _resolveCaps,
|
|
684
|
-
};
|
|
647
|
+
// compliancePosture is assembled by gateContract.defineParser below; its
|
|
648
|
+
// wiki section renders from the single-sourced @abiTemplate (defineParser)
|
|
649
|
+
// block in gate-contract.js, instantiated for this guard by the page
|
|
650
|
+
// generator.
|
|
651
|
+
module.exports = gateContract.defineParser({
|
|
652
|
+
name: "sieve",
|
|
653
|
+
entry: parse,
|
|
654
|
+
entryName: "parse",
|
|
655
|
+
errorClass: SafeSieveError,
|
|
656
|
+
profiles: PROFILES,
|
|
657
|
+
postures: COMPLIANCE_POSTURES,
|
|
658
|
+
extra: {
|
|
659
|
+
validate: validate,
|
|
660
|
+
KNOWN_CAPABILITIES: KNOWN_CAPABILITIES,
|
|
661
|
+
// Internal exports for the interpreter at lib/mail-sieve.js.
|
|
662
|
+
_tokenize: _tokenize,
|
|
663
|
+
_resolveCaps: _resolveCaps,
|
|
664
|
+
},
|
|
665
|
+
});
|
|
@@ -175,7 +175,7 @@ function validateIdentifier(name, opts) {
|
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
177
|
* @primitive b.safeSql.quoteIdentifier
|
|
178
|
-
* @signature b.safeSql.quoteIdentifier(name, dialect?)
|
|
178
|
+
* @signature b.safeSql.quoteIdentifier(name, dialect?, opts?)
|
|
179
179
|
* @since 0.1.0
|
|
180
180
|
* @status stable
|
|
181
181
|
* @related b.safeSql.validateIdentifier, b.safeSql.quoteQualified
|
|
@@ -185,6 +185,17 @@ function validateIdentifier(name, opts) {
|
|
|
185
185
|
* MySQL. Default dialect is `"sqlite"`. Throws `SafeSqlError` if the
|
|
186
186
|
* identifier fails `validateIdentifier`.
|
|
187
187
|
*
|
|
188
|
+
* `opts` is forwarded to `validateIdentifier` — pass
|
|
189
|
+
* `{ allowReserved: true }` to quote a name that collides with a SQL
|
|
190
|
+
* keyword (a column literally named `from` / `select`). Quoting is
|
|
191
|
+
* exactly what makes a reserved word safe in identifier position, so the
|
|
192
|
+
* query builder (`b.sql`) routes every identifier through here with
|
|
193
|
+
* `allowReserved` on; the default still rejects reserved words so a bare
|
|
194
|
+
* caller catches the likely typo.
|
|
195
|
+
*
|
|
196
|
+
* @opts
|
|
197
|
+
* allowReserved: boolean, // default: false — permit SQL-keyword names (safe once quoted)
|
|
198
|
+
*
|
|
188
199
|
* @example
|
|
189
200
|
* var b = require("blamejs");
|
|
190
201
|
* b.safeSql.quoteIdentifier("users");
|
|
@@ -193,11 +204,14 @@ function validateIdentifier(name, opts) {
|
|
|
193
204
|
* b.safeSql.quoteIdentifier("Order", "postgres");
|
|
194
205
|
* // → '"Order"'
|
|
195
206
|
*
|
|
207
|
+
* b.safeSql.quoteIdentifier("from", "postgres", { allowReserved: true });
|
|
208
|
+
* // → '"from"'
|
|
209
|
+
*
|
|
196
210
|
* b.safeSql.quoteIdentifier("users", "mysql");
|
|
197
211
|
* // → "`users`"
|
|
198
212
|
*/
|
|
199
|
-
function quoteIdentifier(name, dialect) {
|
|
200
|
-
validateIdentifier(name);
|
|
213
|
+
function quoteIdentifier(name, dialect, opts) {
|
|
214
|
+
validateIdentifier(name, opts);
|
|
201
215
|
dialect = (dialect || "sqlite").toLowerCase();
|
|
202
216
|
if (dialect === "mysql") return "`" + name + "`";
|
|
203
217
|
// sqlite + postgres both use double-quote per SQL standard
|
|
@@ -258,6 +272,53 @@ function quoteQualified(parts, dialect) {
|
|
|
258
272
|
return quoted.join(".");
|
|
259
273
|
}
|
|
260
274
|
|
|
275
|
+
/**
|
|
276
|
+
* @primitive b.safeSql.quoteList
|
|
277
|
+
* @signature b.safeSql.quoteList(names, dialect?, opts?)
|
|
278
|
+
* @since 0.15.0
|
|
279
|
+
* @status stable
|
|
280
|
+
* @related b.safeSql.quoteIdentifier, b.safeSql.quoteQualified, b.sql
|
|
281
|
+
*
|
|
282
|
+
* Quote a list of identifiers into a comma-joined fragment — each name
|
|
283
|
+
* validated + quoted via `quoteIdentifier`. The "many" companion to
|
|
284
|
+
* `quoteIdentifier` (one) and `quoteQualified` (a dotted name): use it for
|
|
285
|
+
* SELECT projections and INSERT column lists so the recurring
|
|
286
|
+
* `cols.map(quoteIdentifier).join(", ")` shape is composed, not hand-rolled.
|
|
287
|
+
*
|
|
288
|
+
* There is deliberately NO value/string-literal quoter in this module:
|
|
289
|
+
* values flow as bound placeholders (`?` / `$N`), never interpolated, which
|
|
290
|
+
* is what makes the injection class structurally impossible. Quoting a
|
|
291
|
+
* literal would reopen it — use the query builder's parameter binding.
|
|
292
|
+
*
|
|
293
|
+
* `opts` is forwarded to each `quoteIdentifier` (e.g.
|
|
294
|
+
* `{ allowReserved: true }` for column lists that may contain SQL-keyword
|
|
295
|
+
* names, as `b.sql` does).
|
|
296
|
+
*
|
|
297
|
+
* Throws `SafeSqlError` (`sql/empty`) on an empty array and (per
|
|
298
|
+
* `quoteIdentifier`) on any invalid identifier.
|
|
299
|
+
*
|
|
300
|
+
* @opts
|
|
301
|
+
* allowReserved: boolean, // default: false — forwarded to quoteIdentifier
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* var b = require("blamejs");
|
|
305
|
+
* b.safeSql.quoteList(["id", "createdAt"], "postgres");
|
|
306
|
+
* // → '"id", "createdAt"'
|
|
307
|
+
*
|
|
308
|
+
* b.safeSql.quoteList(["queueName", "status"], "mysql");
|
|
309
|
+
* // → "`queueName`, `status`"
|
|
310
|
+
*/
|
|
311
|
+
function quoteList(names, dialect, opts) {
|
|
312
|
+
if (!Array.isArray(names) || names.length === 0) {
|
|
313
|
+
throw new SafeSqlError("quoteList requires a non-empty array of identifiers", "sql/empty");
|
|
314
|
+
}
|
|
315
|
+
var out = [];
|
|
316
|
+
for (var i = 0; i < names.length; i++) {
|
|
317
|
+
out.push(quoteIdentifier(names[i], dialect, opts));
|
|
318
|
+
}
|
|
319
|
+
return out.join(", ");
|
|
320
|
+
}
|
|
321
|
+
|
|
261
322
|
/**
|
|
262
323
|
* @primitive b.safeSql.assertOneOf
|
|
263
324
|
* @signature b.safeSql.assertOneOf(name, allowlist)
|
|
@@ -310,6 +371,64 @@ function assertOneOf(name, allowlist) {
|
|
|
310
371
|
return name;
|
|
311
372
|
}
|
|
312
373
|
|
|
374
|
+
/**
|
|
375
|
+
* @primitive b.safeSql.countPlaceholders
|
|
376
|
+
* @signature b.safeSql.countPlaceholders(sql)
|
|
377
|
+
* @since 0.14.29
|
|
378
|
+
* @status stable
|
|
379
|
+
* @related b.safeSql.quoteIdentifier, b.safeSql.validateIdentifier
|
|
380
|
+
*
|
|
381
|
+
* Count the bound `?` placeholders in a SQL string, skipping any `?`
|
|
382
|
+
* that appears inside a string literal (`'...'` / `"..."`, doubled-quote
|
|
383
|
+
* escape aware) or inside a line or block comment. The canonical quote-
|
|
384
|
+
* and comment-aware scanner the query builder uses to check placeholder /
|
|
385
|
+
* param parity and the residency write-gate uses to align bound values;
|
|
386
|
+
* both compose this so the skip rules live in one place.
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* var b = require("blamejs");
|
|
390
|
+
* b.safeSql.countPlaceholders("a = ? AND b = ?");
|
|
391
|
+
* // → 2
|
|
392
|
+
*
|
|
393
|
+
* b.safeSql.countPlaceholders("note = 'is ? literal' AND id = ?");
|
|
394
|
+
* // → 1
|
|
395
|
+
*/
|
|
396
|
+
function countPlaceholders(sql) {
|
|
397
|
+
var count = 0;
|
|
398
|
+
var i = 0;
|
|
399
|
+
var len = sql.length;
|
|
400
|
+
while (i < len) {
|
|
401
|
+
var ch = sql.charAt(i);
|
|
402
|
+
var next = i + 1 < len ? sql.charAt(i + 1) : "";
|
|
403
|
+
if (ch === "'" || ch === '"') {
|
|
404
|
+
var quote = ch;
|
|
405
|
+
i += 1;
|
|
406
|
+
while (i < len) {
|
|
407
|
+
if (sql.charAt(i) === quote) {
|
|
408
|
+
// SQL doubles the quote char to escape it within a literal.
|
|
409
|
+
if (sql.charAt(i + 1) === quote) { i += 2; continue; }
|
|
410
|
+
i += 1; break;
|
|
411
|
+
}
|
|
412
|
+
i += 1;
|
|
413
|
+
}
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (ch === "-" && next === "-") {
|
|
417
|
+
while (i < len && sql.charAt(i) !== "\n") i += 1;
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
if (ch === "/" && next === "*") {
|
|
421
|
+
i += 2;
|
|
422
|
+
while (i < len && !(sql.charAt(i) === "*" && sql.charAt(i + 1) === "/")) i += 1;
|
|
423
|
+
i += 2;
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
if (ch === "?") count += 1;
|
|
427
|
+
i += 1;
|
|
428
|
+
}
|
|
429
|
+
return count;
|
|
430
|
+
}
|
|
431
|
+
|
|
313
432
|
/**
|
|
314
433
|
* @primitive b.safeSql.DEFAULT_IDENTIFIER_RE
|
|
315
434
|
* @signature b.safeSql.DEFAULT_IDENTIFIER_RE
|
|
@@ -351,11 +470,101 @@ function assertOneOf(name, allowlist) {
|
|
|
351
470
|
* // → 63
|
|
352
471
|
*/
|
|
353
472
|
|
|
473
|
+
/**
|
|
474
|
+
* @primitive b.safeSql.assertSingleStatement
|
|
475
|
+
* @signature b.safeSql.assertSingleStatement(sql, opts?)
|
|
476
|
+
* @since 0.15.4
|
|
477
|
+
* @status stable
|
|
478
|
+
* @related b.safeSql.quoteIdentifier, b.safeSql.countPlaceholders, b.sql
|
|
479
|
+
*
|
|
480
|
+
* The one quote/comment-aware single-statement gate for any FINISHED SQL
|
|
481
|
+
* string that reaches a driver. Refuses a NUL, a lone surrogate, a
|
|
482
|
+
* top-level ';' (stacked statement), an unterminated quote, and unbalanced
|
|
483
|
+
* parentheses - while CORRECTLY allowing those characters inside a balanced
|
|
484
|
+
* quoted label (e.g. a MySQL ENUM('a;b')). Hand-rolled DDL (schema
|
|
485
|
+
* reconcile, the DSR store, migrations) and the b.sql builder's own output
|
|
486
|
+
* gates route through this single scan so the injection backstop cannot
|
|
487
|
+
* drift between the structured builder and the raw-DDL paths. Returns the
|
|
488
|
+
* input string so a caller can wrap inline:
|
|
489
|
+
* runSql(db, safeSql.assertSingleStatement(ddl, { label: "schema" }));
|
|
490
|
+
*
|
|
491
|
+
* @opts
|
|
492
|
+
* label: string, // message prefix (default: "sql")
|
|
493
|
+
* makeError: function, // (message, codeSuffix) => Error (default: SafeSqlError "sql/<suffix>")
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* var ddl = b.safeSql.assertSingleStatement("CREATE TABLE t (id INTEGER)", { label: "schema" });
|
|
497
|
+
* // returns the input string; throws sql/stacked-statement on a stacked DDL
|
|
498
|
+
*/
|
|
499
|
+
function assertSingleStatement(sql, opts) {
|
|
500
|
+
opts = opts || {};
|
|
501
|
+
var label = typeof opts.label === "string" ? opts.label : "sql";
|
|
502
|
+
var mkErr = typeof opts.makeError === "function"
|
|
503
|
+
? opts.makeError
|
|
504
|
+
: function (msg, suffix) { return new SafeSqlError(msg, "sql/" + suffix); };
|
|
505
|
+
// Backtick written via its code point so no NUL byte can reach this source.
|
|
506
|
+
var BACKTICK = String.fromCharCode(96);
|
|
507
|
+
if (typeof sql !== "string" || sql.length === 0) {
|
|
508
|
+
throw mkErr(label + ": SQL must be a non-empty string", "empty-sql");
|
|
509
|
+
}
|
|
510
|
+
if (sql.indexOf(String.fromCharCode(0)) !== -1) {
|
|
511
|
+
throw mkErr(label + ": SQL contains a NUL byte - rejected", "null-byte-sql");
|
|
512
|
+
}
|
|
513
|
+
if (typeof sql.isWellFormed === "function" && !sql.isWellFormed()) {
|
|
514
|
+
throw mkErr(label + ": SQL contains invalid Unicode (lone surrogates) - rejected",
|
|
515
|
+
"invalid-encoding-sql");
|
|
516
|
+
}
|
|
517
|
+
var i = 0;
|
|
518
|
+
var len = sql.length;
|
|
519
|
+
var depth = 0;
|
|
520
|
+
while (i < len) {
|
|
521
|
+
var ch = sql.charAt(i);
|
|
522
|
+
var next = i + 1 < len ? sql.charAt(i + 1) : "";
|
|
523
|
+
if (ch === "'" || ch === '"' || ch === BACKTICK) {
|
|
524
|
+
var qch = ch;
|
|
525
|
+
var closed = false;
|
|
526
|
+
i += 1;
|
|
527
|
+
while (i < len) {
|
|
528
|
+
if (sql.charAt(i) === qch) {
|
|
529
|
+
if (sql.charAt(i + 1) === qch) { i += 2; continue; } // doubled quote = escaped literal
|
|
530
|
+
i += 1; closed = true; break;
|
|
531
|
+
}
|
|
532
|
+
i += 1;
|
|
533
|
+
}
|
|
534
|
+
if (!closed) {
|
|
535
|
+
throw mkErr(label + ": unterminated quote in SQL (quote-jump / breakout risk)",
|
|
536
|
+
"unterminated-quote");
|
|
537
|
+
}
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
if (ch === "-" && next === "-") { while (i < len && sql.charAt(i) !== "\n") i += 1; continue; }
|
|
541
|
+
if (ch === "/" && next === "*") {
|
|
542
|
+
i += 2;
|
|
543
|
+
while (i < len && !(sql.charAt(i) === "*" && sql.charAt(i + 1) === "/")) i += 1;
|
|
544
|
+
i += 2;
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
if (ch === "(") { depth += 1; }
|
|
548
|
+
else if (ch === ")") { depth -= 1; }
|
|
549
|
+
else if (ch === ";") {
|
|
550
|
+
throw mkErr(label + ": emitted a top-level ';' - exactly one statement", "stacked-statement");
|
|
551
|
+
}
|
|
552
|
+
i += 1;
|
|
553
|
+
}
|
|
554
|
+
if (depth !== 0) {
|
|
555
|
+
throw mkErr(label + ": unbalanced parentheses in SQL", "unbalanced");
|
|
556
|
+
}
|
|
557
|
+
return sql;
|
|
558
|
+
}
|
|
559
|
+
|
|
354
560
|
module.exports = {
|
|
355
561
|
validateIdentifier: validateIdentifier,
|
|
562
|
+
assertSingleStatement: assertSingleStatement,
|
|
356
563
|
quoteIdentifier: quoteIdentifier,
|
|
357
564
|
quoteQualified: quoteQualified,
|
|
565
|
+
quoteList: quoteList,
|
|
358
566
|
assertOneOf: assertOneOf,
|
|
567
|
+
countPlaceholders: countPlaceholders,
|
|
359
568
|
SafeSqlError: SafeSqlError,
|
|
360
569
|
// Exposed so consumers can compose their own validators
|
|
361
570
|
DEFAULT_IDENTIFIER_RE: DEFAULT_IDENTIFIER_RE,
|
|
@@ -53,6 +53,11 @@ var nodeUrl = require("node:url");
|
|
|
53
53
|
var { URL } = require("node:url");
|
|
54
54
|
|
|
55
55
|
var audit = lazyRequire(function () { return require("./audit"); });
|
|
56
|
+
// ssrf-guard requires safe-url at top of file (the SSRF gate parses through
|
|
57
|
+
// the framework's defensive URL parser); lazyRequire breaks that cycle so the
|
|
58
|
+
// canonicalizer can reuse ssrf-guard's IP-literal canonicalization without an
|
|
59
|
+
// at-load circular require.
|
|
60
|
+
var ssrfGuard = lazyRequire(function () { return require("./ssrf-guard"); });
|
|
56
61
|
|
|
57
62
|
/**
|
|
58
63
|
* @primitive b.safeUrl.ALLOW_HTTP_TLS
|
|
@@ -168,9 +173,9 @@ var ALLOW_ANY = Object.freeze(["http:", "https:", "ws:", "wss:"]);
|
|
|
168
173
|
* Extends `FrameworkError`. Carries a stable `.code`:
|
|
169
174
|
* `safe-url/missing` / `safe-url/too-long` / `safe-url/malformed` /
|
|
170
175
|
* `safe-url/protocol-disallowed` / `safe-url/userinfo-disallowed` /
|
|
171
|
-
* `safe-url/idn-homograph` / `safe-url/
|
|
172
|
-
* inspects `.code` to translate
|
|
173
|
-
* leaking parser internals.
|
|
176
|
+
* `safe-url/idn-homograph` / `safe-url/uncanonicalizable` /
|
|
177
|
+
* `safe-url/bad-opt`. HTTP middleware inspects `.code` to translate
|
|
178
|
+
* the throw into a 400 without leaking parser internals.
|
|
174
179
|
*
|
|
175
180
|
* @example
|
|
176
181
|
* var b = require("blamejs");
|
|
@@ -205,6 +210,38 @@ function _makeError(errorClass, code, message) {
|
|
|
205
210
|
// payloads) override via opts.maxUrlLength.
|
|
206
211
|
var DEFAULT_MAX_URL_LENGTH = C.BYTES.kib(8);
|
|
207
212
|
|
|
213
|
+
// RFC 3986 §6.2.2 percent-encoding normalization applied CONSERVATIVELY to a
|
|
214
|
+
// path segment string: uppercase the two hex digits of every valid escape
|
|
215
|
+
// (§6.2.2.2) and decode an escape of an unreserved character to its literal
|
|
216
|
+
// (§6.2.2.3). A malformed escape (`%`, `%g`, `%4`) is passed through verbatim
|
|
217
|
+
// so no information is invented. Query and fragment are NOT touched —
|
|
218
|
+
// reordering / re-decoding there can change application semantics (a `%26`
|
|
219
|
+
// inside a value is NOT the same as a literal `&`).
|
|
220
|
+
function _normalizePctPath(path) {
|
|
221
|
+
if (typeof path !== "string" || path.indexOf("%") === -1) return path;
|
|
222
|
+
var out = "";
|
|
223
|
+
for (var i = 0; i < path.length; i += 1) {
|
|
224
|
+
var ch = path.charAt(i);
|
|
225
|
+
if (ch === "%") {
|
|
226
|
+
// The escape's two hex digits — sliced to a fixed two-char window so
|
|
227
|
+
// the regex test runs on a length-bounded string (no ReDoS surface).
|
|
228
|
+
var pair = path.slice(i + 1, i + 3);
|
|
229
|
+
if (pair.length === 2 && codepointClass.HEX_PAIR_RE.test(pair)) {
|
|
230
|
+
var cc = parseInt(pair, 16);
|
|
231
|
+
if (codepointClass.isUnreserved(cc)) {
|
|
232
|
+
out += String.fromCharCode(cc);
|
|
233
|
+
} else {
|
|
234
|
+
out += "%" + pair.toUpperCase();
|
|
235
|
+
}
|
|
236
|
+
i += 2;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
out += ch;
|
|
241
|
+
}
|
|
242
|
+
return out;
|
|
243
|
+
}
|
|
244
|
+
|
|
208
245
|
/**
|
|
209
246
|
* @primitive b.safeUrl.parse
|
|
210
247
|
* @signature b.safeUrl.parse(url, opts?)
|
|
@@ -416,9 +453,139 @@ function format(url) {
|
|
|
416
453
|
}
|
|
417
454
|
}
|
|
418
455
|
|
|
456
|
+
/**
|
|
457
|
+
* @primitive b.safeUrl.canonicalize
|
|
458
|
+
* @signature b.safeUrl.canonicalize(input, opts?)
|
|
459
|
+
* @since 0.15.6
|
|
460
|
+
* @status stable
|
|
461
|
+
* @related b.safeUrl.parse, b.ssrfGuard.canonicalizeHost, b.ssrfGuard.checkUrl
|
|
462
|
+
*
|
|
463
|
+
* Return the single canonical, comparable form of a URL so two
|
|
464
|
+
* spellings of the same destination compare equal as strings. The use
|
|
465
|
+
* cases are host allowlists, dedup / cache keys, and SSRF pre-checks —
|
|
466
|
+
* exactly the places an attacker reaches for an obfuscated host
|
|
467
|
+
* (`http://0177.0.0.1/`, `http://2130706433/`, `http://[::ffff:7f00:1]/`,
|
|
468
|
+
* an IDN homograph, a trailing-dot or default-port variation) to slip
|
|
469
|
+
* past a naive `===` allowlist. Routing every comparison through one
|
|
470
|
+
* audited canonicalizer closes that class instead of leaving each
|
|
471
|
+
* caller to re-derive normalization (which is how the bypasses happen).
|
|
472
|
+
*
|
|
473
|
+
* The canonical form is built from `parse`'s defensive gates plus the
|
|
474
|
+
* security-relevant normalization set:
|
|
475
|
+
*
|
|
476
|
+
* - Scheme and host lowercased (the WHATWG URL parser does this).
|
|
477
|
+
* - Host IDN labels emitted as their punycode `xn--` A-label; a
|
|
478
|
+
* mixed-script / confusable host label THROWS exactly as
|
|
479
|
+
* `parse` does (a homograph is never silently passed) unless the
|
|
480
|
+
* caller opts in via `allowMixedScript` / `allowedScripts`.
|
|
481
|
+
* - A trailing dot on the host is removed (`example.com.` →
|
|
482
|
+
* `example.com`) — DNS-equivalent but breaks string comparison.
|
|
483
|
+
* - An IP-literal host in ANY notation collapses to one canonical
|
|
484
|
+
* string via `b.ssrfGuard.canonicalizeHost` (the SAME byte parser
|
|
485
|
+
* the SSRF classifier matches on): IPv4 decimal / octal / hex /
|
|
486
|
+
* shorthand → dotted-quad; IPv6 (incl. IPv4-mapped + any
|
|
487
|
+
* zero-compression) → RFC 5952 lower-hex, bracketed.
|
|
488
|
+
* - The default port for the scheme is stripped (`:80` http/ws,
|
|
489
|
+
* `:443` https/wss — the parser does this).
|
|
490
|
+
* - Path `.` / `..` segments resolved (WHATWG), then RFC 3986 §6.2.2
|
|
491
|
+
* percent-normalization applied to the path: hex digits uppercased,
|
|
492
|
+
* escapes of unreserved characters decoded. Query and fragment are
|
|
493
|
+
* left BYTE-FOR-BYTE as parsed — reordering or re-decoding there can
|
|
494
|
+
* change application semantics.
|
|
495
|
+
*
|
|
496
|
+
* Throws `SafeUrlError` (or `opts.errorClass`): the `parse` codes for a
|
|
497
|
+
* missing / too-long / malformed / disallowed-scheme / userinfo /
|
|
498
|
+
* homograph input, plus `safe-url/uncanonicalizable` when a parsed URL
|
|
499
|
+
* cannot be reduced to a safe canonical form. This is a config /
|
|
500
|
+
* entry-point validator — it THROWS on bad input, it does NOT return a
|
|
501
|
+
* best-effort string.
|
|
502
|
+
*
|
|
503
|
+
* @opts
|
|
504
|
+
* allowedSchemes: string[], // default ALLOW_ANY (http/https/ws/wss); canonicalize is a compare tool, not a fetch gate
|
|
505
|
+
* allowUserinfo: boolean, // default false; opt-in to keep user:pass@ (still discouraged)
|
|
506
|
+
* allowMixedScript: boolean, // default false; opt-in to mixed-script host labels
|
|
507
|
+
* allowedScripts: string[], // narrow mixed-script allowlist (e.g. ["latin","cyrillic"])
|
|
508
|
+
* maxUrlLength: number, // default 8192 (RFC 7230 §3.1.1)
|
|
509
|
+
* errorClass: Function, // throw this instead of SafeUrlError
|
|
510
|
+
*
|
|
511
|
+
* @example
|
|
512
|
+
* var b = require("blamejs");
|
|
513
|
+
*
|
|
514
|
+
* // Every obfuscated loopback spelling collapses to one string.
|
|
515
|
+
* b.safeUrl.canonicalize("http://0177.0.0.1/"); // → "http://127.0.0.1/"
|
|
516
|
+
* b.safeUrl.canonicalize("http://2130706433/"); // → "http://127.0.0.1/"
|
|
517
|
+
* b.safeUrl.canonicalize("http://127.1/"); // → "http://127.0.0.1/"
|
|
518
|
+
*
|
|
519
|
+
* // Case, default port, trailing dot, and `..` all normalize.
|
|
520
|
+
* b.safeUrl.canonicalize("https://Example.COM:443/a/../b");
|
|
521
|
+
* // → "https://example.com/b"
|
|
522
|
+
*
|
|
523
|
+
* // A disallowed scheme throws SafeUrlError.
|
|
524
|
+
* try { b.safeUrl.canonicalize("ftp://example.com/"); }
|
|
525
|
+
* catch (e) { e.code; } // → "safe-url/protocol-disallowed"
|
|
526
|
+
*/
|
|
527
|
+
function canonicalize(input, opts) {
|
|
528
|
+
opts = opts || {};
|
|
529
|
+
var allowedSchemes = Array.isArray(opts.allowedSchemes) && opts.allowedSchemes.length > 0
|
|
530
|
+
? opts.allowedSchemes
|
|
531
|
+
: ALLOW_ANY;
|
|
532
|
+
|
|
533
|
+
// Route through parse — it owns the length cap, the scheme allowlist,
|
|
534
|
+
// userinfo refusal, and the IDN-homograph defense. A parse throw (with
|
|
535
|
+
// its stable .code) propagates unchanged so callers branch on the same
|
|
536
|
+
// codes parse documents.
|
|
537
|
+
var parsed = parse(input, {
|
|
538
|
+
allowedProtocols: allowedSchemes,
|
|
539
|
+
maxUrlLength: opts.maxUrlLength,
|
|
540
|
+
allowUserinfo: opts.allowUserinfo,
|
|
541
|
+
allowMixedScript: opts.allowMixedScript,
|
|
542
|
+
allowedScripts: opts.allowedScripts,
|
|
543
|
+
errorClass: opts.errorClass,
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
try {
|
|
547
|
+
// The parser already lowercased the scheme + host, stripped the
|
|
548
|
+
// default port, emitted IDN as punycode, and resolved `.`/`..`. The
|
|
549
|
+
// canonicalizer adds: IP-literal collapse (shared with the SSRF
|
|
550
|
+
// classifier), trailing-dot removal, and path percent-normalization.
|
|
551
|
+
var scheme = parsed.protocol; // e.g. "https:"
|
|
552
|
+
var rawHost = parsed.hostname; // already lowercase / punycode / default-port-free
|
|
553
|
+
var canonHost = ssrfGuard().canonicalizeHost(rawHost);
|
|
554
|
+
// canonicalizeHost returns IPv6 UNbracketed; a URL authority needs the
|
|
555
|
+
// brackets back. net.isIP via the colon is a sufficient discriminator
|
|
556
|
+
// (a DNS label can't contain a colon).
|
|
557
|
+
var host = canonHost.indexOf(":") !== -1 ? "[" + canonHost + "]" : canonHost;
|
|
558
|
+
|
|
559
|
+
// Userinfo (`user:pass@`) is deliberately DROPPED from the canonical form.
|
|
560
|
+
// The canonical string is built to be compared, used as a dedup / cache
|
|
561
|
+
// key, or logged; carrying a credential into any of those leaks it, and
|
|
562
|
+
// the username/password are not part of the resource's target identity for
|
|
563
|
+
// an allowlist / SSRF decision. parse() already refuses userinfo unless
|
|
564
|
+
// allowUserinfo:true; even then, the canonical output omits it (never reads
|
|
565
|
+
// parsed.password), so two URLs that differ only in credentials canonicalize
|
|
566
|
+
// equal.
|
|
567
|
+
|
|
568
|
+
var port = parsed.port !== "" ? ":" + parsed.port : "";
|
|
569
|
+
var path = _normalizePctPath(parsed.pathname);
|
|
570
|
+
// Query + fragment: byte-for-byte as the parser emitted them. Reordering
|
|
571
|
+
// a query or re-decoding a value changes semantics — out of scope for a
|
|
572
|
+
// safe canonicalizer.
|
|
573
|
+
var query = parsed.search;
|
|
574
|
+
var fragment = parsed.hash;
|
|
575
|
+
|
|
576
|
+
return scheme + "//" + host + port + path + query + fragment;
|
|
577
|
+
} catch (e) {
|
|
578
|
+
if (e && e.isSafeUrlError) throw e;
|
|
579
|
+
throw _makeError(opts.errorClass, "safe-url/uncanonicalizable",
|
|
580
|
+
"safeUrl.canonicalize could not reduce the URL to a canonical form: " +
|
|
581
|
+
((e && e.message) || String(e)));
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
419
585
|
module.exports = {
|
|
420
586
|
parse: parse,
|
|
421
587
|
format: format,
|
|
588
|
+
canonicalize: canonicalize,
|
|
422
589
|
SafeUrlError: SafeUrlError,
|
|
423
590
|
ALLOW_HTTP_TLS: ALLOW_HTTP_TLS,
|
|
424
591
|
ALLOW_HTTP_ALL: ALLOW_HTTP_ALL,
|