@blamejs/blamejs-shop 0.4.69 → 0.4.70
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/admin.js +8 -6
- package/lib/asset-manifest.json +1 -1
- package/lib/security-middleware.js +65 -23
- 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
|
@@ -145,6 +145,45 @@ async function testMiddlewareScopeDenied() {
|
|
|
145
145
|
body && body.error && body.error.code === -32001);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
async function testMiddlewareScopeNonStringThrows() {
|
|
149
|
+
// A non-string scope VALUE (operator typo, e.g. an array) must be refused at
|
|
150
|
+
// construction — else the runtime `typeof requiredScope === "string"` gate
|
|
151
|
+
// silently skips, a fail-open authorization bypass on a gated skill.
|
|
152
|
+
var threw = null;
|
|
153
|
+
try {
|
|
154
|
+
b.a2a.middleware.tasks({ scopes: { transfer: ["a2a:transfer"] }, handler: function () {} });
|
|
155
|
+
} catch (e) { threw = e; }
|
|
156
|
+
check("middleware: non-string scope value rejected at construction",
|
|
157
|
+
threw && threw.code === "a2a-tasks/bad-mw-opts");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function testMiddlewareRejectsHostileSkill() {
|
|
161
|
+
// The untrusted-peer ingress must validate task.skill (the client send path
|
|
162
|
+
// does); an oversized / metacharacter-laden skill must never reach the handler.
|
|
163
|
+
var handlerRan = false;
|
|
164
|
+
var mw = b.a2a.middleware.tasks({ handler: function () { handlerRan = true; return null; } });
|
|
165
|
+
var hostile = "A".repeat(5000);
|
|
166
|
+
var req = _mockReq("POST", "application/json",
|
|
167
|
+
Buffer.from(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tasks/send", params: { task: { skill: hostile } } })),
|
|
168
|
+
[]);
|
|
169
|
+
var res = _mockRes();
|
|
170
|
+
var body = await _runMwAndDecode(mw, req, res);
|
|
171
|
+
check("middleware: oversized skill rejected (-32602), handler not run",
|
|
172
|
+
body && body.error && body.error.code === -32602 && handlerRan === false);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function testMiddlewareRejectsHostileTaskId() {
|
|
176
|
+
var handlerRan = false;
|
|
177
|
+
var mw = b.a2a.middleware.tasks({ handler: function () { handlerRan = true; return null; } });
|
|
178
|
+
var req = _mockReq("POST", "application/json",
|
|
179
|
+
Buffer.from(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tasks/get", params: { taskId: "../../etc/passwd" } })),
|
|
180
|
+
[]);
|
|
181
|
+
var res = _mockRes();
|
|
182
|
+
var body = await _runMwAndDecode(mw, req, res);
|
|
183
|
+
check("middleware: traversal taskId rejected (-32602), handler not run",
|
|
184
|
+
body && body.error && body.error.code === -32602 && handlerRan === false);
|
|
185
|
+
}
|
|
186
|
+
|
|
148
187
|
async function testMiddlewareSuccessSend() {
|
|
149
188
|
var captured = null;
|
|
150
189
|
var mw = b.a2a.middleware.tasks({
|
|
@@ -199,6 +238,9 @@ async function run() {
|
|
|
199
238
|
await testMiddlewareInvalidJson();
|
|
200
239
|
await testMiddlewareMethodNotFound();
|
|
201
240
|
await testMiddlewareScopeDenied();
|
|
241
|
+
await testMiddlewareScopeNonStringThrows();
|
|
242
|
+
await testMiddlewareRejectsHostileSkill();
|
|
243
|
+
await testMiddlewareRejectsHostileTaskId();
|
|
202
244
|
await testMiddlewareSuccessSend();
|
|
203
245
|
await testAgentCardMiddleware();
|
|
204
246
|
}
|
|
@@ -385,6 +385,41 @@ async function testEnvelopeMacTamperFails() {
|
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
+
async function testCrossTopicReplayDropped() {
|
|
389
|
+
// A genuinely-MAC'd envelope for topic A, replayed verbatim onto topic B's
|
|
390
|
+
// channel, must NOT be delivered to B's handler — even when A and B share
|
|
391
|
+
// a schema + tenant. The MAC binds _topic=A (so it cannot be forged), but
|
|
392
|
+
// the subscriber must additionally bind the authenticated _topic to the
|
|
393
|
+
// channel it was registered for.
|
|
394
|
+
var tmpDir = _tmp();
|
|
395
|
+
await helpers.setupVaultOnly(tmpDir);
|
|
396
|
+
try {
|
|
397
|
+
var pubsub = _capturingPubsub();
|
|
398
|
+
var bus = b.agent.eventBus.create({ pubsub: pubsub });
|
|
399
|
+
bus.registerTopic("mail.scan.alpha", { schema: { source: "string" }, posture: "soc2" });
|
|
400
|
+
bus.registerTopic("mail.scan.beta", { schema: { source: "string" }, posture: "soc2" });
|
|
401
|
+
var gotA = [], gotB = [];
|
|
402
|
+
await bus.subscribe("mail.scan.alpha", function (p) { gotA.push(p); });
|
|
403
|
+
await bus.subscribe("mail.scan.beta", function (p) { gotB.push(p); });
|
|
404
|
+
|
|
405
|
+
await bus.publish("mail.scan.alpha", { source: "from-a" }, { actor: { id: "p1" } });
|
|
406
|
+
await helpers.waitUntil(function () { return gotA.length >= 1; }, {
|
|
407
|
+
timeoutMs: 5000, label: "cross-topic: baseline A delivery",
|
|
408
|
+
});
|
|
409
|
+
var genuineA = pubsub._lastEnvelope();
|
|
410
|
+
check("genuine A envelope carries _topic=mail.scan.alpha + a MAC",
|
|
411
|
+
genuineA._topic === "mail.scan.alpha" && typeof genuineA._mac === "string");
|
|
412
|
+
|
|
413
|
+
// Replay the verbatim, MAC-valid A envelope onto B's channel.
|
|
414
|
+
pubsub._deliver("mail.scan.beta", JSON.parse(JSON.stringify(genuineA)));
|
|
415
|
+
await helpers.passiveObserve(40, "cross-topic: A-event replayed onto B must NOT reach B's handler");
|
|
416
|
+
check("cross-topic replay (A envelope on B channel) is dropped", gotB.length === 0);
|
|
417
|
+
check("the legitimate A subscriber still got exactly its one event", gotA.length === 1);
|
|
418
|
+
} finally {
|
|
419
|
+
helpers.teardownVaultOnly(tmpDir);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
388
423
|
async function testEnvelopeMacPublishFailsClosedWithoutVault() {
|
|
389
424
|
// requireMac default ON + no vault → publish refuses (fail-closed)
|
|
390
425
|
// rather than emitting an unauthenticatable envelope.
|
|
@@ -419,6 +454,7 @@ async function run() {
|
|
|
419
454
|
await testEnvelopeMacForgedRefused();
|
|
420
455
|
await testEnvelopeMacHonestDelivered();
|
|
421
456
|
await testEnvelopeMacTamperFails();
|
|
457
|
+
await testCrossTopicReplayDropped();
|
|
422
458
|
await testEnvelopeMacPublishFailsClosedWithoutVault();
|
|
423
459
|
}
|
|
424
460
|
|
|
Binary file
|
|
@@ -481,6 +481,52 @@ async function runReseal() {
|
|
|
481
481
|
}
|
|
482
482
|
}
|
|
483
483
|
|
|
484
|
+
async function testAllowPlaintextRefusedUnderRegulatedPosture() {
|
|
485
|
+
// #112 — allowPlaintext claimed to be "refused under hipaa/pci-dss/gdpr/soc2"
|
|
486
|
+
// but had NO posture check: persist wrote a plaintext body and load waived
|
|
487
|
+
// signature verification (CWE-347) under a regulated posture. The dev escape
|
|
488
|
+
// hatch must actually refuse under a regulated posture, at BOTH boundaries.
|
|
489
|
+
var backend = _fakeBackend();
|
|
490
|
+
try {
|
|
491
|
+
b.compliance.clear();
|
|
492
|
+
b.compliance.set("hipaa");
|
|
493
|
+
|
|
494
|
+
// (A) persist must refuse the no-sealer plaintext path under hipaa.
|
|
495
|
+
var plain = b.agent.snapshot.create({
|
|
496
|
+
orchestrator: _fakeOrch(),
|
|
497
|
+
backend: backend,
|
|
498
|
+
signer: _fakeSigner(), // signer wired; sealer omitted -> plaintext body
|
|
499
|
+
allowPlaintext: true,
|
|
500
|
+
});
|
|
501
|
+
var sA = await plain.takeSnapshot({});
|
|
502
|
+
await expectRejection("persist allowPlaintext refused under hipaa",
|
|
503
|
+
plain.persist(sA), "agent-snapshot/plaintext-refused-under-posture");
|
|
504
|
+
|
|
505
|
+
// (B) load must NOT waive verify for an attacker-written unsigned snapshot
|
|
506
|
+
// under hipaa (the CWE-347 path).
|
|
507
|
+
var signed = _signedSnapshot({ backend: backend });
|
|
508
|
+
var sB = await signed.takeSnapshot({});
|
|
509
|
+
await backend.put(sB.snapshotId, Object.assign({}, sB, { sig: null }));
|
|
510
|
+
var plainLoader = b.agent.snapshot.create({
|
|
511
|
+
orchestrator: _fakeOrch(), backend: backend, signer: _fakeSigner(), allowPlaintext: true,
|
|
512
|
+
});
|
|
513
|
+
await expectRejection("load unsigned NOT waived under hipaa",
|
|
514
|
+
plainLoader.loadById(sB.snapshotId), "agent-snapshot/plaintext-refused-under-posture");
|
|
515
|
+
} finally {
|
|
516
|
+
b.compliance.clear();
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// (C) control — with no regulated posture (dev), the escape hatch still works.
|
|
520
|
+
var devBackend = _fakeBackend();
|
|
521
|
+
var dev = b.agent.snapshot.create({
|
|
522
|
+
orchestrator: _fakeOrch(), backend: devBackend, signer: _fakeSigner(), allowPlaintext: true,
|
|
523
|
+
});
|
|
524
|
+
var sC = await dev.takeSnapshot({});
|
|
525
|
+
var persisted = await dev.persist(sC);
|
|
526
|
+
check("dev allowPlaintext persists without a regulated posture",
|
|
527
|
+
persisted && typeof persisted.snapshotId === "string");
|
|
528
|
+
}
|
|
529
|
+
|
|
484
530
|
async function run() {
|
|
485
531
|
testSurface();
|
|
486
532
|
await testCreateRequiresOrchestrator();
|
|
@@ -491,6 +537,7 @@ async function run() {
|
|
|
491
537
|
await testLoadById();
|
|
492
538
|
await testTamperedEnvelopeRefused();
|
|
493
539
|
await testUnsignedEnvelopeRefused();
|
|
540
|
+
await testAllowPlaintextRefusedUnderRegulatedPosture();
|
|
494
541
|
await testRestoreTopologyChange();
|
|
495
542
|
await testRefuseOnTopologyChange();
|
|
496
543
|
await testSchemaVersionMismatch();
|
|
@@ -54,6 +54,29 @@ async function testGzBombCapRefused() {
|
|
|
54
54
|
refused !== null);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
async function testGzRandomAccessSourceCapped() {
|
|
58
|
+
// The random-access branch must cap the COMPRESSED read before
|
|
59
|
+
// adapter.range(0, size) allocates the whole file — a hostile .gz (or an
|
|
60
|
+
// objectStore/HTTP source advertising a huge size) is an OOM lever.
|
|
61
|
+
// maxDecompressedBytes bounds the compressed read too. Use incompressible
|
|
62
|
+
// bytes so compressed.length stays well above the small cap.
|
|
63
|
+
var src = require("node:crypto").randomBytes(8192);
|
|
64
|
+
var compressed = b.archive.gz(src).toBuffer(); // ~8 KiB (random → no compression)
|
|
65
|
+
var refused = null;
|
|
66
|
+
try {
|
|
67
|
+
await b.archive.read.gz(b.archive.adapters.buffer(compressed), {
|
|
68
|
+
maxDecompressedBytes: 1024,
|
|
69
|
+
}).toBuffer();
|
|
70
|
+
} catch (e) { refused = e; }
|
|
71
|
+
check("archive.read.gz: random-access source over the read cap refused (OOM defense)",
|
|
72
|
+
refused && /source-too-large/.test(refused.code || refused.message));
|
|
73
|
+
// A generous cap still reads it (no over-rejection).
|
|
74
|
+
var ok = await b.archive.read.gz(b.archive.adapters.buffer(compressed), {
|
|
75
|
+
maxDecompressedBytes: 64 * 1024 * 1024,
|
|
76
|
+
}).toBuffer();
|
|
77
|
+
check("archive.read.gz: still reads within the cap", ok.equals(src));
|
|
78
|
+
}
|
|
79
|
+
|
|
57
80
|
async function testTarToGzip() {
|
|
58
81
|
var t = b.archive.tar();
|
|
59
82
|
t.addFile("payload.txt", "hello world");
|
|
@@ -144,6 +167,7 @@ async function run() {
|
|
|
144
167
|
await testGzRoundTrip();
|
|
145
168
|
await testGzBadMagicRefused();
|
|
146
169
|
await testGzBombCapRefused();
|
|
170
|
+
await testGzRandomAccessSourceCapped();
|
|
147
171
|
await testTarToGzip();
|
|
148
172
|
await testBackupTarGzRoundTrip();
|
|
149
173
|
await testBackupTarGzHighRatioRoundTrip();
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.archive.read.tar hardening — uncapped random-access read + PAX `size`
|
|
4
|
+
* NaN walker-desync.
|
|
5
|
+
*
|
|
6
|
+
* - random-access cap: the random-access adapter branch read the WHOLE
|
|
7
|
+
* source with `adapter.range(0, size)` and no ceiling — a multi-GiB
|
|
8
|
+
* source is an OOM lever. It now refuses a source larger than the bomb
|
|
9
|
+
* policy's maxTotalDecompressedBytes.
|
|
10
|
+
* - PAX size NaN: a PAX extended header carrying a non-numeric `size`
|
|
11
|
+
* ("abc") was `parseInt`'d to NaN, which silently bypassed the
|
|
12
|
+
* entry-size bomb check (`NaN > max` is false) AND desynced the 512-byte
|
|
13
|
+
* block walker (`Math.ceil(NaN / 512)` is NaN). It now refuses a
|
|
14
|
+
* malformed PAX size.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
var helpers = require("../helpers");
|
|
18
|
+
var check = helpers.check;
|
|
19
|
+
var b = helpers.b;
|
|
20
|
+
|
|
21
|
+
var BLOCK = 512;
|
|
22
|
+
|
|
23
|
+
// Minimal ustar header builder with a valid POSIX checksum (sum of all 512
|
|
24
|
+
// bytes with the chksum field treated as 8 spaces). Enough for the reader's
|
|
25
|
+
// _parseHeader (magic + chksum validated).
|
|
26
|
+
function _octal(n, width) {
|
|
27
|
+
var s = n.toString(8);
|
|
28
|
+
while (s.length < width - 1) s = "0" + s;
|
|
29
|
+
return s; // caller pads with NUL
|
|
30
|
+
}
|
|
31
|
+
function _header(name, size, typeflag) {
|
|
32
|
+
var buf = Buffer.alloc(BLOCK, 0);
|
|
33
|
+
buf.write(name, 0, Math.min(name.length, 100), "ascii");
|
|
34
|
+
buf.write("0000644\0", 100, 8, "ascii"); // mode
|
|
35
|
+
buf.write("0000000\0", 108, 8, "ascii"); // uid
|
|
36
|
+
buf.write("0000000\0", 116, 8, "ascii"); // gid
|
|
37
|
+
buf.write(_octal(size, 12) + "\0", 124, 12, "ascii"); // size (11 octal + NUL)
|
|
38
|
+
buf.write("00000000000\0", 136, 12, "ascii"); // mtime
|
|
39
|
+
buf.write(typeflag, 156, 1, "ascii"); // typeflag
|
|
40
|
+
buf.write("ustar\0", 257, 6, "ascii"); // magic
|
|
41
|
+
buf.write("00", 263, 2, "ascii"); // version
|
|
42
|
+
// checksum: chksum field = 8 spaces while summing
|
|
43
|
+
for (var i = 148; i < 156; i++) buf[i] = 0x20;
|
|
44
|
+
var sum = 0;
|
|
45
|
+
for (var j = 0; j < BLOCK; j++) sum += buf[j];
|
|
46
|
+
var oct = sum.toString(8);
|
|
47
|
+
while (oct.length < 6) oct = "0" + oct;
|
|
48
|
+
buf.write(oct + "\0 ", 148, 8, "ascii"); // 6 octal + NUL + space
|
|
49
|
+
return buf;
|
|
50
|
+
}
|
|
51
|
+
function _pad512(buf) {
|
|
52
|
+
var rem = buf.length % BLOCK;
|
|
53
|
+
return rem === 0 ? buf : Buffer.concat([buf, Buffer.alloc(BLOCK - rem, 0)]);
|
|
54
|
+
}
|
|
55
|
+
// Build a tar whose first entry is a PAX extended header (typeflag 'x')
|
|
56
|
+
// carrying `size=<paxSizeValue>`, followed by a real file entry.
|
|
57
|
+
function _paxTar(paxSizeValue, fileBody) {
|
|
58
|
+
var rec = " size=" + paxSizeValue + "\n";
|
|
59
|
+
// PAX record = "<total-length> size=<value>\n"; total counts its own digits.
|
|
60
|
+
var total = rec.length;
|
|
61
|
+
while (String(total).length + rec.length !== total) total = String(total).length + rec.length;
|
|
62
|
+
var paxBody = Buffer.from(String(total) + rec, "ascii");
|
|
63
|
+
var paxHdr = _header("PaxHeader/file.txt", paxBody.length, "x");
|
|
64
|
+
var fileHdr = _header("file.txt", fileBody.length, "0");
|
|
65
|
+
return Buffer.concat([
|
|
66
|
+
paxHdr, _pad512(paxBody),
|
|
67
|
+
fileHdr, _pad512(Buffer.from(fileBody)),
|
|
68
|
+
Buffer.alloc(BLOCK * 2, 0), // end-of-archive
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function _codeOf(p) {
|
|
73
|
+
return p.then(function () { return null; }, function (e) { return (e && e.code) || (e && e.message) || "threw"; });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function testRandomAccessReadIsCapped() {
|
|
77
|
+
var t = b.archive.tar();
|
|
78
|
+
t.addFile("a.txt", "hello world payload that is comfortably over a hundred bytes long ............................");
|
|
79
|
+
var bytes = t.toBuffer(); // ~2 KiB, random-access buffer adapter
|
|
80
|
+
// A tiny bomb policy → the 2 KiB source exceeds the read cap → refuse.
|
|
81
|
+
var code = await _codeOf(
|
|
82
|
+
b.archive.read.tar(b.archive.adapters.buffer(bytes), {
|
|
83
|
+
bombPolicy: { maxTotalDecompressedBytes: 100 },
|
|
84
|
+
}).inspect());
|
|
85
|
+
check("read.tar refuses a random-access source over the read cap (OOM defense)",
|
|
86
|
+
code === "archive-tar/source-too-large");
|
|
87
|
+
|
|
88
|
+
// A generous cap still reads it (no over-rejection).
|
|
89
|
+
var entries = await b.archive.read.tar(b.archive.adapters.buffer(bytes), {
|
|
90
|
+
bombPolicy: { maxTotalDecompressedBytes: 64 * 1024 * 1024 },
|
|
91
|
+
}).inspect();
|
|
92
|
+
check("read.tar still reads within the cap", entries.length === 1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function testPaxMalformedSizeRefused() {
|
|
96
|
+
// Truthy-but-non-integer PAX size values that reach the parser (an empty
|
|
97
|
+
// string is correctly treated as "no override" by the `if (pax.size)` guard).
|
|
98
|
+
var malformed = ["abc", "1e9", "100abc", "-5"];
|
|
99
|
+
for (var i = 0; i < malformed.length; i++) {
|
|
100
|
+
var tar = _paxTar(malformed[i], "x");
|
|
101
|
+
var code = await _codeOf(b.archive.read.tar(b.archive.adapters.buffer(tar)).inspect());
|
|
102
|
+
check("read.tar refuses malformed PAX size " + JSON.stringify(malformed[i]) +
|
|
103
|
+
" (no NaN bomb-bypass / walker desync)", code === "archive-tar/bad-pax-size");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function testPaxValidSizeAccepted() {
|
|
108
|
+
// A well-formed PAX size override is honored (no over-rejection).
|
|
109
|
+
var tar = _paxTar("1", "x");
|
|
110
|
+
var entries = await b.archive.read.tar(b.archive.adapters.buffer(tar)).inspect();
|
|
111
|
+
check("read.tar accepts a valid PAX size override", entries.length >= 1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Build a tar whose first entry is a PAX extended header ('x') declaring a
|
|
115
|
+
// LARGE body size, followed by a real file. The per-entry decompressed-bytes
|
|
116
|
+
// cap previously only guarded regular entries (the check sat after the PAX
|
|
117
|
+
// `continue`), so a PAX header body escaped it.
|
|
118
|
+
function _largePaxBodyTar(bodyLen) {
|
|
119
|
+
var paxBody = Buffer.alloc(bodyLen, 0x41); // content irrelevant — cap fires before parse
|
|
120
|
+
var paxHdr = _header("PaxHeader/file.txt", paxBody.length, "x");
|
|
121
|
+
var fileHdr = _header("file.txt", 1, "0");
|
|
122
|
+
return Buffer.concat([
|
|
123
|
+
paxHdr, _pad512(paxBody),
|
|
124
|
+
fileHdr, _pad512(Buffer.from("x")),
|
|
125
|
+
Buffer.alloc(BLOCK * 2, 0),
|
|
126
|
+
]);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function testPaxBodyRespectsPerEntryCap() {
|
|
130
|
+
// A 4 KiB PAX header body under a 1 KiB per-entry cap must be refused — it
|
|
131
|
+
// previously bypassed maxEntryDecompressedBytes (which a regular 4 KiB entry
|
|
132
|
+
// would trip), forcing an over-cap string + record-Object materialization.
|
|
133
|
+
var tar = _largePaxBodyTar(4096);
|
|
134
|
+
var code = await _codeOf(b.archive.read.tar(b.archive.adapters.buffer(tar), {
|
|
135
|
+
bombPolicy: { maxEntryDecompressedBytes: 1024 },
|
|
136
|
+
}).inspect());
|
|
137
|
+
check("read.tar caps an oversized PAX header body (no per-entry-cap bypass)",
|
|
138
|
+
code === "archive-tar/entry-too-large");
|
|
139
|
+
|
|
140
|
+
// A small PAX body under the cap still parses (no over-rejection).
|
|
141
|
+
var okTar = _paxTar("1", "x");
|
|
142
|
+
var entries = await b.archive.read.tar(b.archive.adapters.buffer(okTar), {
|
|
143
|
+
bombPolicy: { maxEntryDecompressedBytes: 1024 },
|
|
144
|
+
}).inspect();
|
|
145
|
+
check("read.tar still accepts a small PAX body under the cap", entries.length >= 1);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function run() {
|
|
149
|
+
await testRandomAccessReadIsCapped();
|
|
150
|
+
await testPaxMalformedSizeRefused();
|
|
151
|
+
await testPaxValidSizeAccepted();
|
|
152
|
+
await testPaxBodyRespectsPerEntryCap();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports = { run: run };
|
|
156
|
+
|
|
157
|
+
if (require.main === module) {
|
|
158
|
+
run().then(function () { console.log("OK"); })
|
|
159
|
+
.catch(function (e) { console.error(e.stack || e); process.exit(1); });
|
|
160
|
+
}
|
|
@@ -89,9 +89,54 @@ function testUnwrapExplicit() {
|
|
|
89
89
|
asn1.readUnsignedInt(inner) === 7);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
function testOidFirstSubidMultibyte() {
|
|
93
|
+
// OID 2.999 — first subidentifier = 40*2 + 999 = 1079, which is 0x88 0x37
|
|
94
|
+
// in base-128 (DER 06 02 88 37). A single-byte first-subid decoder reads
|
|
95
|
+
// it as "2.56.55"; a single-byte encoder truncates "2.999" to one octet
|
|
96
|
+
// and round-trips to "1.15".
|
|
97
|
+
var der = Buffer.from([0x06, 0x02, 0x88, 0x37]);
|
|
98
|
+
check("readOid: 2.999 (multi-byte first subidentifier) decodes correctly",
|
|
99
|
+
asn1.readOid(asn1.readNode(der)) === "2.999");
|
|
100
|
+
var enc = asn1.writeOid("2.999");
|
|
101
|
+
check("writeOid: 2.999 round-trips through readOid",
|
|
102
|
+
asn1.readOid(asn1.readNode(enc)) === "2.999");
|
|
103
|
+
check("writeOid: 2.999 emits the canonical 06 02 88 37",
|
|
104
|
+
Buffer.compare(enc, der) === 0);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function testOidNonMinimalRejected() {
|
|
108
|
+
// 06 04 2a 80 86 48 — the third subidentifier (840) carries a leading
|
|
109
|
+
// 0x80 continuation octet, which X.690 §8.19.2 forbids in DER. It would
|
|
110
|
+
// otherwise alias "1.2.840".
|
|
111
|
+
var der = Buffer.from([0x06, 0x04, 0x2a, 0x80, 0x86, 0x48]);
|
|
112
|
+
var threw = null;
|
|
113
|
+
try { asn1.readOid(asn1.readNode(der)); } catch (e) { threw = e; }
|
|
114
|
+
check("readOid: non-minimal (leading 0x80) subidentifier is refused",
|
|
115
|
+
threw && threw.code === "asn1/oid-non-minimal");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function testHighTagNumberNoOverflow() {
|
|
119
|
+
// 1f ff ff ff ff 7f 00 — high-tag-number form, five 7-bit octets =
|
|
120
|
+
// 0x7ffffffff = 34359738367. A `tag << 7` accumulator overflows 32-bit
|
|
121
|
+
// signed int and yields a negative/wrong tag; base-128 multiplication
|
|
122
|
+
// keeps it correct.
|
|
123
|
+
var node = asn1.readNode(Buffer.from([0x1f, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00]));
|
|
124
|
+
check("readNode: high-tag-number does not overflow to a negative tag",
|
|
125
|
+
node.tag === 34359738367);
|
|
126
|
+
// A genuinely oversized tag (well past 2^53) must be refused, not wrap.
|
|
127
|
+
var huge = Buffer.from([0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00]);
|
|
128
|
+
var threw = null;
|
|
129
|
+
try { asn1.readNode(huge); } catch (e) { threw = e; }
|
|
130
|
+
check("readNode: tag past MAX_SAFE_INTEGER is refused",
|
|
131
|
+
threw && threw.code === "asn1/tag-too-large");
|
|
132
|
+
}
|
|
133
|
+
|
|
92
134
|
async function run() {
|
|
93
135
|
testReadNodeShortFormLength();
|
|
94
136
|
testReadOid();
|
|
137
|
+
testOidFirstSubidMultibyte();
|
|
138
|
+
testOidNonMinimalRejected();
|
|
139
|
+
testHighTagNumberNoOverflow();
|
|
95
140
|
testReadSequence();
|
|
96
141
|
testReadOctetString();
|
|
97
142
|
testReadBitString();
|
|
@@ -40,6 +40,35 @@ function testReadsBufferAndString() {
|
|
|
40
40
|
} finally { dir.cleanup(); }
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
function testWithStat() {
|
|
44
|
+
// #341: withStat returns the bound fd's fstat alongside the bytes, so the
|
|
45
|
+
// mode/owner describe the exact inode read (TOCTOU-free), not a re-stat.
|
|
46
|
+
var dir = _dir();
|
|
47
|
+
try {
|
|
48
|
+
var p = path.join(dir.path, "secret.bin");
|
|
49
|
+
var payload = Buffer.from("with-stat payload", "utf8");
|
|
50
|
+
fs.writeFileSync(p, payload, { mode: 0o600 });
|
|
51
|
+
var r = b.atomicFile.fdSafeReadSync(p, { withStat: true });
|
|
52
|
+
check("fdSafeReadSync withStat: returns { bytes, stat }",
|
|
53
|
+
r && Buffer.isBuffer(r.bytes) && r.bytes.equals(payload) && r.stat && typeof r.stat === "object");
|
|
54
|
+
check("fdSafeReadSync withStat: stat.size matches bytes", r.stat.size === payload.length);
|
|
55
|
+
check("fdSafeReadSync withStat: stat carries mode + ino",
|
|
56
|
+
typeof r.stat.mode === "number" && typeof r.stat.ino === "number");
|
|
57
|
+
// The mode is the bound fd's — on POSIX a 0o600 secret has no group/other
|
|
58
|
+
// bits. Windows doesn't enforce Unix permission bits, so gate the assertion.
|
|
59
|
+
if (process.platform !== "win32") {
|
|
60
|
+
check("fdSafeReadSync withStat: mode reflects 0o600 (no group/other)", (r.stat.mode & 0o077) === 0);
|
|
61
|
+
}
|
|
62
|
+
// withStat composes with encoding: bytes is the decoded string.
|
|
63
|
+
var rEnc = b.atomicFile.fdSafeReadSync(p, { withStat: true, encoding: "utf8" });
|
|
64
|
+
check("fdSafeReadSync withStat + encoding: bytes is a string",
|
|
65
|
+
rEnc.bytes === payload.toString("utf8") && rEnc.stat.size === payload.length);
|
|
66
|
+
// Without withStat, the bare value is still returned (back-compat).
|
|
67
|
+
var bare = b.atomicFile.fdSafeReadSync(p);
|
|
68
|
+
check("fdSafeReadSync: default still returns the bare Buffer", Buffer.isBuffer(bare));
|
|
69
|
+
} finally { dir.cleanup(); }
|
|
70
|
+
}
|
|
71
|
+
|
|
43
72
|
function testMaxBytesCap() {
|
|
44
73
|
var dir = _dir();
|
|
45
74
|
try {
|
|
@@ -134,6 +163,7 @@ function testRefuseSymlinkAndInodeHappyPath() {
|
|
|
134
163
|
|
|
135
164
|
async function run() {
|
|
136
165
|
testReadsBufferAndString();
|
|
166
|
+
testWithStat();
|
|
137
167
|
testMaxBytesCap();
|
|
138
168
|
testExpectedHash();
|
|
139
169
|
testEnoent();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// b.atomicFile.openNoFollowSync — O_NOFOLLOW read-only open for streaming reads
|
|
3
|
+
// that cannot buffer (static-serve range / SRI hashing, object-store download).
|
|
4
|
+
// Opens a path read-only and refuses a symlink at the final component (ELOOP)
|
|
5
|
+
// instead of following it — the streaming counterpart to fdSafeReadSync's
|
|
6
|
+
// refuseSymlink. POSIX-only; on a platform without O_NOFOLLOW the flag is 0.
|
|
7
|
+
|
|
8
|
+
var fs = require("node:fs");
|
|
9
|
+
var os = require("node:os");
|
|
10
|
+
var path = require("node:path");
|
|
11
|
+
var helpers = require("../helpers");
|
|
12
|
+
var b = helpers.b;
|
|
13
|
+
var check = helpers.check;
|
|
14
|
+
|
|
15
|
+
function run() {
|
|
16
|
+
check("b.atomicFile.openNoFollowSync is a function",
|
|
17
|
+
typeof b.atomicFile.openNoFollowSync === "function");
|
|
18
|
+
|
|
19
|
+
var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-nofollow-"));
|
|
20
|
+
try {
|
|
21
|
+
var real = path.join(dir, "real.txt");
|
|
22
|
+
fs.writeFileSync(real, "payload-bytes");
|
|
23
|
+
|
|
24
|
+
// Regular file: opens, returns a numeric fd, reads the bytes, closes.
|
|
25
|
+
var fd = b.atomicFile.openNoFollowSync(real);
|
|
26
|
+
check("returns a numeric fd for a regular file", typeof fd === "number" && fd >= 0);
|
|
27
|
+
var buf = Buffer.alloc(64);
|
|
28
|
+
var n = fs.readSync(fd, buf, 0, buf.length, 0);
|
|
29
|
+
fs.closeSync(fd);
|
|
30
|
+
check("the fd reads the real file's bytes", buf.slice(0, n).toString("utf8") === "payload-bytes");
|
|
31
|
+
|
|
32
|
+
// Stream from the fd (the intended consumer shape).
|
|
33
|
+
var streamed = "";
|
|
34
|
+
// (synchronous read above already proves the fd; the stream shape is
|
|
35
|
+
// exercised by static-serve / object-store integration — here just assert
|
|
36
|
+
// the fd is consumable by createReadStream without double-open.)
|
|
37
|
+
var fd2 = b.atomicFile.openNoFollowSync(real);
|
|
38
|
+
var rs = fs.createReadStream(real, { fd: fd2 });
|
|
39
|
+
rs.on("data", function (c) { streamed += c.toString("utf8"); });
|
|
40
|
+
// Missing file → ENOENT (propagated raw).
|
|
41
|
+
var enoentCode = null;
|
|
42
|
+
try { b.atomicFile.openNoFollowSync(path.join(dir, "nope.txt")); }
|
|
43
|
+
catch (e) { enoentCode = e && e.code; }
|
|
44
|
+
check("missing file throws ENOENT", enoentCode === "ENOENT");
|
|
45
|
+
|
|
46
|
+
// Symlink at the final component → refused with ELOOP (POSIX). On a
|
|
47
|
+
// platform without O_NOFOLLOW (Windows) the flag is 0 and the open follows;
|
|
48
|
+
// symlink creation also often requires privilege there, so gate the assertion.
|
|
49
|
+
if (process.platform !== "win32") {
|
|
50
|
+
var linkPath = path.join(dir, "link.txt");
|
|
51
|
+
var symlinkMade = false;
|
|
52
|
+
try { fs.symlinkSync(real, linkPath); symlinkMade = true; } catch (_e) { /* no symlink priv */ }
|
|
53
|
+
if (symlinkMade) {
|
|
54
|
+
var loopCode = null;
|
|
55
|
+
try { b.atomicFile.openNoFollowSync(linkPath); }
|
|
56
|
+
catch (e) { loopCode = e && e.code; }
|
|
57
|
+
check("symlink final component is refused (ELOOP, not followed)", loopCode === "ELOOP");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return new Promise(function (resolve) {
|
|
62
|
+
rs.on("end", function () {
|
|
63
|
+
check("an fd from openNoFollowSync is consumable by createReadStream",
|
|
64
|
+
streamed === "payload-bytes");
|
|
65
|
+
resolve();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
} finally {
|
|
69
|
+
try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_e) { /* best-effort */ }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = { run: run };
|
|
74
|
+
if (require.main === module) {
|
|
75
|
+
Promise.resolve(run()).then(
|
|
76
|
+
function () { console.log("OK — atomicFile.openNoFollowSync (" + helpers.getChecks() + " checks)"); process.exit(0); },
|
|
77
|
+
function (e) { console.error("FAIL: " + (e && e.message)); process.exit(1); }
|
|
78
|
+
);
|
|
79
|
+
}
|