@blamejs/core 0.18.13 → 0.18.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 CHANGED
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.18.x
10
10
 
11
+ - v0.18.14 (2026-08-06) — **Broader offline test coverage across the error-page renderer, the request body parser, and the safe-YAML parser.** Error, edge, and fallback branches of b.errorPage, b.middleware.bodyParser, and b.parsers.yaml are now under explicit test — b.errorPage gains its first dedicated test suite. No behaviour changes: exercising every path confirmed each already behaves per its documented contract. One unreachable duplicate of the multipart parser's per-part reset — dead code that was never called — was removed. The vendored @noble/curves and @blamejs/pki bundles are refreshed. **Changed:** *Error- and edge-path tests for the error-page renderer, request body parser, and safe-YAML parser* — b.errorPage (classification of every recognized error class and the generic-500 fallback, JSON-vs-HTML content negotiation, the development-vs-production render difference, sensitive-header redaction, the write-response fallbacks when the response object lacks writeHead, and the audit record's redaction, outcome, stack-only-for-5xx, and trusted-proxy actor-IP invariants), b.middleware.bodyParser (RFC 5987 filename* decoding, multipart part-header and body framing split across socket reads, disk-mode temp-directory creation failure, repeated-field aggregation, and the request-stream-error refusal), and b.parsers.yaml (numeric-literal overflow falling back to the string form, block- and flow-collection edges, merge-key and duplicate-key refusals, block-scalar header parsing, and the prototype-pollution key guard across every decode path) now have explicit tests for their boundary branches. Alongside the tests, an unreachable duplicate of the multipart parser's per-part reset — a helper that was never called, its logic already performed inline where each part finalizes — was removed. Behaviour is unchanged. · *Vendored @noble/curves refreshed to 2.3.0* — The vendored @noble/curves bundle behind b.crypto.oprf is refreshed from 2.2.0 to 2.3.0. Upstream reworked the oblivious-pseudorandom-function batch path to a shared-doubling multi-scalar multiplication — applied only to values that are public by construction (hash-derived transcript weights, wire-decoded points, and proof scalars) — and now rejects a non-function rng option outright. The verified outputs are unchanged; no blamejs public API changes. · *Vendored @blamejs/pki refreshed to 0.3.33* — The vendored @blamejs/pki bundle behind the mutual-TLS CA engine is refreshed from 0.3.32 to 0.3.33, which adds the compact certificate-policies value form for C509 (CBOR-encoded) certificates and the accompanying certificate-policy and policy-qualifier identifiers to that toolkit's OID registry. The CA engine uses only the toolkit's X.509, PKCS#12, and CRL surfaces, so there is no behaviour change to the certificate-authority surface — this is a supply-chain currency refresh.
12
+
11
13
  - v0.18.13 (2026-08-06) — **`b.db.collection` no longer corrupts a BLOB overflow column (its bytes were spread as numeric keys, destroying the JSON payload); broader offline test coverage across db-collection, TLS, external-db residency, notify, and BIMI.** A schemaless collection with a BLOB overflow column read that column back as a byte array. Because a byte array is a JavaScript object, the collection treated it as an already-parsed overflow object and spread its bytes as numeric keys on read, on update, and on insert — silently destroying the stored JSON payload. The overflow value is now decoded as JSON at every one of those points. The release also closes offline test-coverage gaps and adds an explicit test that the external-database residency boundary is enforced. **Changed:** *Broader offline test coverage and an explicit external-database residency-enforcement test* — b.db.collection (BLOB overflow round-trips, introspection-error wrapping, overflow-as-jsonColumn), b.network.tls.connectWithEch (the connect success, timeout, unreachable-peer, and synchronous-throw paths driven against local servers), b.externalDb (the boot-time data-residency check now has an explicit test that a personal-data backend outside the declared region is refused and compliant backends are admitted), b.notify (the default HTTP client is bound at send time), and b.mail.bimi (a certificate chain deeper than the maximum is rejected) now have tests for these boundary paths. **Fixed:** *b.db.collection no longer corrupts a BLOB overflow column* — A collection configured with an overflow column (unknown fields folded into a JSON column) reads that column back as a byte array when the column is declared BLOB. A byte array's typeof is "object", so the collection took it to be an already-parsed overflow object and spread its raw bytes as numeric-index keys — on read (surfacing { "0": …, "1": … } onto the row), on a $set/$unset update (rewriting the column to the byte spread and destroying the original keys), and on an insert that supplied a Buffer overflow value. The overflow value is now decoded from its bytes as JSON at all three points, so the stored payload survives; a BLOB holding non-JSON bytes falls back to an empty object instead of corrupting, matching the existing non-JSON-string behaviour.
12
14
 
13
15
  - v0.18.12 (2026-08-05) — **Certificate Transparency SCT verification now works for real certificates (it previously rejected every embedded SCT); b.network.tls.ct.verifyScts / requireScts now require the issuing CA; broader Redis-client and TLS-verification test coverage.** b.network.tls.ct.verifyScts reconstructed the wrong RFC 6962 signed input for the SCTs embedded in a certificate — it built an x509_entry over the bare TBSCertificate instead of the precert_entry (issuer_key_hash + TBSCertificate) the log actually signed, and separately read the SCT's signature algorithm from a field the parser does not emit. Either alone made verification impossible: verifyScts and requireScts rejected every real embedded SCT. Verification now builds the RFC 6962 §3.2 precert_entry and requires the issuing CA so it can bind issuer_key_hash. This is a fail-closed fix — nothing invalid was ever trusted — but SCT enforcement now needs the issuer supplied. **Changed:** *b.network.tls.ct.verifyScts and requireScts now require the issuing CA* — Because an embedded SCT binds the issuing CA's key, verifying one needs that key. Pass the issuer to verifyScts as issuerCertDer (the issuer certificate DER), issuerSpkiDer (its SubjectPublicKeyInfo DER), or issuerKeyHash (the precomputed 32-byte SHA-256). Without it, verifyScts returns { ok: false, reason: "issuer-key-required" } and does not verify. requireScts derives the issuer from the peer's certificate chain automatically and accepts the same options; supply one explicitly only when the issuer is not present in the chain. verifyInclusion takes the same issuer options for embedded (precert_entry) leaves. · *Vendored @blamejs/pki refreshed to 0.3.32* — The vendored @blamejs/pki bundle behind the mutual-TLS CA engine is refreshed to 0.3.32, which adds C509 (CBOR-encoded certificate) extended-key-usage and extension registries (RFC 7633 TLS Feature, RFC 4556 PKINIT, and SSH/CMC/Bundle-Security purposes). No blamejs public API changes. · *Error- and edge-path tests for the Redis client and TLS-verification primitives* — b.redisClient (RESP framing and protocol-error edges, split replies, AUTH / SELECT on connect, push-message demux, connect/command timeouts, socket-error and close teardown) and b.network.tls (OCSP and Certificate-Transparency parse and verification branches, ECH config framing, and PKIX host/SAN matching) now have explicit tests for their boundary branches, driven through local fake servers and crafted certificates. **Fixed:** *Certificate Transparency SCT verification now works for certificate-embedded SCTs* — For an SCT embedded in a certificate's X.509v3 extension, the log computed its signature over the RFC 6962 §3.2 precert_entry — issuer_key_hash (SHA-256 of the issuing CA's SubjectPublicKeyInfo) followed by the TBSCertificate with the SCT extension removed. b.network.tls.ct.verifyScts instead reconstructed an x509_entry over just the stripped TBSCertificate, with no issuer binding, so the reconstructed input never matched what the log signed and no real embedded SCT could verify. It additionally read the SCT's signature-algorithm from a field name the parser does not emit, so the algorithm cross-check failed before the signature was even reached. verifyScts now builds the precert_entry and reads the parsed algorithm field, so a valid SCT verifies and a genuine mismatch is still refused. b.network.tls.ct.verifyInclusion builds the RFC 9162 §4.6 leaf the same way. The prior behaviour failed closed (it rejected rather than accepted), so no invalid certificate was ever trusted. **Security:** *SCT verification now binds each log key to the SCT's log ID* — An SCT's log ID is the SHA-256 of the log's public key (RFC 6962 §3.2). b.network.tls.ct.verifyScts now recomputes that hash from the configured key and refuses the SCT (reason "log-id-key-mismatch") when it does not equal the log ID the SCT names, before checking the signature. This prevents a misconfigured logKeys map from letting a key impersonate an approved log. · *SCT verification counts distinct logs, refuses future timestamps, and validates the minimum* — verifyScts now counts SCTs from distinct logs, so duplicate SCTs from a single log can no longer satisfy a "minimum 2 SCTs" diversity policy (verifiedCount reflects distinct logs). It refuses an SCT whose timestamp is in the future (RFC 6962 §5.2; opts.now / opts.clockSkewMs override the clock and the default five-minute skew). A minScts that is not a positive integer is rejected at the entry point rather than silently accepting an unverified certificate. Parsing an SCT extension from a peer certificate now fails closed on malformed DER — it returns no SCT instead of throwing, so a hostile certificate cannot raise an uncaught error out of verifyScts, parseScts, or the requireScts predicate.
package/NOTICE CHANGED
@@ -26,7 +26,7 @@ Used for: XChaCha20-Poly1305 authenticated encryption (lib/crypto.js,
26
26
  Thank you to Paul Miller for the audited noble-ciphers suite.
27
27
  --------------------------------------------------------------------------------
28
28
  Component: @noble/curves
29
- Version: 2.2.0
29
+ Version: 2.3.0
30
30
  Source: https://github.com/paulmillr/noble-curves
31
31
  License: MIT
32
32
  Copyright: Copyright (c) 2022 Paul Miller (https://paulmillr.com)
@@ -60,7 +60,7 @@ Used for: WebAuthn / passkey registration + authentication response
60
60
  server.cjs).
61
61
  --------------------------------------------------------------------------------
62
62
  Component: @blamejs/pki
63
- Version: 0.3.32
63
+ Version: 0.3.33
64
64
  Source: https://github.com/blamejs/pki
65
65
  License: Apache-2.0
66
66
  Copyright: Copyright (c) blamejs contributors
@@ -839,21 +839,6 @@ async function _parseMultipart(req, opts, ctParams) {
839
839
  // boundary) but never written to disk.
840
840
  var currentEffectiveLimit = 0; // per-field-or-global cap; recomputed at part start.
841
841
 
842
- function _resetCurrent() {
843
- currentHeaders = null;
844
- currentField = null;
845
- currentFilename = null;
846
- currentMime = null;
847
- currentTmpPath = null;
848
- if (currentFd !== null) { try { nodeFs.closeSync(currentFd); } catch (_e) { /* fd already closed */ } currentFd = null; }
849
- currentSize = 0;
850
- currentHash = null;
851
- currentBuf = null;
852
- currentIsFile = false;
853
- currentDiscarded = false;
854
- currentEffectiveLimit = 0;
855
- }
856
-
857
842
  function _emitRejection(field, filename, mimeType, code, message) {
858
843
  filesRejected.push({
859
844
  field: field,
@@ -18,10 +18,10 @@
18
18
  "hashes": {
19
19
  "server": "sha256:2b30a26f728c5349f4c4b47834f862a4f77393b1224fc12b22abe3ce2cfab78f"
20
20
  },
21
- "refreshedAt": "2026-08-06T00:01:44.784Z"
21
+ "refreshedAt": "2026-08-06T21:56:25.536Z"
22
22
  },
23
23
  "@noble/curves": {
24
- "version": "2.2.0",
24
+ "version": "2.3.0",
25
25
  "license": "MIT",
26
26
  "author": "Paul Miller",
27
27
  "source": "https://github.com/paulmillr/noble-curves",
@@ -35,12 +35,12 @@
35
35
  "server": "lib/vendor/noble-curves.cjs"
36
36
  },
37
37
  "bundler": "esbuild --format=cjs --platform=node",
38
- "bundledAt": "2026-07-13T00:00:00Z",
39
- "cpe": "cpe:2.3:a:paulmillr:noble-curves:2.2.0:*:*:*:*:node.js:*:*",
38
+ "bundledAt": "2026-08-06T00:00:00Z",
39
+ "cpe": "cpe:2.3:a:paulmillr:noble-curves:2.3.0:*:*:*:*:node.js:*:*",
40
40
  "hashes": {
41
- "server": "sha256:2880c288b1285ef51d356d057bee6f0c8a00de36638cf47b47617e8c1faf10d5"
41
+ "server": "sha256:b5fe88d1ea780d0581dee6145d666f89d46fc9531b5db35db2e5b16627840890"
42
42
  },
43
- "refreshedAt": "2026-08-06T00:01:44.784Z"
43
+ "refreshedAt": "2026-08-06T21:56:25.536Z"
44
44
  },
45
45
  "@noble/post-quantum": {
46
46
  "version": "0.6.1",
@@ -71,7 +71,7 @@
71
71
  "hashes": {
72
72
  "server": "sha256:f9c94094b3c10fe73dac5343289da582454ea6053494fab2bf66099d9103d6c3"
73
73
  },
74
- "refreshedAt": "2026-08-06T00:01:44.784Z"
74
+ "refreshedAt": "2026-08-06T21:56:25.536Z"
75
75
  },
76
76
  "@simplewebauthn/server": {
77
77
  "version": "13.3.2",
@@ -94,7 +94,7 @@
94
94
  "hashes": {
95
95
  "server": "sha256:e83195dc9f189385da9c856ef38843f4466f93ea8f3d7fc2efcb1e1b18da6f20"
96
96
  },
97
- "refreshedAt": "2026-08-06T00:01:44.784Z"
97
+ "refreshedAt": "2026-08-06T21:56:25.536Z"
98
98
  },
99
99
  "SecLists-common-passwords-top-10000": {
100
100
  "version": "10k-most-common (master)",
@@ -114,7 +114,7 @@
114
114
  },
115
115
  "runtime_artifact": "lib/vendor/common-passwords-top-10000.data.js",
116
116
  "integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
117
- "refreshedAt": "2026-08-06T00:01:44.784Z"
117
+ "refreshedAt": "2026-08-06T21:56:25.536Z"
118
118
  },
119
119
  "bimi-trust-anchors": {
120
120
  "version": "operator-managed",
@@ -139,7 +139,7 @@
139
139
  },
140
140
  "runtime_artifact": "lib/vendor/bimi-trust-anchors.data.js",
141
141
  "integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
142
- "refreshedAt": "2026-08-06T00:01:44.784Z"
142
+ "refreshedAt": "2026-08-06T21:56:25.536Z"
143
143
  },
144
144
  "publicsuffix-list": {
145
145
  "version": "master",
@@ -159,10 +159,10 @@
159
159
  },
160
160
  "runtime_artifact": "lib/vendor/public-suffix-list.data.js",
161
161
  "integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
162
- "refreshedAt": "2026-08-06T00:01:44.784Z"
162
+ "refreshedAt": "2026-08-06T21:56:25.536Z"
163
163
  },
164
164
  "@blamejs/pki": {
165
- "version": "0.3.32",
165
+ "version": "0.3.33",
166
166
  "license": "Apache-2.0",
167
167
  "author": "blamejs",
168
168
  "source": "https://github.com/blamejs/pki",
@@ -182,12 +182,12 @@
182
182
  "server": "lib/vendor/blamejs-pki.cjs"
183
183
  },
184
184
  "bundler": "esbuild --format=cjs --platform=node --external:crypto --external:node:crypto",
185
- "bundledAt": "2026-08-05T00:00:00Z",
186
- "cpe": "cpe:2.3:a:blamejs:pki:0.3.32:*:*:*:*:node.js:*:*",
185
+ "bundledAt": "2026-08-06T00:00:00Z",
186
+ "cpe": "cpe:2.3:a:blamejs:pki:0.3.33:*:*:*:*:node.js:*:*",
187
187
  "hashes": {
188
- "server": "sha256:275a0ef678a74e9ce035658ae95b18c05dcf69200bf934f609f149d1c8e739e3"
188
+ "server": "sha256:2c297ccbbb48e04306bd6cb681e80f41549fd12267dde9db2a771d55d688745b"
189
189
  },
190
- "refreshedAt": "2026-08-06T00:01:44.784Z"
190
+ "refreshedAt": "2026-08-06T21:56:25.536Z"
191
191
  }
192
192
  }
193
193
  }
@@ -1,4 +1,4 @@
1
- // @blamejs/pki v0.3.32 — vendored (Apache-2.0). Zero-dep pure CJS.
1
+ // @blamejs/pki v0.3.33 — vendored (Apache-2.0). Zero-dep pure CJS.
2
2
  // https://github.com/blamejs/pki Exports: x509, crl, pkcs12, key, webcrypto, schema, csr, cms, ...
3
3
  // Backs lib/mtls-engine-default.js (PQC-capable CA + PKCS#12 engine).
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -137,7 +137,7 @@ var require_package = __commonJS({
137
137
  "node_modules/@blamejs/pki/package.json"(exports2, module2) {
138
138
  module2.exports = {
139
139
  name: "@blamejs/pki",
140
- version: "0.3.32",
140
+ version: "0.3.33",
141
141
  description: "Pure-JavaScript PKI toolkit that owns its stack \u2014 X.509, ASN.1/DER, CMS, PQC-first.",
142
142
  license: "Apache-2.0",
143
143
  author: "blamejs contributors",
@@ -2561,6 +2561,38 @@ var require_oid = __commonJS({
2561
2561
  // negative ints: id-on-hardwareModuleName (RFC 4108), id-on-SmtpUTF8Mailbox (RFC 9598),
2562
2562
  // id-on-MACAddress (I-D.ietf-lamps-macaddress-on) on the id-pkix 8 arc.
2563
2563
  pkixOn: { base: [1, 3, 6, 1, 5, 5, 7, 8], of: { hardwareModuleName: 4, smtpUtf8Mailbox: 9, macAddress: 12 } },
2564
+ // CA/Browser Forum certificate policies (draft-ietf-cose-cbor-encoded-cert sec. 8.9 ints 1-4) on the
2565
+ // 2.23.140.1 arc: the id-cabf validation levels under .2, ev-guidelines under .1.
2566
+ cabfPolicy: { base: [2, 23, 140, 1], of: {
2567
+ "ev-guidelines": 1,
2568
+ "domain-validated": [2, 1],
2569
+ "organization-validated": [2, 2],
2570
+ "individual-validated": [2, 3]
2571
+ } },
2572
+ // id-cp-ipAddr-asNumber (RFC 3779 / RFC 8360) on the id-pkix 14 arc -- the RPKI resource-certificate
2573
+ // policy OIDs (sec. 8.9 ints 7-8).
2574
+ idCp: { base: [1, 3, 6, 1, 5, 5, 7, 14], of: { "id-cp-ipAddr-asNumber": 2, "id-cp-ipAddr-asNumber-v2": 3 } },
2575
+ // GSMA SGP.22 Remote SIM Provisioning roles (sec. 8.9 ints 24-38) on the 2.23.146.1.2.1 id-rspRole arc.
2576
+ // The -v2 roles are shallow leaves .{1..7}; the non-v2 roles descend into deeper sub-arcs (draft-20).
2577
+ gsmaRspRole: { base: [2, 23, 146, 1, 2, 1], of: {
2578
+ "id-rspRole-ci": 0,
2579
+ "id-rspRole-euicc-v2": 1,
2580
+ "id-rspRole-euicc": [0, 0, 0, 0, 0],
2581
+ "id-rspRole-eum-v2": 2,
2582
+ "id-rspRole-eum": [0, 0, 0],
2583
+ "id-rspRole-dp-tls-v2": 3,
2584
+ "id-rspRole-dp-tls": [0, 0, 1, 0],
2585
+ "id-rspRole-dp-auth-v2": 4,
2586
+ "id-rspRole-dp-auth": [0, 0, 1, 1],
2587
+ "id-rspRole-dp-pb-v2": 5,
2588
+ "id-rspRole-dp-pb": [0, 0, 1, 2],
2589
+ "id-rspRole-ds-tls-v2": 6,
2590
+ "id-rspRole-ds-tls": [0, 0, 2, 0],
2591
+ "id-rspRole-ds-auth-v2": 7,
2592
+ "id-rspRole-ds-auth": [0, 0, 2, 1]
2593
+ } },
2594
+ // id-qt policy qualifiers (RFC 5280 sec. 4.2.1.4) on the id-pkix 2 arc -- the C509 sec. 8.10 registry.
2595
+ pkixQt: { base: [1, 3, 6, 1, 5, 5, 7, 2], of: { cps: 1, unotice: 2 } },
2564
2596
  // OCSP (RFC 6960) on the id-pkix-ocsp arc (= id-ad-ocsp). id-pkix-ocsp-basic is
2565
2597
  // the ResponseBytes.responseType this build decodes; id-pkix-ocsp-nonce (sec. 4.4.1)
2566
2598
  // names the nonce extension; the remaining members name the other OCSP extensions
@@ -7503,6 +7535,7 @@ var require_schema_c509 = __commonJS({
7503
7535
  3: _name("subjectAltName"),
7504
7536
  4: _name("basicConstraints"),
7505
7537
  5: _name("cRLDistributionPoints"),
7538
+ 6: _name("certificatePolicies"),
7506
7539
  7: _name("authorityKeyIdentifier"),
7507
7540
  8: _name("extKeyUsage"),
7508
7541
  9: _name("authorityInfoAccess"),
@@ -7529,7 +7562,8 @@ var require_schema_c509 = __commonJS({
7529
7562
  cRLDistributionPoints: 1,
7530
7563
  freshestCRL: 1,
7531
7564
  authorityInfoAccess: 1,
7532
- subjectInfoAccess: 1
7565
+ subjectInfoAccess: 1,
7566
+ certificatePolicies: 1
7533
7567
  };
7534
7568
  var EKU_BY_INT = {
7535
7569
  0: _name("anyExtendedKeyUsage"),
@@ -7575,6 +7609,39 @@ var require_schema_c509 = __commonJS({
7575
7609
  Object.keys(IA_BY_INT).forEach(function(k) {
7576
7610
  IA_TO_INT[oid.byName(IA_BY_INT[k])] = Number(k);
7577
7611
  });
7612
+ var CP_BY_INT = {
7613
+ 0: _name("anyPolicy"),
7614
+ 1: _name("domain-validated"),
7615
+ 2: _name("organization-validated"),
7616
+ 3: _name("individual-validated"),
7617
+ 4: _name("ev-guidelines"),
7618
+ 7: _name("id-cp-ipAddr-asNumber"),
7619
+ 8: _name("id-cp-ipAddr-asNumber-v2"),
7620
+ 24: _name("id-rspRole-ci"),
7621
+ 25: _name("id-rspRole-euicc-v2"),
7622
+ 26: _name("id-rspRole-euicc"),
7623
+ 27: _name("id-rspRole-eum-v2"),
7624
+ 28: _name("id-rspRole-eum"),
7625
+ 29: _name("id-rspRole-dp-tls-v2"),
7626
+ 30: _name("id-rspRole-dp-tls"),
7627
+ 31: _name("id-rspRole-dp-auth-v2"),
7628
+ 32: _name("id-rspRole-dp-auth"),
7629
+ 33: _name("id-rspRole-dp-pb-v2"),
7630
+ 34: _name("id-rspRole-dp-pb"),
7631
+ 35: _name("id-rspRole-ds-tls-v2"),
7632
+ 36: _name("id-rspRole-ds-tls"),
7633
+ 37: _name("id-rspRole-ds-auth-v2"),
7634
+ 38: _name("id-rspRole-ds-auth")
7635
+ };
7636
+ var CP_TO_INT = {};
7637
+ Object.keys(CP_BY_INT).forEach(function(k) {
7638
+ CP_TO_INT[oid.byName(CP_BY_INT[k])] = Number(k);
7639
+ });
7640
+ var PQ_BY_INT = { 1: _name("cps"), 2: _name("unotice") };
7641
+ var PQ_TO_INT = {};
7642
+ Object.keys(PQ_BY_INT).forEach(function(k) {
7643
+ PQ_TO_INT[oid.byName(PQ_BY_INT[k])] = Number(k);
7644
+ });
7578
7645
  function _biguint(node, code, label) {
7579
7646
  if (!node || node.majorType !== 2) throw _err(code, label + " must be an unwrapped CBOR byte string (~biguint)");
7580
7647
  var b2 = node.content;
@@ -7908,6 +7975,61 @@ var require_schema_c509 = __commonJS({
7908
7975
  if (mag.length === 0) return Buffer.from([0]);
7909
7976
  return mag[0] & 128 ? Buffer.concat([Buffer.from([0]), mag]) : mag;
7910
7977
  }
7978
+ function _ia5Universal(text, label) {
7979
+ if (text.length === 0) throw _err("c509/bad-extensions", label + " must be non-empty");
7980
+ for (var i = 0; i < text.length; i++) {
7981
+ if (text.charCodeAt(i) > 127) throw _err("c509/bad-extensions", label + " must be a 7-bit IA5String");
7982
+ }
7983
+ guard.name.assertPrintableIa5(Buffer.from(text, "latin1"), _err, "c509/bad-extensions", label);
7984
+ return b.ia5(text);
7985
+ }
7986
+ function _policyIdToDerOid(node) {
7987
+ if (node.majorType === 0 || node.majorType === 1) {
7988
+ var i = Number(cbor.read.int(node)), nm = CP_BY_INT[i];
7989
+ if (nm === void 0) throw _err("c509/bad-extensions", "a certificatePolicies policy int " + i + " has no C509 sec. 8.9 registry row");
7990
+ return oid.byName(nm);
7991
+ }
7992
+ return _oidName(node, "c509/bad-extensions", "a certificatePolicies policyIdentifier").oid;
7993
+ }
7994
+ function _policyIdFromDer(dotted) {
7995
+ var i = CP_TO_INT[dotted];
7996
+ return i !== void 0 ? cbor.build.int(BigInt(i)) : _oidCbor(dotted);
7997
+ }
7998
+ function _qualifierToDer(qidNode, qtextNode) {
7999
+ if (qidNode.majorType !== 0 && qidNode.majorType !== 1) throw _err("c509/bad-extensions", "a policyQualifierId must be a C509 sec. 8.10 int (a ~oid qualifier is not compact-representable)");
8000
+ var qi = Number(cbor.read.int(qidNode)), nm = PQ_BY_INT[qi];
8001
+ if (nm === void 0) throw _err("c509/bad-extensions", "a policyQualifierId int " + qi + " has no C509 sec. 8.10 registry row");
8002
+ if (qtextNode.majorType !== 3) throw _err("c509/bad-extensions", "a policyQualifier value must be a CBOR text string");
8003
+ var text = cbor.read.textString(qtextNode);
8004
+ if (qi === 1) return b.sequence([b.oid(oid.byName("cps")), _ia5Universal(text, "a CPSuri")]);
8005
+ if (text.length === 0) throw _err("c509/bad-extensions", "a UserNotice explicitText must be non-empty (DisplayText SIZE 1..200)");
8006
+ return b.sequence([b.oid(oid.byName("unotice")), b.sequence([b.utf8(text)])]);
8007
+ }
8008
+ function _qualifierFromDer(pq) {
8009
+ var qid = asn1.read.oid(pq.children[0]), qi = PQ_TO_INT[qid];
8010
+ if (qi === void 0) return null;
8011
+ var q = pq.children[1];
8012
+ if (qi === 1) {
8013
+ if (q.tagClass !== "universal" || q.tagNumber !== asn1.TAGS.IA5_STRING) return null;
8014
+ var uri;
8015
+ try {
8016
+ uri = asn1.read.string(q);
8017
+ } catch (_e) {
8018
+ return null;
8019
+ }
8020
+ return [cbor.build.int(1n), cbor.build.textString(uri)];
8021
+ }
8022
+ if (q.tagClass !== "universal" || q.tagNumber !== asn1.TAGS.SEQUENCE || !q.children || q.children.length !== 1) return null;
8023
+ var dt = q.children[0];
8024
+ if (dt.tagClass !== "universal" || dt.tagNumber !== asn1.TAGS.UTF8_STRING) return null;
8025
+ var txt;
8026
+ try {
8027
+ txt = asn1.read.string(dt);
8028
+ } catch (_e2) {
8029
+ return null;
8030
+ }
8031
+ return [cbor.build.int(2n), cbor.build.textString(txt)];
8032
+ }
7911
8033
  function _dpToDer(dpNode) {
7912
8034
  if (dpNode.majorType !== 4 || !dpNode.children || dpNode.children.length !== 3) throw _err("c509/bad-extensions", "a DistributionPoint must be a CBOR [ fullName, reasons, cRLIssuer ] array");
7913
8035
  var fullName = dpNode.children[0], reasons = dpNode.children[1], crlIssuer = dpNode.children[2];
@@ -8052,6 +8174,28 @@ var require_schema_c509 = __commonJS({
8052
8174
  return b.sequence(node.children.map(function(dp) {
8053
8175
  return _dpToDer(dp);
8054
8176
  }));
8177
+ case "certificatePolicies": {
8178
+ if (node.majorType !== 4 || !node.children) throw _err("c509/bad-extensions", "a certificatePolicies value must be a CBOR array");
8179
+ var cpKids = node.children;
8180
+ if (cpKids.length === 0 || cpKids.length % 2 !== 0) throw _err("c509/bad-extensions", "a certificatePolicies array must be non-empty (policyIdentifier, qualifiers) pairs (sec. 3.3)");
8181
+ var polInfos = [], seenPolicy = {};
8182
+ for (var cpi = 0; cpi + 1 < cpKids.length; cpi += 2) {
8183
+ var quals = cpKids[cpi + 1];
8184
+ if (quals.majorType !== 4 || !quals.children) throw _err("c509/bad-extensions", "a certificatePolicies qualifiers slot must be a CBOR array");
8185
+ var policyOid = _policyIdToDerOid(cpKids[cpi]);
8186
+ if (seenPolicy[policyOid]) throw _err("c509/bad-extensions", "a certificatePolicies policy OID must not appear more than once (RFC 5280 sec. 4.2.1.4)");
8187
+ seenPolicy[policyOid] = true;
8188
+ var polFields = [b.oid(policyOid)];
8189
+ if (quals.children.length) {
8190
+ if (quals.children.length % 2 !== 0) throw _err("c509/bad-extensions", "a policyQualifiers array must be (policyQualifierId, qualifier) pairs");
8191
+ var pqDers = [];
8192
+ for (var qk = 0; qk + 1 < quals.children.length; qk += 2) pqDers.push(_qualifierToDer(quals.children[qk], quals.children[qk + 1]));
8193
+ polFields.push(b.sequence(pqDers));
8194
+ }
8195
+ polInfos.push(b.sequence(polFields));
8196
+ }
8197
+ return b.sequence(polInfos);
8198
+ }
8055
8199
  default:
8056
8200
  throw _err("c509/bad-extensions", "extension " + name + " has no compact value decoder");
8057
8201
  }
@@ -8167,6 +8311,30 @@ var require_schema_c509 = __commonJS({
8167
8311
  return r.triple;
8168
8312
  }));
8169
8313
  }
8314
+ case "certificatePolicies": {
8315
+ if (node.tagClass !== "universal" || node.tagNumber !== asn1.TAGS.SEQUENCE || !node.children || node.children.length < 1) return null;
8316
+ var cpOut = [];
8317
+ for (var pli = 0; pli < node.children.length; pli++) {
8318
+ var pol = node.children[pli];
8319
+ if (pol.tagClass !== "universal" || pol.tagNumber !== asn1.TAGS.SEQUENCE || !pol.children || pol.children.length < 1 || pol.children.length > 2) return null;
8320
+ cpOut.push(_policyIdFromDer(asn1.read.oid(pol.children[0])));
8321
+ var qArr = [];
8322
+ if (pol.children.length === 2) {
8323
+ var qseq = pol.children[1];
8324
+ if (qseq.tagClass !== "universal" || qseq.tagNumber !== asn1.TAGS.SEQUENCE || !qseq.children || !qseq.children.length) return null;
8325
+ for (var qj = 0; qj < qseq.children.length; qj++) {
8326
+ var pq = qseq.children[qj];
8327
+ if (pq.tagClass !== "universal" || pq.tagNumber !== asn1.TAGS.SEQUENCE || !pq.children || pq.children.length !== 2) return null;
8328
+ var qpair = _qualifierFromDer(pq);
8329
+ if (qpair == null) return null;
8330
+ qArr.push(qpair[0]);
8331
+ qArr.push(qpair[1]);
8332
+ }
8333
+ }
8334
+ cpOut.push(cbor.build.array(qArr));
8335
+ }
8336
+ return cbor.build.array(cpOut);
8337
+ }
8170
8338
  default:
8171
8339
  return null;
8172
8340
  }