@blamejs/blamejs-shop 0.5.12 → 0.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/sales-tax-filings.js +143 -0
- package/lib/vendor/MANIFEST.json +632 -416
- package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +21 -10
- package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +30 -9
- package/lib/vendor/blamejs/.github/dependabot.yml +8 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/ci.yml +21 -21
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +4 -4
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +7 -7
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
- package/lib/vendor/blamejs/.gitignore +2 -0
- package/lib/vendor/blamejs/CHANGELOG.md +122 -0
- package/lib/vendor/blamejs/NOTICE +26 -1
- package/lib/vendor/blamejs/SECURITY.md +1 -1
- package/lib/vendor/blamejs/api-snapshot.json +207 -5
- package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +29 -0
- package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +1 -0
- package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +31 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +109 -0
- package/lib/vendor/blamejs/examples/wiki/nginx.conf +106 -0
- package/lib/vendor/blamejs/examples/wiki/package-lock.json +1 -1
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +92 -0
- package/lib/vendor/blamejs/fuzz/asn1-der.fuzz.js +28 -0
- package/lib/vendor/blamejs/fuzz/cms-codec.fuzz.js +17 -0
- package/lib/vendor/blamejs/fuzz/link-header.fuzz.js +18 -0
- package/lib/vendor/blamejs/index.js +6 -0
- package/lib/vendor/blamejs/lib/a2a.js +4 -4
- package/lib/vendor/blamejs/lib/agent-idempotency.js +18 -2
- package/lib/vendor/blamejs/lib/agent-saga.js +36 -8
- package/lib/vendor/blamejs/lib/agent-snapshot.js +63 -6
- package/lib/vendor/blamejs/lib/app-shutdown.js +23 -20
- package/lib/vendor/blamejs/lib/archive-read.js +8 -0
- package/lib/vendor/blamejs/lib/archive-tar.js +12 -2
- package/lib/vendor/blamejs/lib/arg-parser.js +49 -5
- package/lib/vendor/blamejs/lib/asn1-der.js +12 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +47 -1
- package/lib/vendor/blamejs/lib/audit.js +71 -8
- package/lib/vendor/blamejs/lib/auth/ciba.js +14 -2
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +58 -47
- package/lib/vendor/blamejs/lib/auth/jwt.js +34 -0
- package/lib/vendor/blamejs/lib/auth/oauth.js +90 -13
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +20 -17
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +214 -23
- package/lib/vendor/blamejs/lib/auth/saml.js +38 -20
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +10 -4
- package/lib/vendor/blamejs/lib/backup/bundle.js +13 -3
- package/lib/vendor/blamejs/lib/backup/index.js +16 -5
- package/lib/vendor/blamejs/lib/backup/manifest.js +18 -5
- package/lib/vendor/blamejs/lib/base32.js +21 -0
- package/lib/vendor/blamejs/lib/break-glass.js +19 -10
- package/lib/vendor/blamejs/lib/bundler.js +5 -1
- package/lib/vendor/blamejs/lib/cache-status.js +3 -7
- package/lib/vendor/blamejs/lib/calendar.js +21 -1
- package/lib/vendor/blamejs/lib/cbor.js +12 -1
- package/lib/vendor/blamejs/lib/cert.js +29 -11
- package/lib/vendor/blamejs/lib/cli.js +94 -24
- package/lib/vendor/blamejs/lib/cloud-events.js +2 -2
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +13 -3
- package/lib/vendor/blamejs/lib/codepoint-class.js +85 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +8 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +19 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act.js +6 -8
- package/lib/vendor/blamejs/lib/content-credentials.js +1 -1
- package/lib/vendor/blamejs/lib/content-digest.js +2 -1
- package/lib/vendor/blamejs/lib/cookies.js +13 -3
- package/lib/vendor/blamejs/lib/cose.js +31 -16
- package/lib/vendor/blamejs/lib/crypto-field.js +32 -0
- package/lib/vendor/blamejs/lib/crypto-oprf.js +11 -2
- package/lib/vendor/blamejs/lib/crypto-xwing.js +22 -4
- package/lib/vendor/blamejs/lib/crypto.js +314 -19
- package/lib/vendor/blamejs/lib/csp.js +11 -3
- package/lib/vendor/blamejs/lib/daemon.js +270 -24
- package/lib/vendor/blamejs/lib/dark-patterns.js +5 -2
- package/lib/vendor/blamejs/lib/db-declare-view.js +2 -2
- package/lib/vendor/blamejs/lib/db-query.js +34 -7
- package/lib/vendor/blamejs/lib/db.js +18 -6
- package/lib/vendor/blamejs/lib/dbsc.js +17 -0
- package/lib/vendor/blamejs/lib/dev.js +21 -2
- package/lib/vendor/blamejs/lib/dsr.js +157 -102
- package/lib/vendor/blamejs/lib/external-db.js +41 -2
- package/lib/vendor/blamejs/lib/file-upload.js +25 -7
- package/lib/vendor/blamejs/lib/flag-providers.js +27 -15
- package/lib/vendor/blamejs/lib/forms.js +113 -27
- package/lib/vendor/blamejs/lib/gate-contract.js +23 -10
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +8 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +1 -1
- package/lib/vendor/blamejs/lib/guard-archive.js +2 -2
- package/lib/vendor/blamejs/lib/guard-cidr.js +27 -1
- package/lib/vendor/blamejs/lib/guard-graphql.js +30 -11
- package/lib/vendor/blamejs/lib/guard-html.js +26 -53
- package/lib/vendor/blamejs/lib/guard-image.js +24 -5
- package/lib/vendor/blamejs/lib/guard-imap-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-jmap.js +19 -3
- package/lib/vendor/blamejs/lib/guard-jwt.js +18 -2
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -2
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-markdown.js +11 -24
- package/lib/vendor/blamejs/lib/guard-pdf.js +18 -2
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-regex.js +8 -5
- package/lib/vendor/blamejs/lib/guard-svg.js +31 -32
- package/lib/vendor/blamejs/lib/guard-time.js +1 -1
- package/lib/vendor/blamejs/lib/guard-uuid.js +2 -2
- package/lib/vendor/blamejs/lib/html-balance.js +3 -3
- package/lib/vendor/blamejs/lib/http-client-cache.js +82 -4
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +16 -1
- package/lib/vendor/blamejs/lib/http-client.js +39 -7
- package/lib/vendor/blamejs/lib/http-message-signature.js +35 -1
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +31 -6
- package/lib/vendor/blamejs/lib/i18n.js +35 -21
- package/lib/vendor/blamejs/lib/incident-report.js +8 -1
- package/lib/vendor/blamejs/lib/ip-utils.js +11 -1
- package/lib/vendor/blamejs/lib/json-path.js +23 -1
- package/lib/vendor/blamejs/lib/jwk.js +20 -3
- package/lib/vendor/blamejs/lib/link-header.js +2 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream.js +8 -3
- package/lib/vendor/blamejs/lib/mail-auth.js +123 -22
- package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
- package/lib/vendor/blamejs/lib/mail-bounce.js +8 -0
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +46 -10
- package/lib/vendor/blamejs/lib/mail-dav.js +22 -3
- package/lib/vendor/blamejs/lib/mail-dkim.js +96 -30
- package/lib/vendor/blamejs/lib/mail-helo.js +6 -1
- package/lib/vendor/blamejs/lib/mail-scan.js +23 -16
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +34 -8
- package/lib/vendor/blamejs/lib/mail-server-imap.js +3 -4
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +7 -11
- package/lib/vendor/blamejs/lib/mail-server-mx.js +93 -7
- package/lib/vendor/blamejs/lib/mail-server-registry.js +8 -2
- package/lib/vendor/blamejs/lib/mail.js +15 -5
- package/lib/vendor/blamejs/lib/metrics.js +268 -63
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +7 -1
- package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +19 -4
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +5 -9
- package/lib/vendor/blamejs/lib/middleware/csp-report.js +40 -8
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +24 -10
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -5
- package/lib/vendor/blamejs/lib/middleware/index.js +6 -1
- package/lib/vendor/blamejs/lib/middleware/nel.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +12 -1
- package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +6 -2
- package/lib/vendor/blamejs/lib/mtls-ca.js +18 -2
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +11 -1
- package/lib/vendor/blamejs/lib/network-dns.js +21 -2
- package/lib/vendor/blamejs/lib/network-tls.js +69 -7
- package/lib/vendor/blamejs/lib/nonce-store.js +9 -1
- package/lib/vendor/blamejs/lib/object-store/gcs.js +7 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +31 -0
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +1 -1
- package/lib/vendor/blamejs/lib/outbox.js +1 -1
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +9 -1
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +10 -1
- package/lib/vendor/blamejs/lib/permissions.js +6 -3
- package/lib/vendor/blamejs/lib/pid-probe.js +55 -0
- package/lib/vendor/blamejs/lib/pqc-agent.js +8 -1
- package/lib/vendor/blamejs/lib/pqc-software.js +3 -3
- package/lib/vendor/blamejs/lib/pubsub.js +20 -9
- package/lib/vendor/blamejs/lib/queue-local.js +8 -1
- package/lib/vendor/blamejs/lib/queue-redis.js +5 -0
- package/lib/vendor/blamejs/lib/redact.js +54 -0
- package/lib/vendor/blamejs/lib/restore-bundle.js +10 -2
- package/lib/vendor/blamejs/lib/restore.js +19 -0
- package/lib/vendor/blamejs/lib/retention.js +12 -4
- package/lib/vendor/blamejs/lib/router.js +60 -6
- package/lib/vendor/blamejs/lib/safe-buffer.js +69 -0
- package/lib/vendor/blamejs/lib/safe-decompress.js +8 -2
- package/lib/vendor/blamejs/lib/safe-dns.js +25 -20
- package/lib/vendor/blamejs/lib/safe-ical.js +7 -3
- package/lib/vendor/blamejs/lib/safe-mime.js +12 -1
- package/lib/vendor/blamejs/lib/safe-object.js +80 -0
- package/lib/vendor/blamejs/lib/safe-path.js +52 -4
- package/lib/vendor/blamejs/lib/safe-redirect.js +10 -2
- package/lib/vendor/blamejs/lib/safe-schema.js +4 -1
- package/lib/vendor/blamejs/lib/safe-sieve.js +33 -1
- package/lib/vendor/blamejs/lib/safe-sql.js +88 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +7 -3
- package/lib/vendor/blamejs/lib/scheduler.js +8 -1
- package/lib/vendor/blamejs/lib/security-assert.js +6 -6
- package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +74 -27
- package/lib/vendor/blamejs/lib/self-update.js +517 -87
- package/lib/vendor/blamejs/lib/server-timing.js +2 -4
- package/lib/vendor/blamejs/lib/session.js +34 -12
- package/lib/vendor/blamejs/lib/sql.js +15 -1
- package/lib/vendor/blamejs/lib/ssrf-guard.js +52 -0
- package/lib/vendor/blamejs/lib/static.js +55 -13
- package/lib/vendor/blamejs/lib/storage.js +8 -2
- package/lib/vendor/blamejs/lib/subject.js +6 -1
- package/lib/vendor/blamejs/lib/tenant-quota.js +52 -13
- package/lib/vendor/blamejs/lib/tls-exporter.js +3 -3
- package/lib/vendor/blamejs/lib/tsa.js +34 -13
- package/lib/vendor/blamejs/lib/uri-template.js +8 -1
- package/lib/vendor/blamejs/lib/vault/index.js +13 -0
- package/lib/vendor/blamejs/lib/vc.js +44 -5
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +26 -26
- package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +792 -1
- package/lib/vendor/blamejs/lib/vendor/noble-curves.cjs +3443 -1
- package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +2737 -1
- package/lib/vendor/blamejs/lib/vendor/pki.cjs +39550 -13
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +55 -53
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5551 -5554
- package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +22459 -32
- package/lib/vendor/blamejs/lib/watcher.js +89 -17
- package/lib/vendor/blamejs/lib/webhook-dispatcher.js +25 -3
- package/lib/vendor/blamejs/lib/webhook.js +12 -2
- package/lib/vendor/blamejs/lib/ws-client.js +32 -1
- package/lib/vendor/blamejs/lib/xml-c14n.js +29 -1
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +1 -1
- package/lib/vendor/blamejs/package-lock.json +2 -2
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.x.json +1462 -0
- package/lib/vendor/blamejs/release-notes/v0.17.0.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.1.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.10.json +30 -0
- package/lib/vendor/blamejs/release-notes/v0.17.11.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.12.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.13.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.17.14.json +40 -0
- package/lib/vendor/blamejs/release-notes/v0.17.15.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.16.json +27 -0
- package/lib/vendor/blamejs/release-notes/v0.17.17.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.18.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.19.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.2.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.20.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.21.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.22.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.3.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.4.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.5.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.6.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.7.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.8.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.9.json +26 -0
- package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +4 -0
- package/lib/vendor/blamejs/scripts/vendor-update.sh +293 -9
- package/lib/vendor/blamejs/socket.yml +94 -0
- package/lib/vendor/blamejs/test/00-primitives.js +605 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +12 -9
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +90 -1
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +122 -1
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +30 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +29 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-mysql.test.js +479 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-pg.test.js +487 -0
- package/lib/vendor/blamejs/test/integration/federation-auth.test.js +313 -5
- package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +80 -0
- package/lib/vendor/blamejs/test/integration/openid-federation-chain.test.js +275 -0
- package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +1649 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +33 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +60 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +891 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +128 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-adapters.test.js +271 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +628 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-read.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-wrap.test.js +227 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +141 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-tools.test.js +624 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit.test.js +769 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth.test.js +1697 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oid4vp.test.js +495 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-password.test.js +500 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml.test.js +1809 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +243 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index.test.js +2226 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/base32.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +693 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +603 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +557 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/case-insensitive-security-token-guard.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cbor.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +843 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba-authreqid-binding.test.js +77 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli.test.js +2790 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster-lease-renewal-ttl.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster.test.js +1032 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +739 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +8 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +194 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-lifecycle-proto-key.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +254 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +613 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/cookies.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +407 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-adversarial.test.js +352 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +24 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-oprf.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-spki-pin.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-xwing.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto.test.js +345 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +459 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query.test.js +1019 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db.test.js +1621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +462 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +323 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-verify.test.js +562 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +901 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +249 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +561 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db.test.js +1214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +46 -20
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +147 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +907 -38
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload.test.js +1135 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/forms.test.js +747 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract.test.js +1855 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-auth.test.js +117 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-cidr.test.js +151 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-domain.test.js +422 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +424 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-gate-disposition-coverage.test.js → guard-gate-disposition.test.js} +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-graphql.test.js +340 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +519 -45
- 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 +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jsonpath.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jwt.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +16 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mime.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-oauth.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-regex.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-shell.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-sql-coverage.test.js → guard-sql.test.js} +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-template.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-time.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-uuid.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache-authorization.test.js +347 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +908 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client.test.js +2874 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +643 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +1153 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +494 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jsdoc-example-execution.test.js +177 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-path.test.js +103 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-schema.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwk.test.js +41 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +276 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream.test.js +241 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +441 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +2136 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +759 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +811 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +401 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +751 -8
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +864 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +830 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +635 -86
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +199 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +686 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +778 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +1438 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +449 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +773 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +623 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +13 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +877 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +326 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +242 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +1596 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +440 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics.test.js +1230 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-assetlinks.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-body-parser.test.js +1169 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-bot-disclose.test.js +123 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-cookies.test.js +108 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-gpc.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-headers.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-host-allowlist.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-require-content-type.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{middleware-tus-upload-coverage.test.js → middleware-tus-upload.test.js} +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-web-app-manifest.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-crl-fingerprint-only.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +1727 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/network-nts.test.js +699 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy.test.js +1181 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +1876 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-enforce-replay-nonboolean.test.js +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ntp-check.test.js +122 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +378 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oid4vci.test.js +1016 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/openid-federation.test.js +946 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/own-property-membership-guards.test.js +109 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-toml.test.js +543 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{parsers-safe-yaml-coverage.test.js → parsers-safe-yaml.test.js} +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +649 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +22 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +295 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact.test.js +1031 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/require-bound-key.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/restore-empty-manifest-wipe.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention.test.js +600 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-host-path-injection.test.js +231 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router.test.js +1277 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +245 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ini.test.js +376 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-json.test.js +175 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +518 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-object.test.js +62 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-schema.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-sql.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +26 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scheduler.test.js +157 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +396 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +930 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +755 -82
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +1053 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +200 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-strict-binding-missing-failclosed.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +636 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-offset-without-limit.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +1190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +850 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage.test.js +621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/subject.test.js +47 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/template.test.js +582 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +587 -25
- package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +11 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tsa.test.js +524 -63
- package/lib/vendor/blamejs/test/layer-0-primitives/uri-template.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-rotate.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +215 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +550 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +388 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +262 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket.test.js +1294 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +629 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/xml-c14n-attr-normalization.test.js +164 -0
- package/lib/vendor/blamejs/test/smoke.js +2 -0
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.0.json +0 -44
- package/lib/vendor/blamejs/release-notes/v0.16.1.json +0 -36
- package/lib/vendor/blamejs/release-notes/v0.16.2.json +0 -71
- package/lib/vendor/blamejs/release-notes/v0.16.3.json +0 -71
- package/lib/vendor/blamejs/test/layer-0-primitives/acme-coverage.test.js +0 -441
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth-coverage.test.js +0 -482
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml-coverage.test.js +0 -671
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index-coverage.test.js +0 -690
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-coverage.test.js +0 -238
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js +0 -319
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain-coverage.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth-coverage.test.js +0 -437
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js +0 -443
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js +0 -395
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy-coverage.test.js +0 -565
- package/lib/vendor/blamejs/test/layer-0-primitives/router-coverage.test.js +0 -592
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-coverage.test.js +0 -422
- package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +0 -573
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - SNI callback (exact + wildcard + fallback)
|
|
11
11
|
* - manual refresh() with a mocked ACME client
|
|
12
12
|
* - audit emission on issue / renew / renew-failed
|
|
13
|
-
* - key escrow (encrypt-to-recipient via b.crypto.
|
|
13
|
+
* - key escrow (encrypt-to-recipient via b.crypto.encrypt)
|
|
14
14
|
*
|
|
15
15
|
* The live ACME path against an external CA isn't exercised here
|
|
16
16
|
* (no test CA shipped in the framework); the issue/renew flow is
|
|
@@ -106,6 +106,30 @@ async function _selfSignedCert(domains, validityDays) {
|
|
|
106
106
|
return { keyPem: keyPem, certPem: cert.toString("pem") };
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
async function _selfSignedCertNoSan(cn, validityDays) {
|
|
110
|
+
// Like _selfSignedCert but with NO SubjectAlternativeName extension —
|
|
111
|
+
// so the parsed cert's subjectAltName is undefined, exercising the
|
|
112
|
+
// `cert.subjectAltName || null` fallback in the manager's _certMeta.
|
|
113
|
+
var pki = require("../../lib/vendor/pki.cjs");
|
|
114
|
+
var x509 = pki.x509;
|
|
115
|
+
var keys = await crypto.webcrypto.subtle.generateKey(
|
|
116
|
+
{ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
|
|
117
|
+
var now = new Date();
|
|
118
|
+
var cert = await x509.X509CertificateGenerator.createSelfSigned({
|
|
119
|
+
serialNumber: "02",
|
|
120
|
+
name: "CN=" + cn,
|
|
121
|
+
notBefore: now,
|
|
122
|
+
notAfter: new Date(now.getTime() + validityDays * 24 * 3600 * 1000),
|
|
123
|
+
signingAlgorithm: { name: "ECDSA", hash: "SHA-256" },
|
|
124
|
+
keys: keys,
|
|
125
|
+
extensions: [],
|
|
126
|
+
});
|
|
127
|
+
var pkcs8 = await crypto.webcrypto.subtle.exportKey("pkcs8", keys.privateKey);
|
|
128
|
+
var pkB64 = Buffer.from(pkcs8).toString("base64").match(/.{1,64}/g).join("\n");
|
|
129
|
+
var keyPem = "-----BEGIN PRIVATE KEY-----\n" + pkB64 + "\n-----END PRIVATE KEY-----\n";
|
|
130
|
+
return { keyPem: keyPem, certPem: cert.toString("pem") };
|
|
131
|
+
}
|
|
132
|
+
|
|
109
133
|
function _mockAcmeClient(pem) {
|
|
110
134
|
// Fulfills the b.acme.create contract the cert manager calls into.
|
|
111
135
|
// Every method returns synchronously-resolved promises with the
|
|
@@ -505,10 +529,9 @@ async function testKeyEscrow() {
|
|
|
505
529
|
var vault = _ephemeralVault();
|
|
506
530
|
var pem = await _selfSignedCert(["example.com"], 90);
|
|
507
531
|
|
|
508
|
-
//
|
|
509
|
-
//
|
|
510
|
-
var
|
|
511
|
-
var recipientPubBytes = recipient.publicKey.export({ type: "spki", format: "der" });
|
|
532
|
+
// Offline break-glass recipient — an ML-KEM-1024 (+ P-384 hybrid)
|
|
533
|
+
// keypair. b.crypto.encrypt seals the escrowed key to its public keys.
|
|
534
|
+
var recipientKp = b.crypto.generateEncryptionKeyPair();
|
|
512
535
|
|
|
513
536
|
// Seed a fake cert so start() doesn't try to ACME.
|
|
514
537
|
fs.mkdirSync(path.join(tmp, "main"), { recursive: true });
|
|
@@ -527,7 +550,7 @@ async function testKeyEscrow() {
|
|
|
527
550
|
name: "main",
|
|
528
551
|
domains: ["example.com"],
|
|
529
552
|
challenge: { type: "http-01", provision: async function () {}, cleanup: async function () {} },
|
|
530
|
-
keyEscrow: { recipient:
|
|
553
|
+
keyEscrow: { recipient: { publicKey: recipientKp.publicKey, ecPublicKey: recipientKp.ecPublicKey } },
|
|
531
554
|
}],
|
|
532
555
|
audit: false,
|
|
533
556
|
});
|
|
@@ -741,6 +764,803 @@ async function testCorruptAccountKeyClearError() {
|
|
|
741
764
|
try { await mgr.stop(); } catch (_e) {}
|
|
742
765
|
}
|
|
743
766
|
|
|
767
|
+
// ---- Shared fixtures for the issue / renewal / OCSP branch tests ----
|
|
768
|
+
|
|
769
|
+
var GOOD_CHALLENGE = { type: "http-01", provision: async function () {}, cleanup: async function () {} };
|
|
770
|
+
|
|
771
|
+
// Patch the acme module's `create` so the manager's internal
|
|
772
|
+
// `_bootAcme` (require("./acme").create) returns an injected stub
|
|
773
|
+
// instead of building a live RFC 8555 client. Returns a restore fn —
|
|
774
|
+
// ALWAYS call it in a finally so module state isn't polluted for the
|
|
775
|
+
// next test file sharing the process. This is collaborator injection,
|
|
776
|
+
// not a network call: the stub fulfils the same contract cert.js calls.
|
|
777
|
+
function _stubAcmeCreate(client) {
|
|
778
|
+
var acmeMod = require("../../lib/acme");
|
|
779
|
+
var orig = acmeMod.create;
|
|
780
|
+
acmeMod.create = function () { return client; };
|
|
781
|
+
return function restore() { acmeMod.create = orig; };
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// Patch b.network.tls.ocsp.fetch (the OCSP responder call the manager
|
|
785
|
+
// composes for stapling). The ocsp object itself is frozen, so swap the
|
|
786
|
+
// whole object on the (writable) module property, preserving every other
|
|
787
|
+
// method. Same injection discipline as _stubAcmeCreate.
|
|
788
|
+
function _stubOcspFetch(fn) {
|
|
789
|
+
var nt = require("../../lib/network-tls");
|
|
790
|
+
var origOcsp = nt.ocsp;
|
|
791
|
+
var patched = {};
|
|
792
|
+
Object.keys(origOcsp).forEach(function (k) { patched[k] = origOcsp[k]; });
|
|
793
|
+
patched.fetch = fn;
|
|
794
|
+
nt.ocsp = patched;
|
|
795
|
+
return function restore() { nt.ocsp = origOcsp; };
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// A flexible ACME stub. `opts` toggles the adversarial shapes each
|
|
799
|
+
// branch test needs (auth already valid, CA offers no matching
|
|
800
|
+
// challenge, ARI throws, a later retrieveCert throws).
|
|
801
|
+
function _makeStubAcme(pem, opts) {
|
|
802
|
+
opts = opts || {};
|
|
803
|
+
var retrieveCalls = 0;
|
|
804
|
+
return {
|
|
805
|
+
fetchDirectory: async function () { return {}; },
|
|
806
|
+
newAccount: async function () { return { url: "mock://acct" }; },
|
|
807
|
+
newOrder: async function (o) {
|
|
808
|
+
return {
|
|
809
|
+
url: "mock://order/1",
|
|
810
|
+
status: "pending",
|
|
811
|
+
authorizations: o.identifiers.map(function (id, i) { return "mock://auth/" + i; }),
|
|
812
|
+
finalize: "mock://order/1/finalize",
|
|
813
|
+
};
|
|
814
|
+
},
|
|
815
|
+
fetchAuthorization: async function (u) {
|
|
816
|
+
if (opts.authValid) {
|
|
817
|
+
return { url: u, status: "valid", identifier: { type: "dns", value: "a.example" }, challenges: [] };
|
|
818
|
+
}
|
|
819
|
+
var chs = opts.omitChallengeType
|
|
820
|
+
? [{ type: "dns-01", url: u + "/d", token: "t", status: "pending" }]
|
|
821
|
+
: [
|
|
822
|
+
{ type: "http-01", url: u + "/h", token: "t", status: "pending" },
|
|
823
|
+
{ type: "dns-01", url: u + "/d", token: "t", status: "pending" },
|
|
824
|
+
{ type: "tls-alpn-01", url: u + "/a", token: "t", status: "pending" },
|
|
825
|
+
];
|
|
826
|
+
return { url: u, status: "pending", identifier: { type: "dns", value: "a.example" }, challenges: chs };
|
|
827
|
+
},
|
|
828
|
+
notifyChallengeReady: async function () { return {}; },
|
|
829
|
+
waitForAuthorization: async function (u) { return { url: u, status: "valid" }; },
|
|
830
|
+
keyAuthorization: function (t) { return t + ".thumb"; },
|
|
831
|
+
tlsAlpn01KeyAuthorization: function (t) { return t + ".alpn"; },
|
|
832
|
+
buildCsr: function () { return "-----BEGIN CERTIFICATE REQUEST-----\nMOCK\n-----END CERTIFICATE REQUEST-----"; },
|
|
833
|
+
finalize: async function () { return { url: "mock://order/1", status: "valid" }; },
|
|
834
|
+
retrieveCert: async function () {
|
|
835
|
+
retrieveCalls += 1;
|
|
836
|
+
if (opts.failRetrieveAfter && retrieveCalls > opts.failRetrieveAfter) {
|
|
837
|
+
if (Object.prototype.hasOwnProperty.call(opts, "retrieveThrowValue")) throw opts.retrieveThrowValue;
|
|
838
|
+
throw new Error("mock retrieveCert failed (network down)");
|
|
839
|
+
}
|
|
840
|
+
return pem;
|
|
841
|
+
},
|
|
842
|
+
renewIfDue: async function () {
|
|
843
|
+
if (opts.onRenewIfDue) opts.onRenewIfDue();
|
|
844
|
+
if (opts.renewIfDueThrows) throw new Error("mock ARI renewalInfo fetch failed");
|
|
845
|
+
return { shouldRenew: !!opts.renewIfDueShould };
|
|
846
|
+
},
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// ---- _positiveFiniteOrDefault — every rejection + null-defaults path ----
|
|
851
|
+
|
|
852
|
+
function testPositiveFiniteOptRejections() {
|
|
853
|
+
var threw = function (fn) { try { fn(); return null; } catch (e) { return e; } };
|
|
854
|
+
function mk(extra) {
|
|
855
|
+
var base = {
|
|
856
|
+
storage: { type: "sealed-disk", rootDir: _tmpDir(), vault: _ephemeralVault() },
|
|
857
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
858
|
+
certs: [{ name: "m", domains: ["a.com"], challenge: GOOD_CHALLENGE }],
|
|
859
|
+
audit: false,
|
|
860
|
+
};
|
|
861
|
+
return Object.assign(base, extra);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Non-number → throw (typeof branch).
|
|
865
|
+
var eStr = threw(function () { b.cert.create(mk({ renew: { intervalMs: "soon" } })); });
|
|
866
|
+
check("renew.intervalMs non-number → cert/bad-renew-interval", eStr && eStr.code === "cert/bad-renew-interval");
|
|
867
|
+
|
|
868
|
+
// Non-finite (Infinity / NaN) → throw (isFinite branch).
|
|
869
|
+
var eInf = threw(function () { b.cert.create(mk({ ocsp: { refreshMs: Infinity } })); });
|
|
870
|
+
check("ocsp.refreshMs Infinity → cert/bad-ocsp-refresh", eInf && eInf.code === "cert/bad-ocsp-refresh");
|
|
871
|
+
var eNaN = threw(function () { b.cert.create(mk({ renew: { minDaysBeforeExpiry: NaN } })); });
|
|
872
|
+
check("renew.minDaysBeforeExpiry NaN → cert/bad-renew-window", eNaN && eNaN.code === "cert/bad-renew-window");
|
|
873
|
+
|
|
874
|
+
// Zero / negative → throw (<= 0 branch).
|
|
875
|
+
var eZero = threw(function () { b.cert.create(mk({ renew: { intervalMs: 0 } })); });
|
|
876
|
+
check("renew.intervalMs 0 → cert/bad-renew-interval", eZero && eZero.code === "cert/bad-renew-interval");
|
|
877
|
+
var eNeg = threw(function () { b.cert.create(mk({ ocsp: { refreshMs: -5 } })); });
|
|
878
|
+
check("ocsp.refreshMs negative → cert/bad-ocsp-refresh", eNeg && eNeg.code === "cert/bad-ocsp-refresh");
|
|
879
|
+
|
|
880
|
+
// Explicit null → falls back to the default (no throw): exercises the
|
|
881
|
+
// `value === null` short-circuit at the top of _positiveFiniteOrDefault.
|
|
882
|
+
var eNull = threw(function () { b.cert.create(mk({ renew: { intervalMs: null, minDaysBeforeExpiry: null }, ocsp: { refreshMs: null } })); });
|
|
883
|
+
check("null interval/window/refresh → default (no throw)", !eNull);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// ---- Manifest size + per-cert shape rejections (uncovered guard rows) ----
|
|
887
|
+
|
|
888
|
+
function testManifestSizeAndShapeRejections() {
|
|
889
|
+
var threw = function (fn) { try { fn(); return null; } catch (e) { return e; } };
|
|
890
|
+
function mk(certs, extraStorage) {
|
|
891
|
+
return {
|
|
892
|
+
storage: Object.assign({ type: "sealed-disk", rootDir: _tmpDir(), vault: _ephemeralVault() }, extraStorage || {}),
|
|
893
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
894
|
+
certs: certs,
|
|
895
|
+
audit: false,
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// certs array over the manager cap.
|
|
900
|
+
var tooMany = [];
|
|
901
|
+
for (var i = 0; i < 1001; i += 1) {
|
|
902
|
+
tooMany.push({ name: "c" + i, domains: ["a.com"], challenge: GOOD_CHALLENGE });
|
|
903
|
+
}
|
|
904
|
+
var eManyCerts = threw(function () { b.cert.create(mk(tooMany)); });
|
|
905
|
+
check("certs over cap → cert/too-many-certs", eManyCerts && eManyCerts.code === "cert/too-many-certs");
|
|
906
|
+
|
|
907
|
+
// domains array over the per-cert cap.
|
|
908
|
+
var manyDomains = [];
|
|
909
|
+
for (var d = 0; d < 101; d += 1) { manyDomains.push("d" + d + ".example"); }
|
|
910
|
+
var eManyDomains = threw(function () { b.cert.create(mk([{ name: "m", domains: manyDomains, challenge: GOOD_CHALLENGE }])); });
|
|
911
|
+
check("domains over cap → cert/too-many-domains", eManyDomains && eManyDomains.code === "cert/too-many-domains");
|
|
912
|
+
|
|
913
|
+
// A non-string / empty domain entry.
|
|
914
|
+
var eBadDomain = threw(function () { b.cert.create(mk([{ name: "m", domains: ["ok.example", 123], challenge: GOOD_CHALLENGE }])); });
|
|
915
|
+
check("non-string domain entry → cert/bad-domain", eBadDomain && eBadDomain.code === "cert/bad-domain");
|
|
916
|
+
var eEmptyDomain = threw(function () { b.cert.create(mk([{ name: "m", domains: [""], challenge: GOOD_CHALLENGE }])); });
|
|
917
|
+
check("empty-string domain entry → cert/bad-domain", eEmptyDomain && eEmptyDomain.code === "cert/bad-domain");
|
|
918
|
+
|
|
919
|
+
// Missing / non-object challenge block.
|
|
920
|
+
var eNoChallenge = threw(function () { b.cert.create(mk([{ name: "m", domains: ["a.com"] }])); });
|
|
921
|
+
check("missing challenge block → cert/bad-challenge", eNoChallenge && eNoChallenge.code === "cert/bad-challenge");
|
|
922
|
+
|
|
923
|
+
// challenge present but provision/cleanup not both functions.
|
|
924
|
+
var eBadCbs = threw(function () {
|
|
925
|
+
b.cert.create(mk([{ name: "m", domains: ["a.com"], challenge: { type: "http-01", provision: "nope", cleanup: function () {} } }]));
|
|
926
|
+
});
|
|
927
|
+
check("non-function challenge callbacks → cert/bad-challenge-callbacks", eBadCbs && eBadCbs.code === "cert/bad-challenge-callbacks");
|
|
928
|
+
|
|
929
|
+
// keyAlg outside the allowed set.
|
|
930
|
+
var eBadAlg = threw(function () {
|
|
931
|
+
b.cert.create(mk([{ name: "m", domains: ["a.com"], keyAlg: "ecdsa-p521", challenge: GOOD_CHALLENGE }]));
|
|
932
|
+
});
|
|
933
|
+
check("unsupported keyAlg → cert/bad-key-alg", eBadAlg && eBadAlg.code === "cert/bad-key-alg");
|
|
934
|
+
|
|
935
|
+
// keyEscrow present but recipient is neither Buffer nor string.
|
|
936
|
+
var eBadEscrow = threw(function () {
|
|
937
|
+
b.cert.create(mk([{ name: "m", domains: ["a.com"], challenge: GOOD_CHALLENGE, keyEscrow: { recipient: 123 } }]));
|
|
938
|
+
});
|
|
939
|
+
check("keyEscrow recipient wrong type → cert/bad-key-escrow", eBadEscrow && eBadEscrow.code === "cert/bad-key-escrow");
|
|
940
|
+
|
|
941
|
+
// storage.type omitted → the `|| "sealed-disk"` default is taken and
|
|
942
|
+
// create() succeeds (constructs without throwing).
|
|
943
|
+
var eNoType = threw(function () {
|
|
944
|
+
b.cert.create({
|
|
945
|
+
storage: { rootDir: _tmpDir(), vault: _ephemeralVault() },
|
|
946
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
947
|
+
certs: [{ name: "m", domains: ["a.com"], challenge: GOOD_CHALLENGE }],
|
|
948
|
+
audit: false,
|
|
949
|
+
});
|
|
950
|
+
});
|
|
951
|
+
check("omitted storage.type → default sealed-disk (no throw)", !eNoType);
|
|
952
|
+
|
|
953
|
+
// storage.vault omitted → falls back to b.vault.getDefaultStore()
|
|
954
|
+
// (the `opts.vault || vault().getDefaultStore()` default in the
|
|
955
|
+
// storage factory). create() must still succeed.
|
|
956
|
+
var eNoVault = threw(function () {
|
|
957
|
+
b.cert.create({
|
|
958
|
+
storage: { type: "sealed-disk", rootDir: _tmpDir() },
|
|
959
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
960
|
+
certs: [{ name: "m", domains: ["a.com"], challenge: GOOD_CHALLENGE }],
|
|
961
|
+
audit: false,
|
|
962
|
+
});
|
|
963
|
+
});
|
|
964
|
+
check("omitted storage.vault → default store (no throw)", !eNoVault);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// ---- getContext / refresh error branches ----
|
|
968
|
+
|
|
969
|
+
function testGetContextAndRefreshErrors() {
|
|
970
|
+
var tmp = _tmpDir();
|
|
971
|
+
var mgr = b.cert.create({
|
|
972
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: _ephemeralVault() },
|
|
973
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
974
|
+
certs: [{ name: "main", domains: ["example.com"], challenge: GOOD_CHALLENGE }],
|
|
975
|
+
audit: false,
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
var threw = function (fn) { try { fn(); return null; } catch (e) { return e; } };
|
|
979
|
+
|
|
980
|
+
// Unknown name (not in the manifest).
|
|
981
|
+
var eUnknown = threw(function () { mgr.getContext("nope"); });
|
|
982
|
+
check("getContext unknown name → cert/unknown-name", eUnknown && eUnknown.code === "cert/unknown-name");
|
|
983
|
+
|
|
984
|
+
// Declared but not yet loaded (start() never ran).
|
|
985
|
+
var eNotLoaded = threw(function () { mgr.getContext("main"); });
|
|
986
|
+
check("getContext before start → cert/not-loaded", eNotLoaded && eNotLoaded.code === "cert/not-loaded");
|
|
987
|
+
|
|
988
|
+
// refresh() on an unknown name.
|
|
989
|
+
var eRefresh = null;
|
|
990
|
+
return mgr.refresh("nope").then(
|
|
991
|
+
function () { check("refresh unknown name → should reject", false); },
|
|
992
|
+
function (e) { eRefresh = e; check("refresh unknown name → cert/unknown-name", eRefresh && eRefresh.code === "cert/unknown-name"); }
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// ---- sniCallback error paths: no context + createSecureContext throw ----
|
|
997
|
+
|
|
998
|
+
async function testSniCallbackErrorPaths() {
|
|
999
|
+
// (a) No certs loaded at all → cb(cert/no-context).
|
|
1000
|
+
var tmp = _tmpDir();
|
|
1001
|
+
var mgr = b.cert.create({
|
|
1002
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: _ephemeralVault() },
|
|
1003
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1004
|
+
certs: [{ name: "main", domains: ["example.com"], challenge: GOOD_CHALLENGE }],
|
|
1005
|
+
audit: false,
|
|
1006
|
+
});
|
|
1007
|
+
var noCtxErr = await new Promise(function (resolve) {
|
|
1008
|
+
mgr.sniCallback("anything.example", function (err) { resolve(err); });
|
|
1009
|
+
});
|
|
1010
|
+
check("sniCallback with nothing loaded → cert/no-context",
|
|
1011
|
+
noCtxErr && noCtxErr.code === "cert/no-context");
|
|
1012
|
+
|
|
1013
|
+
// (b) A loaded context whose KEY is garbage → tls.createSecureContext
|
|
1014
|
+
// throws and the callback receives that error (the try/catch tail).
|
|
1015
|
+
var tmp2 = _tmpDir();
|
|
1016
|
+
var vault = _ephemeralVault();
|
|
1017
|
+
var pem = await _selfSignedCert(["bad.example"], 90);
|
|
1018
|
+
fs.mkdirSync(path.join(tmp2, "bad"), { recursive: true });
|
|
1019
|
+
fs.writeFileSync(path.join(tmp2, "bad", "cert.pem.sealed"), vault.seal(Buffer.from(pem.certPem)));
|
|
1020
|
+
// Valid cert (so _certMeta parses + the context loads) but an
|
|
1021
|
+
// unparseable private key, so createSecureContext rejects the pair.
|
|
1022
|
+
fs.writeFileSync(path.join(tmp2, "bad", "key.pem.sealed"), vault.seal(Buffer.from("-----BEGIN PRIVATE KEY-----\nnot-a-real-key\n-----END PRIVATE KEY-----\n")));
|
|
1023
|
+
fs.writeFileSync(path.join(tmp2, "bad", "meta.json"), JSON.stringify({
|
|
1024
|
+
expiresAt: Date.now() + 90 * 86400000, issuedAt: Date.now(),
|
|
1025
|
+
fingerprintSha256: "bad", subject: "CN=bad.example",
|
|
1026
|
+
lastRenewedAt: Date.now(), keyAlg: "ecdsa-p256",
|
|
1027
|
+
}));
|
|
1028
|
+
var mgr2 = b.cert.create({
|
|
1029
|
+
storage: { type: "sealed-disk", rootDir: tmp2, vault: vault },
|
|
1030
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1031
|
+
certs: [{ name: "bad", domains: ["bad.example"], challenge: GOOD_CHALLENGE }],
|
|
1032
|
+
audit: false,
|
|
1033
|
+
});
|
|
1034
|
+
await mgr2.start();
|
|
1035
|
+
var secCtxErr = await new Promise(function (resolve) {
|
|
1036
|
+
mgr2.sniCallback("bad.example", function (err) { resolve(err); });
|
|
1037
|
+
});
|
|
1038
|
+
check("sniCallback with unparseable key → createSecureContext error forwarded", secCtxErr instanceof Error);
|
|
1039
|
+
await mgr2.stop();
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// ---- start() after stop() is refused ----
|
|
1043
|
+
|
|
1044
|
+
async function testStartAfterStopRejects() {
|
|
1045
|
+
var tmp = _tmpDir();
|
|
1046
|
+
var mgr = b.cert.create({
|
|
1047
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: _ephemeralVault() },
|
|
1048
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1049
|
+
certs: [{ name: "main", domains: ["example.com"], challenge: GOOD_CHALLENGE }],
|
|
1050
|
+
audit: false,
|
|
1051
|
+
});
|
|
1052
|
+
await mgr.stop(); // flips the stopped latch without ever starting
|
|
1053
|
+
var threw = null;
|
|
1054
|
+
try { await mgr.start(); } catch (e) { threw = e; }
|
|
1055
|
+
check("start() after stop() → cert/already-stopped", threw && threw.code === "cert/already-stopped");
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// ---- Full ACME issue flow via an injected stub (no network) ----
|
|
1059
|
+
|
|
1060
|
+
async function testIssueFlowHappyPath() {
|
|
1061
|
+
await helpers.withTestTimeout("cert issue keyAlg matrix", async function () {
|
|
1062
|
+
var tmp = _tmpDir();
|
|
1063
|
+
var vault = _ephemeralVault();
|
|
1064
|
+
var pem = await _selfSignedCert(["issued.example"], 90);
|
|
1065
|
+
|
|
1066
|
+
// Pre-seed a VALID sealed account JWK so _loadOrGenerateAccountKey
|
|
1067
|
+
// takes its read-existing branch (not the generate branch).
|
|
1068
|
+
var acctPair = crypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
1069
|
+
var acctJwk = acctPair.publicKey.export({ format: "jwk" });
|
|
1070
|
+
acctJwk.privatePem = acctPair.privateKey.export({ type: "pkcs8", format: "pem" });
|
|
1071
|
+
acctJwk.publicPem = acctPair.publicKey.export({ type: "spki", format: "pem" });
|
|
1072
|
+
fs.mkdirSync(path.join(tmp, "account"), { recursive: true });
|
|
1073
|
+
fs.writeFileSync(path.join(tmp, "account", "jwk.json.sealed"), vault.seal(Buffer.from(JSON.stringify(acctJwk))));
|
|
1074
|
+
|
|
1075
|
+
var issued = [];
|
|
1076
|
+
var restore = _stubAcmeCreate(_makeStubAcme(pem.certPem));
|
|
1077
|
+
var mgr = b.cert.create({
|
|
1078
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: vault },
|
|
1079
|
+
acme: { directory: "https://example/", contactEmail: "ops@example.com", accountKey: "auto" },
|
|
1080
|
+
// One cert per keyAlg so every _generateLeafKeypair switch arm runs.
|
|
1081
|
+
// The tls-alpn-01 cert exercises the RFC 8737 key-authorization
|
|
1082
|
+
// branch; the last cert's cleanup throws to exercise the
|
|
1083
|
+
// cleanup-failure drop-silent audit inside _issueCert.
|
|
1084
|
+
certs: [
|
|
1085
|
+
{ name: "ec256", domains: ["ec256.example"], keyAlg: "ecdsa-p256", challenge: GOOD_CHALLENGE },
|
|
1086
|
+
{ name: "ec384", domains: ["ec384.example"], keyAlg: "ecdsa-p384", challenge: GOOD_CHALLENGE },
|
|
1087
|
+
{ name: "rsa2k", domains: ["rsa2k.example"], keyAlg: "rsa-2048", challenge: { type: "dns-01", provision: async function () {}, cleanup: async function () {} } },
|
|
1088
|
+
// rsa3k's cleanup throws an Error with an EMPTY message (so
|
|
1089
|
+
// `cleanupErr.message` is falsy); rsa4k's throws an Error with a
|
|
1090
|
+
// message. Together they cover both arms of the cleanup-failure
|
|
1091
|
+
// audit's `(cleanupErr && cleanupErr.message) || String(cleanupErr)`.
|
|
1092
|
+
{ name: "rsa3k", domains: ["rsa3k.example"], keyAlg: "rsa-3072", challenge: { type: "tls-alpn-01", provision: async function () {}, cleanup: async function () { throw new Error(""); } } },
|
|
1093
|
+
{ name: "rsa4k", domains: ["rsa4k.example"], keyAlg: "rsa-4096", challenge: { type: "http-01", provision: async function () {}, cleanup: async function () { throw new Error("cleanup boom"); } } },
|
|
1094
|
+
],
|
|
1095
|
+
ocsp: { stapling: false },
|
|
1096
|
+
audit: false,
|
|
1097
|
+
});
|
|
1098
|
+
mgr.on("cert.issued", function (ev) { issued.push(ev.name); });
|
|
1099
|
+
try {
|
|
1100
|
+
await mgr.start();
|
|
1101
|
+
["ec256", "ec384", "rsa2k", "rsa3k", "rsa4k"].forEach(function (n) {
|
|
1102
|
+
var ctx = mgr.getContext(n);
|
|
1103
|
+
check("issue: " + n + " context has the retrieved cert PEM",
|
|
1104
|
+
ctx.cert.indexOf("BEGIN CERTIFICATE") !== -1);
|
|
1105
|
+
check("issue: " + n + " context has a fingerprint",
|
|
1106
|
+
typeof ctx.fingerprintSha256 === "string" && ctx.fingerprintSha256.length > 0);
|
|
1107
|
+
// meta.json was persisted to disk by _persistCert.
|
|
1108
|
+
check("issue: " + n + " meta.json persisted",
|
|
1109
|
+
fs.existsSync(path.join(tmp, n, "meta.json")));
|
|
1110
|
+
});
|
|
1111
|
+
check("issue: cert.issued fired for every manifest cert", issued.length === 5);
|
|
1112
|
+
} finally {
|
|
1113
|
+
await mgr.stop();
|
|
1114
|
+
restore();
|
|
1115
|
+
}
|
|
1116
|
+
}, { timeoutMs: 120000 });
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
async function testIssueNoMatchingChallenge() {
|
|
1120
|
+
var tmp = _tmpDir();
|
|
1121
|
+
var vault = _ephemeralVault();
|
|
1122
|
+
var pem = await _selfSignedCert(["nomatch.example"], 90);
|
|
1123
|
+
var restore = _stubAcmeCreate(_makeStubAcme(pem.certPem, { omitChallengeType: true }));
|
|
1124
|
+
var mgr = b.cert.create({
|
|
1125
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: vault },
|
|
1126
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1127
|
+
// Requests http-01 but the stubbed CA only offers dns-01.
|
|
1128
|
+
certs: [{ name: "m", domains: ["nomatch.example"], challenge: GOOD_CHALLENGE }],
|
|
1129
|
+
ocsp: { stapling: false },
|
|
1130
|
+
audit: false,
|
|
1131
|
+
});
|
|
1132
|
+
var threw = null;
|
|
1133
|
+
try { await mgr.start(); } catch (e) { threw = e; }
|
|
1134
|
+
finally { await mgr.stop(); restore(); }
|
|
1135
|
+
check("issue: CA offers no matching challenge → cert/no-matching-challenge",
|
|
1136
|
+
threw && threw.code === "cert/no-matching-challenge");
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
async function testIssueValidAuthSkipsProvision() {
|
|
1140
|
+
var tmp = _tmpDir();
|
|
1141
|
+
var vault = _ephemeralVault();
|
|
1142
|
+
var pem = await _selfSignedCert(["already.example"], 90);
|
|
1143
|
+
var provisioned = false;
|
|
1144
|
+
var restore = _stubAcmeCreate(_makeStubAcme(pem.certPem, { authValid: true }));
|
|
1145
|
+
var mgr = b.cert.create({
|
|
1146
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: vault },
|
|
1147
|
+
// A provided accountKey object (not "auto") — exercises the
|
|
1148
|
+
// else-arm of `accountKey === "auto" || !accountKey` in _bootAcme
|
|
1149
|
+
// (the manager uses the supplied key rather than generating one).
|
|
1150
|
+
acme: { directory: "https://example/", accountKey: { privatePem: "PRIV", publicPem: "PUB" } },
|
|
1151
|
+
certs: [{ name: "m", domains: ["already.example"],
|
|
1152
|
+
challenge: { type: "http-01", provision: async function () { provisioned = true; }, cleanup: async function () {} } }],
|
|
1153
|
+
ocsp: { stapling: false },
|
|
1154
|
+
audit: false,
|
|
1155
|
+
});
|
|
1156
|
+
try {
|
|
1157
|
+
await mgr.start();
|
|
1158
|
+
// The authorization was already `valid`, so the manager skipped the
|
|
1159
|
+
// challenge solve entirely (the `continue`) and still finalized.
|
|
1160
|
+
check("issue: already-valid authorization skips provision", provisioned === false);
|
|
1161
|
+
check("issue: already-valid authorization still yields a context",
|
|
1162
|
+
mgr.getContext("m").cert.indexOf("BEGIN CERTIFICATE") !== -1);
|
|
1163
|
+
} finally { await mgr.stop(); restore(); }
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// Key escrow seals the renewed private key to the operator's offline
|
|
1167
|
+
// break-glass recipient (an ML-KEM-1024 keypair from
|
|
1168
|
+
// b.crypto.generateEncryptionKeyPair) via b.crypto.encrypt, writing
|
|
1169
|
+
// <name>/key.pem.escrow. The operator recovers it offline with
|
|
1170
|
+
// b.crypto.decrypt. Issue #446 — the prior implementation called a
|
|
1171
|
+
// nonexistent b.crypto.encryptEnvelope and threw on the first issue.
|
|
1172
|
+
async function testKeyEscrowSealsRecoverableEnvelope() {
|
|
1173
|
+
var tmp = _tmpDir();
|
|
1174
|
+
var vault = _ephemeralVault();
|
|
1175
|
+
var pem = await _selfSignedCert(["escrow.example"], 90);
|
|
1176
|
+
var kp = b.crypto.generateEncryptionKeyPair();
|
|
1177
|
+
var restore = _stubAcmeCreate(_makeStubAcme(pem.certPem));
|
|
1178
|
+
var mgr = b.cert.create({
|
|
1179
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: vault },
|
|
1180
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1181
|
+
certs: [{ name: "m", domains: ["escrow.example"], challenge: GOOD_CHALLENGE,
|
|
1182
|
+
keyEscrow: { recipient: { publicKey: kp.publicKey, ecPublicKey: kp.ecPublicKey } } }],
|
|
1183
|
+
ocsp: { stapling: false },
|
|
1184
|
+
audit: false,
|
|
1185
|
+
});
|
|
1186
|
+
var threw = null;
|
|
1187
|
+
try { await mgr.start(); } catch (e) { threw = e; }
|
|
1188
|
+
finally { await mgr.stop(); restore(); }
|
|
1189
|
+
check("keyEscrow write path no longer throws", threw === null);
|
|
1190
|
+
|
|
1191
|
+
var escrowPath = path.join(tmp, "m", "key.pem.escrow");
|
|
1192
|
+
check("keyEscrow: escrow envelope written to <name>/key.pem.escrow",
|
|
1193
|
+
fs.existsSync(escrowPath));
|
|
1194
|
+
|
|
1195
|
+
// Break-glass recovery: the offline operator decrypts the escrow with
|
|
1196
|
+
// the recipient private key(s) and gets a usable private-key PEM back.
|
|
1197
|
+
var envelope = fs.readFileSync(escrowPath, "utf8").trim();
|
|
1198
|
+
var recovered = b.crypto.decrypt(envelope,
|
|
1199
|
+
{ privateKey: kp.privateKey, ecPrivateKey: kp.ecPrivateKey });
|
|
1200
|
+
var recoveredPem = Buffer.isBuffer(recovered) ? recovered.toString("utf8") : recovered;
|
|
1201
|
+
check("keyEscrow: recovered plaintext is a PEM private key",
|
|
1202
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----/.test(recoveredPem));
|
|
1203
|
+
|
|
1204
|
+
// An unrecognized recipient shape is refused at config time.
|
|
1205
|
+
var badThrew = null;
|
|
1206
|
+
try {
|
|
1207
|
+
b.cert.create({
|
|
1208
|
+
storage: { type: "sealed-disk", rootDir: _tmpDir(), vault: _ephemeralVault() },
|
|
1209
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1210
|
+
certs: [{ name: "b", domains: ["b.example"], challenge: GOOD_CHALLENGE,
|
|
1211
|
+
keyEscrow: { recipient: 123 } }],
|
|
1212
|
+
});
|
|
1213
|
+
} catch (e) { badThrew = e; }
|
|
1214
|
+
check("keyEscrow: non-string / non-object recipient refused at config time",
|
|
1215
|
+
badThrew && badThrew.code === "cert/bad-key-escrow");
|
|
1216
|
+
|
|
1217
|
+
// An object-form recipient with an empty publicKey is refused too: the
|
|
1218
|
+
// object path must require a non-empty key like the string path does, or a
|
|
1219
|
+
// "" key slips through config and fails deeper at b.crypto.encrypt time.
|
|
1220
|
+
var emptyKeyThrew = null;
|
|
1221
|
+
try {
|
|
1222
|
+
b.cert.create({
|
|
1223
|
+
storage: { type: "sealed-disk", rootDir: _tmpDir(), vault: _ephemeralVault() },
|
|
1224
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1225
|
+
certs: [{ name: "c", domains: ["c.example"], challenge: GOOD_CHALLENGE,
|
|
1226
|
+
keyEscrow: { recipient: { publicKey: "" } } }],
|
|
1227
|
+
});
|
|
1228
|
+
} catch (e) { emptyKeyThrew = e; }
|
|
1229
|
+
check("keyEscrow: object recipient with empty publicKey refused at config time",
|
|
1230
|
+
emptyKeyThrew && emptyKeyThrew.code === "cert/bad-key-escrow");
|
|
1231
|
+
|
|
1232
|
+
// A present-but-empty ecPublicKey (the optional P-384 hybrid leg) is refused
|
|
1233
|
+
// too — an empty hybrid key must not silently downgrade to ML-KEM-only.
|
|
1234
|
+
var emptyEcThrew = null;
|
|
1235
|
+
try {
|
|
1236
|
+
b.cert.create({
|
|
1237
|
+
storage: { type: "sealed-disk", rootDir: _tmpDir(), vault: _ephemeralVault() },
|
|
1238
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1239
|
+
certs: [{ name: "d", domains: ["d.example"], challenge: GOOD_CHALLENGE,
|
|
1240
|
+
keyEscrow: { recipient: { publicKey: "ml-kem-pem", ecPublicKey: "" } } }],
|
|
1241
|
+
});
|
|
1242
|
+
} catch (e) { emptyEcThrew = e; }
|
|
1243
|
+
check("keyEscrow: object recipient with empty ecPublicKey refused at config time",
|
|
1244
|
+
emptyEcThrew && emptyEcThrew.code === "cert/bad-key-escrow");
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// ---- Corrupt sealed cert that unseals but won't parse → re-issue ----
|
|
1248
|
+
|
|
1249
|
+
async function testUnparseableSealedCertReissues() {
|
|
1250
|
+
var tmp = _tmpDir();
|
|
1251
|
+
var vault = _ephemeralVault();
|
|
1252
|
+
var keyPem = (await _selfSignedCert(["parse.example"], 90)).keyPem;
|
|
1253
|
+
var pem = await _selfSignedCert(["parse.example"], 90);
|
|
1254
|
+
|
|
1255
|
+
fs.mkdirSync(path.join(tmp, "main"), { recursive: true });
|
|
1256
|
+
// Sealed blob unseals cleanly (XOR vault) but the plaintext is NOT a
|
|
1257
|
+
// certificate, so _certMeta's X509Certificate parse throws and the
|
|
1258
|
+
// manager routes to re-issue (distinct from an unseal failure).
|
|
1259
|
+
fs.writeFileSync(path.join(tmp, "main", "cert.pem.sealed"), vault.seal(Buffer.from("this is definitely not a certificate")));
|
|
1260
|
+
fs.writeFileSync(path.join(tmp, "main", "key.pem.sealed"), vault.seal(Buffer.from(keyPem)));
|
|
1261
|
+
fs.writeFileSync(path.join(tmp, "main", "meta.json"), JSON.stringify({
|
|
1262
|
+
expiresAt: Date.now() + 90 * 86400000, issuedAt: Date.now(),
|
|
1263
|
+
fingerprintSha256: "ff", subject: "CN=parse.example",
|
|
1264
|
+
lastRenewedAt: Date.now(), keyAlg: "ecdsa-p256",
|
|
1265
|
+
}));
|
|
1266
|
+
|
|
1267
|
+
var restore = _stubAcmeCreate(_makeStubAcme(pem.certPem));
|
|
1268
|
+
var mgr = b.cert.create({
|
|
1269
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: vault },
|
|
1270
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1271
|
+
certs: [{ name: "main", domains: ["parse.example"], challenge: GOOD_CHALLENGE }],
|
|
1272
|
+
ocsp: { stapling: false },
|
|
1273
|
+
audit: false,
|
|
1274
|
+
});
|
|
1275
|
+
try {
|
|
1276
|
+
await mgr.start();
|
|
1277
|
+
check("unparseable sealed cert → re-issued to a valid context",
|
|
1278
|
+
mgr.getContext("main").cert.indexOf("BEGIN CERTIFICATE") !== -1);
|
|
1279
|
+
} finally { await mgr.stop(); restore(); }
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// ---- readSealed returns a non-Buffer (string) unseal result ----
|
|
1283
|
+
|
|
1284
|
+
async function testReadSealedStringUnseal() {
|
|
1285
|
+
// A vault whose unseal returns a STRING (not a Buffer) — exercises
|
|
1286
|
+
// readSealed's `Buffer.isBuffer(plain) ? plain : Buffer.from(plain)`
|
|
1287
|
+
// else-arm. PEM is ASCII so the utf8 round-trip is lossless.
|
|
1288
|
+
var key = crypto.randomBytes(32);
|
|
1289
|
+
var stringVault = {
|
|
1290
|
+
seal: function (buf) {
|
|
1291
|
+
var out = Buffer.alloc(buf.length);
|
|
1292
|
+
for (var i = 0; i < buf.length; i++) out[i] = buf[i] ^ key[i % key.length];
|
|
1293
|
+
return out;
|
|
1294
|
+
},
|
|
1295
|
+
unseal: function (buf) {
|
|
1296
|
+
var out = Buffer.alloc(buf.length);
|
|
1297
|
+
for (var i = 0; i < buf.length; i++) out[i] = buf[i] ^ key[i % key.length];
|
|
1298
|
+
return out.toString("utf8");
|
|
1299
|
+
},
|
|
1300
|
+
};
|
|
1301
|
+
var tmp = _tmpDir();
|
|
1302
|
+
var pem = await _selfSignedCert(["strvault.example"], 90);
|
|
1303
|
+
fs.mkdirSync(path.join(tmp, "main"), { recursive: true });
|
|
1304
|
+
fs.writeFileSync(path.join(tmp, "main", "cert.pem.sealed"), stringVault.seal(Buffer.from(pem.certPem)));
|
|
1305
|
+
fs.writeFileSync(path.join(tmp, "main", "key.pem.sealed"), stringVault.seal(Buffer.from(pem.keyPem)));
|
|
1306
|
+
fs.writeFileSync(path.join(tmp, "main", "meta.json"), JSON.stringify({
|
|
1307
|
+
expiresAt: Date.now() + 90 * 86400000, issuedAt: Date.now(),
|
|
1308
|
+
fingerprintSha256: "ff", subject: "CN=strvault.example",
|
|
1309
|
+
lastRenewedAt: Date.now(), keyAlg: "ecdsa-p256",
|
|
1310
|
+
}));
|
|
1311
|
+
var mgr = b.cert.create({
|
|
1312
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: stringVault },
|
|
1313
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1314
|
+
certs: [{ name: "main", domains: ["strvault.example"], challenge: GOOD_CHALLENGE }],
|
|
1315
|
+
ocsp: { stapling: false },
|
|
1316
|
+
audit: false,
|
|
1317
|
+
});
|
|
1318
|
+
try {
|
|
1319
|
+
await mgr.start();
|
|
1320
|
+
check("readSealed string unseal → context still loads cert PEM",
|
|
1321
|
+
mgr.getContext("main").cert.indexOf("BEGIN CERTIFICATE") !== -1);
|
|
1322
|
+
} finally { await mgr.stop(); }
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// ---- Renewal scheduler: due cert renews successfully (+ ARI honored) ----
|
|
1326
|
+
|
|
1327
|
+
async function testSchedulerRenewsDueCert() {
|
|
1328
|
+
await helpers.withTestTimeout("scheduler renews due cert", async function () {
|
|
1329
|
+
var tmp = _tmpDir();
|
|
1330
|
+
var vault = _ephemeralVault();
|
|
1331
|
+
var pem = await _selfSignedCert(["renew.example"], 90);
|
|
1332
|
+
var renewed = [];
|
|
1333
|
+
var restore = _stubAcmeCreate(_makeStubAcme(pem.certPem)); // renewIfDue → { shouldRenew:false }
|
|
1334
|
+
var mgr = b.cert.create({
|
|
1335
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: vault },
|
|
1336
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1337
|
+
certs: [{ name: "main", domains: ["renew.example"], challenge: GOOD_CHALLENGE }],
|
|
1338
|
+
// A huge renewal window forces every scheduler tick to treat the
|
|
1339
|
+
// cert as due; a short interval makes the tick fire promptly.
|
|
1340
|
+
renew: { intervalMs: 30, minDaysBeforeExpiry: 100000 },
|
|
1341
|
+
ocsp: { stapling: false },
|
|
1342
|
+
audit: false,
|
|
1343
|
+
});
|
|
1344
|
+
mgr.on("cert.renewed", function (ev) { renewed.push(ev.name); });
|
|
1345
|
+
try {
|
|
1346
|
+
await mgr.start(); // initial issue (cert.issued), boots acmeClient so ARI runs
|
|
1347
|
+
await helpers.waitUntil(function () { return renewed.length >= 1; },
|
|
1348
|
+
{ timeoutMs: 5000, label: "scheduler: cert.renewed fired" });
|
|
1349
|
+
check("scheduler renews a due cert (ARI honored, time-based renew)", renewed.length >= 1);
|
|
1350
|
+
} finally { await mgr.stop(); restore(); }
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// ---- Renewal scheduler: renew failure + ARI-fetch failure both survive ----
|
|
1355
|
+
|
|
1356
|
+
async function testSchedulerRenewFailureAndAriCatch() {
|
|
1357
|
+
await helpers.withTestTimeout("scheduler renew failure", async function () {
|
|
1358
|
+
var tmp = _tmpDir();
|
|
1359
|
+
var vault = _ephemeralVault();
|
|
1360
|
+
var pem = await _selfSignedCert(["fail.example"], 90);
|
|
1361
|
+
var failures = [];
|
|
1362
|
+
// First retrieveCert (during start's issue) succeeds; every later one
|
|
1363
|
+
// throws → the scheduled renewal fails. renewIfDue always throws →
|
|
1364
|
+
// the ARI try/catch fall-through is exercised too.
|
|
1365
|
+
var restore = _stubAcmeCreate(_makeStubAcme(pem.certPem, { failRetrieveAfter: 1, renewIfDueThrows: true }));
|
|
1366
|
+
var mgr = b.cert.create({
|
|
1367
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: vault },
|
|
1368
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1369
|
+
certs: [{ name: "main", domains: ["fail.example"], challenge: GOOD_CHALLENGE }],
|
|
1370
|
+
renew: { intervalMs: 30, minDaysBeforeExpiry: 100000 },
|
|
1371
|
+
ocsp: { stapling: false },
|
|
1372
|
+
audit: false,
|
|
1373
|
+
});
|
|
1374
|
+
mgr.on("cert.renew-failed", function (ev) { failures.push(ev); });
|
|
1375
|
+
try {
|
|
1376
|
+
await mgr.start(); // succeeds (issue #1), meta persisted, acmeClient booted
|
|
1377
|
+
await helpers.waitUntil(function () { return failures.length >= 1; },
|
|
1378
|
+
{ timeoutMs: 5000, label: "scheduler: cert.renew-failed fired" });
|
|
1379
|
+
check("scheduler: failed renewal emits cert.renew-failed (ARI throw survived)",
|
|
1380
|
+
failures.length >= 1 && failures[0].name === "main" && failures[0].error);
|
|
1381
|
+
} finally { await mgr.stop(); restore(); }
|
|
1382
|
+
|
|
1383
|
+
// Second manager: the renewal failure carries an EMPTY message, so
|
|
1384
|
+
// the renew-failed audit's `(e && e.message) || String(e)` takes its
|
|
1385
|
+
// String(e) fallback arm.
|
|
1386
|
+
var tmp2 = _tmpDir();
|
|
1387
|
+
var pem2 = await _selfSignedCert(["fail2.example"], 90);
|
|
1388
|
+
var failures2 = [];
|
|
1389
|
+
var restore2 = _stubAcmeCreate(_makeStubAcme(pem2.certPem, { failRetrieveAfter: 1, retrieveThrowValue: new Error("") }));
|
|
1390
|
+
var mgr2 = b.cert.create({
|
|
1391
|
+
storage: { type: "sealed-disk", rootDir: tmp2, vault: _ephemeralVault() },
|
|
1392
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1393
|
+
certs: [{ name: "main", domains: ["fail2.example"], challenge: GOOD_CHALLENGE }],
|
|
1394
|
+
renew: { intervalMs: 30, minDaysBeforeExpiry: 100000 },
|
|
1395
|
+
ocsp: { stapling: false },
|
|
1396
|
+
audit: false,
|
|
1397
|
+
});
|
|
1398
|
+
mgr2.on("cert.renew-failed", function (ev) { failures2.push(ev); });
|
|
1399
|
+
try {
|
|
1400
|
+
await mgr2.start();
|
|
1401
|
+
await helpers.waitUntil(function () { return failures2.length >= 1; },
|
|
1402
|
+
{ timeoutMs: 5000, label: "scheduler: cert.renew-failed (non-Error) fired" });
|
|
1403
|
+
check("scheduler: empty-message renewal failure still emits cert.renew-failed",
|
|
1404
|
+
failures2.length >= 1 && failures2[0].error instanceof Error && failures2[0].error.message === "");
|
|
1405
|
+
} finally { await mgr2.stop(); restore2(); }
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// ---- OCSP stapling refresh: success (staple cached) + responder failure ----
|
|
1410
|
+
|
|
1411
|
+
async function testOcspStaplingRefresh() {
|
|
1412
|
+
await helpers.withTestTimeout("ocsp stapling refresh", async function () {
|
|
1413
|
+
var vault = _ephemeralVault();
|
|
1414
|
+
// A two-cert chain (leaf + issuer) so _refreshOcspFor gets past the
|
|
1415
|
+
// "no issuer in the served chain" (chain.length < 2) short-circuit.
|
|
1416
|
+
var leaf = await _selfSignedCert(["ocsp.example"], 90);
|
|
1417
|
+
var issuer = await _selfSignedCert(["issuer.example"], 90);
|
|
1418
|
+
var chainPem = leaf.certPem.trim() + "\n" + issuer.certPem.trim() + "\n";
|
|
1419
|
+
|
|
1420
|
+
function seed(dir) {
|
|
1421
|
+
fs.mkdirSync(path.join(dir, "main"), { recursive: true });
|
|
1422
|
+
fs.writeFileSync(path.join(dir, "main", "cert.pem.sealed"), vault.seal(Buffer.from(chainPem)));
|
|
1423
|
+
fs.writeFileSync(path.join(dir, "main", "key.pem.sealed"), vault.seal(Buffer.from(leaf.keyPem)));
|
|
1424
|
+
fs.writeFileSync(path.join(dir, "main", "meta.json"), JSON.stringify({
|
|
1425
|
+
expiresAt: Date.now() + 90 * 86400000, issuedAt: Date.now(),
|
|
1426
|
+
fingerprintSha256: "ff", subject: "CN=ocsp.example",
|
|
1427
|
+
lastRenewedAt: Date.now(), keyAlg: "ecdsa-p256",
|
|
1428
|
+
}));
|
|
1429
|
+
}
|
|
1430
|
+
function mk(dir) {
|
|
1431
|
+
return b.cert.create({
|
|
1432
|
+
storage: { type: "sealed-disk", rootDir: dir, vault: vault },
|
|
1433
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1434
|
+
certs: [{ name: "main", domains: ["ocsp.example"], challenge: GOOD_CHALLENGE }],
|
|
1435
|
+
ocsp: { stapling: true, refreshMs: 100000 },
|
|
1436
|
+
audit: false,
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
// (a) Responder returns a DER — the manager caches it on the context.
|
|
1441
|
+
var tmpA = _tmpDir();
|
|
1442
|
+
seed(tmpA);
|
|
1443
|
+
var restoreA = _stubOcspFetch(async function (o) {
|
|
1444
|
+
check("ocsp.fetch received leaf + issuer PEMs",
|
|
1445
|
+
typeof o.leafPem === "string" && typeof o.issuerPem === "string" && o.leafPem !== o.issuerPem);
|
|
1446
|
+
return { ocspDer: Buffer.from("mock-ocsp-der") };
|
|
1447
|
+
});
|
|
1448
|
+
var mgrA = mk(tmpA);
|
|
1449
|
+
try {
|
|
1450
|
+
await mgrA.start(); // OCSP initial refresh runs in the background
|
|
1451
|
+
var staple = await helpers.waitUntil(function () {
|
|
1452
|
+
var r = mgrA.getContext("main").ocspResponse;
|
|
1453
|
+
return r ? r : false;
|
|
1454
|
+
}, { timeoutMs: 5000, label: "ocsp: staple cached on context" });
|
|
1455
|
+
check("ocsp: validated DER cached on getContext().ocspResponse",
|
|
1456
|
+
Buffer.isBuffer(staple) && staple.toString() === "mock-ocsp-der");
|
|
1457
|
+
} finally { await mgrA.stop(); restoreA(); }
|
|
1458
|
+
|
|
1459
|
+
// (b) Responder throws — the failure is swallowed (fail-soft) and the
|
|
1460
|
+
// staple stays absent; the manager does not crash.
|
|
1461
|
+
var tmpB = _tmpDir();
|
|
1462
|
+
seed(tmpB);
|
|
1463
|
+
var fetchCalls = 0;
|
|
1464
|
+
var restoreB = _stubOcspFetch(async function () {
|
|
1465
|
+
fetchCalls += 1;
|
|
1466
|
+
throw new Error("mock OCSP responder unreachable");
|
|
1467
|
+
});
|
|
1468
|
+
var mgrB = mk(tmpB);
|
|
1469
|
+
try {
|
|
1470
|
+
await mgrB.start();
|
|
1471
|
+
await helpers.waitUntil(function () { return fetchCalls >= 1; },
|
|
1472
|
+
{ timeoutMs: 5000, label: "ocsp: responder invoked" });
|
|
1473
|
+
check("ocsp: responder failure is fail-soft (no staple, no crash)",
|
|
1474
|
+
mgrB.getContext("main").ocspResponse === null);
|
|
1475
|
+
} finally { await mgrB.stop(); restoreB(); }
|
|
1476
|
+
|
|
1477
|
+
// (c) Responder throws an Error with an EMPTY message — the
|
|
1478
|
+
// refresh-failed audit's `(e && e.message) || String(e)` takes its
|
|
1479
|
+
// String(e) fallback arm.
|
|
1480
|
+
var tmpC = _tmpDir();
|
|
1481
|
+
seed(tmpC);
|
|
1482
|
+
var fetchCallsC = 0;
|
|
1483
|
+
var restoreC = _stubOcspFetch(async function () { fetchCallsC += 1; throw new Error(""); });
|
|
1484
|
+
var mgrC = mk(tmpC);
|
|
1485
|
+
try {
|
|
1486
|
+
await mgrC.start();
|
|
1487
|
+
await helpers.waitUntil(function () { return fetchCallsC >= 1; },
|
|
1488
|
+
{ timeoutMs: 5000, label: "ocsp: empty-message responder invoked" });
|
|
1489
|
+
check("ocsp: empty-message responder failure is also fail-soft",
|
|
1490
|
+
mgrC.getContext("main").ocspResponse === null);
|
|
1491
|
+
} finally { await mgrC.stop(); restoreC(); }
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
// ---- _certMeta with a cert that carries no SubjectAlternativeName ----
|
|
1496
|
+
|
|
1497
|
+
async function testCertMetaNoSubjectAltName() {
|
|
1498
|
+
// A cert with no SAN extension → the parsed subjectAltName is
|
|
1499
|
+
// undefined and _certMeta falls back to null (the `|| null` arm).
|
|
1500
|
+
var tmp = _tmpDir();
|
|
1501
|
+
var vault = _ephemeralVault();
|
|
1502
|
+
var pem = await _selfSignedCertNoSan("nosan.example", 90);
|
|
1503
|
+
fs.mkdirSync(path.join(tmp, "main"), { recursive: true });
|
|
1504
|
+
fs.writeFileSync(path.join(tmp, "main", "cert.pem.sealed"), vault.seal(Buffer.from(pem.certPem)));
|
|
1505
|
+
fs.writeFileSync(path.join(tmp, "main", "key.pem.sealed"), vault.seal(Buffer.from(pem.keyPem)));
|
|
1506
|
+
fs.writeFileSync(path.join(tmp, "main", "meta.json"), JSON.stringify({
|
|
1507
|
+
expiresAt: Date.now() + 90 * 86400000, issuedAt: Date.now(),
|
|
1508
|
+
fingerprintSha256: "ff", subject: "CN=nosan.example",
|
|
1509
|
+
lastRenewedAt: Date.now(), keyAlg: "ecdsa-p256",
|
|
1510
|
+
}));
|
|
1511
|
+
var mgr = b.cert.create({
|
|
1512
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: vault },
|
|
1513
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1514
|
+
certs: [{ name: "main", domains: ["nosan.example"], challenge: GOOD_CHALLENGE }],
|
|
1515
|
+
ocsp: { stapling: false },
|
|
1516
|
+
audit: false,
|
|
1517
|
+
});
|
|
1518
|
+
try {
|
|
1519
|
+
await mgr.start(); // _ensureCert → _certMeta parses the no-SAN cert
|
|
1520
|
+
check("no-SAN cert still loads a usable context",
|
|
1521
|
+
mgr.getContext("main").cert.indexOf("BEGIN CERTIFICATE") !== -1);
|
|
1522
|
+
} finally { await mgr.stop(); }
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
// ---- Renewal scheduler ARI-decision arms with a not-yet-due cert ----
|
|
1526
|
+
|
|
1527
|
+
async function testSchedulerAriDecisionArms() {
|
|
1528
|
+
await helpers.withTestTimeout("scheduler ARI decision arms", async function () {
|
|
1529
|
+
// A freshly-issued cert is comfortably inside its validity window, so
|
|
1530
|
+
// the time-based test says "not due". With the ACME client booted,
|
|
1531
|
+
// each tick still consults ARI. Two managers cover both ARI verdicts:
|
|
1532
|
+
// one where ARI forces a renew (shouldRenew := true), and one where
|
|
1533
|
+
// ARI declines and the tick returns early at `if (!shouldRenew)`.
|
|
1534
|
+
async function runOne(ariSays, label) {
|
|
1535
|
+
var tmp = _tmpDir();
|
|
1536
|
+
var pem = await _selfSignedCert([label + ".example"], 90);
|
|
1537
|
+
var ariCalls = 0;
|
|
1538
|
+
var restore = _stubAcmeCreate(_makeStubAcme(pem.certPem, {
|
|
1539
|
+
renewIfDueShould: ariSays,
|
|
1540
|
+
onRenewIfDue: function () { ariCalls += 1; },
|
|
1541
|
+
}));
|
|
1542
|
+
var mgr = b.cert.create({
|
|
1543
|
+
storage: { type: "sealed-disk", rootDir: tmp, vault: _ephemeralVault() },
|
|
1544
|
+
acme: { directory: "https://example/", accountKey: "auto" },
|
|
1545
|
+
certs: [{ name: "main", domains: [label + ".example"], challenge: GOOD_CHALLENGE }],
|
|
1546
|
+
// Default-ish 14-day window so the fresh 90-day cert is NOT
|
|
1547
|
+
// time-due; the ARI verdict is the only lever this tick.
|
|
1548
|
+
renew: { intervalMs: 30, minDaysBeforeExpiry: 14 },
|
|
1549
|
+
ocsp: { stapling: false },
|
|
1550
|
+
audit: false,
|
|
1551
|
+
});
|
|
1552
|
+
try {
|
|
1553
|
+
await mgr.start(); // issues → boots acmeClient so the ARI branch runs
|
|
1554
|
+
await helpers.waitUntil(function () { return ariCalls >= 1; },
|
|
1555
|
+
{ timeoutMs: 5000, label: "scheduler: renewIfDue consulted (" + label + ")" });
|
|
1556
|
+
check("scheduler consults ARI on a not-yet-due cert (" + label + ")", ariCalls >= 1);
|
|
1557
|
+
} finally { await mgr.stop(); restore(); }
|
|
1558
|
+
}
|
|
1559
|
+
await runOne(true, "ariforce"); // ARI true → shouldRenew := true arm
|
|
1560
|
+
await runOne(false, "aridecline"); // ARI false → `if (!shouldRenew) return` arm
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
|
|
744
1564
|
async function run() {
|
|
745
1565
|
testSurface();
|
|
746
1566
|
testFactoryRefusesBadOpts();
|
|
@@ -754,6 +1574,23 @@ async function run() {
|
|
|
754
1574
|
await testCorruptSealedCertReissues();
|
|
755
1575
|
await testStaleMetaDoesNotServeExpiringCert();
|
|
756
1576
|
await testCorruptAccountKeyClearError();
|
|
1577
|
+
// Error / adversarial / option-default branch coverage.
|
|
1578
|
+
testPositiveFiniteOptRejections();
|
|
1579
|
+
testManifestSizeAndShapeRejections();
|
|
1580
|
+
await testGetContextAndRefreshErrors();
|
|
1581
|
+
await testSniCallbackErrorPaths();
|
|
1582
|
+
await testStartAfterStopRejects();
|
|
1583
|
+
await testIssueFlowHappyPath();
|
|
1584
|
+
await testIssueNoMatchingChallenge();
|
|
1585
|
+
await testIssueValidAuthSkipsProvision();
|
|
1586
|
+
await testKeyEscrowSealsRecoverableEnvelope();
|
|
1587
|
+
await testUnparseableSealedCertReissues();
|
|
1588
|
+
await testReadSealedStringUnseal();
|
|
1589
|
+
await testCertMetaNoSubjectAltName();
|
|
1590
|
+
await testSchedulerRenewsDueCert();
|
|
1591
|
+
await testSchedulerRenewFailureAndAriCatch();
|
|
1592
|
+
await testSchedulerAriDecisionArms();
|
|
1593
|
+
await testOcspStaplingRefresh();
|
|
757
1594
|
}
|
|
758
1595
|
|
|
759
1596
|
module.exports = { run: run };
|