@blamejs/blamejs-shop 0.0.44
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 +87 -0
- package/LICENSE +17 -0
- package/README.md +117 -0
- package/SECURITY.md +139 -0
- package/lib/admin.js +952 -0
- package/lib/analytics.js +267 -0
- package/lib/cart.js +279 -0
- package/lib/catalog-import.js +344 -0
- package/lib/catalog.js +769 -0
- package/lib/checkout.js +320 -0
- package/lib/config.js +151 -0
- package/lib/customers.js +322 -0
- package/lib/email.js +242 -0
- package/lib/externaldb-d1.js +283 -0
- package/lib/index.js +57 -0
- package/lib/inventory-alerts.js +198 -0
- package/lib/newsletter.js +142 -0
- package/lib/order.js +380 -0
- package/lib/payment.js +318 -0
- package/lib/pricing.js +185 -0
- package/lib/r2-bridge.js +169 -0
- package/lib/shipping.js +185 -0
- package/lib/storefront.js +2160 -0
- package/lib/subscriptions.js +410 -0
- package/lib/tax.js +161 -0
- package/lib/theme.js +194 -0
- package/lib/vendor/MANIFEST.json +19 -0
- package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +23 -0
- package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +34 -0
- package/lib/vendor/blamejs/.clusterfuzzlite/project.yaml +16 -0
- package/lib/vendor/blamejs/.dockerignore +45 -0
- package/lib/vendor/blamejs/.gitattributes +42 -0
- package/lib/vendor/blamejs/.github/CODEOWNERS +4 -0
- package/lib/vendor/blamejs/.github/FUNDING.yml +2 -0
- package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/bug_report.md +58 -0
- package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/feature_request.md +99 -0
- package/lib/vendor/blamejs/.github/PULL_REQUEST_TEMPLATE.md +77 -0
- package/lib/vendor/blamejs/.github/dependabot.yml +37 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +148 -0
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +107 -0
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +122 -0
- package/lib/vendor/blamejs/.github/workflows/ci.yml +511 -0
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +50 -0
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +655 -0
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +406 -0
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +101 -0
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +134 -0
- package/lib/vendor/blamejs/.gitignore +102 -0
- package/lib/vendor/blamejs/.gitleaks.toml +166 -0
- package/lib/vendor/blamejs/.hadolint.yaml +18 -0
- package/lib/vendor/blamejs/.npmrc +5 -0
- package/lib/vendor/blamejs/.pinact.yaml +17 -0
- package/lib/vendor/blamejs/ARCHITECTURE.md +158 -0
- package/lib/vendor/blamejs/CHANGELOG.md +1351 -0
- package/lib/vendor/blamejs/CODE_OF_CONDUCT.md +86 -0
- package/lib/vendor/blamejs/CONTRIBUTING.md +156 -0
- package/lib/vendor/blamejs/GOVERNANCE.md +201 -0
- package/lib/vendor/blamejs/LICENSE +201 -0
- package/lib/vendor/blamejs/LTS-CALENDAR.md +29 -0
- package/lib/vendor/blamejs/MIGRATING.md +29 -0
- package/lib/vendor/blamejs/NOTICE +81 -0
- package/lib/vendor/blamejs/README.md +304 -0
- package/lib/vendor/blamejs/SECURITY.md +432 -0
- package/lib/vendor/blamejs/api-snapshot.json +48709 -0
- package/lib/vendor/blamejs/assets/BlameJS_Logo.png +0 -0
- package/lib/vendor/blamejs/assets/BlameJS_Logo.svg +129 -0
- package/lib/vendor/blamejs/bench/README.md +77 -0
- package/lib/vendor/blamejs/bench/_helpers.js +70 -0
- package/lib/vendor/blamejs/bench/baseline.json +183 -0
- package/lib/vendor/blamejs/bench/crypto-hash.bench.js +19 -0
- package/lib/vendor/blamejs/bench/crypto-symmetric.bench.js +28 -0
- package/lib/vendor/blamejs/bench/run.js +140 -0
- package/lib/vendor/blamejs/bench/safe-json.bench.js +31 -0
- package/lib/vendor/blamejs/bin/blamejs.js +13 -0
- package/lib/vendor/blamejs/docker/caddy/Caddyfile +46 -0
- package/lib/vendor/blamejs/docker/coredns/Corefile +37 -0
- package/lib/vendor/blamejs/docker/haproxy/haproxy.cfg +52 -0
- package/lib/vendor/blamejs/docker/init/generate-certs.sh +118 -0
- package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +87 -0
- package/lib/vendor/blamejs/docker/mitmproxy/config.yaml +16 -0
- package/lib/vendor/blamejs/docker/mongo/init-tls.sh +17 -0
- package/lib/vendor/blamejs/docker/mysql/my.cnf +12 -0
- package/lib/vendor/blamejs/docker/nats/nats.conf +33 -0
- package/lib/vendor/blamejs/docker/postgres/init-tls.sh +17 -0
- package/lib/vendor/blamejs/docker/postgres/postgresql.conf +18 -0
- package/lib/vendor/blamejs/docker/rabbitmq/rabbitmq.conf +18 -0
- package/lib/vendor/blamejs/docker/redis/redis.conf +15 -0
- package/lib/vendor/blamejs/docker/squid/squid.conf +24 -0
- package/lib/vendor/blamejs/docker/syslog/syslog-ng.conf +34 -0
- package/lib/vendor/blamejs/docker-compose.test.yml +545 -0
- package/lib/vendor/blamejs/docs/cis-postgres-crosswalk.md +102 -0
- package/lib/vendor/blamejs/docs/cis-sqlite-equivalent.md +92 -0
- package/lib/vendor/blamejs/eslint.config.mjs +204 -0
- package/lib/vendor/blamejs/examples/wiki/Caddyfile +40 -0
- package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +218 -0
- package/lib/vendor/blamejs/examples/wiki/Dockerfile +120 -0
- package/lib/vendor/blamejs/examples/wiki/README.md +157 -0
- package/lib/vendor/blamejs/examples/wiki/cli-snapshot.json +250 -0
- package/lib/vendor/blamejs/examples/wiki/docker-compose.prod.yml +231 -0
- package/lib/vendor/blamejs/examples/wiki/docker-compose.yml +166 -0
- package/lib/vendor/blamejs/examples/wiki/env-snapshot.json +217 -0
- package/lib/vendor/blamejs/examples/wiki/lib/auto-site-entries.js +139 -0
- package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +555 -0
- package/lib/vendor/blamejs/examples/wiki/lib/harvest-cli.js +507 -0
- package/lib/vendor/blamejs/examples/wiki/lib/harvest-env-vars.js +435 -0
- package/lib/vendor/blamejs/examples/wiki/lib/harvest-errors.js +282 -0
- package/lib/vendor/blamejs/examples/wiki/lib/harvest-vendored-deps.js +321 -0
- package/lib/vendor/blamejs/examples/wiki/lib/nav.js +15 -0
- package/lib/vendor/blamejs/examples/wiki/lib/opts-resolver.js +75 -0
- package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +508 -0
- package/lib/vendor/blamejs/examples/wiki/lib/section.js +276 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +587 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +318 -0
- package/lib/vendor/blamejs/examples/wiki/lib/symbol-index.js +122 -0
- package/lib/vendor/blamejs/examples/wiki/migrations/0001-pages-schema.js +74 -0
- package/lib/vendor/blamejs/examples/wiki/package.json +18 -0
- package/lib/vendor/blamejs/examples/wiki/public/img/blamejs-logo.png +0 -0
- package/lib/vendor/blamejs/examples/wiki/public/img/blamejs-logo.svg +129 -0
- package/lib/vendor/blamejs/examples/wiki/public/robots.txt +5 -0
- package/lib/vendor/blamejs/examples/wiki/public/vendor/MANIFEST.json +30 -0
- package/lib/vendor/blamejs/examples/wiki/public/vendor/prism.css +1 -0
- package/lib/vendor/blamejs/examples/wiki/public/vendor/prism.js +15 -0
- package/lib/vendor/blamejs/examples/wiki/public/wiki.css +1250 -0
- package/lib/vendor/blamejs/examples/wiki/routes/admin.js +366 -0
- package/lib/vendor/blamejs/examples/wiki/routes/integration.js +230 -0
- package/lib/vendor/blamejs/examples/wiki/routes/pages.js +266 -0
- package/lib/vendor/blamejs/examples/wiki/scripts/backfill-module-metadata.js +214 -0
- package/lib/vendor/blamejs/examples/wiki/seeders/prod/0001-default-pages.js +35 -0
- package/lib/vendor/blamejs/examples/wiki/seeders/prod/pages/_index.js +34 -0
- package/lib/vendor/blamejs/examples/wiki/seeders/prod/pages/api.js +76 -0
- package/lib/vendor/blamejs/examples/wiki/server.js +129 -0
- package/lib/vendor/blamejs/examples/wiki/site.config.js +197 -0
- package/lib/vendor/blamejs/examples/wiki/snippets/README.md +38 -0
- package/lib/vendor/blamejs/examples/wiki/snippets/auth/password-hash.example.js +15 -0
- package/lib/vendor/blamejs/examples/wiki/src/editor.js +103 -0
- package/lib/vendor/blamejs/examples/wiki/src/wiki.js +349 -0
- package/lib/vendor/blamejs/examples/wiki/test/AUDIT.md +155 -0
- package/lib/vendor/blamejs/examples/wiki/test/codebase-patterns.test.js +594 -0
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +741 -0
- package/lib/vendor/blamejs/examples/wiki/test/find-missing-pages.js +254 -0
- package/lib/vendor/blamejs/examples/wiki/test/integration.js +391 -0
- package/lib/vendor/blamejs/examples/wiki/test/validate-cli-snapshot.js +379 -0
- package/lib/vendor/blamejs/examples/wiki/test/validate-env-snapshot.js +346 -0
- package/lib/vendor/blamejs/examples/wiki/test/validate-nav-coverage.js +212 -0
- package/lib/vendor/blamejs/examples/wiki/test/validate-site-coverage.js +252 -0
- package/lib/vendor/blamejs/examples/wiki/test/validate-source-comment-blocks.js +107 -0
- package/lib/vendor/blamejs/examples/wiki/views/_layout.html +115 -0
- package/lib/vendor/blamejs/examples/wiki/views/admin/api-keys.html +51 -0
- package/lib/vendor/blamejs/examples/wiki/views/admin/dashboard.html +22 -0
- package/lib/vendor/blamejs/examples/wiki/views/admin/edit.html +17 -0
- package/lib/vendor/blamejs/examples/wiki/views/home.html +85 -0
- package/lib/vendor/blamejs/examples/wiki/views/login.html +18 -0
- package/lib/vendor/blamejs/examples/wiki/views/page.html +5 -0
- package/lib/vendor/blamejs/examples/wiki/views/partials/nav.html +13 -0
- package/lib/vendor/blamejs/examples/wiki/views/search.html +19 -0
- package/lib/vendor/blamejs/examples/wiki/wiki.config.js +15 -0
- package/lib/vendor/blamejs/fuzz/README.md +137 -0
- package/lib/vendor/blamejs/fuzz/_expected.js +35 -0
- package/lib/vendor/blamejs/fuzz/guard-agent-registry.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-csv.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/01-basic.csv +3 -0
- package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/02-formula.csv +1 -0
- package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/03-hyperlink.csv +1 -0
- package/lib/vendor/blamejs/fuzz/guard-dsn.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-email.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-email_seed_corpus/01-basic.eml +5 -0
- package/lib/vendor/blamejs/fuzz/guard-envelope.fuzz.js +24 -0
- package/lib/vendor/blamejs/fuzz/guard-event-bus-payload.fuzz.js +24 -0
- package/lib/vendor/blamejs/fuzz/guard-event-bus-topic.fuzz.js +20 -0
- package/lib/vendor/blamejs/fuzz/guard-html.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/01-basic.html +1 -0
- package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/02-script.html +1 -0
- package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/03-event.html +1 -0
- package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/04-jsurl.html +1 -0
- package/lib/vendor/blamejs/fuzz/guard-idempotency-key.fuzz.js +20 -0
- package/lib/vendor/blamejs/fuzz/guard-imap-command.fuzz.js +35 -0
- package/lib/vendor/blamejs/fuzz/guard-jmap.fuzz.js +41 -0
- package/lib/vendor/blamejs/fuzz/guard-json.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/01-basic.json +1 -0
- package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/02-proto.json +1 -0
- package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/03-dupkey.json +1 -0
- package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/04-nan.json +1 -0
- package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/05-bom.json +1 -0
- package/lib/vendor/blamejs/fuzz/guard-list-id.fuzz.js +21 -0
- package/lib/vendor/blamejs/fuzz/guard-list-unsubscribe.fuzz.js +25 -0
- package/lib/vendor/blamejs/fuzz/guard-mail-compose.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-mail-move.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-mail-query.fuzz.js +27 -0
- package/lib/vendor/blamejs/fuzz/guard-mail-reply.fuzz.js +23 -0
- package/lib/vendor/blamejs/fuzz/guard-mail-sieve.fuzz.js +36 -0
- package/lib/vendor/blamejs/fuzz/guard-managesieve-command.fuzz.js +26 -0
- package/lib/vendor/blamejs/fuzz/guard-markdown.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/01-basic.md +2 -0
- package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/02-jsurl.md +1 -0
- package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/03-jsimg.md +1 -0
- package/lib/vendor/blamejs/fuzz/guard-message-id.fuzz.js +26 -0
- package/lib/vendor/blamejs/fuzz/guard-pop3-command.fuzz.js +23 -0
- package/lib/vendor/blamejs/fuzz/guard-posture-chain.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-saga-config.fuzz.js +32 -0
- package/lib/vendor/blamejs/fuzz/guard-smtp-command.fuzz.js +27 -0
- package/lib/vendor/blamejs/fuzz/guard-snapshot-envelope.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-stream-args.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-svg.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-svg_seed_corpus/01-basic.svg +1 -0
- package/lib/vendor/blamejs/fuzz/guard-svg_seed_corpus/02-script.svg +1 -0
- package/lib/vendor/blamejs/fuzz/guard-tenant-id.fuzz.js +20 -0
- package/lib/vendor/blamejs/fuzz/guard-trace-context.fuzz.js +30 -0
- package/lib/vendor/blamejs/fuzz/guard-xml.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-xml_seed_corpus/01-basic.xml +1 -0
- package/lib/vendor/blamejs/fuzz/guard-xml_seed_corpus/02-xxe.xml +1 -0
- package/lib/vendor/blamejs/fuzz/guard-yaml.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/01-basic.yaml +2 -0
- package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/02-anchor.yaml +2 -0
- package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/03-norway.yaml +1 -0
- package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/04-multidoc.yaml +4 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-ini.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-ini_seed_corpus/01-basic.ini +2 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-toml.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-toml_seed_corpus/01-basic.toml +4 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-xml.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-xml_seed_corpus/01-basic.xml +1 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-yaml.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-yaml_seed_corpus/01-basic.yaml +4 -0
- package/lib/vendor/blamejs/fuzz/safe-decompress.fuzz.js +49 -0
- package/lib/vendor/blamejs/fuzz/safe-dns.fuzz.js +29 -0
- package/lib/vendor/blamejs/fuzz/safe-ical.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/safe-icap.fuzz.js +42 -0
- package/lib/vendor/blamejs/fuzz/safe-json.fuzz.js +25 -0
- package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/01-object.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/02-array.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/03-string.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/04-proto.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/05-deep.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-jsonpath.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/01-basic.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/02-filter.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/03-deepscan.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/04-slice.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-mime.fuzz.js +27 -0
- package/lib/vendor/blamejs/fuzz/safe-mount-info.fuzz.js +33 -0
- package/lib/vendor/blamejs/fuzz/safe-sieve.fuzz.js +28 -0
- package/lib/vendor/blamejs/fuzz/safe-smtp.fuzz.js +64 -0
- package/lib/vendor/blamejs/fuzz/safe-url.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/01-basic.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/02-userinfo.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/03-dangerous.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/04-data.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/05-ipv6.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/06-idn.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-vcard.fuzz.js +16 -0
- package/lib/vendor/blamejs/index.js +678 -0
- package/lib/vendor/blamejs/keys/release-pqc-pub.json +7 -0
- package/lib/vendor/blamejs/lib/_test/crypto-fixtures.js +67 -0
- package/lib/vendor/blamejs/lib/a2a-tasks.js +598 -0
- package/lib/vendor/blamejs/lib/a2a.js +407 -0
- package/lib/vendor/blamejs/lib/acme.js +1448 -0
- package/lib/vendor/blamejs/lib/agent-audit.js +45 -0
- package/lib/vendor/blamejs/lib/agent-event-bus.js +382 -0
- package/lib/vendor/blamejs/lib/agent-idempotency.js +497 -0
- package/lib/vendor/blamejs/lib/agent-orchestrator.js +717 -0
- package/lib/vendor/blamejs/lib/agent-posture-chain.js +366 -0
- package/lib/vendor/blamejs/lib/agent-saga.js +321 -0
- package/lib/vendor/blamejs/lib/agent-snapshot.js +676 -0
- package/lib/vendor/blamejs/lib/agent-stream.js +269 -0
- package/lib/vendor/blamejs/lib/agent-tenant.js +632 -0
- package/lib/vendor/blamejs/lib/agent-trace.js +281 -0
- package/lib/vendor/blamejs/lib/ai-adverse-decision.js +184 -0
- package/lib/vendor/blamejs/lib/ai-content-detect.js +268 -0
- package/lib/vendor/blamejs/lib/ai-input.js +201 -0
- package/lib/vendor/blamejs/lib/ai-model-manifest.js +363 -0
- package/lib/vendor/blamejs/lib/ai-pref.js +340 -0
- package/lib/vendor/blamejs/lib/api-key.js +721 -0
- package/lib/vendor/blamejs/lib/api-snapshot.js +458 -0
- package/lib/vendor/blamejs/lib/app-shutdown.js +557 -0
- package/lib/vendor/blamejs/lib/app.js +365 -0
- package/lib/vendor/blamejs/lib/archive.js +547 -0
- package/lib/vendor/blamejs/lib/arg-parser.js +697 -0
- package/lib/vendor/blamejs/lib/argon2-builtin.js +173 -0
- package/lib/vendor/blamejs/lib/asn1-der.js +424 -0
- package/lib/vendor/blamejs/lib/asyncapi-bindings.js +160 -0
- package/lib/vendor/blamejs/lib/asyncapi-traits.js +143 -0
- package/lib/vendor/blamejs/lib/asyncapi.js +575 -0
- package/lib/vendor/blamejs/lib/atomic-file.js +1023 -0
- package/lib/vendor/blamejs/lib/audit-chain.js +266 -0
- package/lib/vendor/blamejs/lib/audit-daily-review.js +389 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +751 -0
- package/lib/vendor/blamejs/lib/audit-tools.js +1113 -0
- package/lib/vendor/blamejs/lib/audit.js +1671 -0
- package/lib/vendor/blamejs/lib/auth/aal.js +169 -0
- package/lib/vendor/blamejs/lib/auth/access-lock.js +220 -0
- package/lib/vendor/blamejs/lib/auth/acr-vocabulary.js +265 -0
- package/lib/vendor/blamejs/lib/auth/ato-kill-switch.js +112 -0
- package/lib/vendor/blamejs/lib/auth/auth-time-tracker.js +111 -0
- package/lib/vendor/blamejs/lib/auth/bot-challenge.js +573 -0
- package/lib/vendor/blamejs/lib/auth/ciba.js +637 -0
- package/lib/vendor/blamejs/lib/auth/dpop.js +516 -0
- package/lib/vendor/blamejs/lib/auth/elevation-grant.js +306 -0
- package/lib/vendor/blamejs/lib/auth/fal.js +229 -0
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +681 -0
- package/lib/vendor/blamejs/lib/auth/jwt-external.js +519 -0
- package/lib/vendor/blamejs/lib/auth/jwt.js +430 -0
- package/lib/vendor/blamejs/lib/auth/lockout.js +449 -0
- package/lib/vendor/blamejs/lib/auth/oauth.js +2141 -0
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +657 -0
- package/lib/vendor/blamejs/lib/auth/oid4vp.js +531 -0
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +600 -0
- package/lib/vendor/blamejs/lib/auth/passkey.js +676 -0
- package/lib/vendor/blamejs/lib/auth/password.js +693 -0
- package/lib/vendor/blamejs/lib/auth/saml.js +2109 -0
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-disclosure.js +95 -0
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-holder.js +225 -0
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-issuer.js +197 -0
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +728 -0
- package/lib/vendor/blamejs/lib/auth/status-list.js +272 -0
- package/lib/vendor/blamejs/lib/auth/step-up-policy.js +335 -0
- package/lib/vendor/blamejs/lib/auth/step-up.js +454 -0
- package/lib/vendor/blamejs/lib/auth-bot-challenge.js +505 -0
- package/lib/vendor/blamejs/lib/auth-header.js +148 -0
- package/lib/vendor/blamejs/lib/backup/bundle.js +265 -0
- package/lib/vendor/blamejs/lib/backup/crypto.js +176 -0
- package/lib/vendor/blamejs/lib/backup/index.js +1001 -0
- package/lib/vendor/blamejs/lib/backup/manifest.js +443 -0
- package/lib/vendor/blamejs/lib/boot-gates.js +174 -0
- package/lib/vendor/blamejs/lib/breach-deadline.js +272 -0
- package/lib/vendor/blamejs/lib/break-glass.js +1753 -0
- package/lib/vendor/blamejs/lib/budr.js +205 -0
- package/lib/vendor/blamejs/lib/bundler.js +461 -0
- package/lib/vendor/blamejs/lib/cache-redis.js +256 -0
- package/lib/vendor/blamejs/lib/cache-status.js +288 -0
- package/lib/vendor/blamejs/lib/cache.js +1331 -0
- package/lib/vendor/blamejs/lib/calendar.js +1240 -0
- package/lib/vendor/blamejs/lib/canonical-json.js +143 -0
- package/lib/vendor/blamejs/lib/cdn-cache-control.js +473 -0
- package/lib/vendor/blamejs/lib/cert.js +763 -0
- package/lib/vendor/blamejs/lib/chain-writer.js +259 -0
- package/lib/vendor/blamejs/lib/circuit-breaker.js +101 -0
- package/lib/vendor/blamejs/lib/cli-helpers.js +237 -0
- package/lib/vendor/blamejs/lib/cli.js +2328 -0
- package/lib/vendor/blamejs/lib/client-hints.js +318 -0
- package/lib/vendor/blamejs/lib/cloud-events.js +277 -0
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +317 -0
- package/lib/vendor/blamejs/lib/cluster-storage.js +351 -0
- package/lib/vendor/blamejs/lib/cluster.js +1017 -0
- package/lib/vendor/blamejs/lib/cms-codec.js +826 -0
- package/lib/vendor/blamejs/lib/codepoint-class.js +262 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-logging.js +190 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +205 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-risk.js +189 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +200 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act.js +821 -0
- package/lib/vendor/blamejs/lib/compliance-eaa.js +204 -0
- package/lib/vendor/blamejs/lib/compliance-sanctions-aliases.js +167 -0
- package/lib/vendor/blamejs/lib/compliance-sanctions-fetcher.js +206 -0
- package/lib/vendor/blamejs/lib/compliance-sanctions-fuzzy.js +297 -0
- package/lib/vendor/blamejs/lib/compliance-sanctions.js +569 -0
- package/lib/vendor/blamejs/lib/compliance.js +1558 -0
- package/lib/vendor/blamejs/lib/config-drift.js +426 -0
- package/lib/vendor/blamejs/lib/config.js +446 -0
- package/lib/vendor/blamejs/lib/consent.js +369 -0
- package/lib/vendor/blamejs/lib/constants.js +209 -0
- package/lib/vendor/blamejs/lib/content-credentials.js +704 -0
- package/lib/vendor/blamejs/lib/cookies.js +560 -0
- package/lib/vendor/blamejs/lib/cra-report.js +299 -0
- package/lib/vendor/blamejs/lib/credential-hash.js +394 -0
- package/lib/vendor/blamejs/lib/crypto-field.js +1017 -0
- package/lib/vendor/blamejs/lib/crypto-hpke-pq.js +187 -0
- package/lib/vendor/blamejs/lib/crypto-hpke.js +256 -0
- package/lib/vendor/blamejs/lib/crypto.js +1908 -0
- package/lib/vendor/blamejs/lib/csp.js +271 -0
- package/lib/vendor/blamejs/lib/csv.js +418 -0
- package/lib/vendor/blamejs/lib/daemon.js +481 -0
- package/lib/vendor/blamejs/lib/dark-patterns.js +488 -0
- package/lib/vendor/blamejs/lib/data-act.js +328 -0
- package/lib/vendor/blamejs/lib/db-collection.js +587 -0
- package/lib/vendor/blamejs/lib/db-declare-row-policy.js +267 -0
- package/lib/vendor/blamejs/lib/db-declare-view.js +420 -0
- package/lib/vendor/blamejs/lib/db-file-lifecycle.js +333 -0
- package/lib/vendor/blamejs/lib/db-query.js +802 -0
- package/lib/vendor/blamejs/lib/db-role-context.js +50 -0
- package/lib/vendor/blamejs/lib/db-schema.js +322 -0
- package/lib/vendor/blamejs/lib/db.js +3111 -0
- package/lib/vendor/blamejs/lib/dbsc.js +299 -0
- package/lib/vendor/blamejs/lib/ddl-change-control.js +523 -0
- package/lib/vendor/blamejs/lib/deprecate.js +377 -0
- package/lib/vendor/blamejs/lib/dev.js +405 -0
- package/lib/vendor/blamejs/lib/dora.js +402 -0
- package/lib/vendor/blamejs/lib/dr-runbook.js +368 -0
- package/lib/vendor/blamejs/lib/dsr.js +1188 -0
- package/lib/vendor/blamejs/lib/dual-control.js +526 -0
- package/lib/vendor/blamejs/lib/early-hints.js +212 -0
- package/lib/vendor/blamejs/lib/error-page.js +420 -0
- package/lib/vendor/blamejs/lib/events.js +214 -0
- package/lib/vendor/blamejs/lib/external-db-migrate.js +659 -0
- package/lib/vendor/blamejs/lib/external-db.js +1877 -0
- package/lib/vendor/blamejs/lib/fapi2.js +394 -0
- package/lib/vendor/blamejs/lib/fda-21cfr11.js +395 -0
- package/lib/vendor/blamejs/lib/fdx.js +370 -0
- package/lib/vendor/blamejs/lib/fedcm.js +264 -0
- package/lib/vendor/blamejs/lib/file-type.js +360 -0
- package/lib/vendor/blamejs/lib/file-upload.js +1256 -0
- package/lib/vendor/blamejs/lib/flag-cache.js +136 -0
- package/lib/vendor/blamejs/lib/flag-evaluation-context.js +135 -0
- package/lib/vendor/blamejs/lib/flag-providers.js +279 -0
- package/lib/vendor/blamejs/lib/flag-targeting.js +210 -0
- package/lib/vendor/blamejs/lib/flag.js +346 -0
- package/lib/vendor/blamejs/lib/forms.js +525 -0
- package/lib/vendor/blamejs/lib/framework-error.js +724 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +845 -0
- package/lib/vendor/blamejs/lib/framework-sha1-hibp.js +34 -0
- package/lib/vendor/blamejs/lib/fsm.js +469 -0
- package/lib/vendor/blamejs/lib/gate-contract.js +1661 -0
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +261 -0
- package/lib/vendor/blamejs/lib/graphql-federation.js +234 -0
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +179 -0
- package/lib/vendor/blamejs/lib/guard-all.js +555 -0
- package/lib/vendor/blamejs/lib/guard-archive.js +901 -0
- package/lib/vendor/blamejs/lib/guard-auth.js +451 -0
- package/lib/vendor/blamejs/lib/guard-cidr.js +676 -0
- package/lib/vendor/blamejs/lib/guard-csv.js +1176 -0
- package/lib/vendor/blamejs/lib/guard-domain.js +814 -0
- package/lib/vendor/blamejs/lib/guard-dsn.js +382 -0
- package/lib/vendor/blamejs/lib/guard-email.js +951 -0
- package/lib/vendor/blamejs/lib/guard-envelope.js +294 -0
- package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +217 -0
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +150 -0
- package/lib/vendor/blamejs/lib/guard-filename.js +956 -0
- package/lib/vendor/blamejs/lib/guard-graphql.js +731 -0
- package/lib/vendor/blamejs/lib/guard-html-wcag-aria.js +164 -0
- package/lib/vendor/blamejs/lib/guard-html-wcag-forms.js +144 -0
- package/lib/vendor/blamejs/lib/guard-html-wcag-tables.js +154 -0
- package/lib/vendor/blamejs/lib/guard-html-wcag-tagwalk.js +44 -0
- package/lib/vendor/blamejs/lib/guard-html-wcag.js +470 -0
- package/lib/vendor/blamejs/lib/guard-html.js +1209 -0
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +151 -0
- package/lib/vendor/blamejs/lib/guard-image.js +584 -0
- package/lib/vendor/blamejs/lib/guard-imap-command.js +337 -0
- package/lib/vendor/blamejs/lib/guard-jmap.js +321 -0
- package/lib/vendor/blamejs/lib/guard-json.js +935 -0
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +512 -0
- package/lib/vendor/blamejs/lib/guard-jwt.js +772 -0
- package/lib/vendor/blamejs/lib/guard-list-id.js +318 -0
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +412 -0
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +282 -0
- package/lib/vendor/blamejs/lib/guard-mail-move.js +202 -0
- package/lib/vendor/blamejs/lib/guard-mail-query.js +310 -0
- package/lib/vendor/blamejs/lib/guard-mail-reply.js +172 -0
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +207 -0
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +566 -0
- package/lib/vendor/blamejs/lib/guard-markdown.js +768 -0
- package/lib/vendor/blamejs/lib/guard-message-id.js +267 -0
- package/lib/vendor/blamejs/lib/guard-mime.js +609 -0
- package/lib/vendor/blamejs/lib/guard-oauth.js +650 -0
- package/lib/vendor/blamejs/lib/guard-pdf.js +569 -0
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +317 -0
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +201 -0
- package/lib/vendor/blamejs/lib/guard-regex.js +632 -0
- package/lib/vendor/blamejs/lib/guard-saga-config.js +157 -0
- package/lib/vendor/blamejs/lib/guard-shell.js +522 -0
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +594 -0
- package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +168 -0
- package/lib/vendor/blamejs/lib/guard-stream-args.js +166 -0
- package/lib/vendor/blamejs/lib/guard-svg.js +1163 -0
- package/lib/vendor/blamejs/lib/guard-template.js +490 -0
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +138 -0
- package/lib/vendor/blamejs/lib/guard-time.js +586 -0
- package/lib/vendor/blamejs/lib/guard-trace-context.js +172 -0
- package/lib/vendor/blamejs/lib/guard-uuid.js +548 -0
- package/lib/vendor/blamejs/lib/guard-xml.js +666 -0
- package/lib/vendor/blamejs/lib/guard-yaml.js +726 -0
- package/lib/vendor/blamejs/lib/hal.js +125 -0
- package/lib/vendor/blamejs/lib/handlers.js +350 -0
- package/lib/vendor/blamejs/lib/honeytoken.js +168 -0
- package/lib/vendor/blamejs/lib/html-balance.js +347 -0
- package/lib/vendor/blamejs/lib/http-client-cache.js +923 -0
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +519 -0
- package/lib/vendor/blamejs/lib/http-client.js +2152 -0
- package/lib/vendor/blamejs/lib/http-message-signature.js +589 -0
- package/lib/vendor/blamejs/lib/http2-teardown.js +34 -0
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +398 -0
- package/lib/vendor/blamejs/lib/i18n.js +931 -0
- package/lib/vendor/blamejs/lib/iab-mspa.js +257 -0
- package/lib/vendor/blamejs/lib/iab-tcf.js +461 -0
- package/lib/vendor/blamejs/lib/importmap-integrity.js +90 -0
- package/lib/vendor/blamejs/lib/inbox.js +435 -0
- package/lib/vendor/blamejs/lib/incident-report.js +314 -0
- package/lib/vendor/blamejs/lib/ip-utils.js +102 -0
- package/lib/vendor/blamejs/lib/jobs.js +185 -0
- package/lib/vendor/blamejs/lib/jose-jwe-experimental.js +228 -0
- package/lib/vendor/blamejs/lib/jsonapi.js +230 -0
- package/lib/vendor/blamejs/lib/keychain.js +865 -0
- package/lib/vendor/blamejs/lib/lazy-require.js +48 -0
- package/lib/vendor/blamejs/lib/legal-hold.js +374 -0
- package/lib/vendor/blamejs/lib/local-db-thin.js +321 -0
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +369 -0
- package/lib/vendor/blamejs/lib/log-stream-local.js +146 -0
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +410 -0
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +286 -0
- package/lib/vendor/blamejs/lib/log-stream-syslog.js +310 -0
- package/lib/vendor/blamejs/lib/log-stream-webhook.js +199 -0
- package/lib/vendor/blamejs/lib/log-stream.js +584 -0
- package/lib/vendor/blamejs/lib/log.js +625 -0
- package/lib/vendor/blamejs/lib/lro.js +200 -0
- package/lib/vendor/blamejs/lib/mail-agent.js +786 -0
- package/lib/vendor/blamejs/lib/mail-arc-sign.js +417 -0
- package/lib/vendor/blamejs/lib/mail-arf.js +343 -0
- package/lib/vendor/blamejs/lib/mail-auth.js +2144 -0
- package/lib/vendor/blamejs/lib/mail-bimi.js +1047 -0
- package/lib/vendor/blamejs/lib/mail-bounce.js +955 -0
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +1286 -0
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +789 -0
- package/lib/vendor/blamejs/lib/mail-crypto.js +108 -0
- package/lib/vendor/blamejs/lib/mail-dav.js +1224 -0
- package/lib/vendor/blamejs/lib/mail-deploy.js +1119 -0
- package/lib/vendor/blamejs/lib/mail-dkim.js +1250 -0
- package/lib/vendor/blamejs/lib/mail-greylist.js +448 -0
- package/lib/vendor/blamejs/lib/mail-helo.js +473 -0
- package/lib/vendor/blamejs/lib/mail-journal.js +435 -0
- package/lib/vendor/blamejs/lib/mail-mdn.js +424 -0
- package/lib/vendor/blamejs/lib/mail-rbl.js +392 -0
- package/lib/vendor/blamejs/lib/mail-require-tls.js +198 -0
- package/lib/vendor/blamejs/lib/mail-scan.js +502 -0
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +629 -0
- package/lib/vendor/blamejs/lib/mail-server-imap.js +1858 -0
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +1565 -0
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +908 -0
- package/lib/vendor/blamejs/lib/mail-server-mx.js +969 -0
- package/lib/vendor/blamejs/lib/mail-server-pop3.js +915 -0
- package/lib/vendor/blamejs/lib/mail-server-rate-limit.js +315 -0
- package/lib/vendor/blamejs/lib/mail-server-registry.js +378 -0
- package/lib/vendor/blamejs/lib/mail-server-submission.js +1396 -0
- package/lib/vendor/blamejs/lib/mail-server-tls.js +445 -0
- package/lib/vendor/blamejs/lib/mail-sieve.js +557 -0
- package/lib/vendor/blamejs/lib/mail-spam-score.js +284 -0
- package/lib/vendor/blamejs/lib/mail-srs.js +248 -0
- package/lib/vendor/blamejs/lib/mail-store-fts.js +394 -0
- package/lib/vendor/blamejs/lib/mail-store.js +929 -0
- package/lib/vendor/blamejs/lib/mail-unsubscribe.js +400 -0
- package/lib/vendor/blamejs/lib/mail.js +1971 -0
- package/lib/vendor/blamejs/lib/mcp-tool-registry.js +473 -0
- package/lib/vendor/blamejs/lib/mcp.js +950 -0
- package/lib/vendor/blamejs/lib/metrics.js +1503 -0
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +177 -0
- package/lib/vendor/blamejs/lib/middleware/ai-act-disclosure.js +203 -0
- package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +981 -0
- package/lib/vendor/blamejs/lib/middleware/assetlinks.js +137 -0
- package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +171 -0
- package/lib/vendor/blamejs/lib/middleware/attach-user.js +220 -0
- package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +293 -0
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +1519 -0
- package/lib/vendor/blamejs/lib/middleware/bot-disclose.js +183 -0
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +217 -0
- package/lib/vendor/blamejs/lib/middleware/clear-site-data.js +122 -0
- package/lib/vendor/blamejs/lib/middleware/compose-pipeline.js +355 -0
- package/lib/vendor/blamejs/lib/middleware/compression.js +489 -0
- package/lib/vendor/blamejs/lib/middleware/cookies.js +130 -0
- package/lib/vendor/blamejs/lib/middleware/cors.js +386 -0
- package/lib/vendor/blamejs/lib/middleware/csp-nonce.js +388 -0
- package/lib/vendor/blamejs/lib/middleware/csp-report.js +167 -0
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +499 -0
- package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +243 -0
- package/lib/vendor/blamejs/lib/middleware/db-role-for.js +304 -0
- package/lib/vendor/blamejs/lib/middleware/dpop.js +402 -0
- package/lib/vendor/blamejs/lib/middleware/error-handler.js +69 -0
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +168 -0
- package/lib/vendor/blamejs/lib/middleware/flag-context.js +110 -0
- package/lib/vendor/blamejs/lib/middleware/gpc.js +153 -0
- package/lib/vendor/blamejs/lib/middleware/headers.js +242 -0
- package/lib/vendor/blamejs/lib/middleware/health.js +438 -0
- package/lib/vendor/blamejs/lib/middleware/host-allowlist.js +189 -0
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +964 -0
- package/lib/vendor/blamejs/lib/middleware/index.js +183 -0
- package/lib/vendor/blamejs/lib/middleware/nel.js +214 -0
- package/lib/vendor/blamejs/lib/middleware/network-allowlist.js +237 -0
- package/lib/vendor/blamejs/lib/middleware/no-cache.js +106 -0
- package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +177 -0
- package/lib/vendor/blamejs/lib/middleware/protected-resource-metadata.js +277 -0
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +556 -0
- package/lib/vendor/blamejs/lib/middleware/request-id.js +79 -0
- package/lib/vendor/blamejs/lib/middleware/request-log.js +205 -0
- package/lib/vendor/blamejs/lib/middleware/require-aal.js +138 -0
- package/lib/vendor/blamejs/lib/middleware/require-auth.js +144 -0
- package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +290 -0
- package/lib/vendor/blamejs/lib/middleware/require-content-type.js +113 -0
- package/lib/vendor/blamejs/lib/middleware/require-methods.js +97 -0
- package/lib/vendor/blamejs/lib/middleware/require-mtls.js +212 -0
- package/lib/vendor/blamejs/lib/middleware/require-step-up.js +226 -0
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +375 -0
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +285 -0
- package/lib/vendor/blamejs/lib/middleware/security-txt.js +170 -0
- package/lib/vendor/blamejs/lib/middleware/span-http-server.js +280 -0
- package/lib/vendor/blamejs/lib/middleware/speculation-rules.js +323 -0
- package/lib/vendor/blamejs/lib/middleware/sse.js +200 -0
- package/lib/vendor/blamejs/lib/middleware/trace-log-correlation.js +167 -0
- package/lib/vendor/blamejs/lib/middleware/trace-propagate.js +148 -0
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +749 -0
- package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +164 -0
- package/lib/vendor/blamejs/lib/migration-files.js +37 -0
- package/lib/vendor/blamejs/lib/migrations.js +385 -0
- package/lib/vendor/blamejs/lib/mime-parse.js +198 -0
- package/lib/vendor/blamejs/lib/money.js +699 -0
- package/lib/vendor/blamejs/lib/mtls-ca.js +572 -0
- package/lib/vendor/blamejs/lib/mtls-engine-default.js +501 -0
- package/lib/vendor/blamejs/lib/network-byte-quota.js +308 -0
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +533 -0
- package/lib/vendor/blamejs/lib/network-dns.js +1930 -0
- package/lib/vendor/blamejs/lib/network-heartbeat.js +425 -0
- package/lib/vendor/blamejs/lib/network-nts.js +574 -0
- package/lib/vendor/blamejs/lib/network-proxy.js +265 -0
- package/lib/vendor/blamejs/lib/network-smtp-policy.js +836 -0
- package/lib/vendor/blamejs/lib/network-tls.js +3126 -0
- package/lib/vendor/blamejs/lib/network.js +346 -0
- package/lib/vendor/blamejs/lib/nis2-report.js +181 -0
- package/lib/vendor/blamejs/lib/nist-crosswalk.js +293 -0
- package/lib/vendor/blamejs/lib/nonce-store.js +177 -0
- package/lib/vendor/blamejs/lib/notify.js +683 -0
- package/lib/vendor/blamejs/lib/ntp-check.js +458 -0
- package/lib/vendor/blamejs/lib/numeric-bounds.js +111 -0
- package/lib/vendor/blamejs/lib/numeric-checks.js +40 -0
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +349 -0
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +488 -0
- package/lib/vendor/blamejs/lib/object-store/gcs-bucket-ops.js +351 -0
- package/lib/vendor/blamejs/lib/object-store/gcs.js +515 -0
- package/lib/vendor/blamejs/lib/object-store/http-put.js +153 -0
- package/lib/vendor/blamejs/lib/object-store/http-request.js +38 -0
- package/lib/vendor/blamejs/lib/object-store/index.js +197 -0
- package/lib/vendor/blamejs/lib/object-store/local.js +163 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +1133 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +957 -0
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +420 -0
- package/lib/vendor/blamejs/lib/observability-tracer.js +395 -0
- package/lib/vendor/blamejs/lib/observability.js +720 -0
- package/lib/vendor/blamejs/lib/openapi-paths-builder.js +248 -0
- package/lib/vendor/blamejs/lib/openapi-schema-walk.js +192 -0
- package/lib/vendor/blamejs/lib/openapi-security.js +169 -0
- package/lib/vendor/blamejs/lib/openapi-yaml.js +154 -0
- package/lib/vendor/blamejs/lib/openapi.js +489 -0
- package/lib/vendor/blamejs/lib/otel-export.js +278 -0
- package/lib/vendor/blamejs/lib/outbox.js +547 -0
- package/lib/vendor/blamejs/lib/pagination.js +542 -0
- package/lib/vendor/blamejs/lib/parsers/index.js +91 -0
- package/lib/vendor/blamejs/lib/parsers/safe-env.js +642 -0
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +293 -0
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +784 -0
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +390 -0
- package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +1015 -0
- package/lib/vendor/blamejs/lib/permissions.js +793 -0
- package/lib/vendor/blamejs/lib/pick.js +105 -0
- package/lib/vendor/blamejs/lib/pqc-agent.js +351 -0
- package/lib/vendor/blamejs/lib/pqc-gate.js +279 -0
- package/lib/vendor/blamejs/lib/pqc-software.js +271 -0
- package/lib/vendor/blamejs/lib/problem-details.js +482 -0
- package/lib/vendor/blamejs/lib/process-spawn.js +196 -0
- package/lib/vendor/blamejs/lib/promise-pool.js +162 -0
- package/lib/vendor/blamejs/lib/protobuf-encoder.js +190 -0
- package/lib/vendor/blamejs/lib/protocol-dispatcher.js +161 -0
- package/lib/vendor/blamejs/lib/public-suffix.js +403 -0
- package/lib/vendor/blamejs/lib/pubsub-cluster.js +154 -0
- package/lib/vendor/blamejs/lib/pubsub-redis.js +167 -0
- package/lib/vendor/blamejs/lib/pubsub.js +463 -0
- package/lib/vendor/blamejs/lib/queue-local.js +476 -0
- package/lib/vendor/blamejs/lib/queue-redis.js +745 -0
- package/lib/vendor/blamejs/lib/queue-sqs.js +319 -0
- package/lib/vendor/blamejs/lib/queue.js +1016 -0
- package/lib/vendor/blamejs/lib/redact.js +1007 -0
- package/lib/vendor/blamejs/lib/redis-client.js +520 -0
- package/lib/vendor/blamejs/lib/render.js +285 -0
- package/lib/vendor/blamejs/lib/request-helpers.js +767 -0
- package/lib/vendor/blamejs/lib/resource-access-lock.js +116 -0
- package/lib/vendor/blamejs/lib/restore-bundle.js +340 -0
- package/lib/vendor/blamejs/lib/restore-rollback.js +365 -0
- package/lib/vendor/blamejs/lib/restore.js +409 -0
- package/lib/vendor/blamejs/lib/retention.js +640 -0
- package/lib/vendor/blamejs/lib/retry.js +523 -0
- package/lib/vendor/blamejs/lib/router.js +1289 -0
- package/lib/vendor/blamejs/lib/safe-async.js +1184 -0
- package/lib/vendor/blamejs/lib/safe-buffer.js +562 -0
- package/lib/vendor/blamejs/lib/safe-decompress.js +297 -0
- package/lib/vendor/blamejs/lib/safe-dns.js +665 -0
- package/lib/vendor/blamejs/lib/safe-ical.js +634 -0
- package/lib/vendor/blamejs/lib/safe-icap.js +502 -0
- package/lib/vendor/blamejs/lib/safe-json.js +946 -0
- package/lib/vendor/blamejs/lib/safe-jsonpath.js +285 -0
- package/lib/vendor/blamejs/lib/safe-mime.js +831 -0
- package/lib/vendor/blamejs/lib/safe-mount-info.js +306 -0
- package/lib/vendor/blamejs/lib/safe-path.js +254 -0
- package/lib/vendor/blamejs/lib/safe-redirect.js +106 -0
- package/lib/vendor/blamejs/lib/safe-schema.js +1810 -0
- package/lib/vendor/blamejs/lib/safe-sieve.js +684 -0
- package/lib/vendor/blamejs/lib/safe-smtp.js +185 -0
- package/lib/vendor/blamejs/lib/safe-sql.js +363 -0
- package/lib/vendor/blamejs/lib/safe-url.js +428 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +473 -0
- package/lib/vendor/blamejs/lib/sandbox-worker.js +135 -0
- package/lib/vendor/blamejs/lib/sandbox.js +358 -0
- package/lib/vendor/blamejs/lib/scheduler.js +827 -0
- package/lib/vendor/blamejs/lib/sd-notify.js +269 -0
- package/lib/vendor/blamejs/lib/sec-cyber.js +214 -0
- package/lib/vendor/blamejs/lib/security-assert.js +395 -0
- package/lib/vendor/blamejs/lib/seeders.js +620 -0
- package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +309 -0
- package/lib/vendor/blamejs/lib/self-update.js +804 -0
- package/lib/vendor/blamejs/lib/server-timing.js +174 -0
- package/lib/vendor/blamejs/lib/session-device-binding.js +431 -0
- package/lib/vendor/blamejs/lib/session-stores.js +138 -0
- package/lib/vendor/blamejs/lib/session.js +1162 -0
- package/lib/vendor/blamejs/lib/slug.js +381 -0
- package/lib/vendor/blamejs/lib/sse.js +349 -0
- package/lib/vendor/blamejs/lib/ssrf-guard.js +792 -0
- package/lib/vendor/blamejs/lib/standard-webhooks.js +183 -0
- package/lib/vendor/blamejs/lib/static.js +1249 -0
- package/lib/vendor/blamejs/lib/storage.js +1272 -0
- package/lib/vendor/blamejs/lib/stream-throttle.js +235 -0
- package/lib/vendor/blamejs/lib/structured-fields.js +244 -0
- package/lib/vendor/blamejs/lib/subject.js +667 -0
- package/lib/vendor/blamejs/lib/tcpa-10dlc.js +175 -0
- package/lib/vendor/blamejs/lib/template.js +931 -0
- package/lib/vendor/blamejs/lib/tenant-quota.js +545 -0
- package/lib/vendor/blamejs/lib/test-harness.js +275 -0
- package/lib/vendor/blamejs/lib/testing.js +1185 -0
- package/lib/vendor/blamejs/lib/time.js +578 -0
- package/lib/vendor/blamejs/lib/tls-exporter.js +239 -0
- package/lib/vendor/blamejs/lib/totp.js +318 -0
- package/lib/vendor/blamejs/lib/tracing.js +546 -0
- package/lib/vendor/blamejs/lib/uuid.js +207 -0
- package/lib/vendor/blamejs/lib/validate-opts.js +381 -0
- package/lib/vendor/blamejs/lib/vault/index.js +638 -0
- package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +311 -0
- package/lib/vendor/blamejs/lib/vault/passphrase-source.js +198 -0
- package/lib/vendor/blamejs/lib/vault/rotate.js +803 -0
- package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +471 -0
- package/lib/vendor/blamejs/lib/vault/wrap.js +296 -0
- package/lib/vendor/blamejs/lib/vault-aad.js +259 -0
- package/lib/vendor/blamejs/lib/vendor/.vendor-data-pubkey +4 -0
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +161 -0
- package/lib/vendor/blamejs/lib/vendor/bimi-trust-anchors.data.js +68 -0
- package/lib/vendor/blamejs/lib/vendor/bimi-trust-anchors.pem +33 -0
- package/lib/vendor/blamejs/lib/vendor/common-passwords-top-10000.data.js +1325 -0
- package/lib/vendor/blamejs/lib/vendor/common-passwords-top-10000.txt +10002 -0
- package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +9 -0
- package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +18 -0
- package/lib/vendor/blamejs/lib/vendor/pki.cjs +181 -0
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +16382 -0
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5881 -0
- package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +328 -0
- package/lib/vendor/blamejs/lib/vendor/vendor-data-pubkey.js +16 -0
- package/lib/vendor/blamejs/lib/vendor-data.js +520 -0
- package/lib/vendor/blamejs/lib/vex.js +630 -0
- package/lib/vendor/blamejs/lib/watcher.js +608 -0
- package/lib/vendor/blamejs/lib/web-push-vapid.js +322 -0
- package/lib/vendor/blamejs/lib/webhook.js +977 -0
- package/lib/vendor/blamejs/lib/websocket-channels.js +327 -0
- package/lib/vendor/blamejs/lib/websocket.js +1561 -0
- package/lib/vendor/blamejs/lib/wiki-concepts.js +338 -0
- package/lib/vendor/blamejs/lib/worker-pool.js +464 -0
- package/lib/vendor/blamejs/lib/ws-client.js +978 -0
- package/lib/vendor/blamejs/lib/xml-c14n.js +506 -0
- package/lib/vendor/blamejs/memory/specs/node-26-map-getorinsert-migration.md +164 -0
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +19 -0
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/README.md +88 -0
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/build.sh +26 -0
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/project.yaml +28 -0
- package/lib/vendor/blamejs/package.json +81 -0
- package/lib/vendor/blamejs/release-notes/v0.0.x.json +310 -0
- package/lib/vendor/blamejs/release-notes/v0.1.x.json +1798 -0
- package/lib/vendor/blamejs/release-notes/v0.10.x.json +1288 -0
- package/lib/vendor/blamejs/release-notes/v0.11.x.json +2551 -0
- package/lib/vendor/blamejs/release-notes/v0.12.0.json +64 -0
- package/lib/vendor/blamejs/release-notes/v0.12.1.json +32 -0
- package/lib/vendor/blamejs/release-notes/v0.12.2.json +45 -0
- package/lib/vendor/blamejs/release-notes/v0.2.x.json +706 -0
- package/lib/vendor/blamejs/release-notes/v0.3.x.json +786 -0
- package/lib/vendor/blamejs/release-notes/v0.4.x.json +588 -0
- package/lib/vendor/blamejs/release-notes/v0.5.x.json +390 -0
- package/lib/vendor/blamejs/release-notes/v0.6.x.json +1947 -0
- package/lib/vendor/blamejs/release-notes/v0.7.x.json +3811 -0
- package/lib/vendor/blamejs/release-notes/v0.8.x.json +3318 -0
- package/lib/vendor/blamejs/release-notes/v0.9.x.json +2257 -0
- package/lib/vendor/blamejs/scripts/build-vendored-sbom.js +325 -0
- package/lib/vendor/blamejs/scripts/check-api-snapshot.js +62 -0
- package/lib/vendor/blamejs/scripts/check-changelog-extract.js +108 -0
- package/lib/vendor/blamejs/scripts/check-pack-against-gitignore.js +83 -0
- package/lib/vendor/blamejs/scripts/check-services.js +483 -0
- package/lib/vendor/blamejs/scripts/check-vendor-currency.js +349 -0
- package/lib/vendor/blamejs/scripts/consolidate-release-notes.js +216 -0
- package/lib/vendor/blamejs/scripts/gen-migrating.js +275 -0
- package/lib/vendor/blamejs/scripts/generate-changelog-entry.js +577 -0
- package/lib/vendor/blamejs/scripts/generate-release-signing-key.js +79 -0
- package/lib/vendor/blamejs/scripts/publish-dep-confusion-placeholder.sh +101 -0
- package/lib/vendor/blamejs/scripts/refresh-api-snapshot.js +31 -0
- package/lib/vendor/blamejs/scripts/refresh-vendor-manifest.js +132 -0
- package/lib/vendor/blamejs/scripts/release.js +652 -0
- package/lib/vendor/blamejs/scripts/sha3-digest.js +62 -0
- package/lib/vendor/blamejs/scripts/sign-release-artifact.js +92 -0
- package/lib/vendor/blamejs/scripts/test-integration.js +181 -0
- package/lib/vendor/blamejs/scripts/test-wiki-integration.js +126 -0
- package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +77 -0
- package/lib/vendor/blamejs/scripts/vendor-data-gen.js +186 -0
- package/lib/vendor/blamejs/scripts/vendor-data-keygen.js +101 -0
- package/lib/vendor/blamejs/scripts/vendor-update.sh +278 -0
- package/lib/vendor/blamejs/test/00-primitives.js +19075 -0
- package/lib/vendor/blamejs/test/10-state.js +622 -0
- package/lib/vendor/blamejs/test/20-db.js +561 -0
- package/lib/vendor/blamejs/test/30-chain.js +2110 -0
- package/lib/vendor/blamejs/test/40-consumers.js +2453 -0
- package/lib/vendor/blamejs/test/50-integration.js +486 -0
- package/lib/vendor/blamejs/test/_helpers.js +10 -0
- package/lib/vendor/blamejs/test/_smoke-worker.js +69 -0
- package/lib/vendor/blamejs/test/fixtures/exploit-corpus/corpus.json +368 -0
- package/lib/vendor/blamejs/test/fixtures/http-client-stream-payload.txt +2 -0
- package/lib/vendor/blamejs/test/fixtures/worker-pool/echo.js +52 -0
- package/lib/vendor/blamejs/test/helpers/_codebase-shingle-worker.js +24 -0
- package/lib/vendor/blamejs/test/helpers/_codebase-shingle.js +203 -0
- package/lib/vendor/blamejs/test/helpers/_shape-match.js +513 -0
- package/lib/vendor/blamejs/test/helpers/check.js +36 -0
- package/lib/vendor/blamejs/test/helpers/cluster.js +70 -0
- package/lib/vendor/blamejs/test/helpers/db.js +143 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +207 -0
- package/lib/vendor/blamejs/test/helpers/fs-watch.js +101 -0
- package/lib/vendor/blamejs/test/helpers/http.js +14 -0
- package/lib/vendor/blamejs/test/helpers/index.js +93 -0
- package/lib/vendor/blamejs/test/helpers/json-round-trip.js +120 -0
- package/lib/vendor/blamejs/test/helpers/mocks.js +20 -0
- package/lib/vendor/blamejs/test/helpers/otel.js +13 -0
- package/lib/vendor/blamejs/test/helpers/services.js +380 -0
- package/lib/vendor/blamejs/test/helpers/wait.js +206 -0
- package/lib/vendor/blamejs/test/integration/cache.test.js +235 -0
- package/lib/vendor/blamejs/test/integration/cluster-provider-mysql.test.js +174 -0
- package/lib/vendor/blamejs/test/integration/federation-auth.test.js +611 -0
- package/lib/vendor/blamejs/test/integration/http-client.test.js +129 -0
- package/lib/vendor/blamejs/test/integration/log-stream.test.js +219 -0
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +181 -0
- package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +152 -0
- package/lib/vendor/blamejs/test/integration/mail-smtp.test.js +161 -0
- package/lib/vendor/blamejs/test/integration/mtls-ca.test.js +289 -0
- package/lib/vendor/blamejs/test/integration/network-dns.test.js +123 -0
- package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +101 -0
- package/lib/vendor/blamejs/test/integration/ntp-check.test.js +89 -0
- package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +403 -0
- package/lib/vendor/blamejs/test/integration/pqc-pkcs8-forward-compat.test.js +271 -0
- package/lib/vendor/blamejs/test/integration/pubsub.test.js +137 -0
- package/lib/vendor/blamejs/test/integration/queue-redis.test.js +352 -0
- package/lib/vendor/blamejs/test/integration/redis-client-tls.test.js +96 -0
- package/lib/vendor/blamejs/test/integration/ssrf-guard.test.js +98 -0
- package/lib/vendor/blamejs/test/integration/websocket-permessage-deflate.test.js +261 -0
- package/lib/vendor/blamejs/test/integration/ws-client-roundtrip.test.js +230 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +211 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a.test.js +59 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/access-lock.test.js +136 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +219 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +266 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +262 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-orchestrator.test.js +390 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-posture-chain.test.js +174 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +279 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +322 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-stream.test.js +227 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-tenant.test.js +302 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-trace.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-adverse-decision.test.js +44 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-content-detect.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-input.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-model-manifest.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-pref.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +1080 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +311 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-zip-stream.test.js +291 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +267 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +108 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asyncapi.test.js +929 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-conflict-path.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-cve-defensive.test.js +176 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-daily-review.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-export-cadf.test.js +97 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-framework-namespaces.test.js +141 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-segregation.test.js +115 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-ml-dsa-65.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-use-store.test.js +246 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge-verifier.test.js +485 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge.test.js +331 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +352 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-lockout.test.js +572 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-password-audit.test.js +61 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-bucket-ops.test.js +258 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-worker.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/bearer-auth.test.js +107 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-chunked-malformed.test.js +131 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-smuggling.test.js +118 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/boot-gates.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/breach-deadline.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +861 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/budr.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/bundler-engine.test.js +209 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cache-status.test.js +129 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +871 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +891 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/canonical-json-jcs.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cdn-cache-control.test.js +243 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +550 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +107 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-api-key.test.js +147 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-audit-verify-chain.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-backup.test.js +135 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-config-drift.test.js +67 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +75 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-file-type.test.js +98 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-helpers.test.js +145 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-mtls.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-password.test.js +97 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-restore.test.js +160 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-retention.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-security.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-vault.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/client-hints.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +237 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +9600 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-cascade.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-eaa.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +712 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +278 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/config-drift.test.js +97 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/config.test.js +424 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cors.test.js +357 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cra-report.test.js +31 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/credential-hash.test.js +226 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-base64url.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-envelope.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +193 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +98 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hpke-pq.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hpke.test.js +155 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-mlkem768-x25519.test.js +129 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-namespace-hash.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-random-int.test.js +72 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-builder.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-nonce.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +180 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +210 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/daily-byte-quota.test.js +153 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/data-act.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-collection-extensions.test.js +226 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-collection.test.js +136 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-init-extensions.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-extensions.test.js +191 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-role-for.test.js +228 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-vacuum.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +184 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/declare-row-policy.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/declare-view.test.js +303 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dns-dnssec-algorithm.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dns-null-mx.test.js +39 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dora.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dr-runbook.test.js +59 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr-state-rules.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +786 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/early-hints.test.js +147 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/events.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/exploit-replay.test.js +243 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +181 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-routing.test.js +531 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +118 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fapi2.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fda-21cfr11.test.js +156 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fdx.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +216 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +434 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +432 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/file-type.test.js +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +887 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/forensic-snapshot.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fsm.test.js +375 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gcs-bucket-ops.test.js +321 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +41 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/graphql-federation.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-agent-registry.test.js +87 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-all.test.js +328 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +339 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +694 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-dsn.test.js +296 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-email.test.js +234 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-envelope.test.js +192 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-event-bus-payload.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-event-bus-topic.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +386 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html-wcag.test.js +859 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +357 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +174 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +317 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-list-id.test.js +199 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-list-unsubscribe.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-compose.test.js +111 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-move.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-query.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-reply.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-sieve.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +301 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +265 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-message-id.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +161 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-posture-chain.test.js +100 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-saga-config.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-smtp-command.test.js +269 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-snapshot-envelope.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-stream-args.test.js +78 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +288 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-tenant-id.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-trace-context.test.js +102 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-xml.test.js +202 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-yaml.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/hal.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/honeytoken.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +37 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +692 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +280 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +225 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +991 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/iab-mspa.test.js +63 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/iab-tcf.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +612 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/importmap-integrity.test.js +56 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +166 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/incident-report.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jose-jwe-experimental.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-api.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-round-trip-helper.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +159 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +118 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/local-db-thin.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-cloudwatch.test.js +489 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-otlp-grpc.test.js +207 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-otlp.test.js +283 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/lro.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +417 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-arf.test.js +208 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +910 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +502 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +680 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-canspam.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +149 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +323 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +297 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +514 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy-tlsrpt.test.js +369 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +199 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +627 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-feedback-id.test.js +56 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-greylist.test.js +217 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +283 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +217 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-mdn.test.js +334 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-rbl.test.js +271 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-require-tls.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +215 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +336 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +732 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +840 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +130 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +285 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +229 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +394 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-tls.test.js +147 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +151 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-spam-score.test.js +204 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-srs.test.js +152 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +279 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +323 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-unsubscribe.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +439 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp-tool-registry.test.js +202 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +155 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-shadow-registry.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +224 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-compose-pipeline.test.js +278 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +376 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-paths.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nel.test.js +200 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-allowlist.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-byte-quota.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +372 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +635 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-heartbeat-passive.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls-build-options.test.js +130 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls-ct-inclusion.test.js +179 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +447 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +369 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nis2-report.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nist-crosswalk.test.js +42 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/no-cache.test.js +98 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/notify.test.js +707 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/numeric-bounds.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +72 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +597 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/openapi.test.js +877 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +257 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pagination.test.js +522 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +216 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +324 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +546 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +153 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-software.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/problem-details.test.js +195 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/process-spawn.test.js +62 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/promise-pool.test.js +93 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/protected-resource-metadata.test.js +68 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/protobuf-encoder.test.js +138 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/protocol-dispatcher.test.js +174 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/public-suffix.test.js +197 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +232 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-dlq-extend-lease.test.js +178 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +322 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-priority-rate-progress.test.js +266 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-sqs.test.js +300 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-cluster.test.js +338 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-registry.test.js +75 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +246 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +130 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +335 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-log.test.js +170 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/require-auth-cache-control.test.js +93 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/require-mtls.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/resource-access-lock.test.js +52 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention-floor.test.js +67 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retry.test.js +535 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-cross-origin-redirect.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-loops.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-parallel.test.js +170 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +248 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +451 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +289 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-icap.test.js +206 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-jsonpath.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +339 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-mount-info.test.js +180 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +78 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-sieve.test.js +123 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-smtp.test.js +95 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-idn-homograph.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +257 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/saml-slo.test.js +249 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +228 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scheduler-exactly-once.test.js +238 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +700 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-notify.test.js +67 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sec-cyber.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +107 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +175 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/seeders.test.js +816 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update-standalone-verifier.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +302 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/server-timing.test.js +93 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +247 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +295 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/shape-match.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +952 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-multipart-sse.test.js +441 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/slug.test.js +330 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/smtp-policy.test.js +233 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/source-comment-blocks.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/speculation-rules.test.js +319 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sse.test.js +148 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +283 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/standard-webhooks.test.js +67 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +266 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/step-up.test.js +487 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage-chunk-scratch.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +773 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/stream-throttle.test.js +173 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/structured-fields.test.js +180 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tcpa-10dlc.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +571 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/test-harness.test.js +190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/testing-request.test.js +119 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/testing.test.js +522 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +151 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tls-exporter.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tls-ocsp-ct.test.js +275 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tls-ocsp-verify.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tls-pinset-drift.test.js +35 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tls-preferred-groups.test.js +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tracing.test.js +280 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/uuid.test.js +93 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-aad.test.js +277 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-seal-pem-file.test.js +252 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vendor-data.test.js +149 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vendor-manifest.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vex.test.js +661 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +308 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/web-push-vapid.test.js +144 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +674 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket-channels.test.js +360 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool.test.js +302 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +349 -0
- package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +717 -0
- package/lib/vendor/blamejs/test/layer-5-integration/bundler-output.test.js +444 -0
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +597 -0
- package/lib/vendor/blamejs/test/layer-5-integration/security-chaos.test.js +308 -0
- package/lib/vendor/blamejs/test/smoke.js +431 -0
- package/lib/webhooks.js +305 -0
- package/package.json +43 -0
|
@@ -0,0 +1,2110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Layer 3 — chain-writing modules (audit, consent, subject, checkpoint)
|
|
4
|
+
* + cluster-storage (SQL dispatcher).
|
|
5
|
+
*
|
|
6
|
+
* (Layer 3: uses db + chain-writer +
|
|
7
|
+
* cluster-storage). Hash-chained log tables and the framework write-path
|
|
8
|
+
* primitives that consume them.
|
|
9
|
+
*
|
|
10
|
+
* cluster-storage — SQL dispatcher (placeholderize, resolveTables,
|
|
11
|
+
* local + cluster dispatch)
|
|
12
|
+
* audit — chain append + verify + self-logging + begin-trace
|
|
13
|
+
* consent — chain append (uses chain-writer)
|
|
14
|
+
* subject — DSAR (export + delete) using audit + db
|
|
15
|
+
* append-only — INSERT-only trigger guards + foreign keys +
|
|
16
|
+
* table metadata reflection
|
|
17
|
+
* checkpoint — sign + verify + tamper detect + rollback detect
|
|
18
|
+
*
|
|
19
|
+
* Layers 0, 1, 2 must run first. Each test sets up its own tmpDir + db.
|
|
20
|
+
*
|
|
21
|
+
* Usage from smoke.js:
|
|
22
|
+
* var chainLayer = require("./30-chain");
|
|
23
|
+
* await chainLayer.run();
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
var helpers = require("./_helpers");
|
|
27
|
+
var b = helpers.b;
|
|
28
|
+
var fs = helpers.fs;
|
|
29
|
+
var os = helpers.os;
|
|
30
|
+
var path = helpers.path;
|
|
31
|
+
var check = helpers.check;
|
|
32
|
+
var setupTestDb = helpers.setupTestDb;
|
|
33
|
+
var teardownTestDb = helpers.teardownTestDb;
|
|
34
|
+
var setTestPassphraseEnv = helpers.setTestPassphraseEnv;
|
|
35
|
+
var _makeSqliteDriver = helpers._makeSqliteDriver;
|
|
36
|
+
|
|
37
|
+
async function testClusterStorageLocalDispatch() {
|
|
38
|
+
// With no cluster.init, executeAll should dispatch to local SQLite.
|
|
39
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cs-"));
|
|
40
|
+
try {
|
|
41
|
+
// Reset cluster BEFORE setupTestDb so its internal audit.checkpoint
|
|
42
|
+
// runs on the permanent-leader fallback (terminated=false).
|
|
43
|
+
b.cluster._resetForTest();
|
|
44
|
+
await setupTestDb(tmpDir);
|
|
45
|
+
|
|
46
|
+
// Seed an audit row via the existing local path so we have something
|
|
47
|
+
// to read back.
|
|
48
|
+
var ev = await b.audit.record({
|
|
49
|
+
actor: { kind: "user", id: "u1" },
|
|
50
|
+
action: "auth.login",
|
|
51
|
+
outcome: "success",
|
|
52
|
+
});
|
|
53
|
+
check("setup: audit row recorded locally", ev !== null);
|
|
54
|
+
|
|
55
|
+
// Now read back through cluster-storage. In single-node mode, should
|
|
56
|
+
// hit the local SQLite, table name is unprefixed.
|
|
57
|
+
check("tableName(audit_log) is unprefixed locally",
|
|
58
|
+
b.clusterStorage.tableName("audit_log") === "audit_log");
|
|
59
|
+
|
|
60
|
+
var rows = await b.clusterStorage.executeAll("SELECT _id, action FROM audit_log");
|
|
61
|
+
check("clusterStorage.executeAll local: row found", rows.length >= 1);
|
|
62
|
+
check("clusterStorage row has audit action", rows[0].action === "auth.login");
|
|
63
|
+
} finally {
|
|
64
|
+
await teardownTestDb(tmpDir);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function testClusterStoragePlaceholderize() {
|
|
69
|
+
check("placeholderize sqlite: passthrough",
|
|
70
|
+
b.clusterStorage.placeholderize("SELECT * FROM t WHERE a = ? AND b = ?", "sqlite") ===
|
|
71
|
+
"SELECT * FROM t WHERE a = ? AND b = ?");
|
|
72
|
+
check("placeholderize postgres: ? → $1, $2",
|
|
73
|
+
b.clusterStorage.placeholderize("SELECT * FROM t WHERE a = ? AND b = ?", "postgres") ===
|
|
74
|
+
"SELECT * FROM t WHERE a = $1 AND b = $2");
|
|
75
|
+
check("placeholderize: skips ? inside single-quoted strings",
|
|
76
|
+
b.clusterStorage.placeholderize("SELECT * FROM t WHERE label = '?' AND id = ?", "postgres") ===
|
|
77
|
+
"SELECT * FROM t WHERE label = '?' AND id = $1");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function testClusterStorageResolveTablesIsNoOpInSingleNode() {
|
|
81
|
+
b.cluster._resetForTest();
|
|
82
|
+
var sql = "SELECT * FROM audit_log";
|
|
83
|
+
check("resolveTables: passthrough when not cluster mode",
|
|
84
|
+
b.clusterStorage.resolveTables(sql) === sql);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function testClusterStorageClusterDispatch() {
|
|
88
|
+
// Spin up a real cluster: full framework + external-db + cluster.init.
|
|
89
|
+
// Then run executeAll against external-db tables created by
|
|
90
|
+
// frameworkSchema.ensureSchema. The resolveTables should rewrite
|
|
91
|
+
// audit_log → _blamejs_audit_log automatically.
|
|
92
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cs-cluster-"));
|
|
93
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
94
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
95
|
+
try {
|
|
96
|
+
b.externalDb.init({
|
|
97
|
+
backends: {
|
|
98
|
+
"ops": { connect: driver.connect, query: driver.query, close: driver.close },
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
await b.frameworkSchema.ensureSchema({
|
|
102
|
+
externalDbBackend: "ops",
|
|
103
|
+
dialect: "sqlite",
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
b.cluster._resetForTest();
|
|
107
|
+
await b.cluster.init({
|
|
108
|
+
nodeId: "cs-cluster-test",
|
|
109
|
+
externalDbBackend: "ops",
|
|
110
|
+
dialect: "sqlite",
|
|
111
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
112
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Now in cluster mode. Insert a row using unprefixed name + ? placeholders.
|
|
116
|
+
await b.clusterStorage.execute(
|
|
117
|
+
"INSERT INTO audit_log (_id, recordedAt, monotonicCounter, action, outcome, prevHash, rowHash, nonce, fencingToken) " +
|
|
118
|
+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
119
|
+
["row1", Date.now(), 1, "auth.login", "success", "", "h1", Buffer.alloc(16), 1]
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// Read back, also via unprefixed name. Dispatcher rewrites both.
|
|
123
|
+
var rows = await b.clusterStorage.executeAll("SELECT _id, action FROM audit_log WHERE _id = ?", ["row1"]);
|
|
124
|
+
check("clusterStorage cluster mode: row found via unprefixed name", rows.length === 1);
|
|
125
|
+
check("clusterStorage cluster mode: row data preserved", rows[0].action === "auth.login");
|
|
126
|
+
|
|
127
|
+
// Verify the row actually landed in the prefixed table
|
|
128
|
+
var directRows = await b.externalDb.query("SELECT _id FROM _blamejs_audit_log WHERE _id = ?", ["row1"]);
|
|
129
|
+
check("cluster row written to _blamejs_-prefixed external table", directRows.rows.length === 1);
|
|
130
|
+
|
|
131
|
+
// tableName getter reflects cluster mode
|
|
132
|
+
check("tableName(audit_log) prefixed in cluster mode",
|
|
133
|
+
b.clusterStorage.tableName("audit_log") === "_blamejs_audit_log");
|
|
134
|
+
} finally {
|
|
135
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
136
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
137
|
+
driver._close();
|
|
138
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Single-node tests for the discovery surface — no external-db needed,
|
|
143
|
+
// run against the permanent-leader fallback.
|
|
144
|
+
function testClusterEndpointSingleNode() {
|
|
145
|
+
b.cluster._resetForTest();
|
|
146
|
+
check("cluster.endpoint() returns null in single-node fallback",
|
|
147
|
+
b.cluster.endpoint() === null);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function testClusterDiscoveryHandlerSingleNode() {
|
|
151
|
+
b.cluster._resetForTest();
|
|
152
|
+
var handler = b.cluster.discoveryHandler();
|
|
153
|
+
check("cluster.discoveryHandler returns a function", typeof handler === "function");
|
|
154
|
+
|
|
155
|
+
// Mock res with capture
|
|
156
|
+
var captured = { status: null, headers: null, body: "" };
|
|
157
|
+
var res = {
|
|
158
|
+
writeHead: function (s, h) { captured.status = s; captured.headers = h; },
|
|
159
|
+
end: function (b) { captured.body += b || ""; },
|
|
160
|
+
};
|
|
161
|
+
await handler({ method: "GET", url: "/cluster/leader" }, res);
|
|
162
|
+
|
|
163
|
+
check("discoveryHandler: 200 in single-node fallback", captured.status === 200);
|
|
164
|
+
check("discoveryHandler: Content-Type is JSON",
|
|
165
|
+
captured.headers["Content-Type"].indexOf("application/json") === 0);
|
|
166
|
+
check("discoveryHandler: Cache-Control is no-store",
|
|
167
|
+
captured.headers["Cache-Control"] === "no-store");
|
|
168
|
+
|
|
169
|
+
var body = JSON.parse(captured.body);
|
|
170
|
+
check("discoveryHandler: leader.nodeId is single-node-local",
|
|
171
|
+
body.leader && body.leader.nodeId === "single-node-local");
|
|
172
|
+
check("discoveryHandler: leader.endpoint is null when unconfigured",
|
|
173
|
+
body.leader.endpoint === null);
|
|
174
|
+
check("discoveryHandler: self.isLeader is true in fallback",
|
|
175
|
+
body.self && body.self.isLeader === true);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function testClusterEndpointInitValidation() {
|
|
179
|
+
b.cluster._resetForTest();
|
|
180
|
+
|
|
181
|
+
// Setup minimal external-db so cluster.init has somewhere to land —
|
|
182
|
+
// we'll never reach the heartbeat because validation throws first.
|
|
183
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-clep-"));
|
|
184
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
185
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
186
|
+
try {
|
|
187
|
+
b.externalDb.init({
|
|
188
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// 1. http:// rejected by default (HTTPS-only allowlist)
|
|
192
|
+
var rejected = null;
|
|
193
|
+
try {
|
|
194
|
+
await b.cluster.init({
|
|
195
|
+
nodeId: "ep-test-1",
|
|
196
|
+
externalDbBackend: "ops",
|
|
197
|
+
dialect: "sqlite",
|
|
198
|
+
endpoint: "http://node1.internal:8080",
|
|
199
|
+
});
|
|
200
|
+
} catch (e) { rejected = e; }
|
|
201
|
+
check("cluster.init: http:// endpoint rejected by default",
|
|
202
|
+
rejected && rejected.code === "INVALID_ENDPOINT");
|
|
203
|
+
b.cluster._resetForTest();
|
|
204
|
+
|
|
205
|
+
// 2. Malformed URL rejected
|
|
206
|
+
var malformed = null;
|
|
207
|
+
try {
|
|
208
|
+
await b.cluster.init({
|
|
209
|
+
nodeId: "ep-test-2",
|
|
210
|
+
externalDbBackend: "ops",
|
|
211
|
+
dialect: "sqlite",
|
|
212
|
+
endpoint: "not-a-url",
|
|
213
|
+
});
|
|
214
|
+
} catch (e) { malformed = e; }
|
|
215
|
+
check("cluster.init: malformed endpoint rejected",
|
|
216
|
+
malformed && malformed.code === "INVALID_ENDPOINT");
|
|
217
|
+
b.cluster._resetForTest();
|
|
218
|
+
|
|
219
|
+
// 3. http:// accepted with explicit allowedProtocols opt-in
|
|
220
|
+
await b.cluster.init({
|
|
221
|
+
nodeId: "ep-test-3",
|
|
222
|
+
externalDbBackend: "ops",
|
|
223
|
+
dialect: "sqlite",
|
|
224
|
+
endpoint: "http://node1.internal:8080",
|
|
225
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL,
|
|
226
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
227
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
228
|
+
});
|
|
229
|
+
check("cluster.endpoint() returns configured value",
|
|
230
|
+
b.cluster.endpoint() === "http://node1.internal:8080");
|
|
231
|
+
} finally {
|
|
232
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
233
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
234
|
+
driver._close();
|
|
235
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function testClusterDiscoveryAcquiredLeader() {
|
|
240
|
+
// Spin up a real cluster, configure an endpoint, verify the leader-row
|
|
241
|
+
// captures it AND discoveryHandler reports it back.
|
|
242
|
+
b.cluster._resetForTest();
|
|
243
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-disc-"));
|
|
244
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
245
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
246
|
+
try {
|
|
247
|
+
b.externalDb.init({
|
|
248
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
249
|
+
});
|
|
250
|
+
await b.frameworkSchema.ensureSchema({
|
|
251
|
+
externalDbBackend: "ops",
|
|
252
|
+
dialect: "sqlite",
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
await b.cluster.init({
|
|
256
|
+
nodeId: "disc-test-1",
|
|
257
|
+
externalDbBackend: "ops",
|
|
258
|
+
dialect: "sqlite",
|
|
259
|
+
endpoint: "https://disc-test-1.internal:8443",
|
|
260
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
261
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
check("cluster.isLeader after acquire", b.cluster.isLeader() === true);
|
|
265
|
+
|
|
266
|
+
var leader = await b.cluster.currentLeader();
|
|
267
|
+
check("currentLeader: nodeId matches",
|
|
268
|
+
leader && leader.nodeId === "disc-test-1");
|
|
269
|
+
check("currentLeader: endpoint persisted to leader row",
|
|
270
|
+
leader.endpoint === "https://disc-test-1.internal:8443");
|
|
271
|
+
check("currentLeader: fencingToken is monotonic (>= 1)",
|
|
272
|
+
typeof leader.fencingToken === "number" && leader.fencingToken >= 1);
|
|
273
|
+
|
|
274
|
+
// discoveryHandler reports leader + endpoint
|
|
275
|
+
var captured = { status: null, body: "" };
|
|
276
|
+
var res = {
|
|
277
|
+
writeHead: function (s, _h) { captured.status = s; },
|
|
278
|
+
end: function (b) { captured.body += b || ""; },
|
|
279
|
+
};
|
|
280
|
+
await b.cluster.discoveryHandler()({ method: "GET" }, res);
|
|
281
|
+
check("discoveryHandler: 200 with active leader", captured.status === 200);
|
|
282
|
+
var body = JSON.parse(captured.body);
|
|
283
|
+
check("discoveryHandler: leader.nodeId in cluster mode",
|
|
284
|
+
body.leader.nodeId === "disc-test-1");
|
|
285
|
+
check("discoveryHandler: leader.endpoint in cluster mode",
|
|
286
|
+
body.leader.endpoint === "https://disc-test-1.internal:8443");
|
|
287
|
+
check("discoveryHandler: self.isLeader is true", body.self.isLeader === true);
|
|
288
|
+
check("discoveryHandler: self.endpoint matches",
|
|
289
|
+
body.self.endpoint === "https://disc-test-1.internal:8443");
|
|
290
|
+
} finally {
|
|
291
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
292
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
293
|
+
driver._close();
|
|
294
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function testClusterAuditTipFencing() {
|
|
299
|
+
// Verify the canonical fencing-token guard on _blamejs_audit_tip:
|
|
300
|
+
// 1. First write (fencingToken=N) — INSERT path, accepted
|
|
301
|
+
// 2. Same-token re-write (N) — UPDATE path with WHERE N <= N, accepted
|
|
302
|
+
// 3. Higher-token write (N+5) — UPDATE path, accepted
|
|
303
|
+
// 4. Lower-token write (N+2) — UPDATE rejected by WHERE clause,
|
|
304
|
+
// audit.checkpoint() throws ClusterError(code=FENCED_OUT).
|
|
305
|
+
//
|
|
306
|
+
// We drive checkpoint() directly rather than recreating the DB row by
|
|
307
|
+
// hand — this proves the framework's actual write path enforces the
|
|
308
|
+
// fence, not just that the SQL would do the right thing in isolation.
|
|
309
|
+
//
|
|
310
|
+
// Forcing the leader's fencingToken to step down is non-trivial in a
|
|
311
|
+
// single-process test (the cluster module's lease state isn't directly
|
|
312
|
+
// mutable). Instead we drive _upsertAuditTip directly with explicit
|
|
313
|
+
// tokens — that's the function the framework's write path calls, and
|
|
314
|
+
// it carries the WHERE-clause guard.
|
|
315
|
+
b.cluster._resetForTest();
|
|
316
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-fence-"));
|
|
317
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
318
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
319
|
+
try {
|
|
320
|
+
b.externalDb.init({
|
|
321
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
322
|
+
});
|
|
323
|
+
await b.frameworkSchema.ensureSchema({
|
|
324
|
+
externalDbBackend: "ops",
|
|
325
|
+
dialect: "sqlite",
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
await b.cluster.init({
|
|
329
|
+
nodeId: "fence-test-1",
|
|
330
|
+
externalDbBackend: "ops",
|
|
331
|
+
dialect: "sqlite",
|
|
332
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
333
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Direct upserts via cluster-storage (bypasses audit.checkpoint's
|
|
337
|
+
// chain-tip read so we can test the fence in isolation).
|
|
338
|
+
async function upsert(counter, hash, signedAt, token) {
|
|
339
|
+
var result = await b.clusterStorage.execute(
|
|
340
|
+
"INSERT INTO _blamejs_audit_tip " +
|
|
341
|
+
" (scope, atMonotonicCounter, rowHash, signedAt, fencingToken) " +
|
|
342
|
+
"VALUES ('audit', ?, ?, ?, ?) " +
|
|
343
|
+
"ON CONFLICT (scope) DO UPDATE SET " +
|
|
344
|
+
" atMonotonicCounter = EXCLUDED.atMonotonicCounter, " +
|
|
345
|
+
" rowHash = EXCLUDED.rowHash, " +
|
|
346
|
+
" signedAt = EXCLUDED.signedAt, " +
|
|
347
|
+
" fencingToken = EXCLUDED.fencingToken " +
|
|
348
|
+
"WHERE _blamejs_audit_tip.fencingToken <= EXCLUDED.fencingToken " +
|
|
349
|
+
"RETURNING fencingToken",
|
|
350
|
+
[counter, hash, signedAt, token]
|
|
351
|
+
);
|
|
352
|
+
return result.rows.length > 0;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// 1. First write (token=3) succeeds — INSERT path
|
|
356
|
+
check("audit-tip: first write at token=3 accepted",
|
|
357
|
+
(await upsert(1, "h1", "1", 3)) === true);
|
|
358
|
+
|
|
359
|
+
// 2. Same-token rewrite (token=3) — WHERE 3<=3 → UPDATE path
|
|
360
|
+
check("audit-tip: same-token rewrite at token=3 accepted",
|
|
361
|
+
(await upsert(2, "h2", "2", 3)) === true);
|
|
362
|
+
|
|
363
|
+
// 3. Higher-token bump (token=8) — WHERE 3<=8 → UPDATE
|
|
364
|
+
check("audit-tip: higher-token write at token=8 accepted",
|
|
365
|
+
(await upsert(3, "h3", "3", 8)) === true);
|
|
366
|
+
|
|
367
|
+
// 4. Stale-token write (token=5, stored=8) — fenced out
|
|
368
|
+
check("audit-tip: lower-token write at token=5 rejected (fenced)",
|
|
369
|
+
(await upsert(4, "h4", "4", 5)) === false);
|
|
370
|
+
|
|
371
|
+
// The stored row should still reflect the highest-accepted token (8)
|
|
372
|
+
var stored = await b.clusterStorage.executeOne(
|
|
373
|
+
"SELECT fencingToken, rowHash FROM _blamejs_audit_tip WHERE scope = 'audit'"
|
|
374
|
+
);
|
|
375
|
+
check("audit-tip: stored token unchanged after rejected write",
|
|
376
|
+
Number(stored.fencingToken) === 8);
|
|
377
|
+
check("audit-tip: stored rowHash unchanged after rejected write",
|
|
378
|
+
stored.rowHash === "h3");
|
|
379
|
+
} finally {
|
|
380
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
381
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
382
|
+
driver._close();
|
|
383
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async function testClusterSessionsSharedAcrossNodes() {
|
|
388
|
+
// Sessions migrated to external-db when migrated to external-db — a session created on
|
|
389
|
+
// the leader must be verifiable by reading the SAME external-db row.
|
|
390
|
+
// We can't truly stand up two node processes in-test, so we simulate
|
|
391
|
+
// the cluster-shared-storage property by:
|
|
392
|
+
// 1. cluster.init as leader, create a session — row lands in
|
|
393
|
+
// external-db's _blamejs_sessions.
|
|
394
|
+
// 2. SELECT directly from external-db; row is there with sealed
|
|
395
|
+
// data (proves it didn't go to local SQLite).
|
|
396
|
+
// 3. session.verify reads through cluster-storage and returns
|
|
397
|
+
// the unsealed row — proves the round-trip works.
|
|
398
|
+
// 4. Compare against the local SQLite _blamejs_sessions table —
|
|
399
|
+
// should be empty (proves cluster mode is actually routing
|
|
400
|
+
// reads/writes away from local).
|
|
401
|
+
b.cluster._resetForTest();
|
|
402
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-sess-cl-"));
|
|
403
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
404
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
405
|
+
try {
|
|
406
|
+
await setupTestDb(tmpDir);
|
|
407
|
+
b.externalDb.init({
|
|
408
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
409
|
+
});
|
|
410
|
+
await b.frameworkSchema.ensureSchema({
|
|
411
|
+
externalDbBackend: "ops",
|
|
412
|
+
dialect: "sqlite",
|
|
413
|
+
});
|
|
414
|
+
await b.cluster.init({
|
|
415
|
+
nodeId: "sess-cluster-1",
|
|
416
|
+
externalDbBackend: "ops",
|
|
417
|
+
dialect: "sqlite",
|
|
418
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
419
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
var s = await b.session.create({ userId: "u-cluster-1", data: { role: "admin" } });
|
|
423
|
+
check("create returned token", typeof s.token === "string");
|
|
424
|
+
|
|
425
|
+
// Row lands in external-db, NOT in local SQLite
|
|
426
|
+
var extRows = await b.externalDb.query(
|
|
427
|
+
"SELECT sidHash FROM _blamejs_sessions",
|
|
428
|
+
[], { backend: "ops" }
|
|
429
|
+
);
|
|
430
|
+
check("session row in external-db", extRows.rows.length === 1);
|
|
431
|
+
check("session row sidHash is hashed (128 hex)",
|
|
432
|
+
/^[0-9a-f]{128}$/.test(extRows.rows[0].sidHash));
|
|
433
|
+
|
|
434
|
+
var localRows = b.db.prepare("SELECT sidHash FROM _blamejs_sessions").all();
|
|
435
|
+
check("local SQLite session table is empty in cluster mode",
|
|
436
|
+
localRows.length === 0);
|
|
437
|
+
|
|
438
|
+
// Round-trip: verify reads back through external-db
|
|
439
|
+
var v = await b.session.verify(s.token);
|
|
440
|
+
check("cluster-mode verify returns userId", v && v.userId === "u-cluster-1");
|
|
441
|
+
check("cluster-mode verify returns unsealed data",
|
|
442
|
+
v && v.data && v.data.role === "admin");
|
|
443
|
+
|
|
444
|
+
// Cleanup via destroy hits external-db too
|
|
445
|
+
var destroyed = await b.session.destroy(s.token);
|
|
446
|
+
check("cluster-mode destroy returns true", destroyed === true);
|
|
447
|
+
var afterDelete = await b.externalDb.query(
|
|
448
|
+
"SELECT COUNT(*) AS n FROM _blamejs_sessions",
|
|
449
|
+
[], { backend: "ops" }
|
|
450
|
+
);
|
|
451
|
+
check("session row removed from external-db", Number(afterDelete.rows[0].n) === 0);
|
|
452
|
+
} finally {
|
|
453
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
454
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
455
|
+
driver._close();
|
|
456
|
+
await teardownTestDb(tmpDir);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async function testClusterConsentTipFencing() {
|
|
461
|
+
// Mirror of testClusterAuditTipFencing but for the consent chain:
|
|
462
|
+
// verify the canonical fencing-token guard on _blamejs_consent_tip
|
|
463
|
+
// through direct upserts (3, 3, 8, 5 token sequence).
|
|
464
|
+
b.cluster._resetForTest();
|
|
465
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cf-"));
|
|
466
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
467
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
468
|
+
try {
|
|
469
|
+
b.externalDb.init({
|
|
470
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
471
|
+
});
|
|
472
|
+
await b.frameworkSchema.ensureSchema({
|
|
473
|
+
externalDbBackend: "ops",
|
|
474
|
+
dialect: "sqlite",
|
|
475
|
+
});
|
|
476
|
+
await b.cluster.init({
|
|
477
|
+
nodeId: "consent-fence-1",
|
|
478
|
+
externalDbBackend: "ops",
|
|
479
|
+
dialect: "sqlite",
|
|
480
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
481
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
async function upsert(counter, hash, signedAt, token) {
|
|
485
|
+
var result = await b.clusterStorage.execute(
|
|
486
|
+
"INSERT INTO _blamejs_consent_tip " +
|
|
487
|
+
" (scope, atMonotonicCounter, rowHash, signedAt, fencingToken) " +
|
|
488
|
+
"VALUES ('consent', ?, ?, ?, ?) " +
|
|
489
|
+
"ON CONFLICT (scope) DO UPDATE SET " +
|
|
490
|
+
" atMonotonicCounter = EXCLUDED.atMonotonicCounter, " +
|
|
491
|
+
" rowHash = EXCLUDED.rowHash, " +
|
|
492
|
+
" signedAt = EXCLUDED.signedAt, " +
|
|
493
|
+
" fencingToken = EXCLUDED.fencingToken " +
|
|
494
|
+
"WHERE _blamejs_consent_tip.fencingToken <= EXCLUDED.fencingToken " +
|
|
495
|
+
"RETURNING fencingToken",
|
|
496
|
+
[counter, hash, signedAt, token]
|
|
497
|
+
);
|
|
498
|
+
return result.rows.length > 0;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
check("consent-tip: first write at token=3 accepted",
|
|
502
|
+
(await upsert(1, "h1", "1", 3)) === true);
|
|
503
|
+
check("consent-tip: same-token rewrite at token=3 accepted",
|
|
504
|
+
(await upsert(2, "h2", "2", 3)) === true);
|
|
505
|
+
check("consent-tip: higher-token write at token=8 accepted",
|
|
506
|
+
(await upsert(3, "h3", "3", 8)) === true);
|
|
507
|
+
check("consent-tip: lower-token write at token=5 rejected (fenced)",
|
|
508
|
+
(await upsert(4, "h4", "4", 5)) === false);
|
|
509
|
+
|
|
510
|
+
var stored = await b.clusterStorage.executeOne(
|
|
511
|
+
"SELECT fencingToken, rowHash FROM _blamejs_consent_tip WHERE scope = 'consent'"
|
|
512
|
+
);
|
|
513
|
+
check("consent-tip: stored token unchanged after rejected write",
|
|
514
|
+
Number(stored.fencingToken) === 8);
|
|
515
|
+
check("consent-tip: stored rowHash unchanged after rejected write",
|
|
516
|
+
stored.rowHash === "h3");
|
|
517
|
+
} finally {
|
|
518
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
519
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
520
|
+
driver._close();
|
|
521
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async function testClusterConsentTipUpdatedOnGrant() {
|
|
526
|
+
// The actual integration: consent.grant in cluster mode writes the
|
|
527
|
+
// chain row AND upserts _blamejs_consent_tip. After a grant, the
|
|
528
|
+
// tip should record the row's monotonicCounter and rowHash.
|
|
529
|
+
b.cluster._resetForTest();
|
|
530
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-ct-grant-"));
|
|
531
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
532
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
533
|
+
try {
|
|
534
|
+
await setupTestDb(tmpDir);
|
|
535
|
+
b.externalDb.init({
|
|
536
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
537
|
+
});
|
|
538
|
+
await b.frameworkSchema.ensureSchema({
|
|
539
|
+
externalDbBackend: "ops",
|
|
540
|
+
dialect: "sqlite",
|
|
541
|
+
});
|
|
542
|
+
await b.cluster.init({
|
|
543
|
+
nodeId: "consent-grant-1",
|
|
544
|
+
externalDbBackend: "ops",
|
|
545
|
+
dialect: "sqlite",
|
|
546
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
547
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
var grantResult = await b.consent.grant({
|
|
551
|
+
subjectId: "subj-1",
|
|
552
|
+
purpose: "marketing.email",
|
|
553
|
+
lawfulBasis: "consent",
|
|
554
|
+
channel: "ui",
|
|
555
|
+
});
|
|
556
|
+
check("consent.grant returned a row with monotonicCounter",
|
|
557
|
+
grantResult && typeof grantResult.monotonicCounter === "number");
|
|
558
|
+
|
|
559
|
+
// Consent tip should now reflect the grant
|
|
560
|
+
var tip = await b.externalDb.query(
|
|
561
|
+
"SELECT atMonotonicCounter, rowHash FROM _blamejs_consent_tip WHERE scope='consent'",
|
|
562
|
+
[], { backend: "ops" }
|
|
563
|
+
);
|
|
564
|
+
check("consent-tip: row exists after first grant",
|
|
565
|
+
tip.rows.length === 1);
|
|
566
|
+
check("consent-tip: counter matches grant's monotonicCounter",
|
|
567
|
+
Number(tip.rows[0].atMonotonicCounter) === Number(grantResult.monotonicCounter));
|
|
568
|
+
check("consent-tip: rowHash matches grant's rowHash",
|
|
569
|
+
tip.rows[0].rowHash === grantResult.rowHash);
|
|
570
|
+
|
|
571
|
+
// Second grant advances the tip
|
|
572
|
+
var grant2 = await b.consent.grant({
|
|
573
|
+
subjectId: "subj-2",
|
|
574
|
+
purpose: "marketing.email",
|
|
575
|
+
lawfulBasis: "consent",
|
|
576
|
+
channel: "ui",
|
|
577
|
+
});
|
|
578
|
+
var tip2 = await b.externalDb.query(
|
|
579
|
+
"SELECT atMonotonicCounter, rowHash FROM _blamejs_consent_tip WHERE scope='consent'",
|
|
580
|
+
[], { backend: "ops" }
|
|
581
|
+
);
|
|
582
|
+
check("consent-tip: counter advanced on second grant",
|
|
583
|
+
Number(tip2.rows[0].atMonotonicCounter) === Number(grant2.monotonicCounter));
|
|
584
|
+
} finally {
|
|
585
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
586
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
587
|
+
driver._close();
|
|
588
|
+
await teardownTestDb(tmpDir);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
async function testClusterConsentRollbackDetected() {
|
|
593
|
+
// Pre-seed consent-tip with counter=999 + empty consent_log; spawn
|
|
594
|
+
// a child that runs cluster.init and verify it exits 1 with the
|
|
595
|
+
// generalized "consent-log rollback detected" message.
|
|
596
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cr-detect-"));
|
|
597
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
598
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
599
|
+
try {
|
|
600
|
+
b.externalDb.init({
|
|
601
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
602
|
+
});
|
|
603
|
+
await b.frameworkSchema.ensureSchema({
|
|
604
|
+
externalDbBackend: "ops",
|
|
605
|
+
dialect: "sqlite",
|
|
606
|
+
});
|
|
607
|
+
await b.externalDb.query(
|
|
608
|
+
"INSERT INTO _blamejs_consent_tip (scope, atMonotonicCounter, rowHash, signedAt, fencingToken) " +
|
|
609
|
+
"VALUES ('consent', 999, 'rolled-hash', '0', 5)"
|
|
610
|
+
);
|
|
611
|
+
await b.externalDb.shutdown();
|
|
612
|
+
driver._close();
|
|
613
|
+
|
|
614
|
+
var spawnSync = require("child_process").spawnSync;
|
|
615
|
+
var indexPath = path.resolve(__dirname, "..", "index.js").replace(/\\/g, "/");
|
|
616
|
+
var dbPathForChild = dbPath.replace(/\\/g, "/");
|
|
617
|
+
var childScript =
|
|
618
|
+
"var b = require('" + indexPath + "');\n" +
|
|
619
|
+
"var sqlite = require('node:sqlite');\n" +
|
|
620
|
+
"var conn = new sqlite.DatabaseSync('" + dbPathForChild + "');\n" +
|
|
621
|
+
"var driver = {\n" +
|
|
622
|
+
" connect: async function () { return { id: 'c1' }; },\n" +
|
|
623
|
+
" query: async function (_c, sql, params) {\n" +
|
|
624
|
+
" var stmt = conn.prepare(sql);\n" +
|
|
625
|
+
" if (/^\\s*SELECT/i.test(sql) || /\\bRETURNING\\b/i.test(sql)) {\n" +
|
|
626
|
+
" return { rows: stmt.all.apply(stmt, params || []), rowCount: 0 };\n" +
|
|
627
|
+
" }\n" +
|
|
628
|
+
" var info = stmt.run.apply(stmt, params || []);\n" +
|
|
629
|
+
" return { rows: [], rowCount: info.changes };\n" +
|
|
630
|
+
" },\n" +
|
|
631
|
+
" close: async function () {},\n" +
|
|
632
|
+
"};\n" +
|
|
633
|
+
"(async function () {\n" +
|
|
634
|
+
" b.externalDb.init({ backends: { ops: driver } });\n" +
|
|
635
|
+
" await b.cluster.init({ nodeId: 'cr-child', externalDbBackend: 'ops', dialect: 'sqlite', leaseTtl: 30000, heartbeatInterval: 10000 });\n" +
|
|
636
|
+
" console.log('UNEXPECTED-BOOT');\n" +
|
|
637
|
+
"})().catch(function (e) { console.error('CHILD-ERR ' + e.message); process.exit(99); });\n";
|
|
638
|
+
var result = spawnSync(process.execPath, ["-e", childScript], { encoding: "utf8" });
|
|
639
|
+
check("consent-rollback boot exits via the catch handler (code 99)",
|
|
640
|
+
result.status === 99);
|
|
641
|
+
check("consent-rollback boot logs the consent-chain message",
|
|
642
|
+
/consent-log rollback detected/i.test(result.stderr || ""));
|
|
643
|
+
} finally {
|
|
644
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
async function testClusterQueueJobsSharedAcrossNodes() {
|
|
649
|
+
// Queue jobs migrated to external-db when migrated to external-db — enqueue from the
|
|
650
|
+
// leader writes to external-db; lease + complete observe the same
|
|
651
|
+
// shared row. Mirrors the session-cluster test's structure: one
|
|
652
|
+
// node process, but verifies storage-routing properties.
|
|
653
|
+
b.cluster._resetForTest();
|
|
654
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-q-cl-"));
|
|
655
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
656
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
657
|
+
try {
|
|
658
|
+
await setupTestDb(tmpDir);
|
|
659
|
+
b.externalDb.init({
|
|
660
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
661
|
+
});
|
|
662
|
+
await b.frameworkSchema.ensureSchema({
|
|
663
|
+
externalDbBackend: "ops",
|
|
664
|
+
dialect: "sqlite",
|
|
665
|
+
});
|
|
666
|
+
await b.cluster.init({
|
|
667
|
+
nodeId: "q-cluster-1",
|
|
668
|
+
externalDbBackend: "ops",
|
|
669
|
+
dialect: "sqlite",
|
|
670
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
671
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
// Use the queue-local protocol directly so the test focuses on the
|
|
675
|
+
// storage-routing semantics, not the dispatcher's audit-emit /
|
|
676
|
+
// breaker layers (those have their own tests).
|
|
677
|
+
var queueLocal = require("../lib/queue-local").create();
|
|
678
|
+
var enq = await queueLocal.enqueue("cl-test", { x: 42 }, { traceId: "trace-1" });
|
|
679
|
+
check("cluster queue: enqueue returns jobId", typeof enq.jobId === "string");
|
|
680
|
+
|
|
681
|
+
// Row lands in external-db, NOT in local SQLite
|
|
682
|
+
var extRows = await b.externalDb.query(
|
|
683
|
+
"SELECT _id, queueName, status FROM _blamejs_jobs",
|
|
684
|
+
[], { backend: "ops" }
|
|
685
|
+
);
|
|
686
|
+
check("cluster queue: row in external-db", extRows.rows.length === 1);
|
|
687
|
+
check("cluster queue: row status is pending", extRows.rows[0].status === "pending");
|
|
688
|
+
|
|
689
|
+
var localRows = b.db.prepare("SELECT _id FROM _blamejs_jobs").all();
|
|
690
|
+
check("cluster queue: local SQLite jobs table empty",
|
|
691
|
+
localRows.length === 0);
|
|
692
|
+
|
|
693
|
+
// Lease via the same external-db storage; payload round-trips
|
|
694
|
+
// through the seal/unseal pipeline correctly
|
|
695
|
+
var leased = await queueLocal.lease("cl-test", b.constants.TIME.seconds(30), 1);
|
|
696
|
+
check("cluster queue: lease returns 1 job", leased.length === 1);
|
|
697
|
+
check("cluster queue: lease unseals payload",
|
|
698
|
+
leased[0].payload && leased[0].payload.x === 42);
|
|
699
|
+
check("cluster queue: lease preserves traceId", leased[0].traceId === "trace-1");
|
|
700
|
+
|
|
701
|
+
// Status transition lands in external-db
|
|
702
|
+
var afterLease = await b.externalDb.query(
|
|
703
|
+
"SELECT status, attempts FROM _blamejs_jobs WHERE _id = ?",
|
|
704
|
+
[enq.jobId], { backend: "ops" }
|
|
705
|
+
);
|
|
706
|
+
check("cluster queue: row status flipped to inflight",
|
|
707
|
+
afterLease.rows[0].status === "inflight");
|
|
708
|
+
check("cluster queue: attempts incremented",
|
|
709
|
+
Number(afterLease.rows[0].attempts) === 1);
|
|
710
|
+
|
|
711
|
+
// Complete cycles back to external-db
|
|
712
|
+
var done = await queueLocal.complete(enq.jobId);
|
|
713
|
+
check("cluster queue: complete returns true", done === true);
|
|
714
|
+
var afterDone = await b.externalDb.query(
|
|
715
|
+
"SELECT status FROM _blamejs_jobs WHERE _id = ?",
|
|
716
|
+
[enq.jobId], { backend: "ops" }
|
|
717
|
+
);
|
|
718
|
+
check("cluster queue: row status now done", afterDone.rows[0].status === "done");
|
|
719
|
+
} finally {
|
|
720
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
721
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
722
|
+
driver._close();
|
|
723
|
+
await teardownTestDb(tmpDir);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
async function testClusterVaultKeyFirstBootRecords() {
|
|
728
|
+
// First cluster boot: no _blamejs_cluster_state row yet. cluster.init
|
|
729
|
+
// should write THIS node's vault-key fingerprint and record nodeId.
|
|
730
|
+
// Subsequent cluster.init from the same vault file passes without
|
|
731
|
+
// changes (idempotent).
|
|
732
|
+
b.cluster._resetForTest();
|
|
733
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-vk-first-"));
|
|
734
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
735
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
736
|
+
try {
|
|
737
|
+
await setupTestDb(tmpDir); // initializes vault
|
|
738
|
+
b.externalDb.init({
|
|
739
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
740
|
+
});
|
|
741
|
+
await b.frameworkSchema.ensureSchema({
|
|
742
|
+
externalDbBackend: "ops",
|
|
743
|
+
dialect: "sqlite",
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
// First boot — writes the row.
|
|
747
|
+
await b.cluster.init({
|
|
748
|
+
nodeId: "vk-first-1",
|
|
749
|
+
externalDbBackend: "ops",
|
|
750
|
+
dialect: "sqlite",
|
|
751
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
752
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
753
|
+
});
|
|
754
|
+
var stored = await b.externalDb.query(
|
|
755
|
+
"SELECT vaultKeyFp, recordedByNode FROM _blamejs_cluster_state WHERE scope='state'",
|
|
756
|
+
[], { backend: "ops" }
|
|
757
|
+
);
|
|
758
|
+
check("cluster-state row recorded after first boot",
|
|
759
|
+
stored.rows.length === 1);
|
|
760
|
+
check("cluster-state recorded by this node",
|
|
761
|
+
stored.rows[0].recordedByNode === "vk-first-1");
|
|
762
|
+
check("cluster-state fingerprint is hex sha3-512 (128 chars)",
|
|
763
|
+
/^[0-9a-f]{128}$/.test(stored.rows[0].vaultKeyFp));
|
|
764
|
+
|
|
765
|
+
// Same vault, fresh cluster.init — passes silently
|
|
766
|
+
await b.cluster.shutdown();
|
|
767
|
+
await b.cluster.init({
|
|
768
|
+
nodeId: "vk-first-2",
|
|
769
|
+
externalDbBackend: "ops",
|
|
770
|
+
dialect: "sqlite",
|
|
771
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
772
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
773
|
+
});
|
|
774
|
+
var stored2 = await b.externalDb.query(
|
|
775
|
+
"SELECT vaultKeyFp, recordedByNode FROM _blamejs_cluster_state WHERE scope='state'",
|
|
776
|
+
[], { backend: "ops" }
|
|
777
|
+
);
|
|
778
|
+
check("cluster-state fingerprint unchanged after same-vault re-init",
|
|
779
|
+
stored2.rows[0].vaultKeyFp === stored.rows[0].vaultKeyFp);
|
|
780
|
+
check("cluster-state recordedByNode unchanged (first writer wins)",
|
|
781
|
+
stored2.rows[0].recordedByNode === "vk-first-1");
|
|
782
|
+
} finally {
|
|
783
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
784
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
785
|
+
driver._close();
|
|
786
|
+
await teardownTestDb(tmpDir);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
async function testClusterVaultKeyMismatchDetected() {
|
|
791
|
+
// Pre-seed the cluster-state row with a fingerprint that won't match
|
|
792
|
+
// the freshly-generated vault keys in the child process. cluster.init
|
|
793
|
+
// detects the drift and process.exit(1)s. Spawn-a-child pattern so
|
|
794
|
+
// we can capture the exit code.
|
|
795
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-vk-drift-"));
|
|
796
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
797
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
798
|
+
try {
|
|
799
|
+
b.externalDb.init({
|
|
800
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
801
|
+
});
|
|
802
|
+
// Provider ensureSchema creates _blamejs_leader + _blamejs_cluster_state.
|
|
803
|
+
// We don't run cluster.init here — just construct the provider manually
|
|
804
|
+
// so the table exists, then pre-seed with a wrong fingerprint.
|
|
805
|
+
var providerDb = require("../lib/cluster-provider-db");
|
|
806
|
+
var prov = providerDb.create({ externalDbBackend: "ops", dialect: "sqlite" });
|
|
807
|
+
await prov.ensureSchema();
|
|
808
|
+
|
|
809
|
+
await b.externalDb.query(
|
|
810
|
+
"INSERT INTO _blamejs_cluster_state (scope, vaultKeyFp, recordedAt, recordedByNode) " +
|
|
811
|
+
"VALUES ('state', ?, ?, 'pre-existing-node')",
|
|
812
|
+
["deadbeef".repeat(16), Date.now()] // 128-hex bogus fingerprint
|
|
813
|
+
);
|
|
814
|
+
await b.externalDb.shutdown();
|
|
815
|
+
driver._close();
|
|
816
|
+
|
|
817
|
+
var spawnSync = require("child_process").spawnSync;
|
|
818
|
+
var indexPath = path.resolve(__dirname, "..", "index.js").replace(/\\/g, "/");
|
|
819
|
+
var dbPathForChild = dbPath.replace(/\\/g, "/");
|
|
820
|
+
var childTmp = path.resolve(tmpDir, "child-data").replace(/\\/g, "/");
|
|
821
|
+
var childScript =
|
|
822
|
+
"var b = require('" + indexPath + "');\n" +
|
|
823
|
+
"var sqlite = require('node:sqlite');\n" +
|
|
824
|
+
"var conn = new sqlite.DatabaseSync('" + dbPathForChild + "');\n" +
|
|
825
|
+
"var driver = {\n" +
|
|
826
|
+
" connect: async function () { return { id: 'c1' }; },\n" +
|
|
827
|
+
" query: async function (_c, sql, params) {\n" +
|
|
828
|
+
" var stmt = conn.prepare(sql);\n" +
|
|
829
|
+
" if (/^\\s*SELECT/i.test(sql) || /\\bRETURNING\\b/i.test(sql)) {\n" +
|
|
830
|
+
" return { rows: stmt.all.apply(stmt, params || []), rowCount: 0 };\n" +
|
|
831
|
+
" }\n" +
|
|
832
|
+
" var info = stmt.run.apply(stmt, params || []);\n" +
|
|
833
|
+
" return { rows: [], rowCount: info.changes };\n" +
|
|
834
|
+
" },\n" +
|
|
835
|
+
" close: async function () {},\n" +
|
|
836
|
+
"};\n" +
|
|
837
|
+
"process.env.BLAMEJS_SKIP_NTP_CHECK = '1';\n" +
|
|
838
|
+
"(async function () {\n" +
|
|
839
|
+
" require('fs').mkdirSync('" + childTmp + "', { recursive: true });\n" +
|
|
840
|
+
" await b.vault.init({ dataDir: '" + childTmp + "', mode: 'plaintext' });\n" +
|
|
841
|
+
" b.externalDb.init({ backends: { ops: driver } });\n" +
|
|
842
|
+
" await b.cluster.init({ nodeId: 'vk-drift-child', externalDbBackend: 'ops', dialect: 'sqlite', leaseTtl: 30000, heartbeatInterval: 10000 });\n" +
|
|
843
|
+
" console.log('UNEXPECTED-BOOT');\n" +
|
|
844
|
+
"})().catch(function (e) { console.error('CHILD-ERR ' + e.message); process.exit(99); });\n";
|
|
845
|
+
var result = spawnSync(process.execPath, ["-e", childScript], { encoding: "utf8" });
|
|
846
|
+
check("vault-key drift boot exits via the catch handler (code 99)",
|
|
847
|
+
result.status === 99);
|
|
848
|
+
check("vault-key drift boot logs detection message",
|
|
849
|
+
/vault-key drift detected/i.test(result.stderr || ""));
|
|
850
|
+
check("vault-key drift boot did NOT print UNEXPECTED-BOOT",
|
|
851
|
+
(result.stdout || "").indexOf("UNEXPECTED-BOOT") === -1);
|
|
852
|
+
} finally {
|
|
853
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
async function testClusterAuditTipRollbackHappyPath() {
|
|
858
|
+
// Happy path — tip matches the row at its recorded counter, cluster.init
|
|
859
|
+
// proceeds. Pre-populate an audit_log row + a matching tip row, then
|
|
860
|
+
// run cluster.init; verify the rollback-check log line and a clean
|
|
861
|
+
// boot.
|
|
862
|
+
b.cluster._resetForTest();
|
|
863
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-rb-ok-"));
|
|
864
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
865
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
866
|
+
try {
|
|
867
|
+
b.externalDb.init({
|
|
868
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
869
|
+
});
|
|
870
|
+
await b.frameworkSchema.ensureSchema({
|
|
871
|
+
externalDbBackend: "ops",
|
|
872
|
+
dialect: "sqlite",
|
|
873
|
+
});
|
|
874
|
+
// One audit_log row at counter=1 with hash "h1", plus a tip row
|
|
875
|
+
// pointing at it. cluster.init should accept this as a clean state.
|
|
876
|
+
await b.externalDb.query(
|
|
877
|
+
"INSERT INTO _blamejs_audit_log " +
|
|
878
|
+
"(_id, recordedAt, monotonicCounter, action, outcome, prevHash, rowHash, nonce, fencingToken) " +
|
|
879
|
+
"VALUES ('row-ok', ?, 1, 'system.test.ok', 'success', '', 'h1', x'00000000000000000000000000000000', 1)",
|
|
880
|
+
[Date.now()]
|
|
881
|
+
);
|
|
882
|
+
await b.externalDb.query(
|
|
883
|
+
"INSERT INTO _blamejs_audit_tip (scope, atMonotonicCounter, rowHash, signedAt, fencingToken) " +
|
|
884
|
+
"VALUES ('audit', 1, 'h1', '0', 1)"
|
|
885
|
+
);
|
|
886
|
+
|
|
887
|
+
var threw = null;
|
|
888
|
+
try {
|
|
889
|
+
await b.cluster.init({
|
|
890
|
+
nodeId: "rb-ok-1",
|
|
891
|
+
externalDbBackend: "ops",
|
|
892
|
+
dialect: "sqlite",
|
|
893
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
894
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
895
|
+
});
|
|
896
|
+
} catch (e) { threw = e; }
|
|
897
|
+
check("cluster.init: happy-path rollback check passes", threw === null);
|
|
898
|
+
check("cluster.init: lease acquired", b.cluster.isLeader() === true);
|
|
899
|
+
} finally {
|
|
900
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
901
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
902
|
+
driver._close();
|
|
903
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
async function testClusterAuditTipRollbackDetected() {
|
|
908
|
+
// Pre-seed a tip recording counter=999 with no matching audit_log
|
|
909
|
+
// row. cluster.init should detect this as rollback (current MAX <
|
|
910
|
+
// tip counter) and process.exit(1). We fork a child process to
|
|
911
|
+
// capture the exit code and stderr — same pattern as the
|
|
912
|
+
// single-node testRollbackDetection.
|
|
913
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-rb-detect-"));
|
|
914
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
915
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
916
|
+
try {
|
|
917
|
+
b.externalDb.init({
|
|
918
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
919
|
+
});
|
|
920
|
+
await b.frameworkSchema.ensureSchema({
|
|
921
|
+
externalDbBackend: "ops",
|
|
922
|
+
dialect: "sqlite",
|
|
923
|
+
});
|
|
924
|
+
// Empty audit_log + tip claiming counter=999. Net: rollback.
|
|
925
|
+
await b.externalDb.query(
|
|
926
|
+
"INSERT INTO _blamejs_audit_tip (scope, atMonotonicCounter, rowHash, signedAt, fencingToken) " +
|
|
927
|
+
"VALUES ('audit', 999, 'rolled-hash', '0', 5)"
|
|
928
|
+
);
|
|
929
|
+
await b.externalDb.shutdown();
|
|
930
|
+
driver._close();
|
|
931
|
+
|
|
932
|
+
// Spawn a child that re-opens the same db file and calls
|
|
933
|
+
// cluster.init. The rollback check should fire and exit(1).
|
|
934
|
+
var spawnSync = require("child_process").spawnSync;
|
|
935
|
+
var indexPath = path.resolve(__dirname, "..", "index.js").replace(/\\/g, "/");
|
|
936
|
+
var dbPathForChild = dbPath.replace(/\\/g, "/");
|
|
937
|
+
var childScript =
|
|
938
|
+
"var b = require('" + indexPath + "');\n" +
|
|
939
|
+
"var sqlite = require('node:sqlite');\n" +
|
|
940
|
+
"var conn = new sqlite.DatabaseSync('" + dbPathForChild + "');\n" +
|
|
941
|
+
"var driver = {\n" +
|
|
942
|
+
" connect: async function () { return { id: 'c1' }; },\n" +
|
|
943
|
+
" query: async function (_c, sql, params) {\n" +
|
|
944
|
+
" var stmt = conn.prepare(sql);\n" +
|
|
945
|
+
" if (/^\\s*SELECT/i.test(sql) || /\\bRETURNING\\b/i.test(sql)) {\n" +
|
|
946
|
+
" return { rows: stmt.all.apply(stmt, params || []), rowCount: 0 };\n" +
|
|
947
|
+
" }\n" +
|
|
948
|
+
" var info = stmt.run.apply(stmt, params || []);\n" +
|
|
949
|
+
" return { rows: [], rowCount: info.changes };\n" +
|
|
950
|
+
" },\n" +
|
|
951
|
+
" close: async function () {},\n" +
|
|
952
|
+
"};\n" +
|
|
953
|
+
"(async function () {\n" +
|
|
954
|
+
" b.externalDb.init({ backends: { ops: driver } });\n" +
|
|
955
|
+
" await b.cluster.init({ nodeId: 'rb-child', externalDbBackend: 'ops', dialect: 'sqlite', leaseTtl: 30000, heartbeatInterval: 10000 });\n" +
|
|
956
|
+
" console.log('UNEXPECTED-BOOT');\n" +
|
|
957
|
+
"})().catch(function (e) { console.error('CHILD-ERR ' + e.message); process.exit(99); });\n";
|
|
958
|
+
var result = spawnSync(process.execPath, ["-e", childScript], { encoding: "utf8" });
|
|
959
|
+
// cluster.init throws ClusterError on rollback detection; the child
|
|
960
|
+
// script's catch handler exits with code 99. Pre-v0.7.0 the lib called
|
|
961
|
+
// process.exit(1) unilaterally; the test now asserts the controlled-throw
|
|
962
|
+
// path so callers can decide their own exit code.
|
|
963
|
+
check("rollback boot exits via the catch handler (code 99)",
|
|
964
|
+
result.status === 99);
|
|
965
|
+
check("rollback boot logs detection message",
|
|
966
|
+
/audit-log rollback detected/i.test(result.stderr || ""));
|
|
967
|
+
check("rollback boot did NOT print UNEXPECTED-BOOT (exited before continuing)",
|
|
968
|
+
(result.stdout || "").indexOf("UNEXPECTED-BOOT") === -1);
|
|
969
|
+
} finally {
|
|
970
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
async function testClusterAuditTipRowHashMismatch() {
|
|
975
|
+
// Same counter on both sides but different rowHash — row substitution
|
|
976
|
+
// at the chain head. Detection path: tipCounter == currentMax, but
|
|
977
|
+
// the row at that counter has a different hash than the tip recorded.
|
|
978
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-rb-hash-"));
|
|
979
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
980
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
981
|
+
try {
|
|
982
|
+
b.externalDb.init({
|
|
983
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
984
|
+
});
|
|
985
|
+
await b.frameworkSchema.ensureSchema({
|
|
986
|
+
externalDbBackend: "ops",
|
|
987
|
+
dialect: "sqlite",
|
|
988
|
+
});
|
|
989
|
+
await b.externalDb.query(
|
|
990
|
+
"INSERT INTO _blamejs_audit_log " +
|
|
991
|
+
"(_id, recordedAt, monotonicCounter, action, outcome, prevHash, rowHash, nonce, fencingToken) " +
|
|
992
|
+
"VALUES ('row-substituted', ?, 1, 'system.test.subst', 'success', '', 'WRONG-HASH', x'00000000000000000000000000000000', 1)",
|
|
993
|
+
[Date.now()]
|
|
994
|
+
);
|
|
995
|
+
// Tip records the OLD hash at the same counter — substitution.
|
|
996
|
+
await b.externalDb.query(
|
|
997
|
+
"INSERT INTO _blamejs_audit_tip (scope, atMonotonicCounter, rowHash, signedAt, fencingToken) " +
|
|
998
|
+
"VALUES ('audit', 1, 'ORIGINAL-HASH', '0', 1)"
|
|
999
|
+
);
|
|
1000
|
+
await b.externalDb.shutdown();
|
|
1001
|
+
driver._close();
|
|
1002
|
+
|
|
1003
|
+
var spawnSync = require("child_process").spawnSync;
|
|
1004
|
+
var indexPath = path.resolve(__dirname, "..", "index.js").replace(/\\/g, "/");
|
|
1005
|
+
var dbPathForChild = dbPath.replace(/\\/g, "/");
|
|
1006
|
+
var childScript =
|
|
1007
|
+
"var b = require('" + indexPath + "');\n" +
|
|
1008
|
+
"var sqlite = require('node:sqlite');\n" +
|
|
1009
|
+
"var conn = new sqlite.DatabaseSync('" + dbPathForChild + "');\n" +
|
|
1010
|
+
"var driver = {\n" +
|
|
1011
|
+
" connect: async function () { return { id: 'c1' }; },\n" +
|
|
1012
|
+
" query: async function (_c, sql, params) {\n" +
|
|
1013
|
+
" var stmt = conn.prepare(sql);\n" +
|
|
1014
|
+
" if (/^\\s*SELECT/i.test(sql) || /\\bRETURNING\\b/i.test(sql)) {\n" +
|
|
1015
|
+
" return { rows: stmt.all.apply(stmt, params || []), rowCount: 0 };\n" +
|
|
1016
|
+
" }\n" +
|
|
1017
|
+
" var info = stmt.run.apply(stmt, params || []);\n" +
|
|
1018
|
+
" return { rows: [], rowCount: info.changes };\n" +
|
|
1019
|
+
" },\n" +
|
|
1020
|
+
" close: async function () {},\n" +
|
|
1021
|
+
"};\n" +
|
|
1022
|
+
"(async function () {\n" +
|
|
1023
|
+
" b.externalDb.init({ backends: { ops: driver } });\n" +
|
|
1024
|
+
" await b.cluster.init({ nodeId: 'rb-hash-child', externalDbBackend: 'ops', dialect: 'sqlite', leaseTtl: 30000, heartbeatInterval: 10000 });\n" +
|
|
1025
|
+
" console.log('UNEXPECTED-BOOT');\n" +
|
|
1026
|
+
"})().catch(function (e) { console.error('CHILD-ERR ' + e.message); process.exit(99); });\n";
|
|
1027
|
+
var result = spawnSync(process.execPath, ["-e", childScript], { encoding: "utf8" });
|
|
1028
|
+
check("row-hash mismatch boot exits via the catch handler (code 99)",
|
|
1029
|
+
result.status === 99);
|
|
1030
|
+
check("row-hash mismatch boot logs detection message",
|
|
1031
|
+
/row-hash mismatch/i.test(result.stderr || ""));
|
|
1032
|
+
} finally {
|
|
1033
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
async function testClusterAuditFlushNoRecursionHang() {
|
|
1038
|
+
// Regression test for :
|
|
1039
|
+
// Before the fix, audit.flush() in cluster mode hung forever because
|
|
1040
|
+
// each drained event wrote through external-db, externalDb.query
|
|
1041
|
+
// emitted a system.externaldb.query audit event back into the same
|
|
1042
|
+
// handler buffer, and handlers.drain's while-loop processed those new
|
|
1043
|
+
// items in the same call — so the buffer refilled as fast as it
|
|
1044
|
+
// emptied. The fix bounds drain to a snapshot of the buffer at start
|
|
1045
|
+
// (matching the documented recursion-safety contract). This test
|
|
1046
|
+
// proves flush() returns within a tight wall-clock budget in cluster
|
|
1047
|
+
// mode under exactly the producer/consumer cycle that triggered the
|
|
1048
|
+
// hang.
|
|
1049
|
+
b.cluster._resetForTest();
|
|
1050
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-flush-rec-"));
|
|
1051
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
1052
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
1053
|
+
try {
|
|
1054
|
+
await setupTestDb(tmpDir);
|
|
1055
|
+
await b.audit.flush(); // drain any boot-time emits in single-node
|
|
1056
|
+
b.externalDb.init({
|
|
1057
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
1058
|
+
});
|
|
1059
|
+
await b.frameworkSchema.ensureSchema({
|
|
1060
|
+
externalDbBackend: "ops",
|
|
1061
|
+
dialect: "sqlite",
|
|
1062
|
+
});
|
|
1063
|
+
await b.cluster.init({
|
|
1064
|
+
nodeId: "flush-rec-1",
|
|
1065
|
+
externalDbBackend: "ops",
|
|
1066
|
+
dialect: "sqlite",
|
|
1067
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
1068
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
// record() bypasses the buffer, but the underlying externalDb.query
|
|
1072
|
+
// emits a system.externaldb.query event INTO the buffer. Subsequent
|
|
1073
|
+
// flush() drains that — and each drained event itself writes through
|
|
1074
|
+
// external-db, emitting more events. Pre-fix: hang. Post-fix: drain
|
|
1075
|
+
// is bounded; lingering events stay for the next call.
|
|
1076
|
+
await b.audit.record({
|
|
1077
|
+
action: "system.test.flush_recursion",
|
|
1078
|
+
outcome: "success",
|
|
1079
|
+
actor: { userId: "rec-test" },
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
// The Promise.race against a 5s timeout is the no-hang regression
|
|
1083
|
+
// guard — if flush() truly hangs it never resolves, raced lands as
|
|
1084
|
+
// "TIMEOUT". Don't add a separate elapsed-< 5000 check: it's
|
|
1085
|
+
// tautologically true when raced === "done" (the timeout would have
|
|
1086
|
+
// fired first otherwise) and produces a Windows boundary flake when
|
|
1087
|
+
// event-loop drift puts the post-await Date.now() at 5001ms.
|
|
1088
|
+
var raced = await Promise.race([
|
|
1089
|
+
b.audit.flush().then(function () { return "done"; }),
|
|
1090
|
+
new Promise(function (r) { setTimeout(function () { r("TIMEOUT"); }, 5000); }),
|
|
1091
|
+
]);
|
|
1092
|
+
check("audit.flush returns (no recursion hang)", raced === "done");
|
|
1093
|
+
} finally {
|
|
1094
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
1095
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
1096
|
+
driver._close();
|
|
1097
|
+
await teardownTestDb(tmpDir);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
async function testClusterAuditTipFencedOutErrorSurface() {
|
|
1102
|
+
// Verify that audit.js's _upsertAuditTip surfaces
|
|
1103
|
+
// ClusterError(code=FENCED_OUT, permanent=true) when the fence rejects
|
|
1104
|
+
// the write. This catches a regression where the original
|
|
1105
|
+
// UPDATE-then-INSERT-if-missing path swallowed silently — operators
|
|
1106
|
+
// MUST see fence rejection.
|
|
1107
|
+
//
|
|
1108
|
+
// We bypass audit.record/flush (which crosses the single-node →
|
|
1109
|
+
// cluster-mode boundary mid-buffer, an unrelated complication) and
|
|
1110
|
+
// exercise audit.checkpoint() directly: insert one audit_log row by
|
|
1111
|
+
// hand into external-db, pre-seed the audit-tip with a higher token
|
|
1112
|
+
// than the local lease has, and assert the checkpoint surfaces the
|
|
1113
|
+
// FENCED_OUT class+code.
|
|
1114
|
+
b.cluster._resetForTest();
|
|
1115
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-fence-err-"));
|
|
1116
|
+
var dbPath = path.join(tmpDir, "ext.db");
|
|
1117
|
+
var driver = _makeSqliteDriver(dbPath);
|
|
1118
|
+
try {
|
|
1119
|
+
// Full framework boot needed because checkpoint() invokes audit-sign
|
|
1120
|
+
// (signature over the chain head) which requires the signing key
|
|
1121
|
+
// initialized via db.init.
|
|
1122
|
+
await setupTestDb(tmpDir);
|
|
1123
|
+
b.externalDb.init({
|
|
1124
|
+
backends: { "ops": { connect: driver.connect, query: driver.query, close: driver.close } },
|
|
1125
|
+
});
|
|
1126
|
+
await b.frameworkSchema.ensureSchema({
|
|
1127
|
+
externalDbBackend: "ops",
|
|
1128
|
+
dialect: "sqlite",
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
await b.cluster.init({
|
|
1132
|
+
nodeId: "fence-err-test-1",
|
|
1133
|
+
externalDbBackend: "ops",
|
|
1134
|
+
dialect: "sqlite",
|
|
1135
|
+
leaseTtl: b.constants.TIME.seconds(30),
|
|
1136
|
+
heartbeatInterval: b.constants.TIME.seconds(10),
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
// Insert one audit_log row directly so checkpoint has a tip to
|
|
1140
|
+
// anchor. clusterStorage rewrites `audit_log` → `_blamejs_audit_log`
|
|
1141
|
+
// in cluster mode, and we use `?` placeholders so the dispatcher's
|
|
1142
|
+
// dialect translation handles the parameterization.
|
|
1143
|
+
await b.clusterStorage.execute(
|
|
1144
|
+
"INSERT INTO audit_log " +
|
|
1145
|
+
" (_id, recordedAt, monotonicCounter, action, outcome, " +
|
|
1146
|
+
" prevHash, rowHash, nonce, fencingToken) " +
|
|
1147
|
+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
1148
|
+
["fence-row", Date.now(), 1, "system.test.fence", "success",
|
|
1149
|
+
"", "fence-hash-1", Buffer.alloc(16), 1]
|
|
1150
|
+
);
|
|
1151
|
+
|
|
1152
|
+
// Pre-seed _blamejs_audit_tip with a token (99) higher than this
|
|
1153
|
+
// node's lease (=1 right after acquire). The checkpoint's
|
|
1154
|
+
// _upsertAuditTip will write fencingToken=1, which is below the
|
|
1155
|
+
// stored 99 — the WHERE-clause guard rejects it.
|
|
1156
|
+
await b.clusterStorage.execute(
|
|
1157
|
+
"INSERT INTO _blamejs_audit_tip " +
|
|
1158
|
+
" (scope, atMonotonicCounter, rowHash, signedAt, fencingToken) " +
|
|
1159
|
+
"VALUES ('audit', ?, ?, ?, ?) " +
|
|
1160
|
+
"ON CONFLICT (scope) DO UPDATE SET " +
|
|
1161
|
+
" fencingToken = EXCLUDED.fencingToken",
|
|
1162
|
+
[0, "preseed", "0", 99]
|
|
1163
|
+
);
|
|
1164
|
+
|
|
1165
|
+
var threw = null;
|
|
1166
|
+
try { await b.audit.checkpoint(); }
|
|
1167
|
+
catch (e) { threw = e; }
|
|
1168
|
+
|
|
1169
|
+
check("checkpoint: fenced-out throws", threw !== null);
|
|
1170
|
+
check("checkpoint: error is ClusterError",
|
|
1171
|
+
threw && threw.isClusterError === true);
|
|
1172
|
+
check("checkpoint: error code is FENCED_OUT",
|
|
1173
|
+
threw && threw.code === "FENCED_OUT");
|
|
1174
|
+
check("checkpoint: error is permanent",
|
|
1175
|
+
threw && threw.permanent === true);
|
|
1176
|
+
} finally {
|
|
1177
|
+
try { await b.cluster.shutdown(); } catch (_e) {}
|
|
1178
|
+
try { await b.externalDb.shutdown(); } catch (_e) {}
|
|
1179
|
+
driver._close();
|
|
1180
|
+
await teardownTestDb(tmpDir);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
async function testAuditChain() {
|
|
1185
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-audit-"));
|
|
1186
|
+
try {
|
|
1187
|
+
await setupTestDb(tmpDir);
|
|
1188
|
+
|
|
1189
|
+
// Unregistered namespace rejected
|
|
1190
|
+
var nsRejected = false;
|
|
1191
|
+
try { await b.audit.record({ action: "orders.created", outcome: "success" }); }
|
|
1192
|
+
catch (_) { nsRejected = true; }
|
|
1193
|
+
check("unregistered namespace rejected", nsRejected);
|
|
1194
|
+
|
|
1195
|
+
// Register + record
|
|
1196
|
+
b.audit.registerNamespace("orders");
|
|
1197
|
+
var ev1 = await b.audit.record({
|
|
1198
|
+
actor: { userId: "user-1", ip: "1.2.3.4" },
|
|
1199
|
+
action: "orders.created",
|
|
1200
|
+
resource: { kind: "order", id: "ord-1" },
|
|
1201
|
+
outcome: "success",
|
|
1202
|
+
metadata: { total: 99.95 },
|
|
1203
|
+
});
|
|
1204
|
+
check("audit.record returns row with rowHash", typeof ev1.rowHash === "string" && ev1.rowHash.length === 128);
|
|
1205
|
+
check("first row's prevHash is ZERO_HASH", ev1.prevHash === b.auditChain.ZERO_HASH);
|
|
1206
|
+
|
|
1207
|
+
var ev2 = await b.audit.record({
|
|
1208
|
+
actor: { userId: "user-1", ip: "1.2.3.4" },
|
|
1209
|
+
action: "auth.login.success",
|
|
1210
|
+
resource: { kind: "user", id: "user-1" },
|
|
1211
|
+
outcome: "success",
|
|
1212
|
+
});
|
|
1213
|
+
check("second row's prevHash = first row's rowHash", ev2.prevHash === ev1.rowHash);
|
|
1214
|
+
check("monotonicCounter increments", ev2.monotonicCounter === ev1.monotonicCounter + 1);
|
|
1215
|
+
|
|
1216
|
+
// Invalid action format
|
|
1217
|
+
var actionRejected = false;
|
|
1218
|
+
try { await b.audit.record({ action: "no-dot", outcome: "success" }); }
|
|
1219
|
+
catch (_) { actionRejected = true; }
|
|
1220
|
+
check("malformed action rejected", actionRejected);
|
|
1221
|
+
|
|
1222
|
+
// Invalid outcome
|
|
1223
|
+
var outcomeRejected = false;
|
|
1224
|
+
try { await b.audit.record({ action: "auth.login.success", outcome: "ok" }); }
|
|
1225
|
+
catch (_) { outcomeRejected = true; }
|
|
1226
|
+
check("invalid outcome rejected", outcomeRejected);
|
|
1227
|
+
|
|
1228
|
+
// safeEmit: fire-and-forget shape with default-fill + try/catch.
|
|
1229
|
+
// Surface contract: never throws on malformed input; valid events
|
|
1230
|
+
// get queued via the handler (full end-to-end landing depends on
|
|
1231
|
+
// cluster init, covered by integration tests elsewhere).
|
|
1232
|
+
check("audit.safeEmit is exposed", typeof b.audit.safeEmit === "function");
|
|
1233
|
+
var threwOnMalformed = null;
|
|
1234
|
+
try {
|
|
1235
|
+
b.audit.safeEmit();
|
|
1236
|
+
b.audit.safeEmit(null);
|
|
1237
|
+
b.audit.safeEmit({}); // missing action
|
|
1238
|
+
b.audit.safeEmit({ outcome: "success" }); // missing action
|
|
1239
|
+
b.audit.safeEmit("not-an-object");
|
|
1240
|
+
} catch (e) { threwOnMalformed = e; }
|
|
1241
|
+
check("safeEmit: malformed inputs silently dropped", threwOnMalformed === null);
|
|
1242
|
+
var threwOnValid = null;
|
|
1243
|
+
try { b.audit.safeEmit({ action: "orders.shipped" }); }
|
|
1244
|
+
catch (e) { threwOnValid = e; }
|
|
1245
|
+
check("safeEmit: valid event accepted without throw", threwOnValid === null);
|
|
1246
|
+
|
|
1247
|
+
// Verify chain is intact
|
|
1248
|
+
var v1 = await b.audit.verify();
|
|
1249
|
+
check("audit.verify() ok after valid records", v1.ok === true && v1.rowsVerified === 2);
|
|
1250
|
+
|
|
1251
|
+
// Query by various criteria
|
|
1252
|
+
var byUser = await b.audit.query({ actorUserId: "user-1" });
|
|
1253
|
+
check("query by sealed actorUserId returns rows", byUser.length === 2);
|
|
1254
|
+
check("query result rows are unsealed", byUser[0].actorUserId === "user-1");
|
|
1255
|
+
var byAction = await b.audit.query({ action: "auth.login.success" });
|
|
1256
|
+
check("query by action returns matching", byAction.length === 1);
|
|
1257
|
+
var byKind = await b.audit.query({ resourceKind: "order" });
|
|
1258
|
+
check("query by resourceKind returns matching", byKind.length === 1);
|
|
1259
|
+
} finally {
|
|
1260
|
+
await teardownTestDb(tmpDir);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
async function testAuditChainBreak() {
|
|
1265
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-broken-"));
|
|
1266
|
+
try {
|
|
1267
|
+
await setupTestDb(tmpDir);
|
|
1268
|
+
b.audit.registerNamespace("test");
|
|
1269
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1270
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1271
|
+
var v1 = await b.audit.verify();
|
|
1272
|
+
check("chain ok before tampering", v1.ok === true);
|
|
1273
|
+
|
|
1274
|
+
// Manually corrupt a row's reason field. Currently the audit_log
|
|
1275
|
+
// table has BEFORE-UPDATE/DELETE triggers blocking direct mutation —
|
|
1276
|
+
// simulating a raw-DB-file tamper that bypassed those guards by
|
|
1277
|
+
// dropping the triggers around the corruption.
|
|
1278
|
+
b.db.runSql("DROP TRIGGER IF EXISTS no_update_audit_log");
|
|
1279
|
+
b.db.prepare('UPDATE audit_log SET reason = ? WHERE monotonicCounter = 1').run("vault:tampered-but-not-actually-sealed");
|
|
1280
|
+
b.db.runSql("CREATE TRIGGER IF NOT EXISTS no_update_audit_log BEFORE UPDATE ON audit_log BEGIN SELECT RAISE(ABORT, 'audit_log is append-only — UPDATE prohibited'); END");
|
|
1281
|
+
var v2 = await b.audit.verify();
|
|
1282
|
+
check("chain detected after row tampering", v2.ok === false);
|
|
1283
|
+
check("chain break reports breakAt index", v2.breakAt === 0 || v2.breakAt === 1);
|
|
1284
|
+
check("chain break reports rowHash mismatch reason",
|
|
1285
|
+
v2.reason === "rowHash mismatch" || v2.reason === "prevHash mismatch");
|
|
1286
|
+
} finally {
|
|
1287
|
+
await teardownTestDb(tmpDir);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
async function testAuditSelfLogging() {
|
|
1292
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-selflog-"));
|
|
1293
|
+
try {
|
|
1294
|
+
await setupTestDb(tmpDir);
|
|
1295
|
+
b.audit.registerNamespace("test");
|
|
1296
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1297
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1298
|
+
|
|
1299
|
+
// A query auto-records an audit.read event before returning rows
|
|
1300
|
+
var beforeCount = b.db.from("audit_log").count();
|
|
1301
|
+
var rows = await b.audit.query({ action: "test.event" });
|
|
1302
|
+
var afterCount = b.db.from("audit_log").count();
|
|
1303
|
+
check("query returned both test.event rows", rows.length === 2);
|
|
1304
|
+
check("query auto-recorded an audit.read event", afterCount === beforeCount + 1);
|
|
1305
|
+
|
|
1306
|
+
// The audit.read row exists
|
|
1307
|
+
var readRows = await b.audit.query({ action: "audit.read" });
|
|
1308
|
+
check("audit.read events queryable directly", readRows.length >= 1);
|
|
1309
|
+
check("audit.read row has criteria metadata",
|
|
1310
|
+
readRows[0].metadata && /criteria/.test(readRows[0].metadata));
|
|
1311
|
+
|
|
1312
|
+
// Querying for audit.read does NOT recursively self-log (else infinite chain)
|
|
1313
|
+
var beforeRecursionCheck = b.db.from("audit_log").count();
|
|
1314
|
+
await b.audit.query({ action: "audit.read" });
|
|
1315
|
+
var afterRecursionCheck = b.db.from("audit_log").count();
|
|
1316
|
+
check("query for audit.read does NOT auto-self-log", afterRecursionCheck === beforeRecursionCheck);
|
|
1317
|
+
|
|
1318
|
+
// Audit chain still verifies through all the self-logging
|
|
1319
|
+
var v = await b.audit.verify();
|
|
1320
|
+
check("audit chain ok after self-log activity", v.ok === true);
|
|
1321
|
+
} finally {
|
|
1322
|
+
await teardownTestDb(tmpDir);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
async function testBeginTrace() {
|
|
1327
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-trace-"));
|
|
1328
|
+
try {
|
|
1329
|
+
await setupTestDb(tmpDir);
|
|
1330
|
+
b.audit.registerNamespace("test");
|
|
1331
|
+
|
|
1332
|
+
var t1 = b.audit.beginTrace();
|
|
1333
|
+
var t2 = b.audit.beginTrace();
|
|
1334
|
+
check("beginTrace returns 32-hex string", typeof t1 === "string" && t1.length === 32 && /^[0-9a-f]+$/.test(t1));
|
|
1335
|
+
check("beginTrace returns unique values", t1 !== t2);
|
|
1336
|
+
|
|
1337
|
+
// Apps thread the traceId through linked events
|
|
1338
|
+
var ev1 = await b.audit.record({
|
|
1339
|
+
action: "test.start",
|
|
1340
|
+
outcome: "success",
|
|
1341
|
+
metadata: { traceId: t1 },
|
|
1342
|
+
});
|
|
1343
|
+
var ev2 = await b.audit.record({
|
|
1344
|
+
action: "test.continue",
|
|
1345
|
+
outcome: "success",
|
|
1346
|
+
metadata: { traceId: t1, parentEventId: ev1._id },
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
// Query and verify trace correlation is queryable from metadata
|
|
1350
|
+
var rows = await b.audit.query({ action: "test.start" });
|
|
1351
|
+
var meta = JSON.parse(rows[0].metadata);
|
|
1352
|
+
check("traceId persists into audit row metadata", meta.traceId === t1);
|
|
1353
|
+
|
|
1354
|
+
var rows2 = await b.audit.query({ action: "test.continue" });
|
|
1355
|
+
var meta2 = JSON.parse(rows2[0].metadata);
|
|
1356
|
+
check("parentEventId persists into audit row", meta2.parentEventId === ev1._id);
|
|
1357
|
+
check("traceId is shared across linked events", meta2.traceId === t1);
|
|
1358
|
+
|
|
1359
|
+
void ev2;
|
|
1360
|
+
} finally {
|
|
1361
|
+
await teardownTestDb(tmpDir);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
async function testConsent() {
|
|
1366
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-consent-"));
|
|
1367
|
+
try {
|
|
1368
|
+
await setupTestDb(tmpDir);
|
|
1369
|
+
|
|
1370
|
+
var subjectId = "user-7";
|
|
1371
|
+
check("isGranted is false before grant", b.consent.isGranted({ subjectId, purpose: "marketing.email" }) === false);
|
|
1372
|
+
|
|
1373
|
+
await b.consent.grant({
|
|
1374
|
+
subjectId: subjectId,
|
|
1375
|
+
purpose: "marketing.email",
|
|
1376
|
+
lawfulBasis: "consent",
|
|
1377
|
+
scope: { channels: ["email"], topics: ["product-updates"] },
|
|
1378
|
+
channel: "web_form_v2",
|
|
1379
|
+
evidenceRef: "/evidence/forms/2026-04-25T...",
|
|
1380
|
+
});
|
|
1381
|
+
check("isGranted true after grant", b.consent.isGranted({ subjectId, purpose: "marketing.email" }) === true);
|
|
1382
|
+
|
|
1383
|
+
await b.consent.withdraw({ subjectId, purpose: "marketing.email" });
|
|
1384
|
+
check("isGranted false after withdraw", b.consent.isGranted({ subjectId, purpose: "marketing.email" }) === false);
|
|
1385
|
+
|
|
1386
|
+
var hist = b.consent.history(subjectId);
|
|
1387
|
+
check("history returns 2 events", hist.length === 2);
|
|
1388
|
+
check("history first event is grant", hist[0].action === "granted");
|
|
1389
|
+
check("history second event is withdraw", hist[1].action === "withdrawn");
|
|
1390
|
+
check("history unsealed subjectId", hist[0].subjectId === subjectId);
|
|
1391
|
+
|
|
1392
|
+
var cv = await b.consent.verify();
|
|
1393
|
+
check("consent.verify() ok", cv.ok === true && cv.rowsVerified === 2);
|
|
1394
|
+
|
|
1395
|
+
// Invalid lawful basis
|
|
1396
|
+
var basisRejected = false;
|
|
1397
|
+
try { await b.consent.grant({ subjectId, purpose: "x", lawfulBasis: "bogus", channel: "x" }); }
|
|
1398
|
+
catch (_) { basisRejected = true; }
|
|
1399
|
+
check("invalid lawfulBasis rejected", basisRejected);
|
|
1400
|
+
} finally {
|
|
1401
|
+
await teardownTestDb(tmpDir);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
async function testSubjectRights() {
|
|
1406
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-subject-"));
|
|
1407
|
+
try {
|
|
1408
|
+
b.vault._resetForTest();
|
|
1409
|
+
b.db._resetForTest();
|
|
1410
|
+
await b.vault.init({ dataDir: tmpDir, mode: "plaintext" });
|
|
1411
|
+
await b.db.init({
|
|
1412
|
+
dataDir: tmpDir,
|
|
1413
|
+
atRest: "plain",
|
|
1414
|
+
auditSigning: { mode: "plaintext" },
|
|
1415
|
+
schema: [
|
|
1416
|
+
{
|
|
1417
|
+
name: "users",
|
|
1418
|
+
columns: {
|
|
1419
|
+
_id: "TEXT PRIMARY KEY",
|
|
1420
|
+
email: "TEXT",
|
|
1421
|
+
emailHash: "TEXT",
|
|
1422
|
+
name: "TEXT",
|
|
1423
|
+
},
|
|
1424
|
+
indexes: ["emailHash"],
|
|
1425
|
+
sealedFields: ["email", "name"],
|
|
1426
|
+
derivedHashes: { emailHash: { from: "email", normalize: function (v) { return String(v).toLowerCase(); } } },
|
|
1427
|
+
subjectField: "_id",
|
|
1428
|
+
personalDataCategories: { email: "email", name: "name" },
|
|
1429
|
+
},
|
|
1430
|
+
{
|
|
1431
|
+
name: "orders",
|
|
1432
|
+
columns: {
|
|
1433
|
+
_id: "TEXT PRIMARY KEY",
|
|
1434
|
+
userId: "TEXT",
|
|
1435
|
+
userIdHash: "TEXT",
|
|
1436
|
+
amount: "REAL",
|
|
1437
|
+
},
|
|
1438
|
+
indexes: ["userIdHash"],
|
|
1439
|
+
sealedFields: [],
|
|
1440
|
+
derivedHashes: { userIdHash: { from: "userId" } },
|
|
1441
|
+
subjectField: "userId",
|
|
1442
|
+
personalDataCategories: {},
|
|
1443
|
+
},
|
|
1444
|
+
],
|
|
1445
|
+
});
|
|
1446
|
+
|
|
1447
|
+
b.db.from("users").insertOne({ _id: "u-alice", email: "alice@x.com", name: "Alice" });
|
|
1448
|
+
b.db.from("users").insertOne({ _id: "u-bob", email: "bob@x.com", name: "Bob" });
|
|
1449
|
+
b.db.from("orders").insertOne({ _id: "o-1", userId: "u-alice", amount: 99.95 });
|
|
1450
|
+
b.db.from("orders").insertOne({ _id: "o-2", userId: "u-alice", amount: 12.50 });
|
|
1451
|
+
b.db.from("orders").insertOne({ _id: "o-3", userId: "u-bob", amount: 7.00 });
|
|
1452
|
+
|
|
1453
|
+
// Export
|
|
1454
|
+
var dump = b.subject.export("u-alice", { reason: "Art. 15 access request 2026-04-25" });
|
|
1455
|
+
check("subject.export returns dump for alice", dump.users && dump.users.length === 1);
|
|
1456
|
+
check("subject.export decrypts sealed fields", dump.users[0].email === "alice@x.com");
|
|
1457
|
+
check("subject.export walks orders too", dump.orders && dump.orders.length === 2);
|
|
1458
|
+
|
|
1459
|
+
// Rectify
|
|
1460
|
+
var ok = b.subject.rectify("u-alice", {
|
|
1461
|
+
table: "users",
|
|
1462
|
+
id: "u-alice",
|
|
1463
|
+
changes: { name: "Alice Updated" },
|
|
1464
|
+
reason: "Art. 16 rectification 2026-04-25",
|
|
1465
|
+
});
|
|
1466
|
+
check("rectify returns true", ok === true);
|
|
1467
|
+
var aliceAfter = b.db.from("users").where({ _id: "u-alice" }).first();
|
|
1468
|
+
check("rectify wrote new value", aliceAfter.name === "Alice Updated");
|
|
1469
|
+
|
|
1470
|
+
// Erase requires both acknowledgements
|
|
1471
|
+
var noAckRejected = false;
|
|
1472
|
+
try { b.subject.erase("u-alice", { reason: "Art. 17", acknowledgements: ["no-litigation-hold"] }); }
|
|
1473
|
+
catch (_) { noAckRejected = true; }
|
|
1474
|
+
check("erase without all acknowledgements rejected", noAckRejected);
|
|
1475
|
+
|
|
1476
|
+
// Erase with all acks
|
|
1477
|
+
var result = b.subject.erase("u-alice", {
|
|
1478
|
+
reason: "Art. 17 erasure request 2026-04-25 ticket #4471",
|
|
1479
|
+
acknowledgements: ["no-litigation-hold", "no-statutory-retention-required"],
|
|
1480
|
+
});
|
|
1481
|
+
check("erase returns rowsDeleted", result.rowsDeleted >= 3);
|
|
1482
|
+
check("alice gone from users", b.db.from("users").where({ _id: "u-alice" }).first() === null);
|
|
1483
|
+
check("alice's orders gone", b.db.from("orders").where({ userIdHash: b.db.hashFor("orders", "userId", "u-alice") }).all().length === 0);
|
|
1484
|
+
check("bob still present", b.db.from("users").where({ _id: "u-bob" }).first() !== null);
|
|
1485
|
+
|
|
1486
|
+
// Erasure marker recorded
|
|
1487
|
+
var erasureRow = b.db.prepare("SELECT subjectIdHash FROM _blamejs_subject_erasures").all();
|
|
1488
|
+
check("subject erasure marker recorded", erasureRow.length === 1);
|
|
1489
|
+
|
|
1490
|
+
// Restrict / isRestricted
|
|
1491
|
+
check("isRestricted false initially", b.subject.isRestricted("u-bob") === false);
|
|
1492
|
+
b.subject.restrict("u-bob", { on: true, reason: "Art. 18 contested accuracy" });
|
|
1493
|
+
check("isRestricted true after restrict", b.subject.isRestricted("u-bob") === true);
|
|
1494
|
+
b.subject.restrict("u-bob", { on: false });
|
|
1495
|
+
check("isRestricted false after lift", b.subject.isRestricted("u-bob") === false);
|
|
1496
|
+
|
|
1497
|
+
// Audit chain still intact after all this activity
|
|
1498
|
+
var av = await b.audit.verify();
|
|
1499
|
+
check("audit chain intact through subject ops", av.ok === true);
|
|
1500
|
+
} finally {
|
|
1501
|
+
await teardownTestDb(tmpDir);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
async function testAppendOnlyTriggers() {
|
|
1506
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-trig-"));
|
|
1507
|
+
try {
|
|
1508
|
+
await setupTestDb(tmpDir);
|
|
1509
|
+
b.audit.registerNamespace("test");
|
|
1510
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1511
|
+
|
|
1512
|
+
var deleteRejected = false;
|
|
1513
|
+
try { b.db.runSql("DELETE FROM audit_log"); }
|
|
1514
|
+
catch (e) { deleteRejected = /append-only|prohibited/i.test(e.message); }
|
|
1515
|
+
check("DELETE on audit_log raises ABORT", deleteRejected);
|
|
1516
|
+
|
|
1517
|
+
var updateRejected = false;
|
|
1518
|
+
try { b.db.runSql("UPDATE audit_log SET outcome = 'denied' WHERE 1=1"); }
|
|
1519
|
+
catch (e) { updateRejected = /append-only|prohibited/i.test(e.message); }
|
|
1520
|
+
check("UPDATE on audit_log raises ABORT", updateRejected);
|
|
1521
|
+
|
|
1522
|
+
// consent_log
|
|
1523
|
+
await b.consent.grant({ subjectId: "u-1", purpose: "x", lawfulBasis: "consent", channel: "api" });
|
|
1524
|
+
var conDelRejected = false;
|
|
1525
|
+
try { b.db.runSql("DELETE FROM consent_log"); }
|
|
1526
|
+
catch (e) { conDelRejected = /append-only|prohibited/i.test(e.message); }
|
|
1527
|
+
check("DELETE on consent_log raises ABORT", conDelRejected);
|
|
1528
|
+
|
|
1529
|
+
// INSERT still works (the framework's API uses it constantly above)
|
|
1530
|
+
var counts = b.db.prepare("SELECT (SELECT COUNT(*) FROM audit_log) AS a, (SELECT COUNT(*) FROM consent_log) AS c").get();
|
|
1531
|
+
check("INSERT on audit_log still works", counts.a >= 1);
|
|
1532
|
+
check("INSERT on consent_log still works", counts.c >= 1);
|
|
1533
|
+
} finally {
|
|
1534
|
+
await teardownTestDb(tmpDir);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
async function testForeignKeys() {
|
|
1539
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-fk-"));
|
|
1540
|
+
try {
|
|
1541
|
+
process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
|
|
1542
|
+
b.vault._resetForTest();
|
|
1543
|
+
b.db._resetForTest();
|
|
1544
|
+
await b.vault.init({ dataDir: tmpDir, mode: "plaintext" });
|
|
1545
|
+
await b.db.init({
|
|
1546
|
+
dataDir: tmpDir,
|
|
1547
|
+
atRest: "plain",
|
|
1548
|
+
auditSigning: { mode: "plaintext" },
|
|
1549
|
+
schema: [
|
|
1550
|
+
{
|
|
1551
|
+
name: "users",
|
|
1552
|
+
columns: { _id: "TEXT", email: "TEXT", emailHash: "TEXT" },
|
|
1553
|
+
primaryKey: "_id",
|
|
1554
|
+
indexes: ["emailHash"],
|
|
1555
|
+
sealedFields: ["email"],
|
|
1556
|
+
derivedHashes: { emailHash: { from: "email", normalize: function (v) { return String(v).toLowerCase(); } } },
|
|
1557
|
+
},
|
|
1558
|
+
{
|
|
1559
|
+
name: "orders",
|
|
1560
|
+
columns: { _id: "TEXT", userId: "TEXT NOT NULL", amount: "REAL" },
|
|
1561
|
+
primaryKey: "_id",
|
|
1562
|
+
foreignKeys: [{ column: "userId", references: "users._id", onDelete: "CASCADE" }],
|
|
1563
|
+
},
|
|
1564
|
+
],
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
// Verify foreign_keys pragma is ON
|
|
1568
|
+
var fkPragma = b.db.prepare("PRAGMA foreign_keys").get();
|
|
1569
|
+
check("foreign_keys pragma is enabled", fkPragma.foreign_keys === 1);
|
|
1570
|
+
|
|
1571
|
+
// Verify FK declared in DDL
|
|
1572
|
+
var fkInfo = b.db.prepare("PRAGMA foreign_key_list(orders)").all();
|
|
1573
|
+
check("orders has 1 FK declared", fkInfo.length === 1);
|
|
1574
|
+
check("FK references users(_id)", fkInfo[0].table === "users" && fkInfo[0].from === "userId" && fkInfo[0].to === "_id");
|
|
1575
|
+
check("FK on_delete is CASCADE", fkInfo[0].on_delete === "CASCADE");
|
|
1576
|
+
|
|
1577
|
+
// Insert valid user + order
|
|
1578
|
+
b.db.from("users").insertOne({ _id: "u-1", email: "a@b.com" });
|
|
1579
|
+
b.db.from("orders").insertOne({ _id: "o-1", userId: "u-1", amount: 100 });
|
|
1580
|
+
check("valid order insert succeeds", b.db.from("orders").where({ _id: "o-1" }).first() !== null);
|
|
1581
|
+
|
|
1582
|
+
// FK violation: order with non-existent userId
|
|
1583
|
+
var fkViolated = false;
|
|
1584
|
+
try { b.db.from("orders").insertOne({ _id: "o-2", userId: "u-nonexistent", amount: 50 }); }
|
|
1585
|
+
catch (e) { fkViolated = /FOREIGN KEY|constraint/i.test(e.message); }
|
|
1586
|
+
check("FK violation rejects insert", fkViolated);
|
|
1587
|
+
|
|
1588
|
+
// Cascade delete: deleting user removes their orders
|
|
1589
|
+
b.db.from("users").where({ _id: "u-1" }).deleteOne();
|
|
1590
|
+
check("ON DELETE CASCADE removes child rows", b.db.from("orders").where({ _id: "o-1" }).first() === null);
|
|
1591
|
+
} finally {
|
|
1592
|
+
await teardownTestDb(tmpDir);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
async function testTableMetadata() {
|
|
1597
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-meta-"));
|
|
1598
|
+
try {
|
|
1599
|
+
process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
|
|
1600
|
+
b.vault._resetForTest();
|
|
1601
|
+
b.db._resetForTest();
|
|
1602
|
+
await b.vault.init({ dataDir: tmpDir, mode: "plaintext" });
|
|
1603
|
+
await b.db.init({
|
|
1604
|
+
dataDir: tmpDir,
|
|
1605
|
+
atRest: "plain",
|
|
1606
|
+
auditSigning: { mode: "plaintext" },
|
|
1607
|
+
schema: [
|
|
1608
|
+
{
|
|
1609
|
+
name: "items",
|
|
1610
|
+
columns: { _id: "TEXT", ownerId: "TEXT", name: "TEXT", nameHash: "TEXT" },
|
|
1611
|
+
primaryKey: "_id",
|
|
1612
|
+
foreignKeys: [{ column: "ownerId", references: "users._id", onDelete: "SET NULL" }],
|
|
1613
|
+
indexes: ["nameHash"],
|
|
1614
|
+
sealedFields: ["name"],
|
|
1615
|
+
derivedHashes: { nameHash: { from: "name" } },
|
|
1616
|
+
subjectField: "ownerId",
|
|
1617
|
+
personalDataCategories: { name: "label" },
|
|
1618
|
+
},
|
|
1619
|
+
// users table with no FKs
|
|
1620
|
+
{ name: "users", columns: { _id: "TEXT" }, primaryKey: "_id" },
|
|
1621
|
+
],
|
|
1622
|
+
});
|
|
1623
|
+
|
|
1624
|
+
var meta = b.db.getTableMetadata("items");
|
|
1625
|
+
check("metadata returns object", typeof meta === "object" && meta !== null);
|
|
1626
|
+
check("metadata.primaryKey is array", Array.isArray(meta.primaryKey) && meta.primaryKey[0] === "_id");
|
|
1627
|
+
check("metadata.foreignKeys captured", meta.foreignKeys.length === 1 && meta.foreignKeys[0].references === "users._id");
|
|
1628
|
+
check("metadata.sealedFields captured", meta.sealedFields[0] === "name");
|
|
1629
|
+
check("metadata.subjectField captured", meta.subjectField === "ownerId");
|
|
1630
|
+
check("metadata.personalDataCategories captured", meta.personalDataCategories.name === "label");
|
|
1631
|
+
|
|
1632
|
+
// Framework tables also show up in metadata
|
|
1633
|
+
var auditMeta = b.db.getTableMetadata("audit_log");
|
|
1634
|
+
check("audit_log metadata available", auditMeta !== null);
|
|
1635
|
+
check("audit_log primaryKey is _id", auditMeta.primaryKey[0] === "_id");
|
|
1636
|
+
|
|
1637
|
+
// Mutating the snapshot doesn't affect framework state
|
|
1638
|
+
meta.foreignKeys.push({ column: "fake" });
|
|
1639
|
+
var freshMeta = b.db.getTableMetadata("items");
|
|
1640
|
+
check("metadata snapshot is deep-copied", freshMeta.foreignKeys.length === 1);
|
|
1641
|
+
} finally {
|
|
1642
|
+
await teardownTestDb(tmpDir);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
async function testAuditSignDefaultsToSlhDsa() {
|
|
1647
|
+
// First-run plaintext init in a fresh data directory should generate
|
|
1648
|
+
// an SLH-DSA-SHAKE-256f keypair (the current default), and the
|
|
1649
|
+
// on-disk file should record `algorithm: "slh-dsa-shake-256f"` so
|
|
1650
|
+
// future loads dispatch correctly without re-detection.
|
|
1651
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-asd-"));
|
|
1652
|
+
try {
|
|
1653
|
+
// This test inspects the on-disk JSON content of audit-sign.key,
|
|
1654
|
+
// which only exists in plaintext audit-signing mode. Override
|
|
1655
|
+
// setupTestDb's secure-mode default explicitly.
|
|
1656
|
+
process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
|
|
1657
|
+
b.cluster._resetForTest();
|
|
1658
|
+
b.audit._resetForTest();
|
|
1659
|
+
b.vault._resetForTest();
|
|
1660
|
+
b.db._resetForTest();
|
|
1661
|
+
await b.vault.init({ dataDir: tmpDir, mode: "plaintext" });
|
|
1662
|
+
await b.db.init({
|
|
1663
|
+
dataDir: tmpDir,
|
|
1664
|
+
atRest: "plain",
|
|
1665
|
+
auditSigning: { mode: "plaintext" },
|
|
1666
|
+
schema: [],
|
|
1667
|
+
});
|
|
1668
|
+
check("auditSign.getAlgorithm is exposed",
|
|
1669
|
+
typeof b.auditSign.getAlgorithm === "function");
|
|
1670
|
+
check("auditSign default alg is SLH-DSA-SHAKE-256f",
|
|
1671
|
+
b.auditSign.getAlgorithm() === "slh-dsa-shake-256f");
|
|
1672
|
+
check("auditSign exposes DEFAULT_SIGNING_ALG constant",
|
|
1673
|
+
b.auditSign.DEFAULT_SIGNING_ALG === "slh-dsa-shake-256f");
|
|
1674
|
+
check("auditSign exposes SUPPORTED_SIGNING_ALGS",
|
|
1675
|
+
Array.isArray(b.auditSign.SUPPORTED_SIGNING_ALGS) &&
|
|
1676
|
+
b.auditSign.SUPPORTED_SIGNING_ALGS.indexOf("slh-dsa-shake-256f") !== -1 &&
|
|
1677
|
+
b.auditSign.SUPPORTED_SIGNING_ALGS.indexOf("ml-dsa-87") !== -1);
|
|
1678
|
+
|
|
1679
|
+
// On-disk file records the algorithm
|
|
1680
|
+
var keyJson = JSON.parse(fs.readFileSync(path.join(tmpDir, "audit-sign.key"), "utf8"));
|
|
1681
|
+
check("on-disk key file records algorithm field",
|
|
1682
|
+
keyJson.algorithm === "slh-dsa-shake-256f");
|
|
1683
|
+
check("on-disk public key is SLH-DSA SPKI PEM",
|
|
1684
|
+
/BEGIN PUBLIC KEY/.test(keyJson.publicKey));
|
|
1685
|
+
|
|
1686
|
+
// Sign + verify round-trip works with the SLH-DSA-SHAKE-256f key
|
|
1687
|
+
var sig = b.auditSign.sign("hello from slh-dsa");
|
|
1688
|
+
check("SLH-DSA sign returns a Buffer", Buffer.isBuffer(sig));
|
|
1689
|
+
// SLH-DSA-SHAKE-256f signatures are ~50 KB
|
|
1690
|
+
check("SLH-DSA-SHAKE-256f signature size matches FIPS 205 (~50 KB)",
|
|
1691
|
+
sig.length > 49000 && sig.length < 51000);
|
|
1692
|
+
check("SLH-DSA verify accepts the signature",
|
|
1693
|
+
b.auditSign.verify("hello from slh-dsa", sig) === true);
|
|
1694
|
+
check("SLH-DSA verify rejects altered payload",
|
|
1695
|
+
b.auditSign.verify("hello from slh-dsa!", sig) === false);
|
|
1696
|
+
} finally {
|
|
1697
|
+
await teardownTestDb(tmpDir);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
async function testAuditSignMlDsaOptIn() {
|
|
1702
|
+
// Operators with throughput-sensitive deployments can opt into
|
|
1703
|
+
// ml-dsa-87 at db.init via auditSigning: { algorithm: "ml-dsa-87" }.
|
|
1704
|
+
// Verify the option propagates and produces a working ML-DSA-87 key.
|
|
1705
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-asm-"));
|
|
1706
|
+
try {
|
|
1707
|
+
process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
|
|
1708
|
+
process.env.BLAMEJS_AUDIT_SIGNING_MODE = "plaintext";
|
|
1709
|
+
b.cluster._resetForTest();
|
|
1710
|
+
b.audit._resetForTest();
|
|
1711
|
+
b.vault._resetForTest();
|
|
1712
|
+
b.db._resetForTest();
|
|
1713
|
+
await b.vault.init({ dataDir: tmpDir, mode: "plaintext" });
|
|
1714
|
+
await b.db.init({
|
|
1715
|
+
dataDir: tmpDir,
|
|
1716
|
+
atRest: "plain",
|
|
1717
|
+
auditSigning: { mode: "plaintext", algorithm: "ml-dsa-87" },
|
|
1718
|
+
schema: [],
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
check("opt-in alg honored in keys.algorithm",
|
|
1722
|
+
b.auditSign.getAlgorithm() === "ml-dsa-87");
|
|
1723
|
+
var keyJson = JSON.parse(fs.readFileSync(path.join(tmpDir, "audit-sign.key"), "utf8"));
|
|
1724
|
+
check("on-disk key file records ml-dsa-87",
|
|
1725
|
+
keyJson.algorithm === "ml-dsa-87");
|
|
1726
|
+
|
|
1727
|
+
var sig = b.auditSign.sign("hello from ml-dsa-87");
|
|
1728
|
+
// ML-DSA-87 signatures are ~5 KB — order-of-magnitude smaller than SLH-DSA
|
|
1729
|
+
check("ML-DSA-87 signature size matches FIPS 204 (~5 KB)",
|
|
1730
|
+
sig.length > 4000 && sig.length < 6000);
|
|
1731
|
+
check("ML-DSA-87 verify round-trip",
|
|
1732
|
+
b.auditSign.verify("hello from ml-dsa-87", sig) === true);
|
|
1733
|
+
} finally {
|
|
1734
|
+
await teardownTestDb(tmpDir);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
async function testAuditSignLegacyFileBackcompat() {
|
|
1739
|
+
// Pre-v1 compat-shim sweep removed the implicit ml-dsa-87 fallback
|
|
1740
|
+
// for key files missing the `algorithm` field. Such files now throw
|
|
1741
|
+
// KEY_FILE_MISSING_ALG at boot — operators rotate the key (deletes
|
|
1742
|
+
// the file and boots fresh) or hand-edit to add the field.
|
|
1743
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-asl-"));
|
|
1744
|
+
try {
|
|
1745
|
+
process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
|
|
1746
|
+
|
|
1747
|
+
var nodeCrypto = require("crypto");
|
|
1748
|
+
var pair = nodeCrypto.generateKeyPairSync("ml-dsa-87", {
|
|
1749
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
1750
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
1751
|
+
});
|
|
1752
|
+
fs.writeFileSync(
|
|
1753
|
+
path.join(tmpDir, "audit-sign.key"),
|
|
1754
|
+
JSON.stringify({ publicKey: pair.publicKey, privateKey: pair.privateKey }, null, 2),
|
|
1755
|
+
{ mode: 0o600 }
|
|
1756
|
+
);
|
|
1757
|
+
|
|
1758
|
+
process.env.BLAMEJS_AUDIT_SIGNING_MODE = "plaintext";
|
|
1759
|
+
b.cluster._resetForTest();
|
|
1760
|
+
b.audit._resetForTest();
|
|
1761
|
+
b.vault._resetForTest();
|
|
1762
|
+
b.db._resetForTest();
|
|
1763
|
+
await b.vault.init({ dataDir: tmpDir, mode: "plaintext" });
|
|
1764
|
+
|
|
1765
|
+
var threw = null;
|
|
1766
|
+
try {
|
|
1767
|
+
await b.db.init({
|
|
1768
|
+
dataDir: tmpDir,
|
|
1769
|
+
atRest: "plain",
|
|
1770
|
+
auditSigning: { mode: "plaintext" },
|
|
1771
|
+
schema: [],
|
|
1772
|
+
});
|
|
1773
|
+
} catch (e) { threw = e; }
|
|
1774
|
+
check("legacy file (no algorithm field) refuses to load — explicit alg required",
|
|
1775
|
+
threw && /MISSING_ALG|missing.*algorithm/i.test(threw.code || threw.message || ""));
|
|
1776
|
+
} finally {
|
|
1777
|
+
await teardownTestDb(tmpDir);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
async function testAuditSignRejectsUnsupportedAlgorithm() {
|
|
1782
|
+
// Typo or unsupported alg name surfaces at init time, not as a deeper
|
|
1783
|
+
// "key generation failed" error from nodeCrypto.
|
|
1784
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-asbad-"));
|
|
1785
|
+
try {
|
|
1786
|
+
process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
|
|
1787
|
+
process.env.BLAMEJS_AUDIT_SIGNING_MODE = "plaintext";
|
|
1788
|
+
b.cluster._resetForTest();
|
|
1789
|
+
b.audit._resetForTest();
|
|
1790
|
+
b.vault._resetForTest();
|
|
1791
|
+
b.db._resetForTest();
|
|
1792
|
+
await b.vault.init({ dataDir: tmpDir, mode: "plaintext" });
|
|
1793
|
+
|
|
1794
|
+
var threw = null;
|
|
1795
|
+
try {
|
|
1796
|
+
await b.db.init({
|
|
1797
|
+
dataDir: tmpDir,
|
|
1798
|
+
atRest: "plain",
|
|
1799
|
+
auditSigning: { mode: "plaintext", algorithm: "ed25519" }, // not in supported list
|
|
1800
|
+
schema: [],
|
|
1801
|
+
});
|
|
1802
|
+
} catch (e) { threw = e; }
|
|
1803
|
+
check("unsupported algorithm rejected at init",
|
|
1804
|
+
threw && /algorithm must be one of/.test(threw.message));
|
|
1805
|
+
} finally {
|
|
1806
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch (_e) {}
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
async function testCheckpointSign() {
|
|
1811
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-ckpt-"));
|
|
1812
|
+
try {
|
|
1813
|
+
await setupTestDb(tmpDir);
|
|
1814
|
+
|
|
1815
|
+
// auditSign module surface
|
|
1816
|
+
check("auditSign namespace present", typeof b.auditSign === "object");
|
|
1817
|
+
check("auditSign.getPublicKey is a function", typeof b.auditSign.getPublicKey === "function");
|
|
1818
|
+
check("auditSign.getPublicKeyFingerprint works",
|
|
1819
|
+
typeof b.auditSign.getPublicKeyFingerprint() === "string" &&
|
|
1820
|
+
b.auditSign.getPublicKeyFingerprint().length === 128);
|
|
1821
|
+
|
|
1822
|
+
// audit-sign keypair file written (wrapped mode is what setupTestDb
|
|
1823
|
+
// configures, matching production posture; sealed file is the
|
|
1824
|
+
// expected on-disk artifact).
|
|
1825
|
+
check("audit-sign.key.sealed file exists in wrapped mode",
|
|
1826
|
+
fs.existsSync(path.join(tmpDir, "audit-sign.key.sealed")));
|
|
1827
|
+
|
|
1828
|
+
// Empty audit_log → checkpoint returns null (nothing to anchor)
|
|
1829
|
+
var emptyResult = await b.audit.checkpoint();
|
|
1830
|
+
check("checkpoint() on empty log returns null", emptyResult === null);
|
|
1831
|
+
|
|
1832
|
+
// Record and checkpoint
|
|
1833
|
+
b.audit.registerNamespace("test");
|
|
1834
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1835
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1836
|
+
var ckpt = await b.audit.checkpoint();
|
|
1837
|
+
check("checkpoint() returns a checkpoint object", ckpt && typeof ckpt._id === "string");
|
|
1838
|
+
check("checkpoint anchors monotonic counter", typeof ckpt.atMonotonicCounter === "number");
|
|
1839
|
+
check("checkpoint includes pubkey fingerprint",
|
|
1840
|
+
ckpt.publicKeyFingerprint === b.auditSign.getPublicKeyFingerprint());
|
|
1841
|
+
|
|
1842
|
+
// skipIfUnchanged: second call with no new audit activity returns null
|
|
1843
|
+
var skipResult = await b.audit.checkpoint({ skipIfUnchanged: true });
|
|
1844
|
+
check("checkpoint(skipIfUnchanged) on unchanged log returns null", skipResult === null);
|
|
1845
|
+
|
|
1846
|
+
// After more activity, skipIfUnchanged anchors a new checkpoint
|
|
1847
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1848
|
+
var freshCkpt = await b.audit.checkpoint({ skipIfUnchanged: true });
|
|
1849
|
+
check("skipIfUnchanged anchors when chain advances", freshCkpt !== null);
|
|
1850
|
+
check("new checkpoint counter > prior checkpoint", freshCkpt.atMonotonicCounter > ckpt.atMonotonicCounter);
|
|
1851
|
+
|
|
1852
|
+
// audit.tip sidecar written
|
|
1853
|
+
var tipPath = path.join(tmpDir, "audit.tip");
|
|
1854
|
+
check("audit.tip sidecar written", fs.existsSync(tipPath));
|
|
1855
|
+
var tip = JSON.parse(fs.readFileSync(tipPath, "utf8"));
|
|
1856
|
+
check("audit.tip records latest counter", tip.atMonotonicCounter === freshCkpt.atMonotonicCounter);
|
|
1857
|
+
} finally {
|
|
1858
|
+
await teardownTestDb(tmpDir);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
async function testCheckpointVerify() {
|
|
1863
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cverify-"));
|
|
1864
|
+
try {
|
|
1865
|
+
await setupTestDb(tmpDir);
|
|
1866
|
+
b.audit.registerNamespace("test");
|
|
1867
|
+
|
|
1868
|
+
// Empty case
|
|
1869
|
+
var v0 = await b.audit.verifyCheckpoints();
|
|
1870
|
+
check("verifyCheckpoints empty case ok", v0.ok === true && v0.checkpointsVerified === 0);
|
|
1871
|
+
|
|
1872
|
+
// Several events + checkpoints
|
|
1873
|
+
for (var i = 0; i < 5; i++) {
|
|
1874
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1875
|
+
await b.audit.checkpoint();
|
|
1876
|
+
}
|
|
1877
|
+
var v1 = await b.audit.verifyCheckpoints();
|
|
1878
|
+
check("verifyCheckpoints ok across multiple anchors", v1.ok === true && v1.checkpointsVerified === 5);
|
|
1879
|
+
|
|
1880
|
+
// Adding more rows then a fresh checkpoint still verifies
|
|
1881
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1882
|
+
await b.audit.checkpoint();
|
|
1883
|
+
var v2 = await b.audit.verifyCheckpoints();
|
|
1884
|
+
check("verifyCheckpoints ok after additional checkpoint", v2.ok === true && v2.checkpointsVerified === 6);
|
|
1885
|
+
} finally {
|
|
1886
|
+
await teardownTestDb(tmpDir);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
async function testCheckpointTamperDetect() {
|
|
1891
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cdetect-"));
|
|
1892
|
+
try {
|
|
1893
|
+
await setupTestDb(tmpDir);
|
|
1894
|
+
b.audit.registerNamespace("test");
|
|
1895
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1896
|
+
await b.audit.checkpoint();
|
|
1897
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1898
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1899
|
+
var anchorCkpt = await b.audit.checkpoint();
|
|
1900
|
+
|
|
1901
|
+
// Tamper with the audit_log row that the checkpoint anchors. Drop the
|
|
1902
|
+
// append-only triggers temporarily, recompute the chain hash so the
|
|
1903
|
+
// per-row chain still verifies (simulating a privileged attacker with
|
|
1904
|
+
// vault key access who's trying to rewrite history). The CHECKPOINT
|
|
1905
|
+
// signature will still mismatch because the original rowHash was signed.
|
|
1906
|
+
b.db.runSql("DROP TRIGGER IF EXISTS no_update_audit_log");
|
|
1907
|
+
var origRow = b.db.prepare("SELECT * FROM audit_log WHERE monotonicCounter = ?").get(anchorCkpt.atMonotonicCounter);
|
|
1908
|
+
// Change something innocuous + recompute rowHash so per-row chain holds
|
|
1909
|
+
var tamperedFields = Object.assign({}, origRow);
|
|
1910
|
+
tamperedFields.outcome = "denied";
|
|
1911
|
+
var nonceBuf = Buffer.isBuffer(origRow.nonce) ? origRow.nonce : Buffer.from(origRow.nonce);
|
|
1912
|
+
var fields = Object.assign({}, tamperedFields);
|
|
1913
|
+
delete fields.prevHash; delete fields.rowHash; delete fields.nonce;
|
|
1914
|
+
var newRowHash = b.auditChain.computeRowHash(origRow.prevHash, fields, nonceBuf);
|
|
1915
|
+
b.db.prepare("UPDATE audit_log SET outcome = ?, rowHash = ? WHERE monotonicCounter = ?")
|
|
1916
|
+
.run("denied", newRowHash, anchorCkpt.atMonotonicCounter);
|
|
1917
|
+
b.db.runSql("CREATE TRIGGER IF NOT EXISTS no_update_audit_log BEFORE UPDATE ON audit_log BEGIN SELECT RAISE(ABORT, 'audit_log is append-only — UPDATE prohibited'); END");
|
|
1918
|
+
|
|
1919
|
+
// Per-row chain may still pass IF attacker also fixed the next row's
|
|
1920
|
+
// prevHash + rowHash recursively. They didn't here; verifyChain might
|
|
1921
|
+
// catch it at the next row. But the CHECKPOINT layer catches it
|
|
1922
|
+
// unconditionally — anchored rowHash no longer matches what's on disk.
|
|
1923
|
+
var ckptResult = await b.audit.verifyCheckpoints();
|
|
1924
|
+
check("checkpoint verify catches anchored-rowHash tampering", ckptResult.ok === false);
|
|
1925
|
+
check("break reason mentions rowHash mismatch",
|
|
1926
|
+
/rowHash mismatch|tampered/i.test(ckptResult.reason || ""));
|
|
1927
|
+
} finally {
|
|
1928
|
+
await teardownTestDb(tmpDir);
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
async function testRollbackDetection() {
|
|
1933
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-rollback-"));
|
|
1934
|
+
try {
|
|
1935
|
+
await setupTestDb(tmpDir);
|
|
1936
|
+
b.audit.registerNamespace("test");
|
|
1937
|
+
for (var i = 0; i < 3; i++) {
|
|
1938
|
+
await b.audit.record({ action: "test.event", outcome: "success" });
|
|
1939
|
+
}
|
|
1940
|
+
await b.audit.checkpoint();
|
|
1941
|
+
|
|
1942
|
+
// audit.tip should now record counter >= 3
|
|
1943
|
+
var tipPath = path.join(tmpDir, "audit.tip");
|
|
1944
|
+
check("audit.tip exists post-checkpoint", fs.existsSync(tipPath));
|
|
1945
|
+
var tip = JSON.parse(fs.readFileSync(tipPath, "utf8"));
|
|
1946
|
+
check("audit.tip records non-zero counter", tip.atMonotonicCounter >= 3);
|
|
1947
|
+
|
|
1948
|
+
// Simulate rollback: write an audit.tip claiming a higher counter than
|
|
1949
|
+
// currently exists in DB. On next boot, db.init() should detect and
|
|
1950
|
+
// refuse — but we can't easily test process.exit() in-process. Verify
|
|
1951
|
+
// the rollback-detection function is wired by inspecting that an
|
|
1952
|
+
// "out of sync" tip would be detected. Use the public surface:
|
|
1953
|
+
// close, write tampered tip, reopen.
|
|
1954
|
+
b.db.close();
|
|
1955
|
+
// CodeQL js/file-system-race: test/ scope only. tipPath is inside the
|
|
1956
|
+
// per-test tmpDir created by setupTestDb (owner-only 0o700); the test
|
|
1957
|
+
// intentionally clobbers it to simulate rollback tampering. No
|
|
1958
|
+
// attacker model in a single-test process; the rule is documented as
|
|
1959
|
+
// a non-finding for test fixtures.
|
|
1960
|
+
fs.writeFileSync(tipPath, JSON.stringify({
|
|
1961
|
+
atMonotonicCounter: 999999,
|
|
1962
|
+
atRowHash: "deadbeef".repeat(16),
|
|
1963
|
+
anchoredAt: Date.now(),
|
|
1964
|
+
checkpointId: "fake",
|
|
1965
|
+
publicKeyFingerprint: "fake",
|
|
1966
|
+
version: 1,
|
|
1967
|
+
}, null, 2));
|
|
1968
|
+
|
|
1969
|
+
// Reopen — should detect rollback and exit. We fork a child to capture
|
|
1970
|
+
// the exit code. The on-disk dataDir is in wrapped/encrypted modes
|
|
1971
|
+
// (setupTestDb's secure default), so the child re-inits in those
|
|
1972
|
+
// same modes; the test passphrase is inherited via env.
|
|
1973
|
+
setTestPassphraseEnv();
|
|
1974
|
+
var spawnSync = require("child_process").spawnSync;
|
|
1975
|
+
var childScript = "var b = require('" + path.resolve("../blamejs/index.js").replace(/\\/g, "/") + "');\n" +
|
|
1976
|
+
"process.env.BLAMEJS_SKIP_NTP_CHECK = '1';\n" +
|
|
1977
|
+
"(async function () {\n" +
|
|
1978
|
+
" await b.vault.init({ dataDir: " + JSON.stringify(tmpDir) + " });\n" +
|
|
1979
|
+
" await b.db.init({ dataDir: " + JSON.stringify(tmpDir) + ", tmpDir: " + JSON.stringify(path.join(tmpDir, "tmpfs")) + ", schema: [] });\n" +
|
|
1980
|
+
"})().catch(function (e) { console.error(e.message); process.exit(99); });\n";
|
|
1981
|
+
var result = spawnSync(process.execPath, ["-e", childScript], { encoding: "utf8" });
|
|
1982
|
+
check("rollback boot exits via the catch handler (code 99)", result.status === 99);
|
|
1983
|
+
check("rollback boot logs detection message", /rollback detected/i.test(result.stderr || ""));
|
|
1984
|
+
} finally {
|
|
1985
|
+
await teardownTestDb(tmpDir);
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
// ---- run() ----
|
|
1990
|
+
|
|
1991
|
+
async function run() {
|
|
1992
|
+
// cluster-storage (SQL dispatcher)
|
|
1993
|
+
await testClusterStorageLocalDispatch();
|
|
1994
|
+
testClusterStoragePlaceholderize();
|
|
1995
|
+
testClusterStorageResolveTablesIsNoOpInSingleNode();
|
|
1996
|
+
await testClusterStorageClusterDispatch();
|
|
1997
|
+
|
|
1998
|
+
// cluster discovery surface (endpoint + discoveryHandler)
|
|
1999
|
+
testClusterEndpointSingleNode();
|
|
2000
|
+
await testClusterDiscoveryHandlerSingleNode();
|
|
2001
|
+
await testClusterEndpointInitValidation();
|
|
2002
|
+
await testClusterDiscoveryAcquiredLeader();
|
|
2003
|
+
|
|
2004
|
+
// cluster-mode audit-tip fencing (canonical fencing-token guard) +
|
|
2005
|
+
// recursion-safety regression for the handlers.drain bug discovered
|
|
2006
|
+
// while wiring the fence test
|
|
2007
|
+
await testClusterAuditTipFencing();
|
|
2008
|
+
await testClusterAuditFlushNoRecursionHang();
|
|
2009
|
+
await testClusterAuditTipFencedOutErrorSurface();
|
|
2010
|
+
|
|
2011
|
+
// cluster-mode boot-time rollback detection on the audit chain
|
|
2012
|
+
await testClusterAuditTipRollbackHappyPath();
|
|
2013
|
+
await testClusterAuditTipRollbackDetected();
|
|
2014
|
+
await testClusterAuditTipRowHashMismatch();
|
|
2015
|
+
|
|
2016
|
+
// cluster-mode vault-key consistency check
|
|
2017
|
+
await testClusterVaultKeyFirstBootRecords();
|
|
2018
|
+
await testClusterVaultKeyMismatchDetected();
|
|
2019
|
+
|
|
2020
|
+
// cluster-mode sessions: session.* now dispatches through
|
|
2021
|
+
// cluster-storage so writes/reads land in external-db, making the
|
|
2022
|
+
// session store shared across all nodes
|
|
2023
|
+
await testClusterSessionsSharedAcrossNodes();
|
|
2024
|
+
|
|
2025
|
+
// cluster-mode queue: queue-local routes through cluster-storage so
|
|
2026
|
+
// the leader's enqueue/lease/complete operate on the shared
|
|
2027
|
+
// external-db queue — followers don't run lease (gated) but observe
|
|
2028
|
+
// the same state via reads
|
|
2029
|
+
await testClusterQueueJobsSharedAcrossNodes();
|
|
2030
|
+
|
|
2031
|
+
// cluster-mode consent integrity: _blamejs_consent_tip is the
|
|
2032
|
+
// canonical fencing-token-guarded coordination row, updated on
|
|
2033
|
+
// every consent.grant / consent.withdraw, checked at boot for
|
|
2034
|
+
// rollback detection — same protections as audit_log
|
|
2035
|
+
await testClusterConsentTipFencing();
|
|
2036
|
+
await testClusterConsentTipUpdatedOnGrant();
|
|
2037
|
+
await testClusterConsentRollbackDetected();
|
|
2038
|
+
|
|
2039
|
+
// audit chain + verify (now exercises chain-writer transitively)
|
|
2040
|
+
await testAuditChain();
|
|
2041
|
+
await testAuditChainBreak();
|
|
2042
|
+
await testAuditSelfLogging();
|
|
2043
|
+
await testBeginTrace();
|
|
2044
|
+
|
|
2045
|
+
// consent (uses chain-writer)
|
|
2046
|
+
await testConsent();
|
|
2047
|
+
|
|
2048
|
+
// subject rights (uses audit + db)
|
|
2049
|
+
await testSubjectRights();
|
|
2050
|
+
|
|
2051
|
+
// append-only triggers + foreign keys + table metadata
|
|
2052
|
+
await testAppendOnlyTriggers();
|
|
2053
|
+
await testForeignKeys();
|
|
2054
|
+
await testTableMetadata();
|
|
2055
|
+
|
|
2056
|
+
// audit-sign algorithm-agility
|
|
2057
|
+
await testAuditSignDefaultsToSlhDsa();
|
|
2058
|
+
await testAuditSignMlDsaOptIn();
|
|
2059
|
+
await testAuditSignLegacyFileBackcompat();
|
|
2060
|
+
await testAuditSignRejectsUnsupportedAlgorithm();
|
|
2061
|
+
|
|
2062
|
+
// checkpoint sign / verify / tamper / rollback
|
|
2063
|
+
await testCheckpointSign();
|
|
2064
|
+
await testCheckpointVerify();
|
|
2065
|
+
await testCheckpointTamperDetect();
|
|
2066
|
+
await testRollbackDetection();
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
module.exports = {
|
|
2070
|
+
name: "Layer 3 — chain (cluster-storage + audit + consent + subject + checkpoint)",
|
|
2071
|
+
run: run,
|
|
2072
|
+
testClusterStorageLocalDispatch: testClusterStorageLocalDispatch,
|
|
2073
|
+
testClusterStoragePlaceholderize: testClusterStoragePlaceholderize,
|
|
2074
|
+
testClusterStorageResolveTablesIsNoOpInSingleNode: testClusterStorageResolveTablesIsNoOpInSingleNode,
|
|
2075
|
+
testClusterStorageClusterDispatch: testClusterStorageClusterDispatch,
|
|
2076
|
+
testClusterEndpointSingleNode: testClusterEndpointSingleNode,
|
|
2077
|
+
testClusterDiscoveryHandlerSingleNode: testClusterDiscoveryHandlerSingleNode,
|
|
2078
|
+
testClusterEndpointInitValidation: testClusterEndpointInitValidation,
|
|
2079
|
+
testClusterDiscoveryAcquiredLeader: testClusterDiscoveryAcquiredLeader,
|
|
2080
|
+
testClusterAuditTipFencing: testClusterAuditTipFencing,
|
|
2081
|
+
testClusterAuditFlushNoRecursionHang: testClusterAuditFlushNoRecursionHang,
|
|
2082
|
+
testClusterAuditTipFencedOutErrorSurface: testClusterAuditTipFencedOutErrorSurface,
|
|
2083
|
+
testClusterAuditTipRollbackHappyPath: testClusterAuditTipRollbackHappyPath,
|
|
2084
|
+
testClusterAuditTipRollbackDetected: testClusterAuditTipRollbackDetected,
|
|
2085
|
+
testClusterAuditTipRowHashMismatch: testClusterAuditTipRowHashMismatch,
|
|
2086
|
+
testClusterVaultKeyFirstBootRecords: testClusterVaultKeyFirstBootRecords,
|
|
2087
|
+
testClusterVaultKeyMismatchDetected: testClusterVaultKeyMismatchDetected,
|
|
2088
|
+
testClusterSessionsSharedAcrossNodes: testClusterSessionsSharedAcrossNodes,
|
|
2089
|
+
testClusterQueueJobsSharedAcrossNodes: testClusterQueueJobsSharedAcrossNodes,
|
|
2090
|
+
testClusterConsentTipFencing: testClusterConsentTipFencing,
|
|
2091
|
+
testClusterConsentTipUpdatedOnGrant: testClusterConsentTipUpdatedOnGrant,
|
|
2092
|
+
testClusterConsentRollbackDetected: testClusterConsentRollbackDetected,
|
|
2093
|
+
testAuditChain: testAuditChain,
|
|
2094
|
+
testAuditChainBreak: testAuditChainBreak,
|
|
2095
|
+
testAuditSelfLogging: testAuditSelfLogging,
|
|
2096
|
+
testBeginTrace: testBeginTrace,
|
|
2097
|
+
testConsent: testConsent,
|
|
2098
|
+
testSubjectRights: testSubjectRights,
|
|
2099
|
+
testAppendOnlyTriggers: testAppendOnlyTriggers,
|
|
2100
|
+
testForeignKeys: testForeignKeys,
|
|
2101
|
+
testTableMetadata: testTableMetadata,
|
|
2102
|
+
testAuditSignDefaultsToSlhDsa: testAuditSignDefaultsToSlhDsa,
|
|
2103
|
+
testAuditSignMlDsaOptIn: testAuditSignMlDsaOptIn,
|
|
2104
|
+
testAuditSignLegacyFileBackcompat: testAuditSignLegacyFileBackcompat,
|
|
2105
|
+
testAuditSignRejectsUnsupportedAlgorithm: testAuditSignRejectsUnsupportedAlgorithm,
|
|
2106
|
+
testCheckpointSign: testCheckpointSign,
|
|
2107
|
+
testCheckpointVerify: testCheckpointVerify,
|
|
2108
|
+
testCheckpointTamperDetect: testCheckpointTamperDetect,
|
|
2109
|
+
testRollbackDetection: testRollbackDetection,
|
|
2110
|
+
};
|