@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
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.atomicFile.writeExclSync — staged exclusive, symlink-refusing write
|
|
4
|
+
* (CWE-377 insecure temp / CWE-59 symlink follow).
|
|
5
|
+
*
|
|
6
|
+
* The non-renaming sibling of writeSync, for write→verify→rename flows
|
|
7
|
+
* (vault seal/unseal/rotate stage the bytes, re-read + decrypt-verify them,
|
|
8
|
+
* THEN rename into place). It must: write the bytes straight to the given
|
|
9
|
+
* path (no rename of its own), clear any stale leftover first, and refuse to
|
|
10
|
+
* follow a symlink pre-planted at the path — a bare writeFileSync would
|
|
11
|
+
* follow it (arbitrary write) or truncate a planted file.
|
|
12
|
+
*
|
|
13
|
+
* Coverage:
|
|
14
|
+
* - round-trip (bytes land at the path verbatim, bytesWritten correct)
|
|
15
|
+
* - a stale regular file at the path is cleared + replaced (retry-safe)
|
|
16
|
+
* - invalid data type is refused (atomic-file/invalid-data)
|
|
17
|
+
* - a symlink at the path is NOT followed: the link is removed and a fresh
|
|
18
|
+
* regular file is created, the symlink's target left untouched (POSIX;
|
|
19
|
+
* skipped where the platform lacks symlink privilege)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
var fs = require("fs");
|
|
23
|
+
var path = require("path");
|
|
24
|
+
|
|
25
|
+
var helpers = require("../helpers");
|
|
26
|
+
var b = helpers.b;
|
|
27
|
+
var check = helpers.check;
|
|
28
|
+
|
|
29
|
+
function _tmpDir() {
|
|
30
|
+
return b.testing.tempDir("atomicfile-writeexcl");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function testRoundTrips() {
|
|
34
|
+
var dir = _tmpDir();
|
|
35
|
+
try {
|
|
36
|
+
var p = path.join(dir.path, "staged.bin");
|
|
37
|
+
var payload = Buffer.from("staged bytes ✓ multibyte", "utf8");
|
|
38
|
+
var res = b.atomicFile.writeExclSync(p, payload, { fileMode: 0o600 });
|
|
39
|
+
check("writeExclSync: bytesWritten matches", res.bytesWritten === payload.length);
|
|
40
|
+
check("writeExclSync: file written at the path verbatim (no rename)",
|
|
41
|
+
fs.existsSync(p) && fs.readFileSync(p).equals(payload));
|
|
42
|
+
} finally {
|
|
43
|
+
dir.cleanup();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function testClearsStale() {
|
|
48
|
+
var dir = _tmpDir();
|
|
49
|
+
try {
|
|
50
|
+
var p = path.join(dir.path, "staged.bin");
|
|
51
|
+
// An aborted prior run left a stale staging file at the (predictable) path.
|
|
52
|
+
fs.writeFileSync(p, "STALE LEFTOVER FROM A CRASHED RUN", { mode: 0o600 });
|
|
53
|
+
var payload = Buffer.from("fresh", "utf8");
|
|
54
|
+
b.atomicFile.writeExclSync(p, payload, { fileMode: 0o600 });
|
|
55
|
+
check("writeExclSync: stale leftover cleared + replaced (retry-safe)",
|
|
56
|
+
fs.readFileSync(p).equals(payload));
|
|
57
|
+
} finally {
|
|
58
|
+
dir.cleanup();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function testInvalidDataRefused() {
|
|
63
|
+
var dir = _tmpDir();
|
|
64
|
+
try {
|
|
65
|
+
var p = path.join(dir.path, "nope.bin");
|
|
66
|
+
var code = null;
|
|
67
|
+
try { b.atomicFile.writeExclSync(p, { not: "bytes" }); }
|
|
68
|
+
catch (e) { code = e && e.code; }
|
|
69
|
+
check("writeExclSync: non-buffer/string data refused", code === "atomic-file/invalid-data");
|
|
70
|
+
} finally {
|
|
71
|
+
dir.cleanup();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function testSymlinkNotFollowed() {
|
|
76
|
+
var dir = _tmpDir();
|
|
77
|
+
try {
|
|
78
|
+
var victim = path.join(dir.path, "victim-secret.txt");
|
|
79
|
+
fs.writeFileSync(victim, "SECRET", { mode: 0o600 });
|
|
80
|
+
var staged = path.join(dir.path, "staged.tmp");
|
|
81
|
+
|
|
82
|
+
var symlinkOk = true;
|
|
83
|
+
try { fs.symlinkSync(victim, staged); }
|
|
84
|
+
catch (_e) { symlinkOk = false; }
|
|
85
|
+
|
|
86
|
+
if (!symlinkOk) {
|
|
87
|
+
check("writeExclSync: symlink case skipped (platform lacks symlink privilege)", true);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
var payload = Buffer.from("staged-not-through-the-link", "utf8");
|
|
92
|
+
b.atomicFile.writeExclSync(staged, payload, { fileMode: 0o600 });
|
|
93
|
+
|
|
94
|
+
// staged is now a regular file with the new bytes (the link was unlinked,
|
|
95
|
+
// then a fresh file created). O_NOFOLLOW would have failed the open if the
|
|
96
|
+
// link had survived. Take type + bytes from one no-follow fd (no race).
|
|
97
|
+
var fd = fs.openSync(staged, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
|
98
|
+
try {
|
|
99
|
+
var st = fs.fstatSync(fd);
|
|
100
|
+
check("writeExclSync: path is a regular file after write",
|
|
101
|
+
st.isFile() && !st.isSymbolicLink());
|
|
102
|
+
var buf = Buffer.alloc(st.size);
|
|
103
|
+
var got = 0;
|
|
104
|
+
while (got < st.size) {
|
|
105
|
+
var n = fs.readSync(fd, buf, got, st.size - got, null);
|
|
106
|
+
if (n === 0) break;
|
|
107
|
+
got += n;
|
|
108
|
+
}
|
|
109
|
+
check("writeExclSync: path holds the new bytes", buf.equals(payload));
|
|
110
|
+
} finally {
|
|
111
|
+
fs.closeSync(fd);
|
|
112
|
+
}
|
|
113
|
+
check("writeExclSync: symlink target (victim) NOT written through",
|
|
114
|
+
fs.readFileSync(victim, "utf8") === "SECRET");
|
|
115
|
+
} finally {
|
|
116
|
+
dir.cleanup();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function run() {
|
|
121
|
+
testRoundTrips();
|
|
122
|
+
testClearsStale();
|
|
123
|
+
testInvalidDataRefused();
|
|
124
|
+
testSymlinkNotFollowed();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = { run: run };
|
|
128
|
+
|
|
129
|
+
if (require.main === module) {
|
|
130
|
+
run().then(function () { console.log("OK"); })
|
|
131
|
+
.catch(function (e) { console.error(e.stack || e); process.exit(1); });
|
|
132
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.atomicFile.writeStream — streaming sibling of writeSync (CWE-377
|
|
4
|
+
* insecure temporary file / CWE-59 symlink follow).
|
|
5
|
+
*
|
|
6
|
+
* A plain fs.createWriteStream(dest) follows a symlink an attacker planted
|
|
7
|
+
* at `dest` (arbitrary write outside the intended tree) and leaves a
|
|
8
|
+
* half-written file at the canonical name if the source aborts mid-stream.
|
|
9
|
+
* writeStream stages the bytes into a CSPRNG-named sibling temp opened
|
|
10
|
+
* O_EXCL | O_NOFOLLOW, fsyncs, then atomically renames over `dest` — so the
|
|
11
|
+
* file appears at `dest` only after the full stream lands, and a symlink at
|
|
12
|
+
* `dest` is replaced by the rename rather than followed.
|
|
13
|
+
*
|
|
14
|
+
* Coverage:
|
|
15
|
+
* - stream round-trip (exact bytes, bytesWritten, no orphan temp)
|
|
16
|
+
* - a non-stream source is refused (atomic-file/invalid-source)
|
|
17
|
+
* - maxBytes overflow rejects, writes NO file, leaves no orphan temp
|
|
18
|
+
* - a source that errors mid-stream leaves NO file + no orphan temp
|
|
19
|
+
* - a symlink at the destination is replaced by the rename, not followed
|
|
20
|
+
* (POSIX only — Windows without symlink privilege skips that assertion)
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
var fs = require("fs");
|
|
24
|
+
var path = require("path");
|
|
25
|
+
var { Readable } = require("node:stream");
|
|
26
|
+
|
|
27
|
+
var helpers = require("../helpers");
|
|
28
|
+
var b = helpers.b;
|
|
29
|
+
var check = helpers.check;
|
|
30
|
+
|
|
31
|
+
function _tmpDir() {
|
|
32
|
+
return b.testing.tempDir("atomicfile-writestream");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function _orphans(dirPath, base) {
|
|
36
|
+
return fs.readdirSync(dirPath).filter(function (n) {
|
|
37
|
+
return n.indexOf(base + ".tmp-") === 0;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// A Readable that emits one chunk then errors — models an upstream that
|
|
42
|
+
// aborts mid-transfer (dropped socket, decompression bomb tripwire, etc.).
|
|
43
|
+
function _erroringSource() {
|
|
44
|
+
var emitted = false;
|
|
45
|
+
return new Readable({
|
|
46
|
+
read: function () {
|
|
47
|
+
if (!emitted) { emitted = true; this.push(Buffer.from("partial")); }
|
|
48
|
+
else { this.destroy(new Error("upstream aborted mid-stream")); }
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function testStreamRoundTrips() {
|
|
54
|
+
var dir = _tmpDir();
|
|
55
|
+
try {
|
|
56
|
+
var p = path.join(dir.path, "obj.bin");
|
|
57
|
+
var payload = Buffer.from("streamed payload ✓ with multibyte", "utf8");
|
|
58
|
+
var res = await b.atomicFile.writeStream(p, Readable.from([payload]), { fileMode: 0o600 });
|
|
59
|
+
check("writeStream: bytesWritten matches", res.bytesWritten === payload.length);
|
|
60
|
+
check("writeStream: dest exists", fs.existsSync(p));
|
|
61
|
+
check("writeStream: content round-trips", fs.readFileSync(p).equals(payload));
|
|
62
|
+
check("writeStream: no temp file leaked", _orphans(dir.path, "obj.bin").length === 0);
|
|
63
|
+
|
|
64
|
+
// Chunked source reassembles in order.
|
|
65
|
+
var p2 = path.join(dir.path, "chunked.bin");
|
|
66
|
+
var chunks = [Buffer.from("aaa"), Buffer.from("bbb"), Buffer.from("ccc")];
|
|
67
|
+
var res2 = await b.atomicFile.writeStream(p2, Readable.from(chunks));
|
|
68
|
+
check("writeStream: chunked bytesWritten matches", res2.bytesWritten === 9);
|
|
69
|
+
check("writeStream: chunked content in order", fs.readFileSync(p2, "utf8") === "aaabbbccc");
|
|
70
|
+
} finally {
|
|
71
|
+
dir.cleanup();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function testInvalidSourceRefused() {
|
|
76
|
+
var dir = _tmpDir();
|
|
77
|
+
try {
|
|
78
|
+
var p = path.join(dir.path, "nope.bin");
|
|
79
|
+
var code = null;
|
|
80
|
+
try { await b.atomicFile.writeStream(p, Buffer.from("not a stream")); }
|
|
81
|
+
catch (e) { code = e && e.code; }
|
|
82
|
+
check("writeStream: non-stream source refused", code === "atomic-file/invalid-source");
|
|
83
|
+
check("writeStream: nothing written for invalid source", !fs.existsSync(p));
|
|
84
|
+
} finally {
|
|
85
|
+
dir.cleanup();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function testMaxBytesOverflow() {
|
|
90
|
+
var dir = _tmpDir();
|
|
91
|
+
try {
|
|
92
|
+
var p = path.join(dir.path, "toobig.bin");
|
|
93
|
+
var big = Buffer.alloc(1024, 0x41); // 1 KiB
|
|
94
|
+
var code = null;
|
|
95
|
+
try {
|
|
96
|
+
await b.atomicFile.writeStream(p, Readable.from([big]), { maxBytes: 16 });
|
|
97
|
+
} catch (e) { code = e && e.code; }
|
|
98
|
+
check("writeStream: overflow rejected with too-large", code === "atomic-file/too-large");
|
|
99
|
+
check("writeStream: overflow wrote NO file at dest", !fs.existsSync(p));
|
|
100
|
+
check("writeStream: overflow left no orphan temp", _orphans(dir.path, "toobig.bin").length === 0);
|
|
101
|
+
} finally {
|
|
102
|
+
dir.cleanup();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function testMidStreamErrorLeavesNothing() {
|
|
107
|
+
var dir = _tmpDir();
|
|
108
|
+
try {
|
|
109
|
+
var p = path.join(dir.path, "aborted.bin");
|
|
110
|
+
var threw = false;
|
|
111
|
+
try { await b.atomicFile.writeStream(p, _erroringSource()); }
|
|
112
|
+
catch (_e) { threw = true; }
|
|
113
|
+
check("writeStream: mid-stream error propagates", threw);
|
|
114
|
+
check("writeStream: aborted stream wrote NO file at dest", !fs.existsSync(p));
|
|
115
|
+
check("writeStream: aborted stream left no orphan temp", _orphans(dir.path, "aborted.bin").length === 0);
|
|
116
|
+
} finally {
|
|
117
|
+
dir.cleanup();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function testSymlinkAtDestinationReplacedNotFollowed() {
|
|
122
|
+
var dir = _tmpDir();
|
|
123
|
+
try {
|
|
124
|
+
var victim = path.join(dir.path, "outside-victim.bin");
|
|
125
|
+
fs.writeFileSync(victim, "DO NOT OVERWRITE", { mode: 0o600 });
|
|
126
|
+
var dest = path.join(dir.path, "dest-link.bin");
|
|
127
|
+
|
|
128
|
+
var symlinkOk = true;
|
|
129
|
+
try { fs.symlinkSync(victim, dest); }
|
|
130
|
+
catch (_e) { symlinkOk = false; }
|
|
131
|
+
|
|
132
|
+
if (!symlinkOk) {
|
|
133
|
+
check("writeStream: dest-symlink case skipped (platform lacks symlink privilege)", true);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
var payload = Buffer.from("fresh streamed contents", "utf8");
|
|
138
|
+
await b.atomicFile.writeStream(dest, Readable.from([payload]));
|
|
139
|
+
|
|
140
|
+
// Open ONE no-follow fd and take both the type check (fstat) and the
|
|
141
|
+
// bytes from it — no lstat-then-read race (CWE-367). O_NOFOLLOW makes the
|
|
142
|
+
// open fail if dest were still a symlink, so a successful open already
|
|
143
|
+
// proves the rename replaced the link with a regular file.
|
|
144
|
+
var destFd = fs.openSync(dest, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
|
145
|
+
try {
|
|
146
|
+
var fst = fs.fstatSync(destFd);
|
|
147
|
+
check("writeStream: destination is a regular file after write",
|
|
148
|
+
fst.isFile() && !fst.isSymbolicLink());
|
|
149
|
+
var destBytes = Buffer.alloc(fst.size);
|
|
150
|
+
var got = 0;
|
|
151
|
+
while (got < fst.size) {
|
|
152
|
+
var n = fs.readSync(destFd, destBytes, got, fst.size - got, null);
|
|
153
|
+
if (n === 0) break;
|
|
154
|
+
got += n;
|
|
155
|
+
}
|
|
156
|
+
check("writeStream: destination holds the new bytes",
|
|
157
|
+
got === payload.length && destBytes.equals(payload));
|
|
158
|
+
} finally {
|
|
159
|
+
fs.closeSync(destFd);
|
|
160
|
+
}
|
|
161
|
+
check("writeStream: symlink target (victim) untouched",
|
|
162
|
+
fs.readFileSync(victim, "utf8") === "DO NOT OVERWRITE");
|
|
163
|
+
} finally {
|
|
164
|
+
dir.cleanup();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function run() {
|
|
169
|
+
await testStreamRoundTrips();
|
|
170
|
+
await testInvalidSourceRefused();
|
|
171
|
+
await testMaxBytesOverflow();
|
|
172
|
+
await testMidStreamErrorLeavesNothing();
|
|
173
|
+
await testSymlinkAtDestinationReplacedNotFollowed();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = { run: run };
|
|
177
|
+
|
|
178
|
+
if (require.main === module) {
|
|
179
|
+
run().then(function () { console.log("OK"); })
|
|
180
|
+
.catch(function (e) { console.error(e.stack || e); process.exit(1); });
|
|
181
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.auditChain.verifyChain — a corrupted / tampered purge anchor must fail
|
|
4
|
+
* CLOSED with a clear { ok:false, reason }, not throw. The anchor's
|
|
5
|
+
* lastPurgedRowHash seeds prevHash; a non-128-hex value used to flow into
|
|
6
|
+
* computeRowHash, which throws "prevHash must be a 128-char hex" — turning a
|
|
7
|
+
* defensive verify into an uncaught exception. A non-numeric lastPurgedCounter
|
|
8
|
+
* (NaN) would skip nothing and surface as an opaque chain-break.
|
|
9
|
+
*
|
|
10
|
+
* Driven through a mock queryAllAsync (no DB) — the fix returns before the
|
|
11
|
+
* chain-rows query, so the rows mock is irrelevant.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
var helpers = require("../helpers");
|
|
15
|
+
var b = helpers.b;
|
|
16
|
+
var check = helpers.check;
|
|
17
|
+
|
|
18
|
+
function _mockQuery(anchorRow) {
|
|
19
|
+
return function (sql /*, params */) {
|
|
20
|
+
if (typeof sql === "string" && sql.indexOf("purge_anchor") !== -1) {
|
|
21
|
+
return Promise.resolve(anchorRow ? [anchorRow] : []);
|
|
22
|
+
}
|
|
23
|
+
// Chain-rows query — a single plausible row (never reached on a corrupt
|
|
24
|
+
// anchor, but present so the non-corrupt branch could walk).
|
|
25
|
+
return Promise.resolve([]);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function _verify(anchorRow) {
|
|
30
|
+
var threw = null, result = null;
|
|
31
|
+
try { result = await b.auditChain.verifyChain(_mockQuery(anchorRow), "audit_log", {}); }
|
|
32
|
+
catch (e) { threw = e; }
|
|
33
|
+
return { threw: threw, result: result };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function run() {
|
|
37
|
+
// Non-hex lastPurgedRowHash → { ok:false, reason }, NOT a throw.
|
|
38
|
+
var bad1 = await _verify({ lastPurgedRowHash: "GARBAGE-not-hex", lastPurgedCounter: "5" });
|
|
39
|
+
check("corrupted anchor (non-hex hash) does not throw", bad1.threw === null);
|
|
40
|
+
check("corrupted anchor (non-hex hash) → ok:false", bad1.result && bad1.result.ok === false);
|
|
41
|
+
check("corrupted anchor → reason names the anchor",
|
|
42
|
+
bad1.result && /anchor/i.test(bad1.result.reason || ""));
|
|
43
|
+
|
|
44
|
+
// Wrong-length hex hash → also refused.
|
|
45
|
+
var bad2 = await _verify({ lastPurgedRowHash: "abcd", lastPurgedCounter: "1" });
|
|
46
|
+
check("corrupted anchor (short hash) → ok:false, no throw",
|
|
47
|
+
bad2.threw === null && bad2.result && bad2.result.ok === false);
|
|
48
|
+
|
|
49
|
+
// Non-numeric lastPurgedCounter → refused (NaN would skip nothing).
|
|
50
|
+
var validHash = b.auditChain.ZERO_HASH;
|
|
51
|
+
var bad3 = await _verify({ lastPurgedRowHash: validHash, lastPurgedCounter: "not-a-number" });
|
|
52
|
+
check("corrupted anchor (non-numeric counter) → ok:false, no throw",
|
|
53
|
+
bad3.threw === null && bad3.result && bad3.result.ok === false);
|
|
54
|
+
|
|
55
|
+
// No anchor at all (never purged) → verifies the (empty) chain fine, ok:true.
|
|
56
|
+
var none = await _verify(null);
|
|
57
|
+
check("no purge anchor → verifies cleanly (ok:true)", none.threw === null && none.result && none.result.ok === true);
|
|
58
|
+
|
|
59
|
+
console.log("[audit-chain-corrupted-anchor] OK — " + helpers.getChecks() + " checks passed");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = { run: run };
|
|
63
|
+
if (require.main === module) {
|
|
64
|
+
run().then(function () {}, function (e) { console.error("FAIL: " + helpers.formatErr(e)); process.exit(1); });
|
|
65
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.auditChain.verifyChain({ from, to }) — incremental audit-chain verify.
|
|
4
|
+
*
|
|
5
|
+
* verify() advertises from/to (start/end monotonicCounter) for "incremental
|
|
6
|
+
* verify after a known-good checkpoint", but verifyChain ignored them and always
|
|
7
|
+
* walked the whole chain. The fix scopes the walk to [from, to] and anchors
|
|
8
|
+
* `from` on the predecessor row's rowHash so the scoped chain math is correct.
|
|
9
|
+
* Security-critical: an in-range tamper MUST be caught; an out-of-range tamper is
|
|
10
|
+
* (correctly) out of scope for an incremental verify.
|
|
11
|
+
*
|
|
12
|
+
* Driven through a mock queryAllAsync over a synthetic, genuinely-chained set of
|
|
13
|
+
* rows (audit_log is append-only, so a real UPDATE can't forge a tamper).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
var helpers = require("../helpers");
|
|
17
|
+
var b = helpers.b;
|
|
18
|
+
var check = helpers.check;
|
|
19
|
+
|
|
20
|
+
var ac = b.auditChain;
|
|
21
|
+
|
|
22
|
+
function _mkChain(n) {
|
|
23
|
+
var rows = [];
|
|
24
|
+
var prev = ac.ZERO_HASH;
|
|
25
|
+
for (var i = 1; i <= n; i++) {
|
|
26
|
+
var nonce = Buffer.alloc(16, i);
|
|
27
|
+
var fields = { monotonicCounter: i, action: "test.row", outcome: "success" };
|
|
28
|
+
var rowHash = ac.computeRowHash(prev, fields, nonce);
|
|
29
|
+
rows.push(Object.assign({}, fields, { prevHash: prev, rowHash: rowHash, nonce: nonce }));
|
|
30
|
+
prev = rowHash;
|
|
31
|
+
}
|
|
32
|
+
return rows;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function _mockQuery(chainRows) {
|
|
36
|
+
return function (sql) {
|
|
37
|
+
if (typeof sql === "string" && sql.indexOf("purge_anchor") !== -1) return Promise.resolve([]);
|
|
38
|
+
// Return a shallow copy so verifyChain's coerce/filter can't mutate ours.
|
|
39
|
+
return Promise.resolve(chainRows.map(function (r) { return Object.assign({}, r); }));
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function _verify(chainRows, opts) {
|
|
44
|
+
return await ac.verifyChain(_mockQuery(chainRows), "audit_log", opts || {});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function run() {
|
|
48
|
+
var chain = _mkChain(6);
|
|
49
|
+
|
|
50
|
+
check("full verify of a clean chain → ok", (await _verify(chain)).ok === true);
|
|
51
|
+
// Incremental from a mid counter: must anchor on row 3's rowHash. A naive
|
|
52
|
+
// ZERO_HASH anchor would falsely report a prevHash break on row 4.
|
|
53
|
+
check("incremental {from:4} on clean chain → ok (predecessor-anchored)",
|
|
54
|
+
(await _verify(chain, { from: 4 })).ok === true);
|
|
55
|
+
check("bounded {to:3} on clean chain → ok", (await _verify(chain, { to: 3 })).ok === true);
|
|
56
|
+
check("windowed {from:3,to:5} on clean chain → ok", (await _verify(chain, { from: 3, to: 5 })).ok === true);
|
|
57
|
+
|
|
58
|
+
// Tamper row 5 (counter 5): flip a hashed field so its stored rowHash no
|
|
59
|
+
// longer matches the recompute.
|
|
60
|
+
var tampered = chain.map(function (r) { return Object.assign({}, r); });
|
|
61
|
+
tampered[4].outcome = "TAMPERED"; // counter 5
|
|
62
|
+
|
|
63
|
+
check("full verify catches the tamper", (await _verify(tampered)).ok === false);
|
|
64
|
+
check("incremental {from:4} catches in-range tamper (counter 5)",
|
|
65
|
+
(await _verify(tampered, { from: 4 })).ok === false);
|
|
66
|
+
// Counter 5 is below from=6 → out of scope for that incremental verify.
|
|
67
|
+
check("incremental {from:6} → ok (tamper at 5 out of scope)",
|
|
68
|
+
(await _verify(tampered, { from: 6 })).ok === true);
|
|
69
|
+
// Counter 5 is above to=4 → out of the window.
|
|
70
|
+
check("bounded {to:4} → ok (tamper at 5 above the window)",
|
|
71
|
+
(await _verify(tampered, { to: 4 })).ok === true);
|
|
72
|
+
|
|
73
|
+
// rowsVerified reflects the scoped count, not the whole chain.
|
|
74
|
+
var inc = await _verify(chain, { from: 4 });
|
|
75
|
+
check("incremental rowsVerified is the scoped count (3 rows: 4,5,6)", inc.rowsVerified === 3);
|
|
76
|
+
|
|
77
|
+
console.log("[audit-chain-incremental-verify] OK — " + helpers.getChecks() + " checks passed");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = { run: run };
|
|
81
|
+
if (require.main === module) {
|
|
82
|
+
run().then(function () {}, function (e) { console.error("FAIL: " + helpers.formatErr(e)); process.exit(1); });
|
|
83
|
+
}
|
|
@@ -11,7 +11,15 @@ function _fakeAudit(rows) {
|
|
|
11
11
|
var emitted = [];
|
|
12
12
|
return {
|
|
13
13
|
safeEmit: function (event) { emitted.push(event); },
|
|
14
|
-
|
|
14
|
+
// Honor order + limit like the real audit.query so order/limit behavior is
|
|
15
|
+
// testable: sort by recordedAt (asc), flip on order:"desc", then slice.
|
|
16
|
+
query: async function (criteria) {
|
|
17
|
+
criteria = criteria || {};
|
|
18
|
+
var out = rows.slice().sort(function (a, b) { return (a.recordedAt || 0) - (b.recordedAt || 0); });
|
|
19
|
+
if (criteria.order === "desc") out.reverse();
|
|
20
|
+
if (criteria.limit != null) out = out.slice(0, criteria.limit);
|
|
21
|
+
return out;
|
|
22
|
+
},
|
|
15
23
|
_emitted: emitted,
|
|
16
24
|
};
|
|
17
25
|
}
|
|
@@ -124,6 +132,43 @@ async function testNotifyFailure() {
|
|
|
124
132
|
}));
|
|
125
133
|
}
|
|
126
134
|
|
|
135
|
+
async function testCustomClassifyUnknownSeverityIncluded() {
|
|
136
|
+
// A custom classify() returning an UNKNOWN severity must not silently drop
|
|
137
|
+
// the event from the review — it is surfaced (fail safe) so a buggy classify
|
|
138
|
+
// can't hide flagged events. (Was: _severityAtLeast returned false on an
|
|
139
|
+
// unrecognized severity, dropping the hit.)
|
|
140
|
+
var rows = [{ action: "weird.event", outcome: "denied", recordedAt: Date.now() }];
|
|
141
|
+
var fakeAudit = _fakeAudit(rows);
|
|
142
|
+
var review = b.auditDailyReview.create({
|
|
143
|
+
audit: fakeAudit,
|
|
144
|
+
severityThreshold: "warning",
|
|
145
|
+
notify: null,
|
|
146
|
+
classify: function () { return "totally-unknown-severity"; },
|
|
147
|
+
});
|
|
148
|
+
var summary = await review.run();
|
|
149
|
+
check("unknown-severity event surfaced, not silently dropped", summary.hitCount >= 1);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function testQueryLimitKeepsNewest() {
|
|
153
|
+
// B5a: under a queryLimit cap the review must keep the NEWEST events (the
|
|
154
|
+
// actionable ones), not the oldest. An ascending+limit query would keep the
|
|
155
|
+
// oldest and silently drop the newest threshold-exceeding events.
|
|
156
|
+
var nowMs = Date.now();
|
|
157
|
+
var rows = [
|
|
158
|
+
{ action: "test.old1", outcome: "success", recordedAt: nowMs - 3000 }, // info (below warning)
|
|
159
|
+
{ action: "test.old2", outcome: "success", recordedAt: nowMs - 2000 }, // info
|
|
160
|
+
{ action: "honeytoken.tripped", outcome: "denied", recordedAt: nowMs - 100 }, // NEWEST → alert (>= warning)
|
|
161
|
+
];
|
|
162
|
+
var fakeAudit = _fakeAudit(rows);
|
|
163
|
+
var review = b.auditDailyReview.create({
|
|
164
|
+
audit: fakeAudit, severityThreshold: "warning", notify: null, queryLimit: 2,
|
|
165
|
+
});
|
|
166
|
+
var summary = await review.run();
|
|
167
|
+
// Newest-2 kept (honeytoken alert + old2) → the alert is surfaced. With the
|
|
168
|
+
// old asc+limit behavior the oldest-2 (both info) survive → alert dropped → 0.
|
|
169
|
+
check("queryLimit keeps newest events (alert not dropped by cap)", summary.hitCount >= 1);
|
|
170
|
+
}
|
|
171
|
+
|
|
127
172
|
async function run() {
|
|
128
173
|
testSurface();
|
|
129
174
|
await testRunSummary();
|
|
@@ -131,6 +176,8 @@ async function run() {
|
|
|
131
176
|
testPostureRequiresNotify();
|
|
132
177
|
testBadSeverity();
|
|
133
178
|
await testNotifyFailure();
|
|
179
|
+
await testCustomClassifyUnknownSeverityIncluded();
|
|
180
|
+
await testQueryLimitKeepsNewest();
|
|
134
181
|
}
|
|
135
182
|
|
|
136
183
|
module.exports = { run: run };
|
|
@@ -151,9 +151,19 @@ async function run() {
|
|
|
151
151
|
// both signatures valid under their own public key. This proves the
|
|
152
152
|
// history is genuinely cross-key, so the verify path below is the real
|
|
153
153
|
// test (not an artifact of identical keys).
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
// Both checkpoint() calls above are awaited, but on a contended /
|
|
155
|
+
// virtualized filesystem (Windows CI) the just-committed checkpoint row can
|
|
156
|
+
// lag the immediate read-back. Poll for both to be visible (poll, don't
|
|
157
|
+
// sleep) before the exact-count assertion — a genuine missing checkpoint
|
|
158
|
+
// still fails via the waitUntil timeout, and a spurious extra row still
|
|
159
|
+
// fails the `=== 2` below, so this hardens the read without masking a bug.
|
|
160
|
+
var rows = [];
|
|
161
|
+
await helpers.waitUntil(async function () {
|
|
162
|
+
rows = await clusterStorage.executeAll(
|
|
163
|
+
"SELECT * FROM audit_checkpoints ORDER BY atMonotonicCounter ASC"
|
|
164
|
+
);
|
|
165
|
+
return rows.length >= 2;
|
|
166
|
+
}, { timeoutMs: 5000, label: "audit-keyrot: both checkpoints visible in audit_checkpoints" });
|
|
157
167
|
check("two checkpoints are stored", rows.length === 2);
|
|
158
168
|
var c1 = rows[0], c2 = rows[1];
|
|
159
169
|
var p1 = _checkpointPayloadFor(c1);
|