@blamejs/blamejs-shop 0.4.69 → 0.4.71
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 +8 -6
- package/lib/asset-manifest.json +1 -1
- package/lib/security-middleware.js +65 -23
- package/lib/shipping-insurance.js +29 -4
- package/lib/sms-dispatcher.js +17 -4
- package/lib/storefront.js +10 -7
- package/lib/vendor/MANIFEST.json +319 -267
- package/lib/vendor/blamejs/CHANGELOG.md +2 -0
- package/lib/vendor/blamejs/api-snapshot.json +58 -5
- package/lib/vendor/blamejs/examples/wiki/README.md +1 -1
- package/lib/vendor/blamejs/examples/wiki/docker-compose.prod.yml +9 -8
- package/lib/vendor/blamejs/examples/wiki/docker-compose.yml +10 -9
- package/lib/vendor/blamejs/examples/wiki/env-snapshot.json +4 -3
- package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +33 -17
- package/lib/vendor/blamejs/examples/wiki/routes/admin.js +7 -10
- package/lib/vendor/blamejs/examples/wiki/server.js +5 -4
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +5 -0
- package/lib/vendor/blamejs/lib/a2a-tasks.js +38 -6
- package/lib/vendor/blamejs/lib/agent-event-bus.js +13 -0
- package/lib/vendor/blamejs/lib/agent-idempotency.js +5 -1
- package/lib/vendor/blamejs/lib/agent-snapshot.js +32 -2
- package/lib/vendor/blamejs/lib/ai-aedt-bias-audit.js +2 -1
- package/lib/vendor/blamejs/lib/ai-content-detect.js +1 -3
- package/lib/vendor/blamejs/lib/ai-frontier-protocol.js +1 -1
- package/lib/vendor/blamejs/lib/ai-model-manifest.js +1 -1
- package/lib/vendor/blamejs/lib/ai-output.js +16 -7
- package/lib/vendor/blamejs/lib/api-snapshot.js +4 -1
- package/lib/vendor/blamejs/lib/app-shutdown.js +7 -1
- package/lib/vendor/blamejs/lib/archive-gz.js +9 -0
- package/lib/vendor/blamejs/lib/archive-read.js +9 -7
- package/lib/vendor/blamejs/lib/archive-tar-read.js +51 -8
- package/lib/vendor/blamejs/lib/archive.js +4 -2
- package/lib/vendor/blamejs/lib/asn1-der.js +70 -22
- package/lib/vendor/blamejs/lib/atomic-file.js +204 -2
- package/lib/vendor/blamejs/lib/audit-chain.js +54 -5
- package/lib/vendor/blamejs/lib/audit-daily-review.js +12 -2
- package/lib/vendor/blamejs/lib/audit-sign.js +2 -1
- package/lib/vendor/blamejs/lib/audit-tools.js +108 -23
- package/lib/vendor/blamejs/lib/audit.js +7 -2
- package/lib/vendor/blamejs/lib/auth/access-lock.js +2 -2
- package/lib/vendor/blamejs/lib/auth/bot-challenge.js +1 -1
- package/lib/vendor/blamejs/lib/auth/ciba.js +43 -4
- package/lib/vendor/blamejs/lib/auth/dpop.js +6 -1
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +5 -1
- package/lib/vendor/blamejs/lib/auth/jwt.js +2 -2
- package/lib/vendor/blamejs/lib/auth/lockout.js +18 -2
- package/lib/vendor/blamejs/lib/auth/passkey.js +1 -1
- package/lib/vendor/blamejs/lib/auth/password.js +1 -1
- package/lib/vendor/blamejs/lib/auth/saml.js +33 -13
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +24 -4
- package/lib/vendor/blamejs/lib/auth/status-list.js +14 -2
- package/lib/vendor/blamejs/lib/auth/step-up.js +9 -1
- package/lib/vendor/blamejs/lib/auth-bot-challenge.js +21 -2
- package/lib/vendor/blamejs/lib/backup/bundle.js +7 -2
- package/lib/vendor/blamejs/lib/backup/crypto.js +23 -8
- package/lib/vendor/blamejs/lib/backup/index.js +41 -20
- package/lib/vendor/blamejs/lib/backup/manifest.js +7 -1
- package/lib/vendor/blamejs/lib/break-glass.js +41 -22
- package/lib/vendor/blamejs/lib/cbor.js +34 -11
- package/lib/vendor/blamejs/lib/cdn-cache-control.js +7 -3
- package/lib/vendor/blamejs/lib/cert.js +5 -3
- package/lib/vendor/blamejs/lib/cli.js +5 -1
- package/lib/vendor/blamejs/lib/cloud-events.js +3 -2
- package/lib/vendor/blamejs/lib/cluster-storage.js +7 -3
- package/lib/vendor/blamejs/lib/codepoint-class.js +17 -0
- package/lib/vendor/blamejs/lib/compliance-eaa.js +1 -1
- package/lib/vendor/blamejs/lib/compliance-sanctions.js +9 -7
- package/lib/vendor/blamejs/lib/compliance.js +1 -1
- package/lib/vendor/blamejs/lib/config-drift.js +22 -8
- package/lib/vendor/blamejs/lib/content-credentials.js +11 -8
- package/lib/vendor/blamejs/lib/content-digest.js +11 -4
- package/lib/vendor/blamejs/lib/cookies.js +10 -2
- package/lib/vendor/blamejs/lib/cose.js +20 -0
- package/lib/vendor/blamejs/lib/crdt.js +2 -1
- package/lib/vendor/blamejs/lib/crypto-field.js +29 -23
- package/lib/vendor/blamejs/lib/crypto.js +18 -4
- package/lib/vendor/blamejs/lib/csp.js +4 -0
- package/lib/vendor/blamejs/lib/daemon.js +4 -1
- package/lib/vendor/blamejs/lib/data-act.js +27 -4
- package/lib/vendor/blamejs/lib/db-file-lifecycle.js +14 -6
- package/lib/vendor/blamejs/lib/db-query.js +69 -9
- package/lib/vendor/blamejs/lib/db.js +32 -15
- package/lib/vendor/blamejs/lib/dora.js +43 -13
- package/lib/vendor/blamejs/lib/dr-runbook.js +1 -1
- package/lib/vendor/blamejs/lib/dsa.js +2 -1
- package/lib/vendor/blamejs/lib/dsr.js +22 -8
- package/lib/vendor/blamejs/lib/early-hints.js +19 -0
- package/lib/vendor/blamejs/lib/eat.js +5 -1
- package/lib/vendor/blamejs/lib/external-db.js +60 -4
- package/lib/vendor/blamejs/lib/fda-21cfr11.js +30 -5
- package/lib/vendor/blamejs/lib/flag-providers.js +6 -2
- package/lib/vendor/blamejs/lib/forms.js +1 -1
- package/lib/vendor/blamejs/lib/gate-contract.js +46 -5
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +18 -9
- package/lib/vendor/blamejs/lib/graphql-federation.js +17 -4
- package/lib/vendor/blamejs/lib/guard-all.js +2 -2
- package/lib/vendor/blamejs/lib/guard-dsn.js +1 -1
- package/lib/vendor/blamejs/lib/guard-envelope.js +1 -1
- package/lib/vendor/blamejs/lib/guard-html.js +9 -11
- package/lib/vendor/blamejs/lib/guard-imap-command.js +1 -1
- package/lib/vendor/blamejs/lib/guard-jmap.js +1 -1
- package/lib/vendor/blamejs/lib/guard-json.js +14 -6
- package/lib/vendor/blamejs/lib/guard-mail-move.js +1 -1
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +1 -1
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +1 -1
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +1 -1
- package/lib/vendor/blamejs/lib/guard-svg.js +8 -9
- package/lib/vendor/blamejs/lib/html-balance.js +7 -3
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +33 -12
- package/lib/vendor/blamejs/lib/http-client.js +225 -53
- package/lib/vendor/blamejs/lib/iab-tcf.js +3 -2
- package/lib/vendor/blamejs/lib/importmap-integrity.js +41 -1
- package/lib/vendor/blamejs/lib/incident-report.js +9 -6
- package/lib/vendor/blamejs/lib/json-patch.js +1 -1
- package/lib/vendor/blamejs/lib/json-path.js +24 -3
- package/lib/vendor/blamejs/lib/jtd.js +2 -2
- package/lib/vendor/blamejs/lib/legal-hold.js +24 -8
- package/lib/vendor/blamejs/lib/log.js +2 -2
- package/lib/vendor/blamejs/lib/mail-agent.js +2 -2
- package/lib/vendor/blamejs/lib/mail-arf.js +1 -1
- package/lib/vendor/blamejs/lib/mail-auth.js +3 -3
- package/lib/vendor/blamejs/lib/mail-bimi.js +16 -16
- package/lib/vendor/blamejs/lib/mail-bounce.js +3 -3
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +71 -6
- package/lib/vendor/blamejs/lib/mail-deploy.js +9 -5
- package/lib/vendor/blamejs/lib/mail-greylist.js +2 -4
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -4
- package/lib/vendor/blamejs/lib/mail-journal.js +11 -8
- package/lib/vendor/blamejs/lib/mail-mdn.js +8 -4
- package/lib/vendor/blamejs/lib/mail-rbl.js +2 -4
- package/lib/vendor/blamejs/lib/mail-scan.js +3 -5
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +4 -1
- package/lib/vendor/blamejs/lib/mail-server-registry.js +1 -1
- package/lib/vendor/blamejs/lib/mail-server-tls.js +9 -2
- package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -4
- package/lib/vendor/blamejs/lib/mail-store-fts.js +1 -1
- package/lib/vendor/blamejs/lib/mail.js +22 -2
- package/lib/vendor/blamejs/lib/markup-tokenizer.js +24 -0
- package/lib/vendor/blamejs/lib/mcp.js +6 -4
- package/lib/vendor/blamejs/lib/mdoc.js +26 -3
- package/lib/vendor/blamejs/lib/metrics.js +14 -3
- package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +2 -2
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +10 -4
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +26 -18
- package/lib/vendor/blamejs/lib/middleware/clear-site-data.js +5 -1
- package/lib/vendor/blamejs/lib/middleware/compression.js +9 -0
- package/lib/vendor/blamejs/lib/middleware/cors.js +32 -23
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +60 -21
- package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +6 -4
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +28 -4
- package/lib/vendor/blamejs/lib/middleware/network-allowlist.js +61 -30
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +25 -16
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +2 -1
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +24 -6
- package/lib/vendor/blamejs/lib/middleware/speculation-rules.js +6 -3
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +2 -2
- package/lib/vendor/blamejs/lib/money.js +1 -1
- package/lib/vendor/blamejs/lib/mtls-ca.js +10 -6
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +1 -1
- package/lib/vendor/blamejs/lib/network-dns.js +9 -2
- package/lib/vendor/blamejs/lib/network-dnssec.js +2 -1
- package/lib/vendor/blamejs/lib/network-smtp-policy.js +23 -5
- package/lib/vendor/blamejs/lib/network-tls.js +27 -5
- package/lib/vendor/blamejs/lib/network-tsig.js +2 -2
- package/lib/vendor/blamejs/lib/nis2-report.js +1 -1
- package/lib/vendor/blamejs/lib/nist-crosswalk.js +1 -1
- package/lib/vendor/blamejs/lib/ntp-check.js +28 -0
- package/lib/vendor/blamejs/lib/numeric-bounds.js +9 -0
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +1 -2
- package/lib/vendor/blamejs/lib/object-store/gcs-bucket-ops.js +4 -2
- package/lib/vendor/blamejs/lib/object-store/gcs.js +6 -4
- package/lib/vendor/blamejs/lib/object-store/http-put.js +1 -2
- package/lib/vendor/blamejs/lib/object-store/http-request.js +30 -1
- package/lib/vendor/blamejs/lib/object-store/local.js +37 -17
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +1 -2
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +20 -4
- package/lib/vendor/blamejs/lib/outbox.js +11 -4
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +1 -1
- package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +21 -3
- package/lib/vendor/blamejs/lib/queue-local.js +10 -3
- package/lib/vendor/blamejs/lib/redact.js +7 -3
- package/lib/vendor/blamejs/lib/request-helpers.js +201 -23
- package/lib/vendor/blamejs/lib/resource-access-lock.js +3 -3
- package/lib/vendor/blamejs/lib/restore-bundle.js +46 -18
- package/lib/vendor/blamejs/lib/restore-rollback.js +10 -4
- package/lib/vendor/blamejs/lib/restore.js +19 -0
- package/lib/vendor/blamejs/lib/retention.js +20 -4
- package/lib/vendor/blamejs/lib/router.js +17 -4
- package/lib/vendor/blamejs/lib/safe-ical.js +2 -2
- package/lib/vendor/blamejs/lib/safe-icap.js +1 -1
- package/lib/vendor/blamejs/lib/safe-json.js +44 -0
- package/lib/vendor/blamejs/lib/safe-sieve.js +1 -1
- package/lib/vendor/blamejs/lib/safe-vcard.js +1 -1
- package/lib/vendor/blamejs/lib/sandbox-worker.js +6 -0
- package/lib/vendor/blamejs/lib/sandbox.js +1 -1
- package/lib/vendor/blamejs/lib/scheduler.js +17 -1
- package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +16 -0
- package/lib/vendor/blamejs/lib/session.js +27 -3
- package/lib/vendor/blamejs/lib/sql.js +3 -3
- package/lib/vendor/blamejs/lib/static.js +65 -13
- package/lib/vendor/blamejs/lib/template.js +7 -5
- package/lib/vendor/blamejs/lib/tenant-quota.js +52 -19
- package/lib/vendor/blamejs/lib/tsa.js +5 -2
- package/lib/vendor/blamejs/lib/vault/index.js +5 -0
- package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +22 -26
- package/lib/vendor/blamejs/lib/vault/passphrase-source.js +8 -3
- package/lib/vendor/blamejs/lib/vault/rotate.js +13 -18
- package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +4 -1
- package/lib/vendor/blamejs/lib/vc.js +1 -1
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +10 -10
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +23 -10
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5498 -5494
- package/lib/vendor/blamejs/lib/webhook.js +16 -1
- package/lib/vendor/blamejs/lib/websocket.js +1 -1
- package/lib/vendor/blamejs/lib/worm.js +1 -1
- package/lib/vendor/blamejs/lib/ws-client.js +57 -46
- package/lib/vendor/blamejs/lib/x509-chain.js +44 -0
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.15.14.json +122 -0
- package/lib/vendor/blamejs/scripts/check-vendor-currency.js +119 -4
- package/lib/vendor/blamejs/test/00-primitives.js +5 -1
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +18 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +42 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +47 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-gz.test.js +24 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-hardening.test.js +160 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-fd-read.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-open-nofollow.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-write-excl.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-write-stream.test.js +181 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-chain-corrupted-anchor.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-chain-incremental-verify.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-daily-review.test.js +48 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +13 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +122 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge.test.js +37 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +5 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-lockout.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/bot-guard.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +62 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +12 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster-storage.test.js +40 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +146 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compression-range.test.js +115 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +17 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/cors.test.js +27 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/data-act.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dora.test.js +20 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-alg-kty.test.js +64 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fda-21cfr11.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/graphql-federation.test.js +33 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +12 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +72 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-throttle-transform.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/importmap-integrity.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +20 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-header-injection.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +63 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/mdoc.test.js +41 -14
- package/lib/vendor/blamejs/test/layer-0-primitives/network-allowlist.test.js +61 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/object-store-range-header.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +35 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/output-header-hardening.test.js +102 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parser-verify-hardening.test.js +191 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/proto-shadow-allowlist.test.js +120 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +75 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/restore-blob-remap.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention-sweep-termination.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-body-validation.test.js +98 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-json-stringify-for-script.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +48 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +23 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +54 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +24 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +42 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/step-up.test.js +7 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/template-escape-html.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-default-store.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vendor-currency-classify.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +22 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/x509-chain-ca-enforcement.test.js +149 -0
- package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +34 -0
- package/package.json +1 -1
|
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.15.x
|
|
10
10
|
|
|
11
|
+
- v0.15.14 (2026-06-20) — **A broad security-hardening release: every request-IP trust decision is peer-gated against the immediate connection (closing an X-Forwarded-For spoofing class — one behavior change, see Migration), a wide set of authentication and signature verifiers now fail closed, pre-auth parsers are hardened against algorithmic-complexity and frame-desync attacks, and untrusted-key allowlist lookups, at-rest encryption binding, and on-disk writes are tightened framework-wide; adds streaming and exclusive atomic-write primitives, a script-context JSON serializer, and HTTP-client bandwidth throttling.** This release tightens existing protections across the framework rather than adding opt-ins. The headline behavior change is how the client IP is trusted: the IP gates that make security decisions (network allowlist, rate limiter, break-glass grant pin) no longer trust a bare leftmost X-Forwarded-For value when trustProxy is set without an explicit proxy allowlist — they resolve the client IP by a peer-gated right-to-left walk anchored at the connecting socket, so a direct caller can no longer forge an allowed address. Operators who terminate behind a proxy now declare its address range in trustedProxies (see Migration). Alongside that, a large set of verifiers that could accept a malformed or unsigned credential now fail closed (SAML audience restriction, FDA electronic-signature, SD-JWT key-binding replay, DPoP, CIBA, mdoc trust anchor, COSE algorithm/curve binding, agent-snapshot plaintext under a regulated posture); pre-authentication parsers that an attacker reaches before any credential check are hardened against quadratic-time and frame-desync denial of service (CBOR, JSONPath, tar/PAX, gzip, YAML, ASN.1 DER, the scheduler's timer math); allowlist and posture lookups keyed by untrusted input can no longer reach Object.prototype; the framework's own file writes are now symlink-refusing and atomic; at-rest and egress paths bind ciphertext to its location and redact nested secrets; and several lost-update races are serialized. New surface is additive: streaming and exclusive atomic-write primitives, a serializer safe to embed in an inline <script>, and download/upload bandwidth throttling on the HTTP client. **Added:** *b.atomicFile.writeStream(filepath, source, opts?) and b.atomicFile.writeExclSync(filepath, data, opts?)* — Two atomic-write primitives that complete the read-side fdSafeReadSync family. writeStream writes a stream to a fresh O_EXCL|O_NOFOLLOW temporary file with an output-byte cap, then renames into place; writeExclSync stages a synchronous write through a verify-then-rename (clearing any stale temp under O_EXCL|O_NOFOLLOW first) for the small sealed-state round-trips (vault seal / unseal / rotate). Both refuse to follow a symlink at the destination or temp path, so an attacker who pre-creates a link can't redirect the write. The framework's own predictable-temp and symlink-following write sites now route through them; fdSafeReadSync also gains a withStat option that returns the bound fd's stat alongside the bytes. · *b.safeJson.stringifyForScript(value, opts?)* — A JSON serializer safe to embed directly in an inline <script> block: it escapes <, >, and & and the U+2028 / U+2029 line separators so a value containing </script> or a JS line terminator cannot break out of the script context (DOM-XSS). The import-map helper gains scriptTag(importMap, { nonce }) built on it, and the speculation-rules inline emitter uses it. · *HTTP-client bandwidth throttling* — b.httpClient requests accept a bandwidth limit that interposes a throttling transform on both the download and upload paths, so a large transfer can be rate-shaped without buffering the whole body. Useful for bounding the egress a single outbound call can consume. · *Incremental audit-chain verification* — The audit verifiers accept a from/to range so a long chain can be verified in increments rather than head-to-tail every time, report an accurate rowsVerified count, and keep the newest review when reconciling. b.vault also exposes getDefaultStore() for callers that need the process's default seal/unseal store handle. **Changed:** *DORA major-incident reporting clock is four hours* — The Digital Operational Resilience Act initial-notification deadline is corrected to four hours from classification (it had used the generic 24-hour default), incident classification weighs the affected-client-base percentage, and the deadline anchors to the prior filing where one exists. · *Range and conditional-request correctness* — Compression skips a 206 Partial Content or any Content-Range response (RFC 7233 §4.1), so it can't drop Content-Length over a now-compressed byte interval; static-file conditional handling gives If-None-Match precedence over If-Modified-Since and If-Match over If-Unmodified-Since (RFC 7232); and the object store honors the [start, end] array range contract that previously fell through to a full read. · *Declared request bodies are always validated* — A route that declares a body schema now validates it even when no body was parsed, so an omitted required body is rejected at the router instead of reaching the handler — mirroring the always-run params and query checks. **Fixed:** *Object store getStream works on remote backends* — getStream() returned a stream wrapping an unresolved promise on every remote backend, so it never delivered bytes; it now resolves the object before streaming. · *Queue and body-parser correctness* — The local queue's fail()/complete() paths guard on the current row status so a stale lease can't resurrect or double-process a job, and the body parser's raw() mode no longer matches every content type by default (it had answered 415 to nothing or everything depending on the wildcard). · *Sanctions screening and GraphQL SDL probing no longer under-match* — Sanctions screening no longer skips a candidate whose type doesn't match the query's type filter (a sanctioned entity of an unspecified type was missed) and reports a typeMatch flag; the GraphQL federation guard probes for the _service / _entities introspection fields by word boundary (an aliased field bypassed the prefix check) and over batched-array request bodies. The per-tenant query budget is a true sliding window, so a burst straddling the window boundary can't briefly double the rate. **Security:** *Request-IP trust is peer-gated end to end* — The client-IP resolution that every IP gate depends on is extracted into one trust model (a trustedProxies-aware, peer-gated right-to-left walk) so the network allowlist, rate limiter, break-glass pin, and bot-guard audit attribution all decide identity the same way. X-Forwarded-Proto is peer-gated across the security middleware on the same model, and a WebSocket reconnect to a swapped target is re-validated against the SSRF / blocked-host guard before the connection is reused. See Migration for the one behavior change. · *Authentication and signature verifiers fail closed* — A set of verifiers that could accept a malformed or unsigned credential now refuse it: SAML verifyResponse fails closed on a missing AudienceRestriction or unparseable Conditions; the FDA 21 CFR 11 electronic-signature verify refuses a record with no signature (recordHash alone is self-consistency, not authentication); the SD-JWT key-binding JWT requires an audience and nonce (replay binding) and an expiry when configured; DPoP cross-checks the proof's alg against its embedded JWK key type before importing it; CIBA verifies the ID token on poll and notification; the mdoc trust anchor is enforced (no fail-open); COSE binds the signature algorithm to the key curve; and agent-snapshot refuses allowPlaintext under a regulated compliance posture and gates sealing on vault initialization. · *Pre-auth parsers hardened against complexity and frame-desync denial of service* — Parsers an attacker reaches before any credential check are bounded. CBOR rejects the quadratic-time duplicate-map-key construction (it ran O(n^2) on every COSE / CWT / EAT / mdoc verify) and rejects non-minimal integer/key encodings in canonical mode; JSONPath caps the node-list cross-product that could exhaust memory and rejects the filter expression that could overflow the parser stack; the tar reader caps a random-access entry read and resynchronizes a PAX header whose size field is non-numeric; the gzip reader caps a random-access output size; the YAML scanner replaces a backtracking pattern with a linear scan; the ASN.1 DER reader caps high-tag-number and OID sub-identifier decoding and rejects non-minimal forms; and the scheduler clamps far-future timers so a 32-bit overflow can't make a timer fire immediately or an interval tight-loop. · *Allowlist and posture lookups keyed by untrusted input can't reach the prototype* — Lookups of the form map[untrustedKey] across the framework's allowlist and compliance-posture tables are now own-property checks, so a key like __proto__ or constructor resolves as absent instead of returning an inherited value and walking the gate (CWE-1321). The shared posture-accessor that several guards built by hand is centralized, and an inverse detector flags any re-introduced unguarded indexed lookup. · *Framework file writes are symlink-refusing and atomic* — The predictable-temp and symlink-following write sites (cookie jar, config-drift snapshot, archive extraction, local object store, backup bundle, and the vault passphrase seal/unseal/rotate) now write through the atomic-write primitives above, so a pre-planted symlink at the destination or temp path can no longer redirect a privileged write, and a partial write can't leave a torn file. A re-introduced raw write without the exclusive/no-follow flags is flagged by a detector. · *X.509 issuer chains enforce the CA basic constraint* — Node's checkIssued() does not verify that the issuer certificate is actually a CA (CVE-2002-0862 class), so a leaf signed by a non-CA certificate could be accepted as an issuer. Certificate-chain walking is centralized in one helper that requires the issuer's basicConstraints cA bit and verifies the signature, and every chain consumer (TSA, BIMI, S/MIME, mdoc, content-credentials, the FIDO MDS3 anchor match) routes through it. · *Header, value, and markup injection closed* — Mail rejects CRLF in Reply-To and in custom header keys/values (SMTP header injection); the GDPR data-export CSV writer neutralizes formula-injection prefixes; the content guards decode no-semicolon numeric HTML entities when revealing a dangerous URL scheme (so javascript: is caught); the early-hints emitter rejects CRLF in pushed link values; the HTML/SVG/BIMI comment scanners honor the WHATWG abrupt-comment-close forms (--!>, <!-->) so a mutation-XSS comment can't smuggle markup; and the CSP builder no longer emits a host source alongside 'none'. · *At-rest and egress data is bound to its context* — Backup restore binds each encrypted file blob to its manifest path as additional authenticated data, so a blob remapped to another entry fails the AEAD tag on restore (signed and unsigned bundles alike), and threads the signature-verification options through extraction; the data-subject-request lookup dual-reads the keyed and legacy subject hashes so a subject is still found across the hashing change (GDPR Art. 17); the log redactor collapses a secret nested as an array or object value, not only as a scalar; and the OTLP exporter redacts the span name and status message, not just attributes, before they leave the process. · *Network-protocol verification hardened* — OCSP responses are bound to the requested certificate serial; the WebSocket reader rejects reserved opcodes and reserved bits; the DNS parser refuses a record that reads past the message buffer; the NTP client rejects a reply whose origin timestamp does not echo the request (and the SNTP cookie path was reworked to bind the exchange); DANE TLSA matching is constant-time; and MTA-STS policy fetch fails closed rather than degrading to no-policy on an ambiguous response. · *Byte caps are measured in bytes, and middleware enforcement is per-instance* — Size limits that compared a byte budget against a string's character length (so multibyte input slipped a ~2-4x looser bound) now measure actual bytes — guard-json per-string cap, the per-tenant quota header budget, the JMAP request cap. The CSRF and fetch-metadata middleware track their gate per instance, so mounting a second, stricter instance still enforces (a shared request flag previously let the first, lenient mount disable it); the CSRF cookie is still issued once per response. The WebAssembly global is stripped from the sandbox worker so sandboxed code cannot JIT around the isolation. · *Lost-update races serialized* — Concurrent failure recording in the login lockout and bot-challenge gates is serialized per key, so parallel failed attempts can no longer each read the same pre-write counter and stay under the lockout / challenge threshold. The inline webhook delivery is claimed before the POST so a concurrent retry pass can't double-send it, and a transient transport failure now backs off and retries instead of being dead-lettered. **Migration:** *X-Forwarded-For trust now requires an explicit proxy allowlist* — The IP gates that enforce a security decision — network allowlist, rate limiter, and the break-glass grant IP pin — previously resolved the client IP from the leftmost X-Forwarded-For value whenever trustProxy was truthy. A direct caller could set that header to any address and walk an IP allowlist or evade a rate-limit key (CWE-290 / CWE-348). These gates now fail closed: with trustProxy set but no trustedProxies, they ignore X-Forwarded-For and use the connecting socket's peer address. To keep trusting a forwarded client IP, set trustedProxies to the CIDR(s) of your front proxy/load balancer — the client IP is then resolved by a peer-gated right-to-left walk that stops at the first hop not in the allowlist. For an allowlist / rate-limit / break-glass gate the new default is stricter (it trusts less), so an operator who has not migrated sees tighter enforcement, never looser. Display-only consumers (error pages, request logs) keep the lenient resolution and carry a docstring warning. The wiki example's WIKI_TRUST_PROXY flag is replaced by WIKI_ADMIN_TRUSTED_PROXIES (a CIDR list).
|
|
12
|
+
|
|
11
13
|
- v0.15.13 (2026-06-19) — **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.** 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. **Added:** *b.atomicFile.fdSafeReadSync(filepath, opts?)* — 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. · *Shared substrate primitives for previously-duplicated logic* — 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. **Changed:** *Configuration errors throw typed framework errors* — 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"). **Fixed:** *Webhook deliveries retry transient transport failures instead of dead-lettering them* — 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. · *Webhook deliveries table creates on MySQL* — 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. · *Inline webhook delivery can't be double-sent by concurrent retry processing* — 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. · *Audit checkpoint can never be anchored against a replaced database* — 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. **Security:** *TOCTOU-safe file reads applied uniformly* — 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. · *Injected-dependency and integer-option validation can't silently drift* — 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. · *Single markup-escape chain for the HTML/SVG sanitizers* — 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. · *One hardening point for untrusted public-JWK import* — 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. · *Linear-time Content-Security-Policy header parsing* — 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. · *Parsed INI and OpenAPI path maps can't reach Object.prototype* — 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).
|
|
12
14
|
|
|
13
15
|
- v0.15.12 (2026-06-14) — **Hardens a set of defense-in-depth seams: a single-pass structured-field unescape, a constant-time content-digest member match, complete reserved-character stripping, a Trojan-Source escape on the boot logger, generic body-parse error responses, and an audit trail whenever outbound TLS certificate validation is disabled.** A sweep of low-severity but real hardening items. RFC 8941 structured-field string values (HTTP Message Signatures, Client Hints, Cache-Control) were un-escaped with two chained replaces that mis-decoded an escaped backslash adjacent to another escape; they now use one left-to-right pass that decodes each escape exactly once. The HTTP Message Signature content-digest check dropped a dead identity-replace and now matches the sha3-512 member by an exact, top-level, constant-time comparison instead of an unanchored substring scan. b.guardFilename's reserved-character strip used a non-global regex that left every separator after the first; it now strips all of them. The boot logger's TTY branch wrote raw text, bypassing the Trojan-Source / control-character escape the main logger applies — it now escapes the bidi and C0/newline control classes on every sink. The body parser no longer echoes a caught exception's detail (an fs errno + temp path, or a parse hook's thrown message) to the HTTP client — the client gets a generic status phrase while the full detail stays on the audit chain. And any outbound TLS connection that runs with peer-certificate validation disabled (an explicit operator opt-in, never a default) now emits a tls.insecure_skip_verify audit + observability event so the degraded posture is visible for compliance and incident response. **Added:** *b.structuredFields.unescapeSfStringBody(body)* — A single-pass decode of the RFC 8941 §3.3.3 quoted-string backslash escapes (the bytes between the surrounding quotes). It replaces the chained two-`.replace()` form, which is not equivalent to one decode — whichever pass runs first can rewrite a backslash the other escape sequence owns, so a lone escaped backslash decoded to two. The HTTP Message Signature, Client Hints, and Cache-Control sf-string readers now route through it. · *tls.insecure_skip_verify audit event* — b.network.tls.auditInsecureTls(meta) emits an audit + observability event at the point an outbound TLS connection honors rejectUnauthorized:false / allowInsecure. The connectWithEch, OTLP-gRPC log stream, syslog-TLS log stream, and SMTP transports all emit it when an operator disables certificate validation — parallel to the existing tls.classical_downgrade audit. No default changes; the framework never disables validation itself. **Security:** *Single-pass structured-field string unescape* — The RFC 8941 sf-string readers in HTTP Message Signatures (Signature-Input covered-component names), Client Hints, and Cache-Control directive values un-escaped with `.replace(/\\\\/g,"\\").replace(/\\"/g,'"')` — two sequential passes that mis-decode adjacent escapes (a lone escaped backslash became two). All four sites now use the single-pass b.structuredFields.unescapeSfStringBody. It is fail-closed (a mis-decoded covered-component name just fails the signature check, never bypasses it); the fix restores RFC-conformant interop with peers that legitimately escape these values. · *Constant-time, member-anchored content-digest verification* — b.crypto.httpSig.verify's covered content-digest check dropped a dead no-op replace and now parses the Content-Digest header into its top-level members and matches the sha3-512 member EXACTLY, in constant time (b.crypto.timingSafeEqual), rather than scanning for the digest text as a substring anywhere in the header. The Content-Digest header is already bound by the signature, so the substring form was not reachably exploitable; the change removes the latent ambiguity and the timing channel. · *Reserved-character filename strip removes every occurrence* — b.guardFilename's reservedCharPolicy:"strip" (the permissive profile) used a non-global regex, so only the FIRST reserved character — including path separators — was replaced and the rest leaked through. The strip is now global: every reserved character is removed. Not a traversal bypass (the unconditional security floor still throws on `..`, null bytes, NTFS ADS, UNC, overlong UTF-8), but the strip is now complete and consistent. · *Boot logger escapes control + bidi characters on every sink* — The boot-time logger's TTY branch wrote the raw message, bypassing the Trojan-Source (bidi) and control-character escapes the main logger applies — a hostile message could forge extra log lines on a terminal (CWE-117) or re-order the visible line (CVE-2021-42574). Both boot branches now escape the bidi and C0/newline control classes, matching the create() path and the logger's advertised guarantee. · *Body-parser error responses never echo internal detail* — The body-parser's terminal error path surfaced a caught exception's message verbatim to the HTTP client — a multipart filesystem error leaked the errno + temp path, and a parse hook's thrown error (which can carry secrets) was echoed back. The client now gets a curated message only for a framework-classified 4xx error and a generic status phrase otherwise; the parse-hook wrapper carries a fixed message, and full diagnostics stay on the audit chain server-side (CWE-209). The cluster leader-discovery endpoint's error body is generalized the same way.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"frameworkVersion": "0.15.
|
|
4
|
-
"createdAt": "2026-06-
|
|
3
|
+
"frameworkVersion": "0.15.14",
|
|
4
|
+
"createdAt": "2026-06-21T06:07:03.428Z",
|
|
5
5
|
"exports": {
|
|
6
6
|
"a2a": {
|
|
7
7
|
"type": "object",
|
|
@@ -2346,6 +2346,10 @@
|
|
|
2346
2346
|
"type": "function",
|
|
2347
2347
|
"arity": 3
|
|
2348
2348
|
},
|
|
2349
|
+
"openNoFollowSync": {
|
|
2350
|
+
"type": "function",
|
|
2351
|
+
"arity": 2
|
|
2352
|
+
},
|
|
2349
2353
|
"pathTimestamp": {
|
|
2350
2354
|
"type": "function",
|
|
2351
2355
|
"arity": 1
|
|
@@ -2370,10 +2374,18 @@
|
|
|
2370
2374
|
"type": "function",
|
|
2371
2375
|
"arity": 3
|
|
2372
2376
|
},
|
|
2377
|
+
"writeExclSync": {
|
|
2378
|
+
"type": "function",
|
|
2379
|
+
"arity": 3
|
|
2380
|
+
},
|
|
2373
2381
|
"writeJson": {
|
|
2374
2382
|
"type": "function",
|
|
2375
2383
|
"arity": 3
|
|
2376
2384
|
},
|
|
2385
|
+
"writeStream": {
|
|
2386
|
+
"type": "function",
|
|
2387
|
+
"arity": 3
|
|
2388
|
+
},
|
|
2377
2389
|
"writeSync": {
|
|
2378
2390
|
"type": "function",
|
|
2379
2391
|
"arity": 3
|
|
@@ -4921,7 +4933,7 @@
|
|
|
4921
4933
|
},
|
|
4922
4934
|
"decryptWithPassphrase": {
|
|
4923
4935
|
"type": "function",
|
|
4924
|
-
"arity":
|
|
4936
|
+
"arity": 4
|
|
4925
4937
|
},
|
|
4926
4938
|
"deriveKey": {
|
|
4927
4939
|
"type": "function",
|
|
@@ -4929,11 +4941,11 @@
|
|
|
4929
4941
|
},
|
|
4930
4942
|
"encryptWithFreshSalt": {
|
|
4931
4943
|
"type": "function",
|
|
4932
|
-
"arity":
|
|
4944
|
+
"arity": 3
|
|
4933
4945
|
},
|
|
4934
4946
|
"encryptWithPassphrase": {
|
|
4935
4947
|
"type": "function",
|
|
4936
|
-
"arity":
|
|
4948
|
+
"arity": 4
|
|
4937
4949
|
}
|
|
4938
4950
|
}
|
|
4939
4951
|
},
|
|
@@ -15280,6 +15292,10 @@
|
|
|
15280
15292
|
"type": "primitive",
|
|
15281
15293
|
"valueType": "number"
|
|
15282
15294
|
},
|
|
15295
|
+
"INITIAL_REPORT_OUTER_DEADLINE_MS": {
|
|
15296
|
+
"type": "primitive",
|
|
15297
|
+
"valueType": "number"
|
|
15298
|
+
},
|
|
15283
15299
|
"INTERMEDIATE_REPORT_DEADLINE_MS": {
|
|
15284
15300
|
"type": "primitive",
|
|
15285
15301
|
"valueType": "number"
|
|
@@ -17538,6 +17554,10 @@
|
|
|
17538
17554
|
"type": "function",
|
|
17539
17555
|
"arity": 4
|
|
17540
17556
|
},
|
|
17557
|
+
"makePostureAccessor": {
|
|
17558
|
+
"type": "function",
|
|
17559
|
+
"arity": 2
|
|
17560
|
+
},
|
|
17541
17561
|
"makeProfileBuilder": {
|
|
17542
17562
|
"type": "function",
|
|
17543
17563
|
"arity": 1
|
|
@@ -39868,6 +39888,10 @@
|
|
|
39868
39888
|
"build": {
|
|
39869
39889
|
"type": "function",
|
|
39870
39890
|
"arity": 1
|
|
39891
|
+
},
|
|
39892
|
+
"scriptTag": {
|
|
39893
|
+
"type": "function",
|
|
39894
|
+
"arity": 2
|
|
39871
39895
|
}
|
|
39872
39896
|
}
|
|
39873
39897
|
},
|
|
@@ -49218,6 +49242,14 @@
|
|
|
49218
49242
|
"safeHeadersDistinct": {
|
|
49219
49243
|
"type": "function",
|
|
49220
49244
|
"arity": 1
|
|
49245
|
+
},
|
|
49246
|
+
"trustedClientIp": {
|
|
49247
|
+
"type": "function",
|
|
49248
|
+
"arity": 1
|
|
49249
|
+
},
|
|
49250
|
+
"trustedProtocol": {
|
|
49251
|
+
"type": "function",
|
|
49252
|
+
"arity": 1
|
|
49221
49253
|
}
|
|
49222
49254
|
}
|
|
49223
49255
|
},
|
|
@@ -50638,6 +50670,10 @@
|
|
|
50638
50670
|
"type": "function",
|
|
50639
50671
|
"arity": 2
|
|
50640
50672
|
},
|
|
50673
|
+
"stringifyForScript": {
|
|
50674
|
+
"type": "function",
|
|
50675
|
+
"arity": 2
|
|
50676
|
+
},
|
|
50641
50677
|
"validate": {
|
|
50642
50678
|
"type": "function",
|
|
50643
50679
|
"arity": 3
|
|
@@ -53134,6 +53170,19 @@
|
|
|
53134
53170
|
"type": "function",
|
|
53135
53171
|
"arity": 4
|
|
53136
53172
|
},
|
|
53173
|
+
"Store": {
|
|
53174
|
+
"type": "object",
|
|
53175
|
+
"members": {
|
|
53176
|
+
"seal": {
|
|
53177
|
+
"type": "function",
|
|
53178
|
+
"arity": 1
|
|
53179
|
+
},
|
|
53180
|
+
"unseal": {
|
|
53181
|
+
"type": "function",
|
|
53182
|
+
"arity": 1
|
|
53183
|
+
}
|
|
53184
|
+
}
|
|
53185
|
+
},
|
|
53137
53186
|
"VaultError": {
|
|
53138
53187
|
"type": "function",
|
|
53139
53188
|
"arity": 4
|
|
@@ -53199,6 +53248,10 @@
|
|
|
53199
53248
|
"type": "function",
|
|
53200
53249
|
"arity": 0
|
|
53201
53250
|
},
|
|
53251
|
+
"getDefaultStore": {
|
|
53252
|
+
"type": "function",
|
|
53253
|
+
"arity": 0
|
|
53254
|
+
},
|
|
53202
53255
|
"getDerivedHashMacKey": {
|
|
53203
53256
|
"type": "function",
|
|
53204
53257
|
"arity": 0
|
|
@@ -105,7 +105,7 @@ The wiki ships with development-friendly defaults (`vault: { mode: "plaintext" }
|
|
|
105
105
|
3. Change `db.atRest` to `"encrypted"` (default; requires tmpfs at `/dev/shm` or `BLAMEJS_TMPDIR`)
|
|
106
106
|
4. Change `db.auditSigning.mode` to `"wrapped"`
|
|
107
107
|
5. Deploy behind a TLS-terminating reverse proxy OR enable `b.pqcGate`
|
|
108
|
-
6. Set `
|
|
108
|
+
6. Set `WIKI_ADMIN_TRUSTED_PROXIES` to your reverse proxy's CIDR(s) if the wiki sits behind a TLS terminator that injects `x-forwarded-*`. The wiki then peer-gates those headers — `x-forwarded-proto` (cookie Secure flag) and `x-forwarded-for` (the `/admin` CIDR fence) are honoured only when the request arrives via a trusted proxy, so a direct caller can't forge `https` or an allowed IP. Left empty (the default), forwarded headers are ignored and only the real TLS socket / socket address count.
|
|
109
109
|
7. Set `WIKI_ADMIN_PASSWORD` to a strong value managed by your secret-store
|
|
110
110
|
|
|
111
111
|
## Trust model for editable page bodies
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
# BLAMEJS_DEPRECATIONS=warn
|
|
32
32
|
#
|
|
33
33
|
# # security hardening (production overlay defaults shown)
|
|
34
|
-
#
|
|
34
|
+
# WIKI_ADMIN_TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12 # peer-gate x-forwarded-* to your proxy CIDRs
|
|
35
35
|
# WIKI_REQUIRE_PROD_ASSERTS=1 # refuse to boot on weak posture
|
|
36
36
|
# WIKI_ADMIN_ALLOWED_CIDRS=10.0.0.0/8,192.168.0.0/16 # in-process CIDR fence on /admin
|
|
37
37
|
# WIKI_ADMIN_DENIED_CIDRS= # deny-then-allow precedence
|
|
@@ -94,13 +94,14 @@ services:
|
|
|
94
94
|
WIKI_WEBHOOK_URL: ${WIKI_WEBHOOK_URL:-}
|
|
95
95
|
WIKI_WEBHOOK_SECRET: ${WIKI_WEBHOOK_SECRET:-}
|
|
96
96
|
# ---- Wiki security hardening ----
|
|
97
|
-
# Caddy terminates TLS
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
|
|
97
|
+
# Caddy terminates TLS on the internal docker network and injects
|
|
98
|
+
# x-forwarded-*. Peer-gating honours those headers ONLY from a
|
|
99
|
+
# trusted-proxy peer, so the default is the private ranges (Caddy's
|
|
100
|
+
# internal address) — the wiki container receives traffic only from
|
|
101
|
+
# Caddy, so a forged X-Forwarded-For / -Proto from a non-internal
|
|
102
|
+
# peer is rejected. Drives the /admin CIDR fence and the cookie
|
|
103
|
+
# Secure flag. Narrow this to Caddy's exact CIDR for a tighter gate.
|
|
104
|
+
WIKI_ADMIN_TRUSTED_PROXIES: ${WIKI_ADMIN_TRUSTED_PROXIES:-10.0.0.0/8,172.16.0.0/12,192.168.0.0/16}
|
|
104
105
|
# In-process CIDR fence on /admin paths. The production overlay
|
|
105
106
|
# leaves this empty by default — the operator sets the corporate
|
|
106
107
|
# bastion / VPN / cloud-LB private CIDRs in their .env. Both
|
|
@@ -33,19 +33,20 @@ services:
|
|
|
33
33
|
WIKI_ADMIN_PASSWORD: ${WIKI_ADMIN_PASSWORD:-}
|
|
34
34
|
WIKI_WEBHOOK_URL: ${WIKI_WEBHOOK_URL:-}
|
|
35
35
|
WIKI_WEBHOOK_SECRET: ${WIKI_WEBHOOK_SECRET:-}
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
|
|
36
|
+
# Reverse-proxy CIDRs (Caddy / Nginx / Traefik / cloud LB). When
|
|
37
|
+
# set, the wiki peer-gates the forwarded headers: X-Forwarded-For
|
|
38
|
+
# (the /admin CIDR fence) and X-Forwarded-Proto (the cookie Secure
|
|
39
|
+
# flag) are honoured ONLY when the request's immediate peer is one
|
|
40
|
+
# of these, so a direct caller can't forge an allowed IP or claim
|
|
41
|
+
# https. Empty (default) = ignore forwarded headers and use the
|
|
42
|
+
# socket address / real TLS state.
|
|
43
|
+
WIKI_ADMIN_TRUSTED_PROXIES: ${WIKI_ADMIN_TRUSTED_PROXIES:-}
|
|
43
44
|
# In-process CIDR fence on /admin paths. Comma-separated CIDRs;
|
|
44
45
|
# empty = no fence (operators using a reverse proxy / NACL
|
|
45
46
|
# typically gate at that layer). When set, the wiki mounts
|
|
46
47
|
# b.middleware.networkAllowlist as the in-process layer above
|
|
47
|
-
# the application-layer auth gate. Pair with
|
|
48
|
-
#
|
|
48
|
+
# the application-layer auth gate. Pair with WIKI_ADMIN_TRUSTED_PROXIES
|
|
49
|
+
# so X-Forwarded-For is peer-gated.
|
|
49
50
|
WIKI_ADMIN_ALLOWED_CIDRS: ${WIKI_ADMIN_ALLOWED_CIDRS:-}
|
|
50
51
|
WIKI_ADMIN_DENIED_CIDRS: ${WIKI_ADMIN_DENIED_CIDRS:-}
|
|
51
52
|
# Production-posture assertion gate. "1" = the wiki refuses to
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"WIKI_ADMIN_DENIED_CIDRS",
|
|
55
55
|
"WIKI_ADMIN_EMAIL",
|
|
56
56
|
"WIKI_ADMIN_PASSWORD",
|
|
57
|
+
"WIKI_ADMIN_TRUSTED_PROXIES",
|
|
57
58
|
"WIKI_AUDIT_SIGNING_MODE",
|
|
58
59
|
"WIKI_BIND",
|
|
59
60
|
"WIKI_DATA_DIR",
|
|
@@ -72,7 +73,6 @@
|
|
|
72
73
|
"WIKI_PORT",
|
|
73
74
|
"WIKI_REQUIRE_PROD_ASSERTS",
|
|
74
75
|
"WIKI_SITE_URL",
|
|
75
|
-
"WIKI_TRUST_PROXY",
|
|
76
76
|
"WIKI_VAULT_MODE",
|
|
77
77
|
"WIKI_WEBHOOK_SECRET",
|
|
78
78
|
"WIKI_WEBHOOK_URL"
|
|
@@ -131,6 +131,7 @@
|
|
|
131
131
|
"WIKI_ADMIN_DENIED_CIDRS",
|
|
132
132
|
"WIKI_ADMIN_EMAIL",
|
|
133
133
|
"WIKI_ADMIN_PASSWORD",
|
|
134
|
+
"WIKI_ADMIN_TRUSTED_PROXIES",
|
|
134
135
|
"WIKI_AUDIT_SIGNING_MODE",
|
|
135
136
|
"WIKI_BIND",
|
|
136
137
|
"WIKI_DATA_DIR",
|
|
@@ -138,7 +139,6 @@
|
|
|
138
139
|
"WIKI_PORT",
|
|
139
140
|
"WIKI_REQUIRE_PROD_ASSERTS",
|
|
140
141
|
"WIKI_SITE_URL",
|
|
141
|
-
"WIKI_TRUST_PROXY",
|
|
142
142
|
"WIKI_VAULT_MODE",
|
|
143
143
|
"WIKI_WEBHOOK_SECRET",
|
|
144
144
|
"WIKI_WEBHOOK_URL"
|
|
@@ -202,14 +202,15 @@
|
|
|
202
202
|
"WIKI_ADMIN_DENIED_CIDRS",
|
|
203
203
|
"WIKI_ADMIN_EMAIL",
|
|
204
204
|
"WIKI_ADMIN_PASSWORD",
|
|
205
|
+
"WIKI_ADMIN_TRUSTED_PROXIES",
|
|
205
206
|
"WIKI_AUDIT_SIGNING_MODE",
|
|
206
207
|
"WIKI_BIND",
|
|
207
208
|
"WIKI_DATA_DIR",
|
|
208
209
|
"WIKI_DB_AT_REST",
|
|
209
210
|
"WIKI_PORT",
|
|
211
|
+
"WIKI_PORT",
|
|
210
212
|
"WIKI_REQUIRE_PROD_ASSERTS",
|
|
211
213
|
"WIKI_SITE_URL",
|
|
212
|
-
"WIKI_TRUST_PROXY",
|
|
213
214
|
"WIKI_VAULT_MODE",
|
|
214
215
|
"WIKI_WEBHOOK_SECRET",
|
|
215
216
|
"WIKI_WEBHOOK_URL"
|
|
@@ -65,6 +65,13 @@ async function buildApp(opts) {
|
|
|
65
65
|
var adminPassword = opts.adminPassword || null;
|
|
66
66
|
var webhookUrl = opts.webhookUrl || null;
|
|
67
67
|
var webhookSecret = opts.webhookSecret || null;
|
|
68
|
+
// Rate-limit burst (token count). Default: 2 minutes' worth of refill at
|
|
69
|
+
// 2 tokens/sec, ample for real browsing. Overridable so the e2e link/nav
|
|
70
|
+
// crawler — which fetches every page in a tight loop, far above any human
|
|
71
|
+
// rate — isn't 429'd while exercising completeness.
|
|
72
|
+
var rateLimitBurst = opts.rateLimitBurst !== undefined
|
|
73
|
+
? opts.rateLimitBurst
|
|
74
|
+
: 2 * (b.constants.TIME.minutes(2) / b.constants.TIME.seconds(1));
|
|
68
75
|
|
|
69
76
|
// ---- Build client assets via b.bundler ----
|
|
70
77
|
// Hashes wiki.js + editor.js into public/dist/<name>.<hash>.js so
|
|
@@ -231,13 +238,6 @@ async function buildApp(opts) {
|
|
|
231
238
|
audit: b.audit,
|
|
232
239
|
});
|
|
233
240
|
|
|
234
|
-
// Trust-proxy posture: when WIKI_TRUST_PROXY is set (the operator is
|
|
235
|
-
// behind a TLS terminator that injects x-forwarded-proto), the wiki
|
|
236
|
-
// honours that header for cookie Secure-flag detection. Default off
|
|
237
|
-
// so a misconfigured deployment doesn't accept attacker-supplied
|
|
238
|
-
// x-forwarded-proto: https as proof the request was over TLS.
|
|
239
|
-
var trustProxy = b.safeEnv.readVar("WIKI_TRUST_PROXY", { type: "boolean", default: false });
|
|
240
|
-
|
|
241
241
|
// Network allowlist for /admin paths — when WIKI_ADMIN_ALLOWED_CIDRS
|
|
242
242
|
// is set (comma-separated CIDR list), the wiki mounts
|
|
243
243
|
// b.middleware.networkAllowlist as the in-process CIDR fence above
|
|
@@ -251,6 +251,19 @@ async function buildApp(opts) {
|
|
|
251
251
|
// deny rules.
|
|
252
252
|
var adminDeniedCidrs = (b.safeEnv.readVar("WIKI_ADMIN_DENIED_CIDRS") || "")
|
|
253
253
|
.split(",").map(function (s) { return s.trim(); }).filter(function (s) { return s.length > 0; });
|
|
254
|
+
// Reverse-proxy CIDRs for the deployment. X-Forwarded-For / -Proto are
|
|
255
|
+
// honored only when the request's immediate peer is one of these
|
|
256
|
+
// (peer-gating) — without it the framework uses the socket address /
|
|
257
|
+
// real TLS state and ignores the forgeable headers, so a direct caller
|
|
258
|
+
// can't spoof an allowed IP or claim https. Drives both the /admin CIDR
|
|
259
|
+
// gate and the admin Secure-cookie HTTPS detection.
|
|
260
|
+
var adminTrustedProxies = (b.safeEnv.readVar("WIKI_ADMIN_TRUSTED_PROXIES") || "")
|
|
261
|
+
.split(",").map(function (s) { return s.trim(); }).filter(function (s) { return s.length > 0; });
|
|
262
|
+
// Peer-gated HTTPS detector for the admin Secure-cookie flag — resolves
|
|
263
|
+
// "https" only when X-Forwarded-Proto arrives via a trusted proxy, else
|
|
264
|
+
// from the real TLS socket. Replaces a bare trust-proxy boolean a direct
|
|
265
|
+
// caller could forge to suppress the Secure attribute.
|
|
266
|
+
var secureProtocol = b.requestHelpers.trustedProtocol({ trustedProxies: adminTrustedProxies }).resolve;
|
|
254
267
|
|
|
255
268
|
// Network configurability — read NTP / DNS / proxy / DPI-trust / socket
|
|
256
269
|
// env vars and apply them before the framework's outbound code paths
|
|
@@ -368,12 +381,15 @@ async function buildApp(opts) {
|
|
|
368
381
|
credentials: false,
|
|
369
382
|
},
|
|
370
383
|
rateLimit: {
|
|
371
|
-
// burst
|
|
372
|
-
// an idle visitor accrues a
|
|
373
|
-
// bursts
|
|
384
|
+
// burst is a TOKEN COUNT, not a duration: 2 minutes' worth of refill at
|
|
385
|
+
// 2 tokens/sec, so an idle visitor accrues a minute-scale buffer between
|
|
386
|
+
// bursts and static-asset preloads don't trip the limiter. Expressed as
|
|
387
|
+
// refillPerSecond × window-in-seconds so the count is never a bare magic
|
|
388
|
+
// number. (Was C.TIME.minutes(2) = 120000 — a ms/count unit error that
|
|
389
|
+
// set burst to 120k and effectively disabled the limiter.)
|
|
374
390
|
backend: "memory",
|
|
375
|
-
burst: b.constants.TIME.minutes(2),
|
|
376
391
|
refillPerSecond: 2,
|
|
392
|
+
burst: rateLimitBurst,
|
|
377
393
|
skipPaths: ["/healthz", "/readyz"],
|
|
378
394
|
},
|
|
379
395
|
// cookies + cspNonce + fetchMetadata ride createApp's secure
|
|
@@ -397,11 +413,11 @@ async function buildApp(opts) {
|
|
|
397
413
|
// unset — operators using a reverse proxy / NACL leave this off.
|
|
398
414
|
if (adminAllowedCidrs.length > 0) {
|
|
399
415
|
router.use(b.middleware.networkAllowlist({
|
|
400
|
-
paths:
|
|
401
|
-
allowedCidrs:
|
|
402
|
-
deniedCidrs:
|
|
403
|
-
|
|
404
|
-
audit:
|
|
416
|
+
paths: ["/admin", "/admin/", "/healthz/internal"],
|
|
417
|
+
allowedCidrs: adminAllowedCidrs,
|
|
418
|
+
deniedCidrs: adminDeniedCidrs,
|
|
419
|
+
trustedProxies: adminTrustedProxies,
|
|
420
|
+
audit: b.audit,
|
|
405
421
|
}));
|
|
406
422
|
}
|
|
407
423
|
// bodyParser + cspNonce are wired by createApp (see the middleware
|
|
@@ -461,7 +477,7 @@ async function buildApp(opts) {
|
|
|
461
477
|
notify: notify,
|
|
462
478
|
apiKeys: apiKeys,
|
|
463
479
|
loginLockout: loginLockout,
|
|
464
|
-
|
|
480
|
+
secureProtocol: secureProtocol,
|
|
465
481
|
assets: assets,
|
|
466
482
|
nonceMw: nonceMw,
|
|
467
483
|
siteUrl: opts.siteUrl || "https://blamejs.com",
|
|
@@ -41,17 +41,14 @@ function register(router, ctx) {
|
|
|
41
41
|
var notify = ctx.notify;
|
|
42
42
|
var apiKeys = ctx.apiKeys;
|
|
43
43
|
var loginLockout = ctx.loginLockout;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
// injection. Operators behind a real TLS terminator opt in via
|
|
51
|
-
// WIKI_TRUST_PROXY=1 in build-app.js.
|
|
44
|
+
// Resolve the cookie Secure attribute through the peer-gated protocol
|
|
45
|
+
// detector built in build-app.js: X-Forwarded-Proto is honored only when the
|
|
46
|
+
// request arrives via a declared reverse proxy (WIKI_ADMIN_TRUSTED_PROXIES),
|
|
47
|
+
// otherwise only req.socket.encrypted counts. A direct caller can't forge
|
|
48
|
+
// https with a header to suppress (or force) the Secure flag.
|
|
49
|
+
var secureProtocol = ctx.secureProtocol;
|
|
52
50
|
function _secureCookieFlag(req) {
|
|
53
|
-
return
|
|
54
|
-
? "; Secure" : "";
|
|
51
|
+
return secureProtocol(req) === "https" ? "; Secure" : "";
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
// ---- Login form ----
|
|
@@ -31,10 +31,11 @@
|
|
|
31
31
|
*
|
|
32
32
|
* Security hardening (read directly by lib/build-app.js):
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
34
|
+
* WIKI_ADMIN_TRUSTED_PROXIES comma-separated reverse-proxy CIDRs. When set,
|
|
35
|
+
* peer-gates x-forwarded-for (the /admin fence)
|
|
36
|
+
* and x-forwarded-proto (cookie Secure flag) — the
|
|
37
|
+
* headers are honoured ONLY from a trusted peer, so
|
|
38
|
+
* a direct caller can't forge an IP or claim https.
|
|
38
39
|
* WIKI_ADMIN_ALLOWED_CIDRS comma-separated CIDR list. When set, mounts
|
|
39
40
|
* b.middleware.networkAllowlist as the in-process
|
|
40
41
|
* CIDR fence on /admin paths. Empty = no fence.
|
|
@@ -93,6 +93,11 @@ async function _bootApp() {
|
|
|
93
93
|
port: 0, // ephemeral
|
|
94
94
|
adminEmail: ADMIN_EMAIL,
|
|
95
95
|
adminPassword: ADMIN_PASSWORD,
|
|
96
|
+
// The completeness crawler fetches every nav page + internal link in a
|
|
97
|
+
// tight loop — far above any human browsing rate. Raise the rate-limit
|
|
98
|
+
// burst so those checks aren't 429'd; the limiter's own enforcement is
|
|
99
|
+
// covered by the brute-force lockout assertions below.
|
|
100
|
+
rateLimitBurst: 100000,
|
|
96
101
|
});
|
|
97
102
|
}
|
|
98
103
|
|
|
@@ -380,6 +380,18 @@ function middlewareTasks(opts) {
|
|
|
380
380
|
throw new A2aTasksError("a2a-tasks/bad-mw-opts",
|
|
381
381
|
"middleware.tasks: opts.scopes must be an object when provided", true);
|
|
382
382
|
}
|
|
383
|
+
// Every scope VALUE must be a non-empty string. A non-string value (e.g.
|
|
384
|
+
// {transfer: ["a2a:transfer"]}) would make the runtime `typeof requiredScope
|
|
385
|
+
// === "string"` gate silently skip — a fail-open authorization bypass on a
|
|
386
|
+
// gated skill. Catch the operator typo at boot, not at request time.
|
|
387
|
+
var scopeKeys = Object.keys(opts.scopes);
|
|
388
|
+
for (var sk = 0; sk < scopeKeys.length; sk += 1) {
|
|
389
|
+
var sv = opts.scopes[scopeKeys[sk]];
|
|
390
|
+
if (typeof sv !== "string" || sv.length === 0) {
|
|
391
|
+
throw new A2aTasksError("a2a-tasks/bad-mw-opts",
|
|
392
|
+
"middleware.tasks: opts.scopes['" + scopeKeys[sk] + "'] must be a non-empty string", true);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
383
395
|
}
|
|
384
396
|
var maxBytes = opts.maxBytes !== undefined ? opts.maxBytes : C.BYTES.mib(1);
|
|
385
397
|
var emitAudit = opts.audit !== false;
|
|
@@ -429,16 +441,36 @@ function middlewareTasks(opts) {
|
|
|
429
441
|
}
|
|
430
442
|
var params = body.params || {};
|
|
431
443
|
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
444
|
+
// Validate the protocol identifiers on the UNTRUSTED-peer ingress before
|
|
445
|
+
// handing them to the operator handler — the client send/get/cancel
|
|
446
|
+
// dispatchers enforce these shapes on egress, the server must too (the
|
|
447
|
+
// sibling b.mcp middleware validates toolName/resourceUri identically).
|
|
448
|
+
if (body.method === "tasks/send") {
|
|
449
|
+
try { _validateTaskShape(params.task, "middleware.tasks.task"); }
|
|
450
|
+
catch (eShape) {
|
|
451
|
+
res.statusCode = 200;
|
|
436
452
|
res.setHeader("Content-Type", "application/json");
|
|
437
453
|
res.end(JSON.stringify(_jsonRpcError(reqId, JSONRPC_INVALID_PARAMS,
|
|
438
|
-
"tasks/send:
|
|
454
|
+
(eShape && eShape.message) || "tasks/send: invalid task")));
|
|
439
455
|
return;
|
|
440
456
|
}
|
|
441
|
-
|
|
457
|
+
} else if (body.method === "tasks/get" || body.method === "tasks/cancel") {
|
|
458
|
+
if (typeof params.taskId !== "string" || params.taskId.length > 64 || !TASK_ID_RE.test(params.taskId)) {
|
|
459
|
+
res.statusCode = 200;
|
|
460
|
+
res.setHeader("Content-Type", "application/json");
|
|
461
|
+
res.end(JSON.stringify(_jsonRpcError(reqId, JSONRPC_INVALID_PARAMS,
|
|
462
|
+
body.method + ": params.taskId must match " + TASK_ID_RE)));
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Scope enforcement for tasks/send (task references a skill).
|
|
468
|
+
if (body.method === "tasks/send" && scopes) {
|
|
469
|
+
// Own-property lookup ONLY — an attacker-controlled skill like
|
|
470
|
+
// "constructor"/"toString" must not resolve an inherited Object.prototype
|
|
471
|
+
// member (proto-shadow). The skill shape is already validated above.
|
|
472
|
+
var hasScope = Object.prototype.hasOwnProperty.call(scopes, params.task.skill);
|
|
473
|
+
var requiredScope = hasScope ? scopes[params.task.skill] : undefined;
|
|
442
474
|
if (typeof requiredScope === "string") {
|
|
443
475
|
var grantedScopes = Array.isArray(req.a2aScopes) ? req.a2aScopes : [];
|
|
444
476
|
if (grantedScopes.indexOf(requiredScope) === -1) {
|
|
@@ -422,6 +422,19 @@ async function _subscribe(topics, pubsub, name, handler, sOpts, permissions, aud
|
|
|
422
422
|
topic: name, reason: "require-mac-disabled", phase: "delivery",
|
|
423
423
|
});
|
|
424
424
|
}
|
|
425
|
+
// Channel binding: the authenticated _topic must match the channel this
|
|
426
|
+
// handler was registered for. The MAC binds _topic, so a pubsub-write
|
|
427
|
+
// attacker cannot FORGE it — but they CAN replay a genuinely-MAC'd
|
|
428
|
+
// envelope from topic A onto topic B's channel. If A and B share a
|
|
429
|
+
// schema and tenant, that replay would otherwise be delivered to B's
|
|
430
|
+
// handler as a B-event. Drop on mismatch.
|
|
431
|
+
if (wrapped._topic !== name) {
|
|
432
|
+
_safeAudit(auditImpl, "agent.event_bus.delivery_dropped", sOpts.actor, {
|
|
433
|
+
topic: name, reason: "topic-channel-mismatch",
|
|
434
|
+
envelopeTopic: typeof wrapped._topic === "string" ? wrapped._topic : null,
|
|
435
|
+
});
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
425
438
|
// Tenant-scope check: subscriber's tenantId must match the
|
|
426
439
|
// publisher's tenantId from the wire envelope. If the envelope
|
|
427
440
|
// lacks _tenantId (publisher omitted), that's a tampered or
|
|
@@ -310,7 +310,11 @@ async function _get(store, method, actorId, key, auditImpl, ttlMs, maxResultByte
|
|
|
310
310
|
var updatedReplayCount;
|
|
311
311
|
if (typeof store.incrementReplayCount === "function") {
|
|
312
312
|
var updated = await store.incrementReplayCount(method, actorId, hash);
|
|
313
|
-
|
|
313
|
+
// Coerce the store's returned count to a number — a backend that returns it
|
|
314
|
+
// as a string (some drivers stringify integer columns) would otherwise make
|
|
315
|
+
// updatedReplayCount a string and corrupt downstream arithmetic/comparison.
|
|
316
|
+
var inc = updated && updated.replayCount != null ? Number(updated.replayCount) : NaN;
|
|
317
|
+
updatedReplayCount = Number.isFinite(inc) ? inc : (Number(row.replayCount) || 0) + 1;
|
|
314
318
|
} else {
|
|
315
319
|
_safeAudit(auditImpl, "agent.idempotency.non_atomic_increment", null, {
|
|
316
320
|
method: method, actorIdHash: _truncHash(_actorIdHash(actorId)),
|