@blamejs/blamejs-shop 0.4.55 → 0.4.57
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/admin.js +385 -2
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +426 -366
- package/lib/vendor/blamejs/.github/codeql/codeql-config.yml +25 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +1 -1
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +1 -1
- package/lib/vendor/blamejs/.github/workflows/ci.yml +13 -13
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +8 -7
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +2 -2
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +4 -4
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +1 -1
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
- package/lib/vendor/blamejs/CHANGELOG.md +2 -0
- package/lib/vendor/blamejs/api-snapshot.json +1381 -4
- package/lib/vendor/blamejs/eslint.config.mjs +1 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +4 -1
- package/lib/vendor/blamejs/examples/wiki/lib/symbol-index.js +5 -2
- package/lib/vendor/blamejs/examples/wiki/routes/pages.js +4 -1
- package/lib/vendor/blamejs/fuzz/guard-text.fuzz.js +20 -0
- package/lib/vendor/blamejs/index.js +2 -0
- package/lib/vendor/blamejs/lib/a2a-tasks.js +7 -23
- package/lib/vendor/blamejs/lib/acme.js +6 -5
- package/lib/vendor/blamejs/lib/agent-event-bus.js +5 -4
- package/lib/vendor/blamejs/lib/agent-idempotency.js +2 -5
- package/lib/vendor/blamejs/lib/agent-orchestrator.js +2 -5
- package/lib/vendor/blamejs/lib/agent-saga.js +3 -5
- package/lib/vendor/blamejs/lib/agent-tenant.js +2 -5
- package/lib/vendor/blamejs/lib/ai-adverse-decision.js +2 -15
- package/lib/vendor/blamejs/lib/ai-capability.js +1 -6
- package/lib/vendor/blamejs/lib/ai-dp.js +1 -5
- package/lib/vendor/blamejs/lib/ai-input.js +2 -2
- package/lib/vendor/blamejs/lib/ai-pref.js +3 -8
- package/lib/vendor/blamejs/lib/ai-quota.js +3 -14
- package/lib/vendor/blamejs/lib/api-key.js +37 -28
- package/lib/vendor/blamejs/lib/archive-adapters.js +2 -4
- package/lib/vendor/blamejs/lib/archive-entry-policy.js +32 -0
- package/lib/vendor/blamejs/lib/archive-read.js +5 -17
- package/lib/vendor/blamejs/lib/archive-tar-read.js +5 -16
- package/lib/vendor/blamejs/lib/archive.js +2 -10
- package/lib/vendor/blamejs/lib/arg-parser.js +7 -6
- package/lib/vendor/blamejs/lib/asyncapi-traits.js +2 -6
- package/lib/vendor/blamejs/lib/atomic-file.js +108 -31
- package/lib/vendor/blamejs/lib/audit-chain.js +133 -53
- package/lib/vendor/blamejs/lib/audit-daily-review.js +24 -14
- package/lib/vendor/blamejs/lib/audit-emit.js +82 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +257 -0
- package/lib/vendor/blamejs/lib/audit.js +84 -0
- package/lib/vendor/blamejs/lib/auth/access-lock.js +5 -27
- package/lib/vendor/blamejs/lib/auth/dpop.js +23 -35
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +9 -15
- package/lib/vendor/blamejs/lib/auth/jwt-external.js +26 -8
- package/lib/vendor/blamejs/lib/auth/jwt.js +13 -15
- package/lib/vendor/blamejs/lib/auth/lockout.js +6 -25
- package/lib/vendor/blamejs/lib/auth/oauth.js +67 -45
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +55 -32
- package/lib/vendor/blamejs/lib/auth/oid4vp.js +3 -2
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +6 -6
- package/lib/vendor/blamejs/lib/auth/passkey.js +3 -3
- package/lib/vendor/blamejs/lib/auth/password.js +7 -1
- package/lib/vendor/blamejs/lib/auth/saml.js +37 -27
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-holder.js +2 -14
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-issuer.js +2 -14
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +1 -1
- package/lib/vendor/blamejs/lib/auth/status-list.js +7 -7
- package/lib/vendor/blamejs/lib/auth/step-up.js +6 -12
- package/lib/vendor/blamejs/lib/auth-bot-challenge.js +6 -37
- package/lib/vendor/blamejs/lib/backup/bundle.js +11 -18
- package/lib/vendor/blamejs/lib/backup/index.js +14 -47
- package/lib/vendor/blamejs/lib/bounded-map.js +112 -1
- package/lib/vendor/blamejs/lib/breach-deadline.js +1 -11
- package/lib/vendor/blamejs/lib/cache.js +7 -18
- package/lib/vendor/blamejs/lib/cbor.js +2 -1
- package/lib/vendor/blamejs/lib/cdn-cache-control.js +8 -9
- package/lib/vendor/blamejs/lib/cert.js +3 -10
- package/lib/vendor/blamejs/lib/chain-writer.js +162 -47
- package/lib/vendor/blamejs/lib/cli.js +5 -1
- package/lib/vendor/blamejs/lib/client-hints.js +7 -9
- package/lib/vendor/blamejs/lib/cloud-events.js +40 -31
- package/lib/vendor/blamejs/lib/cluster-storage.js +2 -38
- package/lib/vendor/blamejs/lib/cms-codec.js +2 -1
- package/lib/vendor/blamejs/lib/codepoint-class.js +67 -1
- package/lib/vendor/blamejs/lib/compliance-ai-act-logging.js +1 -6
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +2 -4
- package/lib/vendor/blamejs/lib/compliance-eaa.js +1 -11
- package/lib/vendor/blamejs/lib/compliance-sanctions-fetcher.js +21 -28
- package/lib/vendor/blamejs/lib/compliance-sanctions.js +2 -14
- package/lib/vendor/blamejs/lib/compliance.js +2 -9
- package/lib/vendor/blamejs/lib/config-drift.js +2 -12
- package/lib/vendor/blamejs/lib/content-digest.js +10 -8
- package/lib/vendor/blamejs/lib/cookies.js +5 -11
- package/lib/vendor/blamejs/lib/cose.js +19 -11
- package/lib/vendor/blamejs/lib/cra-report.js +1 -11
- package/lib/vendor/blamejs/lib/crypto-field.js +5 -10
- package/lib/vendor/blamejs/lib/crypto.js +120 -3
- package/lib/vendor/blamejs/lib/csp.js +235 -3
- package/lib/vendor/blamejs/lib/daemon.js +42 -41
- package/lib/vendor/blamejs/lib/data-act.js +19 -9
- package/lib/vendor/blamejs/lib/db-query.js +6 -40
- package/lib/vendor/blamejs/lib/db.js +34 -12
- package/lib/vendor/blamejs/lib/dbsc.js +5 -6
- package/lib/vendor/blamejs/lib/ddl-change-control.js +18 -14
- package/lib/vendor/blamejs/lib/deprecate.js +4 -5
- package/lib/vendor/blamejs/lib/did.js +6 -2
- package/lib/vendor/blamejs/lib/dsr.js +8 -12
- package/lib/vendor/blamejs/lib/external-db-migrate.js +21 -22
- package/lib/vendor/blamejs/lib/external-db.js +14 -26
- package/lib/vendor/blamejs/lib/fda-21cfr11.js +12 -8
- package/lib/vendor/blamejs/lib/fdx.js +22 -18
- package/lib/vendor/blamejs/lib/file-upload.js +80 -66
- package/lib/vendor/blamejs/lib/flag-evaluation-context.js +5 -8
- package/lib/vendor/blamejs/lib/framework-error.js +12 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +19 -0
- package/lib/vendor/blamejs/lib/fsm.js +7 -12
- package/lib/vendor/blamejs/lib/gate-contract.js +869 -38
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +4 -13
- package/lib/vendor/blamejs/lib/graphql-federation.js +1 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +2 -1
- package/lib/vendor/blamejs/lib/guard-all.js +1 -0
- package/lib/vendor/blamejs/lib/guard-archive.js +9 -30
- package/lib/vendor/blamejs/lib/guard-auth.js +23 -80
- package/lib/vendor/blamejs/lib/guard-cidr.js +20 -96
- package/lib/vendor/blamejs/lib/guard-csv.js +135 -196
- package/lib/vendor/blamejs/lib/guard-domain.js +23 -106
- package/lib/vendor/blamejs/lib/guard-dsn.js +16 -13
- package/lib/vendor/blamejs/lib/guard-email.js +46 -53
- package/lib/vendor/blamejs/lib/guard-envelope.js +1 -1
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +2 -1
- package/lib/vendor/blamejs/lib/guard-filename.js +12 -60
- package/lib/vendor/blamejs/lib/guard-graphql.js +28 -75
- package/lib/vendor/blamejs/lib/guard-html-wcag.js +15 -2
- package/lib/vendor/blamejs/lib/guard-html.js +65 -117
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +2 -1
- package/lib/vendor/blamejs/lib/guard-image.js +280 -77
- package/lib/vendor/blamejs/lib/guard-imap-command.js +8 -9
- package/lib/vendor/blamejs/lib/guard-json.js +87 -103
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +20 -88
- package/lib/vendor/blamejs/lib/guard-jwt.js +32 -114
- package/lib/vendor/blamejs/lib/guard-list-id.js +2 -7
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -7
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +5 -6
- package/lib/vendor/blamejs/lib/guard-mail-move.js +2 -1
- package/lib/vendor/blamejs/lib/guard-mail-query.js +5 -3
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +6 -7
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +5 -4
- package/lib/vendor/blamejs/lib/guard-markdown.js +76 -110
- package/lib/vendor/blamejs/lib/guard-message-id.js +5 -6
- package/lib/vendor/blamejs/lib/guard-mime.js +20 -99
- package/lib/vendor/blamejs/lib/guard-oauth.js +19 -73
- package/lib/vendor/blamejs/lib/guard-pdf.js +65 -72
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +12 -13
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +2 -1
- package/lib/vendor/blamejs/lib/guard-regex.js +24 -99
- package/lib/vendor/blamejs/lib/guard-saga-config.js +2 -1
- package/lib/vendor/blamejs/lib/guard-shell.js +22 -87
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +8 -11
- package/lib/vendor/blamejs/lib/guard-sql.js +15 -13
- package/lib/vendor/blamejs/lib/guard-stream-args.js +2 -1
- package/lib/vendor/blamejs/lib/guard-svg.js +95 -140
- package/lib/vendor/blamejs/lib/guard-template.js +23 -80
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +2 -1
- package/lib/vendor/blamejs/lib/guard-text.js +592 -0
- package/lib/vendor/blamejs/lib/guard-time.js +27 -95
- package/lib/vendor/blamejs/lib/guard-uuid.js +21 -93
- package/lib/vendor/blamejs/lib/guard-xml.js +76 -106
- package/lib/vendor/blamejs/lib/guard-yaml.js +24 -60
- package/lib/vendor/blamejs/lib/http-client-cache.js +5 -12
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +2 -4
- package/lib/vendor/blamejs/lib/http-client.js +8 -21
- package/lib/vendor/blamejs/lib/http-message-signature.js +3 -2
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +5 -7
- package/lib/vendor/blamejs/lib/i18n.js +83 -26
- package/lib/vendor/blamejs/lib/inbox.js +8 -8
- package/lib/vendor/blamejs/lib/incident-report.js +3 -21
- package/lib/vendor/blamejs/lib/ip-utils.js +49 -6
- package/lib/vendor/blamejs/lib/jobs.js +3 -2
- package/lib/vendor/blamejs/lib/keychain.js +6 -18
- package/lib/vendor/blamejs/lib/legal-hold.js +6 -15
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +44 -112
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +17 -14
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +16 -80
- package/lib/vendor/blamejs/lib/log-stream-webhook.js +20 -92
- package/lib/vendor/blamejs/lib/mail-arc-sign.js +8 -3
- package/lib/vendor/blamejs/lib/mail-arf.js +3 -2
- package/lib/vendor/blamejs/lib/mail-auth.js +40 -66
- package/lib/vendor/blamejs/lib/mail-bimi.js +19 -39
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +3 -2
- package/lib/vendor/blamejs/lib/mail-dav.js +8 -35
- package/lib/vendor/blamejs/lib/mail-deploy.js +6 -9
- package/lib/vendor/blamejs/lib/mail-dkim.js +19 -38
- package/lib/vendor/blamejs/lib/mail-greylist.js +15 -26
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -3
- package/lib/vendor/blamejs/lib/mail-journal.js +2 -1
- package/lib/vendor/blamejs/lib/mail-mdn.js +4 -3
- package/lib/vendor/blamejs/lib/mail-rbl.js +5 -8
- package/lib/vendor/blamejs/lib/mail-scan.js +2 -2
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +33 -20
- package/lib/vendor/blamejs/lib/mail-server-imap.js +32 -87
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +35 -51
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +29 -83
- package/lib/vendor/blamejs/lib/mail-server-mx.js +33 -74
- package/lib/vendor/blamejs/lib/mail-server-net.js +177 -0
- package/lib/vendor/blamejs/lib/mail-server-pop3.js +30 -83
- package/lib/vendor/blamejs/lib/mail-server-rate-limit.js +30 -6
- package/lib/vendor/blamejs/lib/mail-server-submission.js +34 -73
- package/lib/vendor/blamejs/lib/mail-server-tls.js +89 -0
- package/lib/vendor/blamejs/lib/mail-spam-score.js +7 -8
- package/lib/vendor/blamejs/lib/mail-store.js +3 -2
- package/lib/vendor/blamejs/lib/mail.js +6 -11
- package/lib/vendor/blamejs/lib/markup-escape.js +31 -0
- package/lib/vendor/blamejs/lib/markup-tokenizer.js +54 -0
- package/lib/vendor/blamejs/lib/mcp-tool-registry.js +26 -23
- package/lib/vendor/blamejs/lib/mcp.js +1 -1
- package/lib/vendor/blamejs/lib/mdoc.js +11 -12
- package/lib/vendor/blamejs/lib/metrics.js +32 -30
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +3 -23
- package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +11 -22
- package/lib/vendor/blamejs/lib/middleware/assetlinks.js +2 -7
- package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +2 -1
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +31 -48
- package/lib/vendor/blamejs/lib/middleware/bot-disclose.js +7 -10
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +2 -10
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +13 -2
- package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +6 -26
- package/lib/vendor/blamejs/lib/middleware/deny-response.js +19 -1
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +7 -0
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +4 -20
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +20 -28
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +3 -2
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +9 -1
- package/lib/vendor/blamejs/lib/middleware/security-txt.js +2 -6
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +12 -27
- package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +2 -7
- package/lib/vendor/blamejs/lib/migrations.js +4 -13
- package/lib/vendor/blamejs/lib/mime-parse.js +34 -17
- package/lib/vendor/blamejs/lib/module-loader.js +44 -0
- package/lib/vendor/blamejs/lib/money.js +105 -0
- package/lib/vendor/blamejs/lib/mtls-ca.js +116 -36
- package/lib/vendor/blamejs/lib/network-byte-quota.js +4 -18
- package/lib/vendor/blamejs/lib/network-dane.js +8 -6
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +97 -6
- package/lib/vendor/blamejs/lib/network-dnssec.js +16 -16
- package/lib/vendor/blamejs/lib/network-heartbeat.js +3 -2
- package/lib/vendor/blamejs/lib/network-smtp-policy.js +29 -41
- package/lib/vendor/blamejs/lib/network-tls.js +40 -42
- package/lib/vendor/blamejs/lib/nis2-report.js +1 -11
- package/lib/vendor/blamejs/lib/nonce-store.js +81 -3
- package/lib/vendor/blamejs/lib/numeric-bounds.js +22 -8
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +2 -6
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +5 -45
- package/lib/vendor/blamejs/lib/object-store/gcs.js +8 -71
- package/lib/vendor/blamejs/lib/object-store/http-put.js +1 -5
- package/lib/vendor/blamejs/lib/object-store/http-request.js +128 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +2 -1
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +9 -77
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -25
- package/lib/vendor/blamejs/lib/observability.js +95 -0
- package/lib/vendor/blamejs/lib/openapi-paths-builder.js +7 -3
- package/lib/vendor/blamejs/lib/otel-export.js +7 -10
- package/lib/vendor/blamejs/lib/outbox.js +43 -37
- package/lib/vendor/blamejs/lib/pagination.js +11 -15
- package/lib/vendor/blamejs/lib/parsers/safe-env.js +5 -4
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +10 -4
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +11 -9
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +2 -2
- package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +7 -6
- package/lib/vendor/blamejs/lib/pick.js +63 -5
- package/lib/vendor/blamejs/lib/pipl-cn.js +69 -59
- package/lib/vendor/blamejs/lib/privacy-pass.js +8 -6
- package/lib/vendor/blamejs/lib/problem-details.js +2 -5
- package/lib/vendor/blamejs/lib/pubsub.js +4 -8
- package/lib/vendor/blamejs/lib/redact.js +2 -1
- package/lib/vendor/blamejs/lib/render.js +4 -2
- package/lib/vendor/blamejs/lib/request-helpers.js +133 -6
- package/lib/vendor/blamejs/lib/restore.js +5 -22
- package/lib/vendor/blamejs/lib/retention.js +3 -5
- package/lib/vendor/blamejs/lib/safe-async.js +457 -0
- package/lib/vendor/blamejs/lib/safe-buffer.js +137 -6
- package/lib/vendor/blamejs/lib/safe-decompress.js +2 -1
- package/lib/vendor/blamejs/lib/safe-dns.js +2 -1
- package/lib/vendor/blamejs/lib/safe-ical.js +12 -26
- package/lib/vendor/blamejs/lib/safe-json.js +7 -8
- package/lib/vendor/blamejs/lib/safe-jsonpath.js +6 -5
- package/lib/vendor/blamejs/lib/safe-mime.js +25 -29
- package/lib/vendor/blamejs/lib/safe-redirect.js +2 -5
- package/lib/vendor/blamejs/lib/safe-schema.js +7 -6
- package/lib/vendor/blamejs/lib/safe-sql.js +126 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +12 -26
- package/lib/vendor/blamejs/lib/sandbox-worker.js +9 -2
- package/lib/vendor/blamejs/lib/sandbox.js +3 -2
- package/lib/vendor/blamejs/lib/scheduler.js +5 -12
- package/lib/vendor/blamejs/lib/sec-cyber.js +82 -37
- package/lib/vendor/blamejs/lib/seeders.js +4 -11
- package/lib/vendor/blamejs/lib/self-update.js +92 -69
- package/lib/vendor/blamejs/lib/session-device-binding.js +112 -66
- package/lib/vendor/blamejs/lib/session.js +194 -6
- package/lib/vendor/blamejs/lib/sql.js +225 -78
- package/lib/vendor/blamejs/lib/sse.js +6 -10
- package/lib/vendor/blamejs/lib/static.js +136 -98
- package/lib/vendor/blamejs/lib/storage.js +4 -2
- package/lib/vendor/blamejs/lib/structured-fields.js +275 -16
- package/lib/vendor/blamejs/lib/tenant-quota.js +2 -20
- package/lib/vendor/blamejs/lib/tsa.js +11 -15
- package/lib/vendor/blamejs/lib/validate-opts.js +154 -8
- package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +30 -48
- package/lib/vendor/blamejs/lib/vault-aad.js +3 -2
- package/lib/vendor/blamejs/lib/vc.js +2 -7
- package/lib/vendor/blamejs/lib/vex.js +35 -13
- package/lib/vendor/blamejs/lib/web-push-vapid.js +23 -20
- package/lib/vendor/blamejs/lib/webhook-dispatcher.js +706 -0
- package/lib/vendor/blamejs/lib/webhook.js +43 -18
- package/lib/vendor/blamejs/lib/websocket-channels.js +9 -7
- package/lib/vendor/blamejs/lib/websocket.js +7 -3
- package/lib/vendor/blamejs/lib/worm.js +2 -3
- package/lib/vendor/blamejs/lib/ws-client.js +8 -10
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.15.13.json +81 -0
- package/lib/vendor/blamejs/scripts/check-changelog-extract.js +1 -1
- package/lib/vendor/blamejs/test/00-primitives.js +136 -7
- package/lib/vendor/blamejs/test/10-state.js +9 -3
- package/lib/vendor/blamejs/test/30-chain.js +40 -0
- package/lib/vendor/blamejs/test/helpers/check.js +18 -0
- package/lib/vendor/blamejs/test/helpers/db.js +4 -0
- package/lib/vendor/blamejs/test/helpers/index.js +1 -0
- package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +2 -1
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +2 -1
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +8 -1
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +1 -1
- package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +2 -1
- package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +269 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-fd-read.test.js +149 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-framework-namespaces.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-use-store.test.js +55 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-object-store-adapter.test.js +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/bot-guard.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/bounded-map.test.js +129 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/chain-writer-multichain.test.js +107 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-api-key.test.js +12 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +2788 -3214
- package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +78 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-base64url.test.js +63 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +2 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-builder.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-nonce.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +2 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-resolve-opts.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +9 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract-content-gate.test.js +290 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-email.test.js +78 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +23 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-gate-disposition-coverage.test.js +93 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +60 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +184 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +54 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-text.test.js +365 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-xml.test.js +35 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-yaml.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +20 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-arf.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +39 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-greylist.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-tls.test.js +39 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mime-parse.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-revocation.test.js +98 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-release.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-memory-getorinsert.test.js +100 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-loops.test.js +278 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +37 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-jsonpath.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +2 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/session-valid-from.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +100 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/structured-fields.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/validate-opts-shape.test.js +235 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +374 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +75 -8
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +3 -3
- package/lib/winback-campaigns.js +202 -42
- package/package.json +1 -1
|
@@ -59,6 +59,10 @@ var numericChecks = require("./numeric-checks");
|
|
|
59
59
|
var requestHelpers = require("./request-helpers");
|
|
60
60
|
var validateOpts = require("./validate-opts");
|
|
61
61
|
var { WebhookError } = require("./framework-error");
|
|
62
|
+
// b.webhook.dispatcher — durable signed-webhook delivery store. Lives in its
|
|
63
|
+
// own module (distinct domain: persistence) and lazyRequires this file back,
|
|
64
|
+
// so this top-level require is cycle-safe.
|
|
65
|
+
var webhookDispatcher = require("./webhook-dispatcher");
|
|
62
66
|
|
|
63
67
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
64
68
|
|
|
@@ -832,16 +836,28 @@ function _coerceBodyString(body) {
|
|
|
832
836
|
* HMAC-SHA-256 over the literal `<t>.<rawBody>` string using the
|
|
833
837
|
* operator's `whsec_...` secret bytes verbatim (the prefix IS the key).
|
|
834
838
|
* Refuses signatures older than the tolerance window (default 5 min,
|
|
835
|
-
* minimum 30 s). When `nonceStore` is supplied the verifier
|
|
836
|
-
* the accepted v1 signature so a replay within the tolerance window
|
|
837
|
-
* is refused
|
|
839
|
+
* minimum 30 s). When `nonceStore` is supplied the verifier atomically
|
|
840
|
+
* records the accepted v1 signature so a replay within the tolerance window
|
|
841
|
+
* is refused; the store speaks the same `checkAndInsert(nonce, expireAt) →
|
|
842
|
+
* bool` contract as `b.webhook.verifier` and `b.nonceStore.create`, so the
|
|
843
|
+
* framework's own replay store plugs in directly and concurrent redeliveries
|
|
844
|
+
* cannot race. Constant-time compare via `b.crypto.timingSafeEqual`.
|
|
845
|
+
*
|
|
846
|
+
* @opts
|
|
847
|
+
* alg: "hmac-sha256-stripe",
|
|
848
|
+
* secret: string | Buffer, // whsec_... bytes verbatim
|
|
849
|
+
* header: string, // Stripe-Signature value
|
|
850
|
+
* body: string | Buffer, // raw request body
|
|
851
|
+
* toleranceMs: number, // default 5 min, min 30 s
|
|
852
|
+
* nonceStore: { checkAndInsert(nonce, expireAt) }, // optional atomic replay defense
|
|
838
853
|
*
|
|
839
854
|
* @example
|
|
840
|
-
* b.webhook.verify({
|
|
841
|
-
* alg:
|
|
842
|
-
* secret:
|
|
843
|
-
* header:
|
|
844
|
-
* body:
|
|
855
|
+
* await b.webhook.verify({
|
|
856
|
+
* alg: "hmac-sha256-stripe",
|
|
857
|
+
* secret: "whsec_abc...",
|
|
858
|
+
* header: req.headers["stripe-signature"],
|
|
859
|
+
* body: rawBodyBuffer,
|
|
860
|
+
* nonceStore: b.nonceStore.create({ backend: "memory" }),
|
|
845
861
|
* });
|
|
846
862
|
* // → { ok: true, timestamp: 1700000000, scheme: "v1" }
|
|
847
863
|
*/
|
|
@@ -889,24 +905,30 @@ async function verify(input) {
|
|
|
889
905
|
"verify: no v1 signature matched HMAC-SHA-256 of <t>.<body>");
|
|
890
906
|
}
|
|
891
907
|
|
|
892
|
-
// Optional replay defense — the nonceStore
|
|
893
|
-
// (
|
|
894
|
-
//
|
|
895
|
-
//
|
|
896
|
-
//
|
|
908
|
+
// Optional replay defense — the nonceStore speaks the SAME atomic
|
|
909
|
+
// `checkAndInsert(nonce, expireAt) → bool` contract as b.webhook.verifier
|
|
910
|
+
// and b.nonceStore.create, so the framework's own replay store drops in
|
|
911
|
+
// with no adapter. The check + insert is one atomic step: a non-atomic
|
|
912
|
+
// check-then-set would race two concurrent redeliveries of the same event
|
|
913
|
+
// (both observe "unseen", both proceed) — the exact failure replay defense
|
|
914
|
+
// exists to prevent. checkAndInsert MAY return a Promise (Redis / KV /
|
|
915
|
+
// cluster SQL); we always await it. `expireAt` is the absolute epoch-ms at
|
|
916
|
+
// which the signature itself falls outside the tolerance window, so a stored
|
|
917
|
+
// nonce is retained exactly as long as a replay of it could still be fresh.
|
|
897
918
|
if (input.nonceStore) {
|
|
898
919
|
var ns = input.nonceStore;
|
|
899
|
-
if (typeof ns.
|
|
920
|
+
if (typeof ns.checkAndInsert !== "function") {
|
|
900
921
|
throw new WebhookError("webhook/bad-nonce-store",
|
|
901
|
-
"verify: nonceStore must expose
|
|
922
|
+
"verify: nonceStore must expose checkAndInsert(nonce, expireAt) — the " +
|
|
923
|
+
"b.nonceStore contract shared with b.webhook.verifier");
|
|
902
924
|
}
|
|
903
925
|
var nonceKey = "stripe:" + parsed.timestamp + ":" + expectedHex;
|
|
904
|
-
var
|
|
905
|
-
|
|
926
|
+
var expireAt = C.TIME.seconds(parsed.timestamp) + tolerance;
|
|
927
|
+
var fresh = await ns.checkAndInsert(nonceKey, expireAt);
|
|
928
|
+
if (!fresh) {
|
|
906
929
|
throw new WebhookError("webhook/replay",
|
|
907
930
|
"verify: signature already seen within tolerance window (replay)");
|
|
908
931
|
}
|
|
909
|
-
await ns.set(nonceKey, tolerance);
|
|
910
932
|
}
|
|
911
933
|
|
|
912
934
|
return { ok: true, timestamp: parsed.timestamp, scheme: "v1" };
|
|
@@ -974,4 +996,7 @@ module.exports = {
|
|
|
974
996
|
// v0.11.25 — Stripe-shaped inbound HMAC-SHA-256 verifier + signer.
|
|
975
997
|
verify: verify,
|
|
976
998
|
sign: sign,
|
|
999
|
+
// Durable signed-webhook delivery store (sign + persist + deliver + retry +
|
|
1000
|
+
// dead-letter + operator replay). Defined in lib/webhook-dispatcher.js.
|
|
1001
|
+
dispatcher: webhookDispatcher.dispatcher,
|
|
977
1002
|
};
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
var lazyRequire = require("./lazy-require");
|
|
62
62
|
var pubsub = require("./pubsub");
|
|
63
|
+
var boundedMap = require("./bounded-map");
|
|
63
64
|
var validateOpts = require("./validate-opts");
|
|
64
65
|
var { defineClass } = require("./framework-error");
|
|
65
66
|
|
|
@@ -187,17 +188,18 @@ function create(opts) {
|
|
|
187
188
|
if (typeof channel !== "string" || channel.length === 0) {
|
|
188
189
|
throw _err("INVALID_CHANNEL", "subscribe: channel must be a non-empty string");
|
|
189
190
|
}
|
|
190
|
-
|
|
191
|
+
boundedMap.requirePresent(connToChannels, conn, function () {
|
|
191
192
|
throw _err("NOT_ATTACHED", "subscribe: connection must be attach()-ed first");
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
channelToConns.set(channel, new Set());
|
|
193
|
+
});
|
|
194
|
+
var subs = boundedMap.getOrInsert(channelToConns, channel, function () {
|
|
195
195
|
// First local listener for this channel — open a pubsub
|
|
196
|
-
// subscription so cross-node fan-out reaches us
|
|
196
|
+
// subscription so cross-node fan-out reaches us, recording the
|
|
197
|
+
// token under the same key the new Set is stored at.
|
|
197
198
|
var token = ps.subscribe(channel, _onPubsubMessage);
|
|
198
199
|
channelToToken.set(channel, token);
|
|
199
|
-
|
|
200
|
-
|
|
200
|
+
return new Set();
|
|
201
|
+
});
|
|
202
|
+
subs.add(conn);
|
|
201
203
|
connToChannels.get(conn).add(channel);
|
|
202
204
|
}
|
|
203
205
|
|
|
@@ -82,6 +82,7 @@ var C = require("./constants");
|
|
|
82
82
|
var requestHelpers = require("./request-helpers");
|
|
83
83
|
var safeAsync = require("./safe-async");
|
|
84
84
|
var safeBuffer = require("./safe-buffer");
|
|
85
|
+
var pick = require("./pick");
|
|
85
86
|
var structuredFields = require("./structured-fields");
|
|
86
87
|
var { FrameworkError } = require("./framework-error");
|
|
87
88
|
var { boot } = require("./log");
|
|
@@ -543,7 +544,7 @@ function _parseExtensionHeader(header) {
|
|
|
543
544
|
var kv = parts[j].split("=");
|
|
544
545
|
var k = kv[0].trim().toLowerCase();
|
|
545
546
|
if (!k) continue;
|
|
546
|
-
if (k
|
|
547
|
+
if (pick.isPoisonedKey(k)) continue;
|
|
547
548
|
var v = kv.length > 1 ? kv.slice(1).join("=").trim() : true;
|
|
548
549
|
// Strip surrounding quotes per the token-or-quoted-string grammar.
|
|
549
550
|
if (typeof v === "string") {
|
|
@@ -1128,7 +1129,7 @@ class WebSocketConnection extends EventEmitter {
|
|
|
1128
1129
|
return this._abort(CLOSE_INVALID_PAYLOAD,
|
|
1129
1130
|
"permessage-deflate inflate failed: " + ((e && e.message) || String(e)));
|
|
1130
1131
|
}
|
|
1131
|
-
if (data
|
|
1132
|
+
if (safeBuffer.byteLengthOf(data) > this.maxMessageBytes) {
|
|
1132
1133
|
return this._abort(CLOSE_MESSAGE_TOO_BIG,
|
|
1133
1134
|
"decompressed message exceeds maxMessageBytes");
|
|
1134
1135
|
}
|
|
@@ -1241,8 +1242,11 @@ class WebSocketConnection extends EventEmitter {
|
|
|
1241
1242
|
} else if (Buffer.isBuffer(data)) {
|
|
1242
1243
|
this._sendDataFrame(OPCODE_BINARY, data);
|
|
1243
1244
|
} else {
|
|
1245
|
+
// WebSocketError's constructor is (code, message, closeCode) — code
|
|
1246
|
+
// first — so the (message, code) errorClass path would swap the two.
|
|
1247
|
+
// errorFactory hands toBuffer a (code, message) constructor instead.
|
|
1244
1248
|
data = safeBuffer.toBuffer(data, {
|
|
1245
|
-
|
|
1249
|
+
errorFactory: function (code, message) { return new WebSocketError(code, message); },
|
|
1246
1250
|
typeCode: "ws/invalid-payload",
|
|
1247
1251
|
typeMessage: "send() requires Buffer, Uint8Array, or string",
|
|
1248
1252
|
});
|
|
@@ -113,10 +113,9 @@ function create(opts) {
|
|
|
113
113
|
throw new WormError("worm/bad-opt", "worm.create: defaultRetentionMs must be a non-negative finite number");
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
var _baseAudit = audit().namespaced("worm");
|
|
116
117
|
function _emit(action, outcome, id, metadata) {
|
|
117
|
-
|
|
118
|
-
audit().safeEmit({ action: "worm." + action, actor: { type: "system" }, outcome: outcome, metadata: Object.assign({ id: id, mode: mode }, metadata || {}) });
|
|
119
|
-
} catch (_e) { /* audit is drop-silent by design */ }
|
|
118
|
+
_baseAudit(action, outcome, Object.assign({ id: id, mode: mode }, metadata || {}), { actor: { type: "system" } });
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
function _resolveRetainUntil(now, putOpts) {
|
|
@@ -60,6 +60,7 @@ var audit = lazyRequire(function () { return require("./audit"); });
|
|
|
60
60
|
var networkTls = lazyRequire(function () { return require("./network-tls"); });
|
|
61
61
|
var safeJson = lazyRequire(function () { return require("./safe-json"); });
|
|
62
62
|
var ssrfGuard = require("./ssrf-guard");
|
|
63
|
+
var structuredFields = require("./structured-fields");
|
|
63
64
|
var C = require("./constants");
|
|
64
65
|
var { defineClass } = require("./framework-error");
|
|
65
66
|
|
|
@@ -492,7 +493,7 @@ class WsClient extends EventEmitter {
|
|
|
492
493
|
this._handshakeBuf = Buffer.concat([this._handshakeBuf, chunk]);
|
|
493
494
|
var headerEnd = this._handshakeBuf.indexOf("\r\n\r\n");
|
|
494
495
|
if (headerEnd === -1) {
|
|
495
|
-
if (this._handshakeBuf
|
|
496
|
+
if (safeBuffer.byteLengthOf(this._handshakeBuf) > C.BYTES.kib(64)) {
|
|
496
497
|
this._handleSocketError(new WsClientError("ws-client/handshake-too-large",
|
|
497
498
|
"handshake response exceeded 64 KiB before CRLFCRLF"));
|
|
498
499
|
}
|
|
@@ -526,13 +527,10 @@ class WsClient extends EventEmitter {
|
|
|
526
527
|
}
|
|
527
528
|
|
|
528
529
|
var headers = Object.create(null);
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
var hval = lines[i].slice(idx + 1).trim();
|
|
534
|
-
headers[hkey] = hval;
|
|
535
|
-
}
|
|
530
|
+
var hkvps = structuredFields.parseKeyValuePieces(lines, 1, ":");
|
|
531
|
+
structuredFields.forEachKeyValue(hkvps, function (key, value) {
|
|
532
|
+
headers[key] = value;
|
|
533
|
+
});
|
|
536
534
|
|
|
537
535
|
if ((headers["upgrade"] || "").toLowerCase() !== "websocket" ||
|
|
538
536
|
(headers["connection"] || "").toLowerCase().indexOf("upgrade") === -1) {
|
|
@@ -698,7 +696,7 @@ class WsClient extends EventEmitter {
|
|
|
698
696
|
}
|
|
699
697
|
if (frame.fin) {
|
|
700
698
|
var fullPayload = Buffer.concat(this._fragmentChunks); // allow:handrolled-buffer-collect — bounded by maxMessageBytes below
|
|
701
|
-
if (fullPayload
|
|
699
|
+
if (safeBuffer.byteLengthOf(fullPayload) > this._opts.maxMessageBytes) {
|
|
702
700
|
this._handleSocketError(new WsClientError("ws-client/message-too-big",
|
|
703
701
|
"incoming message exceeds maxMessageBytes (" + this._opts.maxMessageBytes + ")"));
|
|
704
702
|
return;
|
|
@@ -797,7 +795,7 @@ class WsClient extends EventEmitter {
|
|
|
797
795
|
} else {
|
|
798
796
|
payload = Buffer.from(JSON.stringify(data), "utf8");
|
|
799
797
|
}
|
|
800
|
-
if (payload
|
|
798
|
+
if (safeBuffer.byteLengthOf(payload) > this._opts.maxMessageBytes) {
|
|
801
799
|
throw new WsClientError("ws-client/payload-too-big",
|
|
802
800
|
"send: payload exceeds maxMessageBytes (" + this._opts.maxMessageBytes + ")");
|
|
803
801
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../scripts/release-notes-schema.json",
|
|
3
|
+
"version": "0.15.13",
|
|
4
|
+
"date": "2026-06-19",
|
|
5
|
+
"headline": "Consolidates a large set of duplicated, security-sensitive code paths — injected-dependency validation, positive-integer option caps, TOCTOU-safe file reads, markup escaping, audit emission, structured-field parsing, and untrusted-JWK import — onto shared hardened primitives, so a fix or guard now applies everywhere at once; adds a TOCTOU-safe file reader; and makes several configuration errors throw typed framework errors instead of a bare Error",
|
|
6
|
+
"summary": "Across the framework, logic that had been hand-rolled in many places (and had quietly drifted between copies) is now single-sourced through shared primitives. The practical effect for operators is consistency: the strongest available guard is applied everywhere a given operation happens, rather than in whichever copy happened to have it. Injected dependencies (stores, backends, vaults, db handles, query objects) are validated through one contract; operator-tunable positive-integer options (pool sizes, byte caps, timeouts, stream limits) validate through one bounds primitive that forwards the caller's non-retryable / HTTP-status flags; the open-fd → fstat → read-fully pattern that several call sites used to defend file reads against a swap-after-stat race (CWE-367) is now the b.atomicFile.fdSafeReadSync primitive, with symlink refusal, inode-equality, a byte cap, and an integrity hash available as options and each caller's exact typed error preserved; and the XML/HTML markup escapers (including the b.guardHtml / b.guardSvg sanitizer output) route through one b.markupEscape so the base & < > \" chain can't drift between them. A few configuration-validation paths that threw a bare Error (httpClient.configurePool, b.db stream-limit validation) now throw the module's typed framework error. No security defaults change; this release tightens and unifies existing protections rather than adding opt-ins.",
|
|
7
|
+
"sections": [
|
|
8
|
+
{
|
|
9
|
+
"heading": "Added",
|
|
10
|
+
"items": [
|
|
11
|
+
{
|
|
12
|
+
"title": "b.atomicFile.fdSafeReadSync(filepath, opts?)",
|
|
13
|
+
"body": "A TOCTOU-safe synchronous file read (CWE-367 / js/file-system-race): it opens the path read-only, then binds the size, content, and integrity measurement to the inode the fd holds open, so a swap between stat and read can't change which bytes are returned. Optional guards layer on top — maxBytes (refuse an over-cap file), refuseSymlink + inodeCheck (refuse a symlink source and any inode swap, the strongest posture for operator-writable paths), expectedHash (SHA3-512), encoding (return a string), allowShortRead — and an errorFor(kind, detail) callback lets each caller keep its own typed error. The framework's own file reads (atomic-file, the TLS certificate-path loader, the sealed-PEM source reader, the backup bundle reader) all route through it."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"title": "Shared substrate primitives for previously-duplicated logic",
|
|
17
|
+
"body": "A set of shared primitives now back call sites that had each re-implemented the same logic: validateOpts.requireMethods (gains a `permanent` flag) for injected-dependency contract checks; numericBounds positive-finite-int validators (gain an errorOpts { permanent, statusCode }) for tunable integer options; b.markupEscape for the & < > \" markup chain; structured-field parsing helpers (parseTagList, forEachKeyValue, splitUnquoted, stripDoubleQuotes, unfoldHeaderContinuations); crypto.importPublicJwk and crypto.makeBase64UrlDecoder; safeSql.toPositional; safeBuffer.makeByteCoercer; nonceStore.enforceReplay; and the audit/observability namespaced emitters. These are mostly invisible substrate — the operator-visible benefit is that a guard or fix added to one of them now covers every caller."
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"heading": "Changed",
|
|
23
|
+
"items": [
|
|
24
|
+
{
|
|
25
|
+
"title": "Configuration errors throw typed framework errors",
|
|
26
|
+
"body": "Two configuration-validation paths that threw a bare `Error` now throw the module's typed framework error: httpClient.configurePool (bad maxSockets / maxFreeSockets / keepAliveMsecs → HttpClientError, code httpclient/bad-opts) and the b.db stream-limit validation (Query.stream → DbQueryError). The thrown error remains an Error subclass, so existing `instanceof Error` / try-catch handling is unaffected; code that matched the old free-text message should match the typed error's `code` instead. Several routed validators also report a slightly more precise message (\"positive finite integer\" instead of \"positive integer\")."
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"heading": "Security",
|
|
32
|
+
"items": [
|
|
33
|
+
{
|
|
34
|
+
"title": "TOCTOU-safe file reads applied uniformly",
|
|
35
|
+
"body": "The open-fd → fstat → read pattern that anchors a file read to one inode against a swap-after-stat race is now centralized in b.atomicFile.fdSafeReadSync, and every framework file read routes through it. The sealed-PEM source reader keeps the strongest posture (symlink refusal + inode-equality + byte cap), and those guards are now available as options to any caller — a re-introduced hand-rolled read-loop is flagged by a codebase-pattern detector."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"title": "Injected-dependency and integer-option validation can't silently drift",
|
|
39
|
+
"body": "Injected stores / backends / vaults / db handles / query objects are validated through one contract (validateOpts.requireMethods), and operator-tunable positive-integer options through one bounds primitive (numericBounds), both forwarding the caller's non-retryable / HTTP-status error flags so a config error stays permanent / carries its 4xx. Inverse detectors flag any re-introduced inline check, so the validation can no longer be partially copied or weakened in one place."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"title": "Single markup-escape chain for the HTML/SVG sanitizers",
|
|
43
|
+
"body": "The b.guardHtml and b.guardSvg sanitizer output escapers, the AI-Act transparency and mail HTML escapers, and the XML report escapers now share one b.markupEscape for the base & < > \" chain (apostrophe form as a parameter; each caller keeps its own input coercion and any extra escapes such as guardHtml.escapeAttr's backtick / = IE-attribute-injection hardening). Byte-for-byte parity with the prior escapers was verified across an XSS-vector corpus, so the consolidation cannot have introduced an escaping gap; a re-inlined & < > \" chain anywhere else is flagged by a detector."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"title": "One hardening point for untrusted public-JWK import",
|
|
47
|
+
"body": "The createPublicKey-from-JWK call that imports attacker-controlled key material (DID publicKeyJwk, DNSKEY, COSE_Key, DPoP / OAuth / OIDC / DBSC proof JWKs) is centralized in b.crypto.importPublicJwk, so the untrusted-key import has a single audited home; each caller keeps its own kty/crv pre-validation and typed error."
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"title": "Linear-time Content-Security-Policy header parsing",
|
|
51
|
+
"body": "The CSP response-header parser split directives on a `/\\s*,\\s*/` regex that ran in O(n^2) on a long comma-less run of whitespace, letting a crafted header stall a worker (js/polynomial-redos). It now splits on the literal comma and trims each item, which is linear and byte-for-byte equivalent."
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"title": "Parsed INI and OpenAPI path maps can't reach Object.prototype",
|
|
55
|
+
"body": "The INI parser already refuses a `__proto__` / `constructor` / `prototype` section or key (it throws before any write); as defense-in-depth, every parsed node — and the OpenAPI paths map keyed by URL pattern — is now a null-prototype object, so even a future gap could only ever set an own property, never mutate Object.prototype (CWE-1321)."
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"heading": "Fixed",
|
|
61
|
+
"items": [
|
|
62
|
+
{
|
|
63
|
+
"title": "Webhook deliveries retry transient transport failures instead of dead-lettering them",
|
|
64
|
+
"body": "A delivery that failed with a transient transport error (connection reset, timeout, 5xx) was recorded as permanently failed and dead-lettered rather than retried. The destination-safety check (SSRF / blocked-host guard) now runs in its own step and is the only permanent failure; a transport failure backs off and retries up to the configured limit."
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"title": "Webhook deliveries table creates on MySQL",
|
|
68
|
+
"body": "The pending-delivery index used a partial-index (WHERE) clause that MySQL does not support, so the deliveries table failed to create on a MySQL backend. The index is now dialect-aware — partial on PostgreSQL / SQLite, plain on MySQL."
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"title": "Inline webhook delivery can't be double-sent by concurrent retry processing",
|
|
72
|
+
"body": "An inline delivery is now claimed (status set to in-flight with a claim timestamp) before the POST, so a concurrent retry pass can't pick up and re-send the same delivery; a row left in-flight by a crash is reclaimed after the stale-claim window."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"title": "Audit checkpoint can never be anchored against a replaced database",
|
|
76
|
+
"body": "close() fires a best-effort final checkpoint as the database shuts down. In a narrow close-then-reopen window that checkpoint's write could resume after a fresh database had opened and anchor the prior database's chain tip into it. checkpoint() now binds to the database it read the tip from and refuses to write (returns null, fail-closed) if that handle has since been closed or replaced — so a checkpoint is only ever written against the database it measured."
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
@@ -88,7 +88,7 @@ function main() {
|
|
|
88
88
|
// The first line must match the canonical shape — `- vX.Y.Z (date) — summary`.
|
|
89
89
|
var firstLine = section[0];
|
|
90
90
|
var canonical = new RegExp(
|
|
91
|
-
"^- v" + version.replace(
|
|
91
|
+
"^- v" + version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") +
|
|
92
92
|
" \\(\\d{4}-\\d{2}-\\d{2}\\) — \\S.+$"
|
|
93
93
|
);
|
|
94
94
|
if (!canonical.test(firstLine)) {
|
|
@@ -91,6 +91,25 @@ function testSqlSafeIdentifierValidation() {
|
|
|
91
91
|
try { b.safeSql.validateIdentifier("a".repeat(70)); }
|
|
92
92
|
catch (e) { threwLong = e.code === "sql/too-long"; }
|
|
93
93
|
check("safeSql rejects over-long identifier", threwLong);
|
|
94
|
+
|
|
95
|
+
// assertNoRawStringLiteral — the shared raw-SQL string-literal scanner.
|
|
96
|
+
check("assertNoRawStringLiteral: clean parameterized SQL passes",
|
|
97
|
+
b.safeSql.assertNoRawStringLiteral("WHERE id = ? AND name = ?", "x") === undefined);
|
|
98
|
+
var threwLit = false;
|
|
99
|
+
try { b.safeSql.assertNoRawStringLiteral("WHERE name = 'x'", "where"); }
|
|
100
|
+
catch (e) { threwLit = e.code === "sql/raw-literal"; }
|
|
101
|
+
check("assertNoRawStringLiteral: refuses a raw '...' literal", threwLit);
|
|
102
|
+
// A double-quoted identifier (even one containing an apostrophe) and -- / slash-star
|
|
103
|
+
// comments are skipped, not refused.
|
|
104
|
+
var skipOk = true;
|
|
105
|
+
try { b.safeSql.assertNoRawStringLiteral("SELECT \"o'col\" FROM t -- 'note'", "x"); }
|
|
106
|
+
catch (_e) { skipOk = false; }
|
|
107
|
+
check("assertNoRawStringLiteral: skips quoted identifiers + comments", skipOk);
|
|
108
|
+
// A caller-supplied makeError is honored (the per-caller error contract).
|
|
109
|
+
var customMsg = null;
|
|
110
|
+
try { b.safeSql.assertNoRawStringLiteral("a = 'b'", "build", function (w) { return new Error(w + ":custom"); }); }
|
|
111
|
+
catch (e) { customMsg = e.message; }
|
|
112
|
+
check("assertNoRawStringLiteral: honors caller makeError", customMsg === "build:custom");
|
|
94
113
|
}
|
|
95
114
|
|
|
96
115
|
function testSqlSafeQuoteIdentifier() {
|
|
@@ -7127,7 +7146,10 @@ function _spawnFakeIdpServer(routes) {
|
|
|
7127
7146
|
req.body = Buffer.concat(bodyChunks).toString("utf8");
|
|
7128
7147
|
try { handler(req, res, u); }
|
|
7129
7148
|
catch (e) {
|
|
7130
|
-
|
|
7149
|
+
// Fixed-shape, bounded diagnostic — never surface the raw exception
|
|
7150
|
+
// (its message/stack) verbatim into the response body.
|
|
7151
|
+
var emsg = (e && typeof e.message === "string") ? e.message : "error";
|
|
7152
|
+
res.writeHead(500); res.end("fake-idp handler error: " + emsg.slice(0, 120));
|
|
7131
7153
|
}
|
|
7132
7154
|
});
|
|
7133
7155
|
});
|
|
@@ -9218,6 +9240,55 @@ function testSafeSchemaPrototypePollutionDefense() {
|
|
|
9218
9240
|
check("discUnion rejects __proto__ discriminator", threw && threw.code === "safe-schema/poisoned-discriminator");
|
|
9219
9241
|
}
|
|
9220
9242
|
|
|
9243
|
+
function testPickPoisonedKeyPrimitive() {
|
|
9244
|
+
// The single prototype-pollution key guard every parser/decoder/middleware
|
|
9245
|
+
// routes through (lib/pick.js): predicate, throw-guard, add-only extension.
|
|
9246
|
+
var pick = require("../lib/pick");
|
|
9247
|
+
|
|
9248
|
+
// isPoisonedKey — the core JS pollution vectors, nothing else.
|
|
9249
|
+
check("isPoisonedKey __proto__", pick.isPoisonedKey("__proto__") === true);
|
|
9250
|
+
check("isPoisonedKey constructor", pick.isPoisonedKey("constructor") === true);
|
|
9251
|
+
check("isPoisonedKey prototype", pick.isPoisonedKey("prototype") === true);
|
|
9252
|
+
check("isPoisonedKey safe key false", pick.isPoisonedKey("displayName") === false);
|
|
9253
|
+
check("isPoisonedKey non-string false", pick.isPoisonedKey(42) === false &&
|
|
9254
|
+
pick.isPoisonedKey(null) === false &&
|
|
9255
|
+
pick.isPoisonedKey(undefined) === false);
|
|
9256
|
+
|
|
9257
|
+
// POISONED_KEYS — the frozen core snapshot.
|
|
9258
|
+
check("POISONED_KEYS frozen core", Object.isFrozen(pick.POISONED_KEYS) &&
|
|
9259
|
+
pick.POISONED_KEYS.join(",") === "__proto__,constructor,prototype");
|
|
9260
|
+
|
|
9261
|
+
// assertSafeKey — throw-guard form; the caller throws its own typed error.
|
|
9262
|
+
var blocked = null;
|
|
9263
|
+
try { pick.assertSafeKey("__proto__", function () { throw new TypeError("BLOCKED"); }); }
|
|
9264
|
+
catch (e) { blocked = e.message; }
|
|
9265
|
+
check("assertSafeKey fires onPoisoned for poisoned key", blocked === "BLOCKED");
|
|
9266
|
+
var fired = false;
|
|
9267
|
+
pick.assertSafeKey("safe", function () { fired = true; });
|
|
9268
|
+
check("assertSafeKey silent for safe key", fired === false);
|
|
9269
|
+
var badCb = null;
|
|
9270
|
+
try { pick.assertSafeKey("x", 7); } catch (e) { badCb = e.constructor.name; }
|
|
9271
|
+
check("assertSafeKey rejects non-function onPoisoned", badCb === "TypeError");
|
|
9272
|
+
|
|
9273
|
+
// registerPoisonedKeys — add-only defense-in-depth extension. Use a unique
|
|
9274
|
+
// sentinel so registering it can't perturb any other test's data keys
|
|
9275
|
+
// (registration is process-global by design).
|
|
9276
|
+
var SENTINEL = "__blamejs_test_poison_sentinel__";
|
|
9277
|
+
check("sentinel not poisoned by default", pick.isPoisonedKey(SENTINEL) === false);
|
|
9278
|
+
pick.registerPoisonedKeys([SENTINEL]);
|
|
9279
|
+
check("registerPoisonedKeys adds extra key", pick.isPoisonedKey(SENTINEL) === true);
|
|
9280
|
+
check("core vectors still poisoned after extend", pick.isPoisonedKey("__proto__") === true);
|
|
9281
|
+
var badReg = null;
|
|
9282
|
+
try { pick.registerPoisonedKeys("not-array"); } catch (e) { badReg = e.constructor.name; }
|
|
9283
|
+
check("registerPoisonedKeys rejects non-array", badReg === "TypeError");
|
|
9284
|
+
|
|
9285
|
+
// pick() itself strips poisoned keys (built via Object.fromEntries so the
|
|
9286
|
+
// key is a real own property, not a prototype set).
|
|
9287
|
+
var stripped = pick(Object.fromEntries([["a", 1], ["__proto__", { x: 1 }]]), ["a", "__proto__"]);
|
|
9288
|
+
check("pick() strips __proto__ even if allowlisted", stripped.a === 1 &&
|
|
9289
|
+
!Object.prototype.hasOwnProperty.call(stripped, "__proto__"));
|
|
9290
|
+
}
|
|
9291
|
+
|
|
9221
9292
|
function testSafeSchemaErrorIssues() {
|
|
9222
9293
|
var s = b.safeSchema;
|
|
9223
9294
|
var threw = null;
|
|
@@ -14359,6 +14430,20 @@ function testBufferSafeToBuffer() {
|
|
|
14359
14430
|
try { bs.toBuffer(123); }
|
|
14360
14431
|
catch (e) { threwType = e.code === "buffer/wrong-input-type"; }
|
|
14361
14432
|
check("toBuffer: number rejected", threwType);
|
|
14433
|
+
|
|
14434
|
+
// byteLengthOf — the byte-cap measurement primitive: UTF-8 bytes for a
|
|
14435
|
+
// string, .length for a Buffer/Uint8Array (whose .length already IS bytes).
|
|
14436
|
+
check("byteLengthOf is a function", typeof bs.byteLengthOf === "function");
|
|
14437
|
+
check("byteLengthOf: ASCII string", bs.byteLengthOf("abc") === 3);
|
|
14438
|
+
var cjk = String.fromCharCode(0x4e2d); // one 3-byte UTF-8 char, 1 UTF-16 unit
|
|
14439
|
+
check("byteLengthOf: multibyte string measures UTF-8 bytes, not UTF-16 units",
|
|
14440
|
+
bs.byteLengthOf(cjk) === 3 && cjk.length === 1);
|
|
14441
|
+
check("byteLengthOf: Buffer .length is bytes", bs.byteLengthOf(Buffer.from([1, 2, 3, 4])) === 4);
|
|
14442
|
+
check("byteLengthOf: Uint8Array length is bytes", bs.byteLengthOf(new Uint8Array(5)) === 5);
|
|
14443
|
+
check("byteLengthOf: encoding honored (hex)", bs.byteLengthOf("ff00", "hex") === 2);
|
|
14444
|
+
var threwBL = false;
|
|
14445
|
+
try { bs.byteLengthOf({}); } catch (e) { threwBL = e instanceof TypeError; }
|
|
14446
|
+
check("byteLengthOf: non-string/non-bytes throws TypeError", threwBL);
|
|
14362
14447
|
}
|
|
14363
14448
|
|
|
14364
14449
|
function testBufferSafeBoundedChunkCollector() {
|
|
@@ -14657,9 +14742,9 @@ async function testHttpClientConfigurePool() {
|
|
|
14657
14742
|
}
|
|
14658
14743
|
rejects("non-object input", function () { b.httpClient.configurePool("nope"); }, /must be an object/);
|
|
14659
14744
|
rejects("unknown key", function () { b.httpClient.configurePool({ bogus: 1 }); }, /unknown option/);
|
|
14660
|
-
rejects("negative maxSockets", function () { b.httpClient.configurePool({ maxSockets: -1 }); }, /positive integer/);
|
|
14661
|
-
rejects("non-integer keepAliveMsecs",function () { b.httpClient.configurePool({ keepAliveMsecs: 1.5 }); }, /positive integer/);
|
|
14662
|
-
rejects("Infinity maxFreeSockets", function () { b.httpClient.configurePool({ maxFreeSockets: Infinity }); }, /positive integer/);
|
|
14745
|
+
rejects("negative maxSockets", function () { b.httpClient.configurePool({ maxSockets: -1 }); }, /positive finite integer/);
|
|
14746
|
+
rejects("non-integer keepAliveMsecs",function () { b.httpClient.configurePool({ keepAliveMsecs: 1.5 }); }, /positive finite integer/);
|
|
14747
|
+
rejects("Infinity maxFreeSockets", function () { b.httpClient.configurePool({ maxFreeSockets: Infinity }); }, /positive finite integer/);
|
|
14663
14748
|
rejects("non-boolean keepAlive", function () { b.httpClient.configurePool({ keepAlive: "yes" }); }, /must be a boolean/);
|
|
14664
14749
|
rejects("bad scheduling", function () { b.httpClient.configurePool({ scheduling: "rr" }); }, /lifo.*fifo/);
|
|
14665
14750
|
}
|
|
@@ -15016,8 +15101,13 @@ async function testHttpClientRedirectFollow() {
|
|
|
15016
15101
|
|
|
15017
15102
|
async function testHttpClientRedirectMaxHops() {
|
|
15018
15103
|
var http = require("http");
|
|
15104
|
+
// Each hop redirects to a fresh, fixed-shape path (a per-request counter),
|
|
15105
|
+
// never one derived from the incoming request URL — so the location never
|
|
15106
|
+
// repeats and the client keeps following until it hits maxRedirects.
|
|
15107
|
+
var hop = 0;
|
|
15019
15108
|
var server = http.createServer(function (req, res) {
|
|
15020
|
-
|
|
15109
|
+
hop += 1;
|
|
15110
|
+
res.writeHead(302, { "Location": "/loop-" + hop });
|
|
15021
15111
|
res.end();
|
|
15022
15112
|
});
|
|
15023
15113
|
var port = await listenOnRandomPort(server);
|
|
@@ -16399,14 +16489,15 @@ async function testWebSocketConnection() {
|
|
|
16399
16489
|
// Parse HTTP headers (very crude — enough for tests).
|
|
16400
16490
|
var headerLines = headerBuffer.substring(0, idx).split("\r\n");
|
|
16401
16491
|
var requestLine = headerLines[0].split(" ");
|
|
16402
|
-
var
|
|
16492
|
+
var headerPairs = [];
|
|
16403
16493
|
for (var i = 1; i < headerLines.length; i++) {
|
|
16404
16494
|
var p = headerLines[i].indexOf(":");
|
|
16405
16495
|
if (p === -1) continue;
|
|
16406
16496
|
var k = headerLines[i].substring(0, p).trim().toLowerCase();
|
|
16407
16497
|
var v = headerLines[i].substring(p + 1).trim();
|
|
16408
|
-
|
|
16498
|
+
headerPairs.push([k, v]);
|
|
16409
16499
|
}
|
|
16500
|
+
var headers = Object.fromEntries(headerPairs);
|
|
16410
16501
|
var req = { method: requestLine[0], url: requestLine[1], headers: headers };
|
|
16411
16502
|
var head = Buffer.from(headerBuffer.substring(idx + 4), "binary");
|
|
16412
16503
|
var conn = ws.handleUpgrade(req, socket, head, { closeGraceMs: 50 });
|
|
@@ -16490,6 +16581,42 @@ async function testWebSocketConnection() {
|
|
|
16490
16581
|
}
|
|
16491
16582
|
}
|
|
16492
16583
|
|
|
16584
|
+
async function testWebSocketSendBadPayloadErrorOrder() {
|
|
16585
|
+
var EE = require("node:events").EventEmitter;
|
|
16586
|
+
var ws = b.websocket;
|
|
16587
|
+
|
|
16588
|
+
// Minimal socket stand-in: WebSocketConnection only needs the
|
|
16589
|
+
// EventEmitter surface plus write/destroy. send() throws synchronously
|
|
16590
|
+
// on a non-byte payload BEFORE any frame is written, so write/destroy
|
|
16591
|
+
// are never reached on this path.
|
|
16592
|
+
var socket = new EE();
|
|
16593
|
+
socket.write = function () { return true; };
|
|
16594
|
+
socket.destroy = function () {};
|
|
16595
|
+
|
|
16596
|
+
var conn = new ws.WebSocketConnection(socket, { pingIntervalMs: 60000, pongTimeoutMs: 120000 });
|
|
16597
|
+
try {
|
|
16598
|
+
var threw = null;
|
|
16599
|
+
try {
|
|
16600
|
+
// Plain object is not Buffer / Uint8Array / string — toBuffer refuses.
|
|
16601
|
+
conn.send({ not: "bytes" });
|
|
16602
|
+
} catch (e) { threw = e; }
|
|
16603
|
+
|
|
16604
|
+
check("ws send bad payload throws WebSocketError",
|
|
16605
|
+
threw !== null && threw.isWebSocketError === true);
|
|
16606
|
+
// The bug was a swapped (message, code) construction: .code became the
|
|
16607
|
+
// human message and .message became the code string. Assert the correct
|
|
16608
|
+
// order — code is the dotted ws/* identifier, message is the prose.
|
|
16609
|
+
check("ws send bad payload: .code is the ws/* code",
|
|
16610
|
+
threw && threw.code === "ws/invalid-payload");
|
|
16611
|
+
check("ws send bad payload: .message is the prose",
|
|
16612
|
+
threw && threw.message === "send() requires Buffer, Uint8Array, or string");
|
|
16613
|
+
} finally {
|
|
16614
|
+
// Stop the connection's ping timer (it is unref'd, but tidy teardown
|
|
16615
|
+
// keeps the suite's open-handle count clean).
|
|
16616
|
+
socket.emit("close");
|
|
16617
|
+
}
|
|
16618
|
+
}
|
|
16619
|
+
|
|
16493
16620
|
// Test helpers — local to the websocket suite. nodeCrypto for the
|
|
16494
16621
|
// random key, _readUntil/_readNBytes/_readSome for incremental client
|
|
16495
16622
|
// socket reads.
|
|
@@ -18399,6 +18526,7 @@ async function run() {
|
|
|
18399
18526
|
testSafeSchemaRefineTransform();
|
|
18400
18527
|
testSafeSchemaLazyAndPreprocess();
|
|
18401
18528
|
testSafeSchemaPrototypePollutionDefense();
|
|
18529
|
+
testPickPoisonedKeyPrimitive();
|
|
18402
18530
|
testSafeSchemaErrorIssues();
|
|
18403
18531
|
testSafeSchemaImmutability();
|
|
18404
18532
|
// events — moved to test/layer-0-primitives/events.test.js
|
|
@@ -18709,6 +18837,7 @@ async function run() {
|
|
|
18709
18837
|
testWebSocketHandshake();
|
|
18710
18838
|
testWebSocketFrames();
|
|
18711
18839
|
await testWebSocketConnection();
|
|
18840
|
+
await testWebSocketSendBadPayloadErrorOrder();
|
|
18712
18841
|
testRouterWsValidation();
|
|
18713
18842
|
await testRouterSetsRoutePattern();
|
|
18714
18843
|
|
|
@@ -484,10 +484,16 @@ async function testFrameworkSchemaEnsure() {
|
|
|
484
484
|
externalDbBackend: "ops",
|
|
485
485
|
dialect: "sqlite",
|
|
486
486
|
});
|
|
487
|
-
check("ensureSchema returns
|
|
488
|
-
result.tables.length ===
|
|
487
|
+
check("ensureSchema returns 20 tables (_blamejs_session_valid_from is the 20th)",
|
|
488
|
+
result.tables.length === 20);
|
|
489
489
|
check("ensureSchema includes _blamejs_cache_tags",
|
|
490
490
|
result.tables.indexOf("_blamejs_cache_tags") !== -1);
|
|
491
|
+
check("ensureSchema includes _blamejs_session_valid_from",
|
|
492
|
+
result.tables.indexOf("_blamejs_session_valid_from") !== -1);
|
|
493
|
+
check("ensureSchema includes _blamejs_break_glass_policies",
|
|
494
|
+
result.tables.indexOf("_blamejs_break_glass_policies") !== -1);
|
|
495
|
+
check("ensureSchema includes _blamejs_break_glass_grants",
|
|
496
|
+
result.tables.indexOf("_blamejs_break_glass_grants") !== -1);
|
|
491
497
|
check("ensureSchema includes _blamejs_audit_log",
|
|
492
498
|
result.tables.indexOf("_blamejs_audit_log") !== -1);
|
|
493
499
|
check("ensureSchema includes _blamejs_consent_log",
|
|
@@ -538,7 +544,7 @@ async function testFrameworkSchemaEnsure() {
|
|
|
538
544
|
externalDbBackend: "ops",
|
|
539
545
|
dialect: "sqlite",
|
|
540
546
|
});
|
|
541
|
-
check("ensureSchema is idempotent", second.tables.length ===
|
|
547
|
+
check("ensureSchema is idempotent", second.tables.length === 20);
|
|
542
548
|
|
|
543
549
|
// Indexes exist
|
|
544
550
|
var idxRow = await b.externalDb.query(
|