@blamejs/blamejs-shop 0.4.31 → 0.4.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +392 -278
- package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
- package/lib/vendor/blamejs/.gitignore +6 -0
- package/lib/vendor/blamejs/CHANGELOG.md +26 -0
- package/lib/vendor/blamejs/MIGRATING.md +43 -0
- package/lib/vendor/blamejs/README.md +8 -6
- package/lib/vendor/blamejs/SECURITY.md +19 -3
- package/lib/vendor/blamejs/api-snapshot.json +2190 -664
- package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
- package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
- package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
- package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
- package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
- package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
- package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
- package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
- package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
- package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
- package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
- package/lib/vendor/blamejs/index.js +4 -0
- package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
- package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
- package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
- package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
- package/lib/vendor/blamejs/lib/api-key.js +158 -77
- package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
- package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
- package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
- package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
- package/lib/vendor/blamejs/lib/audit.js +259 -123
- package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
- package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
- package/lib/vendor/blamejs/lib/backup/index.js +45 -10
- package/lib/vendor/blamejs/lib/break-glass.js +355 -147
- package/lib/vendor/blamejs/lib/cache.js +174 -105
- package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
- package/lib/vendor/blamejs/lib/cli.js +19 -14
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
- package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
- package/lib/vendor/blamejs/lib/cluster.js +119 -71
- package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
- package/lib/vendor/blamejs/lib/compliance.js +206 -4
- package/lib/vendor/blamejs/lib/consent.js +82 -29
- package/lib/vendor/blamejs/lib/constants.js +27 -11
- package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
- package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
- package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
- package/lib/vendor/blamejs/lib/db-query.js +882 -260
- package/lib/vendor/blamejs/lib/db-schema.js +228 -44
- package/lib/vendor/blamejs/lib/db.js +249 -99
- package/lib/vendor/blamejs/lib/dsr.js +385 -55
- package/lib/vendor/blamejs/lib/error-page.js +14 -1
- package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
- package/lib/vendor/blamejs/lib/external-db.js +549 -34
- package/lib/vendor/blamejs/lib/file-upload.js +52 -7
- package/lib/vendor/blamejs/lib/framework-error.js +20 -1
- package/lib/vendor/blamejs/lib/framework-files.js +73 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
- package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
- package/lib/vendor/blamejs/lib/guard-all.js +1 -0
- package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
- package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
- package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
- package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
- package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
- package/lib/vendor/blamejs/lib/guard-email.js +47 -69
- package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
- package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
- package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
- package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
- package/lib/vendor/blamejs/lib/guard-html.js +53 -108
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
- package/lib/vendor/blamejs/lib/guard-image.js +46 -103
- package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
- package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
- package/lib/vendor/blamejs/lib/guard-json.js +38 -108
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
- package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
- package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
- package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
- package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
- package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
- package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
- package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
- package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
- package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
- package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
- package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
- package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
- package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
- package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
- package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
- package/lib/vendor/blamejs/lib/guard-template.js +35 -172
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-time.js +32 -154
- package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
- package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
- package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
- package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
- package/lib/vendor/blamejs/lib/http-client.js +37 -9
- package/lib/vendor/blamejs/lib/inbox.js +120 -107
- package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
- package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
- package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
- package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
- package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
- package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
- package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
- package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
- package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
- package/lib/vendor/blamejs/lib/mail-store.js +293 -154
- package/lib/vendor/blamejs/lib/mail.js +8 -4
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
- package/lib/vendor/blamejs/lib/migrations.js +108 -66
- package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
- package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
- package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
- package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
- package/lib/vendor/blamejs/lib/observability.js +124 -0
- package/lib/vendor/blamejs/lib/otel-export.js +12 -3
- package/lib/vendor/blamejs/lib/outbox.js +184 -83
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
- package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
- package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
- package/lib/vendor/blamejs/lib/queue-local.js +225 -140
- package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
- package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
- package/lib/vendor/blamejs/lib/queue.js +7 -0
- package/lib/vendor/blamejs/lib/redact.js +68 -11
- package/lib/vendor/blamejs/lib/redis-client.js +160 -31
- package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
- package/lib/vendor/blamejs/lib/retention.js +101 -40
- package/lib/vendor/blamejs/lib/router.js +212 -5
- package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
- package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
- package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
- package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
- package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
- package/lib/vendor/blamejs/lib/safe-url.js +170 -3
- package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
- package/lib/vendor/blamejs/lib/scheduler.js +35 -12
- package/lib/vendor/blamejs/lib/seeders.js +122 -74
- package/lib/vendor/blamejs/lib/session-stores.js +42 -14
- package/lib/vendor/blamejs/lib/session.js +175 -77
- package/lib/vendor/blamejs/lib/sql.js +3842 -0
- package/lib/vendor/blamejs/lib/sse.js +26 -0
- package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
- package/lib/vendor/blamejs/lib/static.js +177 -34
- package/lib/vendor/blamejs/lib/subject.js +96 -49
- package/lib/vendor/blamejs/lib/vault/index.js +3 -2
- package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
- package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
- package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
- package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
- package/lib/vendor/blamejs/lib/websocket.js +35 -5
- package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
- package/lib/vendor/blamejs/package.json +2 -2
- package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
- package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
- package/lib/vendor/blamejs/scripts/check-services.js +21 -0
- package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
- package/lib/vendor/blamejs/scripts/release.js +398 -38
- package/lib/vendor/blamejs/test/00-primitives.js +117 -0
- package/lib/vendor/blamejs/test/10-state.js +140 -14
- package/lib/vendor/blamejs/test/20-db.js +65 -2
- package/lib/vendor/blamejs/test/helpers/db.js +9 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
- package/lib/vendor/blamejs/test/helpers/services.js +21 -0
- package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
- package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
- package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
- package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
- package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
- package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
- package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
- package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
- package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
- package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
- package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
- package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
- package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
- package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
- package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
- package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
- package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
- package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
- package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
- package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
- package/lib/vendor/blamejs/test/smoke.js +79 -21
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
- package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
- package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
- package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
- package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
- package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
- package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
- package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
- package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
- package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
- package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
- package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
- package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
- package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
|
@@ -116,11 +116,7 @@ function _layerDirFor(layerNum) {
|
|
|
116
116
|
return dir;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
// run() / groups[] export shapes; new files only need run().
|
|
121
|
-
async function _runTestModule(modulePath, displayName) {
|
|
122
|
-
var mod = require(modulePath);
|
|
123
|
-
var fileStart = Date.now();
|
|
119
|
+
async function _runModuleBody(mod, displayName) {
|
|
124
120
|
if (typeof mod.run === "function") {
|
|
125
121
|
try { await mod.run(); }
|
|
126
122
|
catch (err) {
|
|
@@ -150,6 +146,37 @@ async function _runTestModule(modulePath, displayName) {
|
|
|
150
146
|
}
|
|
151
147
|
}
|
|
152
148
|
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Run a single test file. Backward compat with the legacy
|
|
152
|
+
// run() / groups[] export shapes; new files only need run().
|
|
153
|
+
//
|
|
154
|
+
// The sequential layers (1-5) run in-process, so unlike the forked
|
|
155
|
+
// layer-0 children they have no per-fork watchdog. A hung async op here
|
|
156
|
+
// (a blocking native call starved on the libuv threadpool, a leaked
|
|
157
|
+
// handle, an awaited promise that never settles) would otherwise ride
|
|
158
|
+
// to the CI job's wall-clock limit as an unattributable multi-hour
|
|
159
|
+
// hang. This in-process watchdog races the file body against the same
|
|
160
|
+
// FILE_TIMEOUT_MS budget: the main loop stays responsive while a
|
|
161
|
+
// threadpool thread is stuck, so the timer fires, names the file, and
|
|
162
|
+
// fails fast and diagnosably.
|
|
163
|
+
async function _runTestModule(modulePath, displayName) {
|
|
164
|
+
var mod = require(modulePath);
|
|
165
|
+
var fileStart = Date.now();
|
|
166
|
+
var watchdog = null;
|
|
167
|
+
var timed = new Promise(function (_resolve, reject) {
|
|
168
|
+
watchdog = setTimeout(function () {
|
|
169
|
+
reject(new Error(displayName + ": sequential-layer watchdog — exceeded " +
|
|
170
|
+
FILE_TIMEOUT_MS + "ms with no completion (likely a hung async op: " +
|
|
171
|
+
"libuv-threadpool starvation, a leaked handle, or a blocking native call)"));
|
|
172
|
+
}, FILE_TIMEOUT_MS);
|
|
173
|
+
if (typeof watchdog.unref === "function") watchdog.unref();
|
|
174
|
+
});
|
|
175
|
+
try {
|
|
176
|
+
await Promise.race([_runModuleBody(mod, displayName), timed]);
|
|
177
|
+
} finally {
|
|
178
|
+
if (watchdog !== null) clearTimeout(watchdog);
|
|
179
|
+
}
|
|
153
180
|
return Date.now() - fileStart;
|
|
154
181
|
}
|
|
155
182
|
|
|
@@ -350,23 +377,51 @@ async function _runLayer(layerNum, legacyPath, layerName) {
|
|
|
350
377
|
// pure-primitive and don't share db/cluster/vault state. Layers 1+
|
|
351
378
|
// stay sequential.
|
|
352
379
|
if (PARALLEL > 1 && layerNum === 0) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
//
|
|
380
|
+
var totalChecks = 0;
|
|
381
|
+
var firstFailure = null;
|
|
382
|
+
var resultsByFile = {};
|
|
383
|
+
var newTimings = {};
|
|
384
|
+
|
|
385
|
+
// A file that declares the SMOKE_RUN_SOLO marker (a CPU-bound test
|
|
386
|
+
// that itself fans out across worker_threads — e.g. the pattern-
|
|
387
|
+
// catalog duplicate-scan) runs ALONE with the whole box, NOT in the
|
|
388
|
+
// parallel pool. On a low-core runner (macos-latest = 3 cores) its
|
|
389
|
+
// internal workers plus the sibling forks oversubscribe the CPU and
|
|
390
|
+
// the scan overruns its per-file budget; given the whole box it
|
|
391
|
+
// finishes in its normal time. Marker-based, not timing-based, so it
|
|
392
|
+
// works on a fresh CI runner that has no persisted timings yet.
|
|
393
|
+
var soloFiles = [];
|
|
394
|
+
var poolFiles = [];
|
|
395
|
+
for (var pf = 0; pf < files.length; pf += 1) {
|
|
396
|
+
var solo = false;
|
|
397
|
+
try {
|
|
398
|
+
solo = fs.readFileSync(path.join(dir, files[pf]), "utf8")
|
|
399
|
+
.slice(0, 2048).indexOf("SMOKE_RUN_SOLO") !== -1;
|
|
400
|
+
} catch (_e) { solo = false; }
|
|
401
|
+
(solo ? soloFiles : poolFiles).push(files[pf]);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Solo phase — heavy files one at a time, each with the full box.
|
|
405
|
+
for (var si = 0; si < soloFiles.length && !firstFailure; si += 1) {
|
|
406
|
+
var sName = soloFiles[si];
|
|
407
|
+
var srv = await _runFileForked(path.join(dir, sName), layerName + " / " + sName);
|
|
408
|
+
resultsByFile[sName] = srv;
|
|
409
|
+
newTimings[sName] = srv.ms;
|
|
410
|
+
if (!srv.ok && !firstFailure) firstFailure = srv;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Pool phase — light remainder, LPT (Longest-Processing-Time-first)
|
|
414
|
+
// scheduled: sort by recorded historical duration descending so the
|
|
415
|
+
// long-tail file starts on worker 1 instead of landing in the last
|
|
416
|
+
// batch (within 4/3 of optimal makespan). Continuous queue: each
|
|
417
|
+
// worker pulls the next file as soon as it finishes.
|
|
359
418
|
var timings = _readTimings();
|
|
360
|
-
var ordered =
|
|
419
|
+
var ordered = poolFiles.slice().sort(function (a, b) {
|
|
361
420
|
var ta = timings[a] || Infinity; // unknown → Infinity → schedule early on first run
|
|
362
421
|
var tb = timings[b] || Infinity;
|
|
363
422
|
return tb - ta;
|
|
364
423
|
});
|
|
365
424
|
var cursor = 0;
|
|
366
|
-
var totalChecks = 0;
|
|
367
|
-
var firstFailure = null;
|
|
368
|
-
var resultsByFile = {};
|
|
369
|
-
var newTimings = {};
|
|
370
425
|
async function worker() {
|
|
371
426
|
while (true) {
|
|
372
427
|
if (firstFailure) return;
|
|
@@ -379,14 +434,17 @@ async function _runLayer(layerNum, legacyPath, layerName) {
|
|
|
379
434
|
if (!rv.ok && !firstFailure) firstFailure = rv;
|
|
380
435
|
}
|
|
381
436
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
437
|
+
if (!firstFailure) {
|
|
438
|
+
var pool = [];
|
|
439
|
+
for (var w = 0; w < PARALLEL; w += 1) pool.push(worker());
|
|
440
|
+
await Promise.all(pool);
|
|
441
|
+
}
|
|
442
|
+
|
|
385
443
|
// Print in original sort() order so the per-run output is stable
|
|
386
|
-
// for diff-based comparison (the LPT order is internal scheduling).
|
|
444
|
+
// for diff-based comparison (the solo/LPT order is internal scheduling).
|
|
387
445
|
for (var p = 0; p < files.length; p += 1) {
|
|
388
446
|
var rf = resultsByFile[files[p]];
|
|
389
|
-
if (!rf) continue; //
|
|
447
|
+
if (!rf) continue; // pool aborted before this file ran
|
|
390
448
|
totalChecks += rf.checks;
|
|
391
449
|
if (!rf.ok) {
|
|
392
450
|
if (rf.stderr) process.stderr.write(rf.stderr);
|
package/package.json
CHANGED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../scripts/release-notes-schema.json",
|
|
3
|
-
"version": "0.14.0",
|
|
4
|
-
"date": "2026-05-29",
|
|
5
|
-
"headline": "Operator-configurable header and field names across SSE, request-id, rate-limit, age-gate, AI-Act disclosure, GraphQL federation, and the HTTP cache",
|
|
6
|
-
"summary": "This release makes operator-facing identifiers that were hardcoded configurable. The framework already let operators rename most names (CSRF cookie/field, cookie parser, i18n header/query/cookie, mTLS CA name, and so on); this closes the remaining gaps so a custom or framework-specific name is never frozen. Every new option defaults to the value emitted today, so upgrading changes no behavior — these are additive knobs. It also fixes a request-id asymmetry (the response header is now written on the same name the inbound id is read from) and wires an SSE proxy-buffering option whose escape hatch was documented but never implemented.",
|
|
7
|
-
"sections": [
|
|
8
|
-
{
|
|
9
|
-
"heading": "Added",
|
|
10
|
-
"items": [
|
|
11
|
-
{
|
|
12
|
-
"title": "Configurable cache-status header on the HTTP client",
|
|
13
|
-
"body": "The outbound HTTP client annotated every cached response with a hardcoded `x-blamejs-cache: HIT|MISS|STALE|REVALIDATED` header. `b.httpClient.cache.create` now takes `statusHeader` (default \"x-blamejs-cache\") — pass a custom name (e.g. \"x-cache\") to rename it, or null/false to suppress it entirely. The decision remains available programmatically on `res.cacheStatus`."
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"title": "Configurable rate-limit header names",
|
|
17
|
-
"body": "`b.middleware.rateLimit` emitted the de-facto `X-RateLimit-Limit` / `X-RateLimit-Remaining` headers (which are not RFC-pinned). It now accepts `headerPrefix` (default \"X-RateLimit-\") so operators can match the unprefixed IETF-draft `RateLimit-*` names or an upstream gateway's convention; the limit/remaining pair is always built from the same prefix."
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"title": "Configurable age-gate and AI-Act disclosure header names",
|
|
21
|
-
"body": "`b.middleware.ageGate` now takes `privacyPostureHeader` (default \"X-Privacy-Posture\"; null/false to suppress), and `b.middleware.aiActDisclosure` takes `headerPrefix` (default \"AI-Act-\") that prefixes the emitted Notice / Article / Policy headers. The EU AI Act mandates the disclosure, not the HTTP spelling, so operators matching a downstream convention can rename these."
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"title": "Configurable GraphQL-federation replay-nonce header",
|
|
25
|
-
"body": "`b.graphqlFederation.guardSdl` read the replay nonce from the Apollo-vendor `x-apollographql-router-nonce` header with no override. It now accepts `nonceHeader` (default unchanged) so an operator fronting the gateway with a non-Apollo router can point the replay check at their own header."
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"title": "SSE proxy-buffering opt-out",
|
|
29
|
-
"body": "`b.sse.create` and `b.middleware.sse` set `X-Accel-Buffering: no` (the nginx hint that disables proxy buffering). They now accept `proxyBuffer` (default true) — pass false when not behind nginx, or when buffering is controlled at the load balancer, to suppress the nginx-specific header. The opt-out was previously referenced in the documentation but not implemented."
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"heading": "Fixed",
|
|
35
|
-
"items": [
|
|
36
|
-
{
|
|
37
|
-
"title": "Request-id middleware reflects the configured header name",
|
|
38
|
-
"body": "`b.log.middleware` read the inbound request id from a configurable `headerName` but always wrote the response on the literal `X-Request-Id`. An operator who set a custom `headerName` (e.g. `X-Correlation-Id`) therefore read from one header and emitted another. The response is now written on the same configured name; the default remains `X-Request-Id`, so deployments that did not set `headerName` are unaffected."
|
|
39
|
-
}
|
|
40
|
-
]
|
|
41
|
-
}
|
|
42
|
-
]
|
|
43
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../scripts/release-notes-schema.json",
|
|
3
|
-
"version": "0.14.1",
|
|
4
|
-
"date": "2026-05-29",
|
|
5
|
-
"headline": "Correctness fixes: JAR request-object typ enforcement, byte-faithful PGP multipart/signed, and SAML InResponseTo binding",
|
|
6
|
-
"summary": "A set of correctness fixes across the auth, mail-crypto, TLS, and encoder surfaces. The most important: JWT-secured authorization requests (RFC 9101) now require the request object to carry the registered `oauth-authz-req+jwt` typ, closing a cross-JWT-confusion vector; the PGP multipart/signed wrapper is now assembled byte-faithfully so non-ASCII signed content can't be corrupted; and SAML response verification now returns the InResponseTo of the SubjectConfirmation that actually validated. Two of these change behavior — see Changed — and are bug fixes rather than new features.",
|
|
7
|
-
"sections": [
|
|
8
|
-
{
|
|
9
|
-
"heading": "Security",
|
|
10
|
-
"items": [
|
|
11
|
-
{
|
|
12
|
-
"title": "JAR request objects must carry the registered typ (RFC 9101)",
|
|
13
|
-
"body": "`b.auth.jar.parse` now requires the request-object JWS header to carry `typ: \"oauth-authz-req+jwt\"` (with or without the `application/` prefix); a request object with an absent or different typ is refused with `auth-jar/bad-typ`. RFC 9101 §10.8 specifies this media type precisely to stop a JWT minted for another purpose (an ID token, an access token, a logout token) and signed by the same client key from being replayed as a request object. The existing `iss` / `aud` / `client_id` bindings already constrained that; this restores the explicit type check as well. This is stricter than before — see Changed."
|
|
14
|
-
}
|
|
15
|
-
]
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"heading": "Changed",
|
|
19
|
-
"items": [
|
|
20
|
-
{
|
|
21
|
-
"title": "JAR parsing rejects untyped request objects (breaking)",
|
|
22
|
-
"body": "Following the typ enforcement above, a request object whose header omits `typ` is now refused. An authorization server whose clients sign request objects without the `oauth-authz-req+jwt` typ must update those clients to set it."
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
"title": "PGP sign() returns multipartSigned as a Buffer (breaking)",
|
|
26
|
-
"body": "`b.mail.crypto.pgp.sign(...).multipartSigned` is now a Buffer instead of a string. The OpenPGP signature covers the signed-part bytes exactly, and a JS-string round trip through latin1/utf8 could corrupt non-ASCII signed content and break verification, so the RFC 3156 multipart/signed wrapper is now assembled as bytes. Code that wrote the previous string to the wire works unchanged when it writes the Buffer; code that did string operations on the value should treat it as a Buffer (its `indexOf` / `toString` still work)."
|
|
27
|
-
}
|
|
28
|
-
]
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"heading": "Fixed",
|
|
32
|
-
"items": [
|
|
33
|
-
{
|
|
34
|
-
"title": "SAML response verification binds InResponseTo to the validated confirmation",
|
|
35
|
-
"body": "`verifyResponse` returned the InResponseTo of the first SubjectConfirmationData in the assertion rather than of the SubjectConfirmation that actually passed bearer validation. When a response carried more than one SubjectConfirmation, the returned value could come from a non-validated confirmation. It is now the InResponseTo of the confirmation that validated."
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"title": "checkServerIdentity9525 emits the documented CN-fallback audit code",
|
|
39
|
-
"body": "A CN-only legacy certificate (a Common Name present, no subjectAltName) is now refused by the exported `b.network.tls.checkServerIdentity9525` with the distinct `tls/pkix-cn-fallback-refused` code its documentation promised, so audit logs can tell a CN-only certificate apart from one carrying neither a SAN nor a CN (which still yields `tls/pkix-san-required`). The accept/refuse outcome is unchanged — both are refused; only the audit granularity improved."
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"title": "OIDC back-channel logout / JARM no longer accept dead override parameters",
|
|
43
|
-
"body": "`verifyBackchannelLogoutToken` and `parseJarmResponse` passed `acceptedAlgs` / `jwksUri` / `maxClockSkewMs` through to the ID-token verifier, which ignored them and applied the configured (create()-time) values. The pass-throughs are removed so the code no longer reads as if those can be overridden per call; the configured trust anchor was — and remains — what applies."
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"title": "Queue lease failures are logged",
|
|
47
|
-
"body": "The consumer loop's lease-acquisition error path swallowed the backend error silently; it now logs at debug so a flapping backend that has not yet tripped the circuit breaker is visible."
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
"title": "Protobuf and ASN.1 encoders reject out-of-range tags",
|
|
51
|
-
"body": "The protobuf tag encoder rejected nothing and would have emitted a wrong tag for field numbers at or above 2^28 (where the 32-bit shift overflows); the ASN.1 context-tag writers silently truncated tag numbers above 30 (which require the multi-byte high-tag-number form). Both now throw a RangeError rather than encode silently-wrong output. The values these encoders serve are well within range, so no current caller is affected."
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"title": "oid4vci proofAlgorithms default documented accurately",
|
|
55
|
-
"body": "The documented default proof-algorithm list now matches the code (`[\"ES256\", \"ES384\", \"EdDSA\"]`); the doc previously omitted EdDSA, which the runtime has always accepted by default."
|
|
56
|
-
}
|
|
57
|
-
]
|
|
58
|
-
}
|
|
59
|
-
]
|
|
60
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../scripts/release-notes-schema.json",
|
|
3
|
-
"version": "0.14.10",
|
|
4
|
-
"date": "2026-05-31",
|
|
5
|
-
"headline": "Full-text-search token hashes move to a keyed MAC; existing mail-store search indexes rebuild automatically on upgrade",
|
|
6
|
-
"summary": "The mail-store full-text-search index hashed its tokens with a hand-rolled salted-SHA3 derived hash. It now routes through the framework's sealed-column hashing primitive in keyed mode (HMAC-SHAKE256 off the per-deployment MAC key), so a search-index token hash is unforgeable and un-correlatable across deployments without that key — the same posture the sealed-column lookup hashes already use. Because the keyed hash changes the stored token values, a mail-store opened after upgrade detects its index as old-format and rebuilds it once from the sealed message rows. The rebuild runs under a format marker: the index is marked `rebuilding` before it is cleared and only marked current after every row is re-hashed inside an explicit transaction, and search falls back to its cursor path (rather than returning partial hits) whenever the marker is not current — so an interrupted rebuild leaves the old index intact and queryable and retries on the next open, never serving a half-built index. A new `b.cryptoField.computeNamespacedHash` primitive backs the keyed hashing for callers that hash outside the registered-column path.",
|
|
7
|
-
"sections": [
|
|
8
|
-
{
|
|
9
|
-
"heading": "Added",
|
|
10
|
-
"items": [
|
|
11
|
-
{
|
|
12
|
-
"title": "`b.cryptoField.computeNamespacedHash`",
|
|
13
|
-
"body": "A mode-aware namespaced hash for indexed-lookup callers that hash a value outside the registered-column derived-hash path. `computeNamespacedHash(ns, value, { mode, truncateBytes })` routes through the same engine as `computeDerived` — `salted-sha3` (default) or the keyed `hmac-shake256` — with optional hex truncation. The mail-store full-text index is the first consumer."
|
|
14
|
-
}
|
|
15
|
-
]
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"heading": "Changed",
|
|
19
|
-
"items": [
|
|
20
|
-
{
|
|
21
|
-
"title": "Mail-store full-text index rehashes to a keyed MAC on upgrade",
|
|
22
|
-
"body": "The full-text-search token hash now uses `b.cryptoField.computeNamespacedHash` in `hmac-shake256` mode instead of a hand-rolled salted-SHA3. The first time a store is opened after upgrade, its index is detected as old-format and rebuilt once from the sealed message rows; subsequent opens are no-ops. Search is unaffected once the rebuild completes. The rebuild requires the vault to be initialized and fails closed (a clear error) at construction if it is not, rather than leaving a stale searchable index."
|
|
23
|
-
}
|
|
24
|
-
]
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"heading": "Security",
|
|
28
|
-
"items": [
|
|
29
|
-
{
|
|
30
|
-
"title": "Keyed, un-correlatable full-text-search token hashes",
|
|
31
|
-
"body": "A search-index token hash is now a keyed MAC over a per-deployment key, not a static-salted digest — it cannot be forged or correlated across deployments without that key, closing the low-entropy-token correlation gap on the search index. The index remains unrecoverable from a database dump alone, as before."
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"heading": "Detectors",
|
|
37
|
-
"items": [
|
|
38
|
-
{
|
|
39
|
-
"title": "Hand-rolled lookup-hash check covers the split form",
|
|
40
|
-
"body": "The check that requires sealed-column lookup hashes to compose the framework primitive now also catches the across-lines hand-roll (`var salt = getDerivedHashSalt(); var hex = salt.toString(...); sha3(hex + ns + value)`), not only the single-expression form, so the bypass that the mail-store index used can't reappear."
|
|
41
|
-
}
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"heading": "Migration",
|
|
46
|
-
"items": [
|
|
47
|
-
{
|
|
48
|
-
"title": "Automatic, one-time full-text index rebuild",
|
|
49
|
-
"body": "No operator action is required: the rebuild runs automatically and idempotently on first open after upgrade, atomically and crash-safe (an interrupted rebuild keeps the old index and retries). The only requirement is that the vault is initialized before the mail-store is constructed. One caveat for shared stores: do not run a pre-upgrade and post-upgrade node against the same backend file concurrently across this format change — the old node would write old-format hashes the new node cannot match. Roll the deployment fully across the upgrade. This re-open condition is lifted once all nodes are on 0.14.10 or later."
|
|
50
|
-
}
|
|
51
|
-
]
|
|
52
|
-
}
|
|
53
|
-
]
|
|
54
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../scripts/release-notes-schema.json",
|
|
3
|
-
"version": "0.14.11",
|
|
4
|
-
"date": "2026-05-31",
|
|
5
|
-
"headline": "Defensive LLM model-I/O primitives, C2PA timestamp countersignatures with CAWG identity assertions, and signed EU AI Act GPAI adherence declarations",
|
|
6
|
-
"summary": "Closes the output side of the LLM trust boundary and hardens content provenance and AI-Act attestation. b.ai.output.sanitize treats model output as untrusted and neutralizes XSS, gates every markdown-image / link and HTML src/href URL against SSRF (the EchoLeak zero-click exfiltration class, CVE-2025-32711), and flags SQL- and command-shaped fragments; b.ai.output.redact strips PII and secret disclosures. b.ai.input.classifyWithSources classifies a prompt together with its retrieval-augmented sources under a stricter, trust-tier-relative threshold, and the new b.ai.prompt namespace assembles prompts with escape-by-default boundaries — untrusted context / user segments are fenced in a per-render crypto-nonce delimiter the content cannot forge and stripped of bidi, control, zero-width, and Unicode-Tags smuggling characters. b.contentCredentials COSE signatures now carry an RFC 3161 timestamp countersignature (C2PA sigTst2, RFC 9921) verified entirely through b.tsa, so a signed manifest stays verifiable after its signing certificate expires, plus a CAWG identity assertion with trust-anchored verification. b.compliance.aiAct.gpai.declareAdherence emits a tamper-evident, ML-DSA-87-signed GPAI Code-of-Practice adherence declaration whose obligation set is derived from the regulation rather than operator-asserted.",
|
|
7
|
-
"sections": [
|
|
8
|
-
{
|
|
9
|
-
"heading": "Added",
|
|
10
|
-
"items": [
|
|
11
|
-
{
|
|
12
|
-
"title": "`b.ai.output.sanitize` and `b.ai.output.redact`",
|
|
13
|
-
"body": "A new `b.ai.output` namespace that treats LLM output as untrusted before it reaches a browser, a downstream fetcher, a SQL / command sink, or a log. `sanitize(text, opts)` neutralizes active markup via `b.guardHtml`, gates every markdown image / link and HTML `src` / `href` URL through `b.safeUrl.parse` (scheme + credential) and `b.ssrfGuard.classify` (internal / loopback / link-local / cloud-metadata IP-range) so auto-fetch URLs to attacker or internal hosts are neutralized, and flags SQL- and command-shaped fragments rather than silently repairing them. `redact(text, opts)` strips PII and secret disclosures via `b.redact` plus an entity-selectable pass (`pan` / `ssn` / `ein` / `iban` / `jwt` / `aws` / `phi` / `email` / `phone`). Defends OWASP LLM05:2025 Improper Output Handling and LLM02:2025 Sensitive Information Disclosure; the markdown-image URL gate closes the EchoLeak zero-click exfiltration class (CVE-2025-32711, CVSS 9.3)."
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"title": "`b.ai.input.classifyWithSources`",
|
|
17
|
-
"body": "Classifies a prompt together with its retrieval-augmented (RAG) sources, applying a stricter, trust-tier-relative threshold to retrieved data. Each source is `{ id, text, trust? }` with `trust` of `trusted` / `internal` / `untrusted` (unset defaults to `untrusted`, fail-closed); untrusted and internal sources escalate to `suspicious` on a single severity-2 signal and to `malicious` on any severity-3, where the direct prompt keeps the baseline threshold. The aggregate verdict is the worst across the prompt and all sources, and every malicious source is reported in `taintedSources`. Defends indirect prompt injection from poisoned context (OWASP LLM01:2025; NIST AI 600-1)."
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"title": "`b.ai.prompt.template`",
|
|
21
|
-
"body": "A new `b.ai.prompt` namespace for assembling LLM prompts with escape-by-default boundaries. The `system` segment is operator-trusted; `context` and `user` segments are treated as untrusted (no global opt-out — mark a segment `{ text, trusted: true }` individually). Untrusted segments are wrapped in a per-render, high-entropy delimiter nonce the content cannot forge, with any forged boundary stripped before wrapping (spotlighting / datamarking, Microsoft 2024; NIST AI 100-2e2025), and stripped of bidi overrides (CVE-2021-42574 Trojan Source), C0 controls, zero-width characters, null bytes, and Unicode Tags (U+E0000..U+E007F ASCII-smuggling). Run `b.ai.input.refuseIfMalicious` on the untrusted content as defense in depth."
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"title": "C2PA RFC 3161 timestamp countersignature and CAWG identity assertion",
|
|
25
|
-
"body": "`b.contentCredentials.signCose` attaches an RFC 3161 timestamp countersignature (C2PA `sigTst2`, RFC 9921) and `b.contentCredentials.verifyCose` verifies it. Pass `timestamp:{ token }` to embed a TimeStampToken, or `timestamp:{}` to get back the DER `application/timestamp-query` to POST to a timestamp authority. `b.contentCredentials.attachIdentityAssertion` / `verifyIdentityAssertion` add the CAWG Identity Assertion v1.2: a signed creator / organization identity hash-bound to a manifest's referenced assertions, where the `x509` binding reports `verified:true` only when an identity trust anchor is supplied and the leaf chain verifies, and the `identity-claims-aggregator` and self-asserted paths stay `verified:false`."
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"title": "`b.compliance.aiAct.gpai.declareAdherence` / `verifyAdherence`",
|
|
29
|
-
"body": "Signed, tamper-evident GPAI Code-of-Practice adherence declarations (Regulation (EU) 2024/1689 Art. 53(1)(a-d); Art. 55 for systemic-risk models under Art. 51(2)). The in-scope obligation set is derived from the classifier, never operator-asserted — a model at or above the 10^25-FLOP systemic-risk threshold that omits the Art. 55 chapter is refused. Each commitment's evidence reference must be a SHA3-512 digest; a malformed hash is rejected so a hollow attestation cannot bind. The declaration ships inside an ML-DSA-87-signed CycloneDX 1.6 ML-BOM via `b.ai.modelManifest`; verify re-canonicalizes before trusting any field and rejects a declaration past its validity window. Cites the GPAI Code of Practice (10 July 2025), Annex XI/XII, and Directive (EU) 2019/790 Art. 4(3)."
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"heading": "Security",
|
|
35
|
-
"items": [
|
|
36
|
-
{
|
|
37
|
-
"title": "Model output is now an untrusted channel by default",
|
|
38
|
-
"body": "When feeding retrieved documents into an LLM, classify them with `b.ai.input.classifyWithSources` (untrusted sources escalate on a single signal) rather than trusting model input; assemble prompts with `b.ai.prompt.template` so untrusted context / user text is fenced in a per-render crypto-nonce boundary it cannot forge; and pass model output through `b.ai.output.sanitize` / `b.ai.output.redact` before it is rendered, fetched, or logged. Each primitive is on by default and fail-closed — no opt-in flag enables the protection."
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"title": "Timestamp verification routes only through `b.tsa.verifyToken`",
|
|
42
|
-
"body": "C2PA `sigTst2` verification performs the full RFC 3161 check (CMS signature over the signed attributes, messageDigest recompute, critical sole `id-kp-timeStamping` EKU) — never a chain-only shortcut — closing the timestamp-validation-bypass class (CVE-2025-52556, CWE-347). Supply `timestampTrustAnchorsPem` to `verifyCose` to check the timestamp certificate chain; `verifyCose` returns `{ valid, reason, claims, alg, timestamp }` and never throws."
|
|
43
|
-
}
|
|
44
|
-
]
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
"heading": "Detectors",
|
|
48
|
-
"items": [
|
|
49
|
-
{
|
|
50
|
-
"title": "LLM output URLs must keep the SSRF gate",
|
|
51
|
-
"body": "A new check requires the output sanitizer to gate every extracted URL through both `b.safeUrl.parse` and `b.ssrfGuard.classify`, so the markdown-image SSRF gate (the EchoLeak class) cannot be silently dropped."
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"title": "RAG sources must compose `classifyWithSources`",
|
|
55
|
-
"body": "A new check flags any code that maps `b.ai.input.classify` over a sources array by hand, which would lose the trust-tier-relative threshold for retrieved data."
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"title": "Prompt boundaries must use a per-render nonce",
|
|
59
|
-
"body": "A new check flags prompt-assembly that wraps untrusted content in a fixed, guessable literal fence (`<user_input>`, `[DATA]`) instead of a per-render high-entropy delimiter the content cannot forge."
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"title": "C2PA timestamp verification must route through `b.tsa`",
|
|
63
|
-
"body": "A new check flags any bespoke certificate-chain-only walk on a timestamp token in place of `b.tsa.verifyToken`, preventing a re-introduction of the timestamp-validation-bypass class."
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"title": "GPAI adherence declarations must be signed",
|
|
67
|
-
"body": "A new check flags any code that emits the GPAI Code-of-Practice adherence property without routing it through the `b.ai.modelManifest` signed envelope, keeping the declaration tamper-evident."
|
|
68
|
-
}
|
|
69
|
-
]
|
|
70
|
-
}
|
|
71
|
-
]
|
|
72
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../scripts/release-notes-schema.json",
|
|
3
|
-
"version": "0.14.12",
|
|
4
|
-
"date": "2026-05-31",
|
|
5
|
-
"headline": "Vault key rotation re-seals AAD-bound storage under the new root instead of silently orphaning it",
|
|
6
|
-
"summary": "Every AAD-sealed cell derives its key from the live vault root, so rotating the vault keypair changes those keys. `b.vaultRotate.rotate` previously re-sealed only legacy `vault:`-prefixed cells in `db.enc` and skipped `vault.aad:` cells, AAD-bound at-rest files, and operator-supplied AAD stores — leaving them encrypted under the retired keypair while still returning a success result and a passing round-trip verify, so the loss was invisible until the old keypair was discarded and the cells became permanently undecryptable. Rotation now re-seals `db.enc` (preserving its dataDir-bound AAD), `db.key.enc` (location-bound), every `{ aad: true }` table column, and the overflow store under the new root; refuses up front with a fail-closed error when operator-supplied AAD stores (agent idempotency / orchestrator / tenant / snapshot) are reachable unless each has been re-sealed via its module hook and explicitly acknowledged; and the round-trip verify now decrypts AAD-sealed cells under the new root and treats any cell that still opens under the old root as a regression. New explicit-root `b.vault.aad` seal / unseal / reseal primitives carry a cell from the old root to the new one while preserving its AAD tuple; `b.archive.rewrapTenant` re-wraps tenant-scoped archive envelopes; and `b.cluster` can adopt a rotated vault-key fingerprint instead of partitioning the membership during a rolling rotation.",
|
|
7
|
-
"sections": [
|
|
8
|
-
{
|
|
9
|
-
"heading": "Added",
|
|
10
|
-
"items": [
|
|
11
|
-
{
|
|
12
|
-
"title": "`b.vault.aad.sealRoot` / `unsealRoot` / `resealRoot`",
|
|
13
|
-
"body": "Explicit-root variants of the AAD seal / unseal that take a root-keypair JSON (`b.vault.getKeysJson()` output) instead of reading the live vault singleton. `resealRoot(value, aadParts, oldRootJson, newRootJson)` opens a cell under the old root and re-seals it under the new one while preserving the same AAD tuple (`table` / `rowId` / `column` / `schemaVersion`), which is what lets a rotation worker move AAD-bound state across a keypair change without altering the bound context. The default-root `b.vault.aad.seal` / `unseal` behaviour is unchanged."
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"title": "Per-store AAD re-seal hooks on the agent primitives",
|
|
17
|
-
"body": "`b.agent.idempotency.reseal`, `b.agent.orchestrator.reseal`, `b.agent.snapshot.reseal`, and the `b.agent.tenant` registry / tenant-cell reseal paths re-seal that module's AAD-bound rows from an old root to a new root over the operator's own store. Each module also exposes an `AAD_ROTATION` descriptor naming the store the rotation pipeline cannot reach on its own, so an operator can enumerate exactly what to re-seal before a rotation."
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"title": "`b.archive.rewrapTenant`",
|
|
21
|
-
"body": "Re-wraps a `recipient: \"tenant\"` archive envelope from an old vault root to a new one for a given `tenantId`, so a keypair rotation does not strand tenant-scoped archives. Opens the blob under the old root + tenantId, refuses a blob that is not a tenant-recipient envelope or that does not open under the supplied old root, and emits a fresh envelope bound to the new root. This is offered alongside the documented re-export path (decrypt with the old keypair, re-archive with the new one) for operators who hold the envelope but not the source."
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"title": "Cluster vault-key rotation acceptance",
|
|
25
|
-
"body": "A vault-key rotation changes the public-key fingerprint recorded in the canonical cluster-state row, which a peer would otherwise report as `VAULT_KEY_DRIFT`. `b.cluster` configuration gains `acceptVaultKeyRotation: true` to declare the change legitimate — the node adopts the rotated fingerprint and bumps a rotation epoch instead of refusing — and an optional `expectedVaultKeyFp` that narrows acceptance to a single blessed post-rotation fingerprint. The drift guard stays in force whenever a rotation is not declared; supplying `expectedVaultKeyFp` without `acceptVaultKeyRotation` is rejected at configuration time as a misconfiguration."
|
|
26
|
-
}
|
|
27
|
-
]
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"heading": "Changed",
|
|
31
|
-
"items": [
|
|
32
|
-
{
|
|
33
|
-
"title": "`b.vaultRotate.rotate` refuses when reachable AAD stores are not acknowledged",
|
|
34
|
-
"body": "Because the rotation pipeline walks only `db.enc` and cannot introspect an operator's own AAD-backed stores, it now detects which AAD-store modules are loadable and throws `vault-rotate/external-aad-unresealed` unless `opts.externalAadResealed` is either `true` (you do not use those features) or an array naming every detected store (you have re-sealed each via its hook). This converts a path that previously discarded data and reported success into a fail-closed gate. The error names each store and the hook to call."
|
|
35
|
-
}
|
|
36
|
-
]
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"heading": "Fixed",
|
|
40
|
-
"items": [
|
|
41
|
-
{
|
|
42
|
-
"title": "Rotation re-seals `vault.aad:` cells and AAD-bound at-rest files",
|
|
43
|
-
"body": "`db.enc` is re-written bound to its dataDir-scoped AAD (it was previously re-written un-bound, silently stripping the at-rest AAD binding on every rotation), `db.key.enc` retains its location-bound AAD, and every `{ aad: true }` table column plus the overflow store is re-sealed under the new root. Previously only `vault:`-prefixed cells were carried across, so AAD-sealed data was left encrypted under the retired keypair and lost once it was discarded."
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"title": "Round-trip verify no longer reports a false success",
|
|
47
|
-
"body": "`b.vaultRotate.verify` now samples and decrypts AAD-sealed cells under the new root and treats any cell that still decrypts under the old root as a regression, so an incomplete rotation fails verification instead of passing it. The prior verify checked only `vault:` cells and therefore reported `ok` even when AAD-sealed cells had been orphaned."
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"heading": "Security",
|
|
53
|
-
"items": [
|
|
54
|
-
{
|
|
55
|
-
"title": "A vault key rotation can no longer silently destroy encrypted data",
|
|
56
|
-
"body": "The orphaning path lost agent idempotency / orchestrator / tenant / snapshot state, `{ aad: true }` columns, and tenant archives with no error and a passing verify; the data became unrecoverable the moment the old keypair was retired. Rotation is now fail-closed end to end: it re-seals what it can reach, refuses to proceed past what it cannot until you acknowledge it, and verifies the result under the new root. If you performed a rotation on v0.14.11 or earlier and still hold the retired keypair, re-seal the affected cells under the current root with the explicit-root primitives before discarding it."
|
|
57
|
-
}
|
|
58
|
-
]
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"heading": "Detectors",
|
|
62
|
-
"items": [
|
|
63
|
-
{
|
|
64
|
-
"title": "AAD-backed store modules must expose a rotation reseal path",
|
|
65
|
-
"body": "A new check flags a module that registers an external `{ aad: true }` store but does not expose an `AAD_ROTATION` descriptor and reseal hook, which would leave its state unreachable by the rotation pipeline."
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
"title": "A root-keyed seal family must ship its reseal",
|
|
69
|
-
"body": "A new check flags adding a `sealRoot` / `unsealRoot` pair without the matching `resealRoot`, since without it a rotated cell cannot be carried from the old root to the new one."
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
"title": "Live-root AAD seals need a reseal path",
|
|
73
|
-
"body": "A new check flags a primitive that AAD-seals under the live vault root without a way to re-seal that state under a new root during rotation."
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
"title": "Tenant archive re-wrapping must compose `b.archive.rewrapTenant`",
|
|
77
|
-
"body": "A new check flags tenant-scoped archive re-wrapping that opens and re-seals a tenant envelope by hand instead of routing through `b.archive.rewrapTenant`."
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"title": "Cluster vault-key drift needs the rotation-epoch accept gate",
|
|
81
|
-
"body": "A new check flags a cluster vault-key fingerprint comparison that hard-rejects a mismatch without honouring the `acceptVaultKeyRotation` epoch window."
|
|
82
|
-
}
|
|
83
|
-
]
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
"heading": "Migration",
|
|
87
|
-
"items": [
|
|
88
|
-
{
|
|
89
|
-
"title": "Re-seal operator AAD stores before rotating",
|
|
90
|
-
"body": "Before calling `b.vaultRotate.rotate`, re-seal each AAD-backed store you use via its hook (`b.agent.idempotency.reseal`, `b.agent.orchestrator.reseal`, `b.agent.snapshot.reseal`, the `b.agent.tenant` `AAD_ROTATION` reseal paths) with the old and new root JSON, re-wrap tenant archives with `b.archive.rewrapTenant`, then pass `opts.externalAadResealed` as an array naming each re-sealed store. If you use none of these features, pass `opts.externalAadResealed: true`. Declare the rotation to each cluster node with `acceptVaultKeyRotation: true` so the membership adopts the new fingerprint rather than reporting drift."
|
|
91
|
-
}
|
|
92
|
-
]
|
|
93
|
-
}
|
|
94
|
-
]
|
|
95
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../scripts/release-notes-schema.json",
|
|
3
|
-
"version": "0.14.13",
|
|
4
|
-
"date": "2026-05-31",
|
|
5
|
-
"headline": "Close advertised-but-missing surface: SRS1 chained forwarding, DCQL array-wildcard claim paths, and in-memory safe-archive extraction",
|
|
6
|
-
"summary": "Three primitives advertised a capability in their documentation or card but refused or omitted it at runtime; this release implements each. b.mail.srs gains srs1Rewrite for the SRS1 double-forward (and multi-hop) case — previously the @intro described SRS1 and create() threw, pointing at a function that was never exported. b.safeArchive gains extractToMemory, the in-memory counterpart to extract for read-only / serverless filesystems — previously the card advertised in-memory extraction but the orchestrator required a destination directory. b.auth.oid4vp.matchDcql now honours a null claims-path segment as the array wildcard the OpenID4VP DCQL spec defines, rather than refusing it as unsupported while the card advertised DCQL. A stale version-pinned wording in a safe-archive error message is corrected. Every change is additive or message-only — no existing caller changes behaviour.",
|
|
7
|
-
"sections": [
|
|
8
|
-
{
|
|
9
|
-
"heading": "Added",
|
|
10
|
-
"items": [
|
|
11
|
-
{
|
|
12
|
-
"title": "`b.mail.srs` SRS1 chained forwarding — `srs1Rewrite`",
|
|
13
|
-
"body": "`b.mail.srs.create(...)` now returns `srs1Rewrite` alongside `rewrite` / `reverse`. `srs1Rewrite(srsAddress)` chains an already-SRS0 (or SRS1) envelope-from for a further forwarding hop: it keeps the original SRS0 body verbatim, prepends the SRS0 originator's domain, and binds the pair with this forwarder's own HMAC-SHA-256 tag — no new timestamp, no repeated original local-part — emitting `SRS1=tag=originator==<SRS0-body>@thisForwarder`. `reverse()` now detects an SRS1 address, verifies this hop's tag and forwarder-domain binding, and unwraps exactly one hop back to the originator's SRS0 so a multi-hop bounce routes straight to the forwarder that can recover the original sender. Typed failure modes: `srs/not-srs0` (input not SRS-encoded), `srs/malformed` (missing the `==` separator), `srs/bad-tag` (tampered), `srs/too-long` (chain exceeds the RFC 5321 256-octet path limit). Implements the Sender Rewriting Scheme SRS1 wire format; the second-hop SPF rationale is RFC 7208 §2.4."
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"title": "`b.safeArchive.extractToMemory` — in-memory safe extraction",
|
|
17
|
-
"body": "An async generator counterpart to `b.safeArchive.extract` for read-only / serverless filesystems: it resolves the source, sniffs the format, auto-unwraps recipient (`BAWRP`) / passphrase (`BAWPP`) envelopes, and dispatches to the zip / tar / tar.gz reader's in-memory `extractEntries()`, yielding `{ name, bytes, size }` per regular-file entry without ever writing to disk. It takes no `destination`. Every defense the disk path runs applies unchanged: the zip-bomb caps (entry-count / per-entry / total / expansion-ratio), the `b.guardArchive` metadata cascade (Zip-Slip / path-traversal / symlink-escape / encrypted-entry refusal, CVE-2025-3445 class), and the entry-type policy. The disk-only realpath-agreement check (CVE-2025-4517 PATH_MAX TOCTOU defense) is intentionally absent — there is no extraction root — so the archive-level name refusals carry containment. Trusted-stream sources are refused upfront (the adversarial-safe central-directory walk needs random access). gzip magic per RFC 1952 §2.3.1."
|
|
18
|
-
}
|
|
19
|
-
]
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"heading": "Fixed",
|
|
23
|
-
"items": [
|
|
24
|
-
{
|
|
25
|
-
"title": "OID4VP DCQL `null` claim-path segment now resolves the array wildcard",
|
|
26
|
-
"body": "`b.auth.oid4vp.matchDcql` previously threw `auth-oid4vp/null-path-segment-not-supported` for a `null` claims-path segment while the namespace card advertised DCQL — under-disclosing a legitimate presentation (CWE-863). Per OpenID4VP 1.0 §7.1.1 a `null` segment selects all elements of the array at that depth; the matcher now recurses over array elements with existence semantics (with DCQL value-matching applied to any selected leaf), composed to arbitrary depth. A `null` segment on a non-array node — like an integer index into a non-array, or a string key into an array — is a clean non-match, not a thrown error, because the matcher walks holder credential data rather than operator config. String and integer claim paths are byte-identical to before; only queries that previously threw now succeed or fail cleanly."
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"title": "safe-archive trusted-stream refusal message no longer cites a stale version",
|
|
30
|
-
"body": "The thrown `safe-archive/trusted-stream-unsupported` message and its comment claimed trusted-stream extraction was \"deferred to v0.12.8 / when the v0.12.8 sequential extract path lands.\" That path shipped long ago — `b.archive.read.zip.fromTrustedStream` and the tar sequential mode exist — so the message now points at them as present capabilities and drops the version-pinned wording. The error code is unchanged."
|
|
31
|
-
}
|
|
32
|
-
]
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"heading": "Detectors",
|
|
36
|
-
"items": [
|
|
37
|
-
{
|
|
38
|
-
"title": "A primitive may not advertise a capability and then throw an unimplemented stub",
|
|
39
|
-
"body": "A new check flags a bare `not yet supported` / `operator demand TBD` / `not supported in v1` refusal in a lib throw string (comments excluded). A defer is only complete with a written re-open condition; the SRS1 and DCQL stubs that this release implements both carried this bare-defer shape, and the detector keeps it from re-entering."
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"title": "DCQL `null` path segments must recurse, never refuse",
|
|
43
|
-
"body": "A new check flags the `null path segment not supported` refusal shape in `lib/auth/oid4vp.js`, so the spec-mandated array wildcard cannot be re-stubbed."
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"title": "`extractToMemory` must stay disk-free",
|
|
47
|
-
"body": "A new check flags any `writeFileSync` / `renameSync` / `mkdirSync` / `createWriteStream` inside the `extractToMemory` generator body, so the read-only / serverless contract cannot regress into a disk write."
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
}
|
|
51
|
-
]
|
|
52
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../scripts/release-notes-schema.json",
|
|
3
|
-
"version": "0.14.14",
|
|
4
|
-
"date": "2026-05-31",
|
|
5
|
-
"headline": "Recognized consent purposes with lawful-basis gating, and a new b.privacy namespace for annual EdTech vendor-review attestations",
|
|
6
|
-
"summary": "Closes the student-data gap where an educational-only consent purpose and an annual third-party vendor-review report were described but never implemented. b.consent gains a recognized-purpose vocabulary: a purpose value matching a recognized key carries lawful-basis constraints that grant() enforces, and the named educational-only purpose (FERPA's school-official exception and California's SOPIPA) refuses a legitimate_interests lawful basis. The new b.privacy namespace ships vendorReview(), a builder for the dated, clause-by-clause annual EdTech third-party / processor review FERPA and SOPIPA expect a school or district to keep — it computes whether every required clause (no targeted advertising, no commercial profiling, no sale of student data, deletion on request, school-official designation, and so on) is attested, names the gaps, and stamps a 365-day re-review clock. Free-form consent purposes keep working unchanged, so the vocabulary is opt-in and additive.",
|
|
7
|
-
"sections": [
|
|
8
|
-
{
|
|
9
|
-
"heading": "Added",
|
|
10
|
-
"items": [
|
|
11
|
-
{
|
|
12
|
-
"title": "`b.consent` recognized-purpose vocabulary + lawful-basis gating",
|
|
13
|
-
"body": "`b.consent.recognizedPurpose(name)` looks up a recognized purpose and `b.consent.listPurposes()` enumerates them. When a `grant({ purpose })` value matches a recognized key, `grant()` enforces that purpose's lawful-basis constraints; the `educational-only` purpose forbids a `legitimate_interests` basis (FERPA 34 CFR 99.31(a)(1) school-official exception; California SOPIPA Cal. B&P 22584; FTC school-authorized COPPA consent 16 CFR 312.5(c)(10)) and marks the data commercial-use-prohibited. The commercial-use prohibition is an operator trust-boundary obligation — `isGranted()` does not re-derive it. Any purpose value NOT in the vocabulary stays free-form and unconstrained, so existing callers are unaffected; the hash-chain column set is unchanged, so `b.consent.verify()` over existing rows is unaffected."
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"title": "`b.privacy.vendorReview` — annual EdTech vendor-review attestation",
|
|
17
|
-
"body": "A new `b.privacy` namespace whose `vendorReview(opts)` builds the dated third-party / processor review a FERPA school-official arrangement and California SOPIPA expect for every vendor that touches student data. The operator supplies a boolean attestation per clause (educational-purpose-only, no-targeted-advertising, no-commercial-profiling, no-sale-of-student-data, security-safeguards, deletion-on-request, sub-processor-currency, breach-notification, school-official-designation, directory-information-handling); `vendorReview` validates the shape, computes whether every required clause is attested (`attested`) and which are not (`gaps`), and stamps `reviewedAt` plus a 365-day `nextReviewDueAt` re-review clock. `b.privacy.listVendorReviewClauses()` returns the clause set with citations. Operator-feeds-metadata: the frozen report is not framework-persisted — compose it into your retention / audit / export sink. A best-effort `privacy.vendor_review.recorded` audit event fires when an audit sink is wired."
|
|
18
|
-
}
|
|
19
|
-
]
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"heading": "Detectors",
|
|
23
|
-
"items": [
|
|
24
|
-
{
|
|
25
|
-
"title": "A gated consent purpose must go through `b.consent`",
|
|
26
|
-
"body": "A new check flags any lib code that mints a consent row with a hardcoded `educational-only` purpose literal without composing the recognized-purpose vocabulary — which would record the value while never enforcing its FERPA / SOPIPA lawful-basis constraint."
|
|
27
|
-
}
|
|
28
|
-
]
|
|
29
|
-
}
|
|
30
|
-
]
|
|
31
|
-
}
|