@blamejs/pki 0.8.49 → 0.8.51

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.
@@ -31,6 +31,14 @@ var OID_AD_OCSP = oid.byName("ocsp");
31
31
  var OID_OCSP_NONCE = oid.byName("ocspNonce");
32
32
 
33
33
  var _append = guard.list.append;
34
+ /** @internal The URL parser, taken at load: the host it reports is the distribution point a fetch is
35
+ * directed to. Its accessors are taken with it, since the constructor's capture does not cover the
36
+ * prototype the value reads its parts off and the host read here is the one the filter applies to. */
37
+ var _URL = intrinsic.URL;
38
+ var _urlHref = intrinsic.urlHref;
39
+ var _urlProtocol = intrinsic.urlProtocol;
40
+ var _urlHostname = intrinsic.urlHostname;
41
+ var _urlSetHash = intrinsic.urlSetHash;
34
42
  var LIMITS = constants.LIMITS;
35
43
 
36
44
  var CHECKER_OPTS = intrinsic.assign(intrinsic.create(null), {
@@ -162,18 +170,19 @@ function _ocspCandidates(descriptions, net_, skips) {
162
170
  function _acceptableUrl(value, net_) {
163
171
  if (typeof value !== "string" || value === "") return _no("is not one this client fetches");
164
172
  var u;
165
- try { u = new URL(value); }
173
+ try { u = new _URL(value); }
166
174
  catch (_e) { return _no("is not one this client fetches"); } // allow:swallow-unverified a value that is not a URL names no location
167
- if (u.protocol !== "https:" && !(net_.allowHttp && u.protocol === "http:")) return _no("is not one this client fetches");
168
- var host = u.hostname;
175
+ var scheme = _urlProtocol(u);
176
+ if (scheme !== "https:" && !(net_.allowHttp && scheme === "http:")) return _no("is not one this client fetches");
177
+ var host = _urlHostname(u);
169
178
  if (host.charAt(0) === "[" && host.charAt(host.length - 1) === "]") host = host.slice(1, -1);
170
179
  if (net.isIP(host) !== 0) {
171
180
  if (httpTransport.isBlockedIp(host)) return _no("is not one this client fetches");
172
181
  } else if (!net_.guardsAddresses) {
173
182
  return _no("names a host whose resolved address opts.transport does not declare it filters");
174
183
  }
175
- u.hash = "";
176
- return { url: u.href, why: null };
184
+ _urlSetHash(u, "");
185
+ return { url: _urlHref(u), why: null };
177
186
  }
178
187
 
179
188
  function _no(why) { return { url: null, why: why }; }
package/lib/scep.js CHANGED
@@ -37,6 +37,21 @@
37
37
 
38
38
  var frameworkError = require("./framework-error");
39
39
  var ScepError = frameworkError.ScepError;
40
+ var intrinsic = require("./guard-intrinsic");
41
+ /** @internal The URL parser, taken at load: what it returns names the host an operation is sent to,
42
+ * and a redirect is resolved against the current operation URL with it. The conversion the parser is
43
+ * handed is taken with it, since a replacement answers with a host of its own. */
44
+ var _URL = intrinsic.URL;
45
+ var _stringOf = intrinsic.String;
46
+ /** @internal And the accessors its result is read through: capturing the constructor does not capture
47
+ * the prototype the value reads its parts off, and the scheme here decides whether a redirect may
48
+ * be followed while the origin decides whether it is a cross-origin one. */
49
+ var _urlHref = intrinsic.urlHref;
50
+ var _urlOrigin = intrinsic.urlOrigin;
51
+ var _urlProtocol = intrinsic.urlProtocol;
52
+ var _urlSearchParams = intrinsic.urlSearchParams;
53
+ var _urlHash = intrinsic.urlHash;
54
+ var _searchSet = intrinsic.uncurry(URLSearchParams.prototype.set);
40
55
  var oid = require("./oid");
41
56
  var asn1 = require("./asn1-der");
42
57
  var b = asn1.build;
@@ -661,28 +676,34 @@ function _responseLen(body) {
661
676
  if (Buffer.isBuffer(body)) return guard.bytes.lengthOf(body);
662
677
  return guard.bytes.lengthOf(guard.bytes.source(body, ScepError, "scep/bad-response", "the response body"));
663
678
  }
679
+ /** @internal The settings this client forwards to its transport. */
680
+ var TLS_SETTINGS = ["anchors", "useSystemStore", "cert", "key", "minVersion", "servername", "checkServerIdentity"];
664
681
  function _tlsForRequest(opts) {
665
- var t = opts.tls || {};
666
- return { anchors: t.anchors, useSystemStore: t.useSystemStore, cert: t.cert, key: t.key, minVersion: t.minVersion, servername: t.servername, checkServerIdentity: t.checkServerIdentity };
682
+ return guard.identifier.pickSettings(opts.tls, TLS_SETTINGS);
667
683
  }
668
684
  function _redirectTarget(current, location, method, allowCrossOrigin) {
669
685
  if (location == null || _trim(String(location)) === "") throw _err("scep/bad-response", "a redirect response carried no Location header");
670
686
  var resolved;
671
- try { resolved = new URL(String(location), current.href); }
687
+ try { resolved = new _URL(_stringOf(location), _urlHref(current)); }
672
688
  catch (e) { throw _err("scep/bad-url", "a redirect Location did not parse: " + location, e); }
673
- if (resolved.protocol !== "http:" && resolved.protocol !== "https:") throw _err("scep/bad-url", "a redirect to a non-HTTP scheme is refused: " + resolved.protocol);
674
- if (current.protocol === "https:" && resolved.protocol !== "https:") throw _err("scep/insecure-redirect", "a redirect from https to " + resolved.protocol + " is refused");
689
+ /** @internal Each scheme read ONCE: the first test decides whether the target is HTTP at all and
690
+ * the second whether it is a downgrade, and those two have to be answered about one value. */
691
+ var toScheme = _urlProtocol(resolved);
692
+ var fromScheme = _urlProtocol(current);
693
+ if (toScheme !== "http:" && toScheme !== "https:") throw _err("scep/bad-url", "a redirect to a non-HTTP scheme is refused: " + toScheme);
694
+ if (fromScheme === "https:" && toScheme !== "https:") throw _err("scep/insecure-redirect", "a redirect from https to " + toScheme + " is refused");
675
695
  var safe = method === "GET" || method === "HEAD";
676
- if (resolved.origin !== current.origin && !safe && !allowCrossOrigin) throw _err("scep/cross-origin-redirect", "a cross-origin redirect on a " + method + " needs opts.allowCrossOriginRedirect");
696
+ if (_urlOrigin(resolved) !== _urlOrigin(current) && !safe && !allowCrossOrigin) throw _err("scep/cross-origin-redirect", "a cross-origin redirect on a " + method + " needs opts.allowCrossOriginRedirect");
677
697
  return resolved;
678
698
  }
679
699
  function _scepUrl(baseUrl, operation) {
680
700
  var url;
681
- try { url = new URL(String(baseUrl)); }
701
+ try { url = new _URL(_stringOf(baseUrl)); }
682
702
  catch (e) { throw _err("scep/bad-url", "the SCEP base URL did not parse: " + _showValue(baseUrl), e); }
683
- if (url.protocol !== "http:" && url.protocol !== "https:") throw _err("scep/bad-url", "the SCEP base URL must be http or https, got " + url.protocol);
684
- if (url.hash) throw _err("scep/bad-url", "the SCEP base URL must not carry a fragment");
685
- url.searchParams.set("operation", operation);
703
+ var baseScheme = _urlProtocol(url);
704
+ if (baseScheme !== "http:" && baseScheme !== "https:") throw _err("scep/bad-url", "the SCEP base URL must be http or https, got " + baseScheme);
705
+ if (_urlHash(url)) throw _err("scep/bad-url", "the SCEP base URL must not carry a fragment");
706
+ _searchSet(_urlSearchParams(url), "operation", operation);
686
707
  return url;
687
708
  }
688
709
  function _pkiMethod(opts) {
@@ -693,27 +714,27 @@ function _pkiMethod(opts) {
693
714
  }
694
715
  function _pkiRequest(method, opUrl, reqMsg, clientOpts) {
695
716
  if (method === "GET") {
696
- var getUrl = new URL(opUrl.href);
697
- getUrl.searchParams.set("message", Buffer.from(reqMsg).toString("base64"));
717
+ var getUrl = new _URL(_urlHref(opUrl));
718
+ _searchSet(_urlSearchParams(getUrl), "message", Buffer.from(reqMsg).toString("base64"));
698
719
  return _client("GET", getUrl, null, {}, clientOpts);
699
720
  }
700
721
  return _client("POST", opUrl, reqMsg, { "content-type": CT_PKI_MESSAGE }, clientOpts);
701
722
  }
702
723
  function _drive(method, url, body, headers, opts, transport, budgets) {
703
- var redirects = 0, curMethod = method, curBody = body, initialOrigin = url.origin;
724
+ var redirects = 0, curMethod = method, curBody = body, initialOrigin = _urlOrigin(url);
704
725
  var proxySnap = _snapshotProxy(opts.proxy);
705
726
  var tlsBase = _tlsForRequest(opts);
706
727
  var allowCrossOrigin = !!opts.allowCrossOriginRedirect;
707
728
  function _hopTls(current) {
708
729
  var t = tlsBase;
709
- if (current.origin !== initialOrigin) {
730
+ if (_urlOrigin(current) !== initialOrigin) {
710
731
  t = Object.assign({}, t);
711
732
  t.cert = null; t.key = null; t.servername = null;
712
733
  }
713
734
  return t;
714
735
  }
715
736
  function step(current) {
716
- var req = { method: curMethod, url: current.href, headers: headers, tls: _hopTls(current), timeout: budgets.timeout, maxResponseBytes: budgets.maxResponseBytes };
737
+ var req = { method: curMethod, url: _urlHref(current), headers: headers, tls: _hopTls(current), timeout: budgets.timeout, maxResponseBytes: budgets.maxResponseBytes };
717
738
  if (proxySnap != null) req.proxy = proxySnap;
718
739
  if (curBody != null) req.body = curBody;
719
740
  return guard.async.invoked(transport, [req], _err, "scep/bad-input", "opts.transport").then(function (res) {
package/lib/x509-sign.js CHANGED
@@ -71,6 +71,7 @@ var KNOWN_EXT_KEYS = intrinsic.assign(intrinsic.create(null), {
71
71
  authorityInfoAccess: 1, cRLDistributionPoints: 1, freshestCRL: 1,
72
72
  policyConstraints: 1, inhibitAnyPolicy: 1, policyMappings: 1, policyMappingsCritical: 1,
73
73
  issuerAltName: 1, qcStatements: 1, qcStatementsCritical: 1,
74
+ tlsFeature: 1, tlsFeatureCritical: 1,
74
75
  precertificatePoison: 1, signedCertificateTimestampList: 1,
75
76
  msCertificateTemplate: 1, msEnrollCertType: 1, msCaVersion: 1, msPreviousCertHash: 1,
76
77
  msApplicationPolicies: 1,
@@ -279,6 +280,9 @@ function _buildExtensions(extSpec, ctx) {
279
280
  * unrecognized critical extension; the default is the form that validates and the knob reaches
280
281
  * the other, as policyMappings and certificatePolicies do. */
281
282
  if (extSpec.qcStatements != null) guard.list.append(out, _ext(O("qcStatements"), !!extSpec.qcStatementsCritical, _extQcStatements(extSpec.qcStatements)));
283
+ /** @internal RFC 7633 sec. 4: SHOULD NOT be critical, and the clause names the case where critical
284
+ * is the desired behavior, so the knob exists and the default is the compatible form. */
285
+ if (extSpec.tlsFeature != null) guard.list.append(out, _ext(O("tlsFeature"), !!extSpec.tlsFeatureCritical, _b.extTlsFeature(extSpec.tlsFeature)));
282
286
  if (extSpec.nameConstraints != null) {
283
287
  if (!caTrue) throw _err("x509/bad-input", "nameConstraints appears only in a CA certificate, so it requires basicConstraints cA=TRUE (RFC 5280 sec. 4.2.1.10)");
284
288
  guard.list.append(out, _ext(O("nameConstraints"), true, _extNameConstraints(extSpec.nameConstraints)));
@@ -628,6 +632,18 @@ function _hasCriticalSan(extSpec) {
628
632
  * know an unknown syntax. `pki.path.validate` does not process this extension, so it is emitted
629
633
  * non-critical unless `qcStatementsCritical` is set.
630
634
  *
635
+ * `extensions.tlsFeature` builds the RFC 7633 TLS Feature extension, the "must-staple" declaration,
636
+ * from a list of TLS ExtensionType values. Each is a number, a bigint, or one of the registry names
637
+ * this build knows, `status_request` and `status_request_v2`. The values come from the IANA TLS
638
+ * Extensions registry that RFC 7633 sec. 4.1 points at, and the type states no range, so a value
639
+ * outside that registry's width encodes and `pki.lint.certificate(der, { profile: "rfc7633" })`
640
+ * reports it rather than the encoder refusing it; the same holds for a repeated value, which the
641
+ * syntax permits. An EMPTY list is refused, because the extension it would emit advertises nothing.
642
+ * Emitted non-critical unless `tlsFeatureCritical` is set: sec. 4 says it SHOULD NOT be critical and
643
+ * names the case where critical is the caller's intent. `pki.path.validate` enforces sec. 4.2.2 over
644
+ * a built path, so a certificate issued under a CA that advertises features must offer the same set
645
+ * or a superset.
646
+ *
631
647
  * `extensions.subjectInfoAccess` takes the same `{ accessMethod, accessLocation }` list
632
648
  * `authorityInfoAccess` takes, since RFC 5280 sec. 4.2.2.2 gives it sec. 4.2.2.1's syntax; the two
633
649
  * access methods that section defines are the registered names `id-ad-caRepository` and
@@ -910,6 +926,10 @@ var EXTENSION_ROWS = [
910
926
  ["cRLDistributionPoints", "cRLDistributionPoints", { fixed: false, cite: "RFC 5280 sec. 4.2.1.13" }, function (v) { return _extCrlDp(v, "cRLDistributionPoints"); }],
911
927
  ["freshestCRL", "freshestCRL", { fixed: false, cite: "RFC 5280 sec. 4.2.1.15" }, function (v) { return _extCrlDp(v, "freshestCRL"); }],
912
928
  ["qcStatements", "qcStatements", { knob: true, dflt: false }, function (v) { return _extQcStatements(v); }],
929
+ /** @internal RFC 7633 sec. 4 reads "SHOULD NOT be marked critical" and then names the case where
930
+ * the critical form is what the caller wants, so this takes a knob defaulting to the compatible
931
+ * form rather than refusing one: `fixed: false` would promote that SHOULD NOT into a refusal. */
932
+ ["tlsFeature", "tlsFeature", { knob: true, dflt: false }, function (v) { return _b.extTlsFeature(v); }],
913
933
  ["subjectDirectoryAttributes", "subjectDirectoryAttributes", { fixed: false, cite: "RFC 5280 sec. 4.2.1.8" }, function (v) { return _b.extSubjectDirectoryAttributes(v); }],
914
934
  ["msCertificateTemplate", "msCertificateTemplate", { fixed: false, cite: "the toolkit issues the form its readers accept" }, function (v) { return _b.extMsCertificateTemplate(v); }],
915
935
  ["msEnrollCertType", "msEnrollCertType", { fixed: false, cite: "the toolkit issues the form its readers accept" }, function (v) { return _b.extMsEnrollCertType(v); }],
@@ -971,8 +991,8 @@ function _extensionCritical(row, opts) {
971
991
  * (the key identifiers, the constraint and policy-constraint extensions, the access and
972
992
  * distribution extensions), or the toolkit issues one form only, `opts.critical` may only restate
973
993
  * that value. `keyUsage` (default critical), `extendedKeyUsage`, `certificatePolicies`,
974
- * `policyMappings`, `qcStatements` (default non-critical), and `subjectAltName` (critical exactly
975
- * when the subject is empty, which the caller knows) take `opts.critical`; `basicConstraints`
994
+ * `policyMappings`, `qcStatements` and `tlsFeature` (default non-critical), and `subjectAltName`
995
+ * (critical exactly when the subject is empty, which the caller knows) take `opts.critical`; `basicConstraints`
976
996
  * carries `critical` inside its value. An unregistered OID takes `opts.critical` as stated.
977
997
  *
978
998
  * @opts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.8.49",
3
+ "version": "0.8.51",
4
4
  "description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
package/sbom.cdx.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
3
3
  "bomFormat": "CycloneDX",
4
4
  "specVersion": "1.5",
5
- "serialNumber": "urn:uuid:850d86e8-9f55-4854-9b3a-89ed4e16dfd5",
5
+ "serialNumber": "urn:uuid:3a1ddfe7-c71b-4cb4-8bd0-256c3b6075ba",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-10-05T08:10:11.062Z",
8
+ "timestamp": "2026-10-05T13:17:14.754Z",
9
9
  "lifecycles": [
10
10
  {
11
11
  "phase": "build"
@@ -19,14 +19,14 @@
19
19
  }
20
20
  ],
21
21
  "component": {
22
- "bom-ref": "@blamejs/pki@0.8.49",
22
+ "bom-ref": "@blamejs/pki@0.8.51",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.8.49",
25
+ "version": "0.8.51",
26
26
  "scope": "required",
27
27
  "author": "blamejs contributors",
28
28
  "description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
29
- "purl": "pkg:npm/%40blamejs/pki@0.8.49",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.8.51",
30
30
  "properties": [],
31
31
  "externalReferences": [
32
32
  {
@@ -54,7 +54,7 @@
54
54
  "components": [],
55
55
  "dependencies": [
56
56
  {
57
- "ref": "@blamejs/pki@0.8.49",
57
+ "ref": "@blamejs/pki@0.8.51",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]