@blamejs/pki 0.5.21 → 0.5.22
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 +15 -1
- package/lib/asn1-der.js +6 -2
- package/lib/path-validate.js +8 -8
- package/lib/pki-build.js +10 -6
- package/lib/x509-sign.js +18 -7
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,21 @@ All notable changes to `@blamejs/pki` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
## v0.5.
|
|
7
|
+
## v0.5.22 — 2026-08-21
|
|
8
|
+
|
|
9
|
+
The list tests that decide whether a caller's argument is refused, and which form an extension spec is in, now answer from operations taken at load rather than read when they run.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- The DER codec, the PKIX builders and the certificate signer are held to the live-read rule, with the reads each still carries declared as a count that can only fall. Certification-path validation's declared count falls in this release as its list doors convert.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- The DER builder refuses a non-list of children under a captured test. Every structure this toolkit encodes passes through that door, and it was the busiest live read in the library by two orders of magnitude.
|
|
18
|
+
- `pki.x509.sign` picks which form the caller's extension spec is in with a captured test. That choice decides which fields are read and therefore what is signed into the certificate: answering wrongly sends an object down the list arm, and the extensions it carries are never seen. The same test decides whether the spec asserts keyCertSign, which is what makes the issued certificate a CA.
|
|
19
|
+
- The name, key-usage and GeneralNames builders refuse a non-list argument under a captured test, as do certification-path validation's own list doors.
|
|
20
|
+
|
|
21
|
+
## v0.5.21 — 2026-08-20
|
|
8
22
|
|
|
9
23
|
The check that holds the crypto engine to load-time captures could be satisfied around, so nine modules had taken the safe primitive at one site and were never held to it anywhere else.
|
|
10
24
|
|
package/lib/asn1-der.js
CHANGED
|
@@ -34,6 +34,10 @@
|
|
|
34
34
|
var constants = require("./constants");
|
|
35
35
|
var frameworkError = require("./framework-error");
|
|
36
36
|
var guard = require("./guard-all");
|
|
37
|
+
var intrinsic = require("./guard-intrinsic");
|
|
38
|
+
// The list test at each builder door decides whether a caller's argument is refused. Read at the
|
|
39
|
+
// moment it runs, a replacement answering true admits a non-list and the door it guards is gone.
|
|
40
|
+
var _isArray = intrinsic.isArray;
|
|
37
41
|
|
|
38
42
|
var Asn1Error = frameworkError.Asn1Error;
|
|
39
43
|
var OidError = frameworkError.OidError;
|
|
@@ -966,7 +970,7 @@ var build = {
|
|
|
966
970
|
// encoder composes, so the minimal-encoding rule lives once (the CHECK side is
|
|
967
971
|
// schema.assertMinimalNamedBits). No bits asserted -> the empty BIT STRING.
|
|
968
972
|
namedBitString: function (positions) {
|
|
969
|
-
if (!
|
|
973
|
+
if (!_isArray(positions)) throw new Asn1Error("asn1/bad-bit-string", "namedBitString requires an array of bit positions");
|
|
970
974
|
var hi = -1, i, p;
|
|
971
975
|
for (i = 0; i < positions.length; i++) {
|
|
972
976
|
p = positions[i];
|
|
@@ -1059,7 +1063,7 @@ function _intContent(v, who) {
|
|
|
1059
1063
|
}
|
|
1060
1064
|
|
|
1061
1065
|
function _asBufferArray(arr, who) {
|
|
1062
|
-
if (!
|
|
1066
|
+
if (!_isArray(arr)) throw new Asn1Error("asn1/bad-children", who + ": expected an array of TLV buffers");
|
|
1063
1067
|
return arr.map(function (b) { return _asBuffer(b, who); });
|
|
1064
1068
|
}
|
|
1065
1069
|
|
package/lib/path-validate.js
CHANGED
|
@@ -771,7 +771,7 @@ function isSubtreeBaseValid(tag, base) {
|
|
|
771
771
|
case 1: case 2: case 6: return typeof base === "string"; // rfc822Name / dNSName / URI
|
|
772
772
|
case 7: return (Buffer.isBuffer(base) || base instanceof Uint8Array) &&
|
|
773
773
|
(base.length === 8 || base.length === 32); // iPAddress: address + mask
|
|
774
|
-
case 4: return base !== null && typeof base === "object" && !!base.rdns &&
|
|
774
|
+
case 4: return base !== null && typeof base === "object" && !!base.rdns && intrinsic.isArray(base.rdns); // directoryName
|
|
775
775
|
default: return base !== undefined;
|
|
776
776
|
}
|
|
777
777
|
}
|
|
@@ -782,7 +782,7 @@ function isSubtreeBaseValid(tag, base) {
|
|
|
782
782
|
// disabling the constraint the caller configured -- so it throws instead.
|
|
783
783
|
function checkedSubtreeSeeds(list, optName) {
|
|
784
784
|
if (list === undefined || list === null) return [];
|
|
785
|
-
if (!
|
|
785
|
+
if (!intrinsic.isArray(list)) throw E("path/bad-input", "validate: opts." + optName + " must be an array of { tag, base } subtree entries");
|
|
786
786
|
return list.map(function (st) {
|
|
787
787
|
if (!st || typeof st !== "object" || !Number.isInteger(st.tag) || st.tag < 0 || st.tag > 8 || !isSubtreeBaseValid(st.tag, st.base)) {
|
|
788
788
|
throw E("path/bad-input", "validate: opts." + optName + " entries must be { tag: <GeneralName tag number 0..8>, base: <that form's constraint value> }");
|
|
@@ -1217,7 +1217,7 @@ async function validate(path, opts) {
|
|
|
1217
1217
|
guard.identifier.assertKnownKeys(opts, _VALIDATE_OPTS, E, "path/bad-input",
|
|
1218
1218
|
"pki.path.validate has an unknown option. The anchor here is `trustAnchor`, singular; " +
|
|
1219
1219
|
"pki.path.build takes `trustAnchors`. The unknown option was: ");
|
|
1220
|
-
if (!
|
|
1220
|
+
if (!intrinsic.isArray(path)) throw E("path/bad-input", "validate: path must be an array of certificates");
|
|
1221
1221
|
// Bound the per-cert asymmetric-verify work BEFORE parsing an untrusted bundle
|
|
1222
1222
|
// (the policy-tree cap guards CVE-2023-0464-style blow-up; this guards linear
|
|
1223
1223
|
// crypto amplification from an oversized path). Entry-point tier: throw.
|
|
@@ -1242,7 +1242,7 @@ async function validate(path, opts) {
|
|
|
1242
1242
|
// Membership is tested with indexOf, so a raw string here would be consulted
|
|
1243
1243
|
// as a SUBSTRING match -- validated to an array of strings instead.
|
|
1244
1244
|
if (opts.userInitialPolicySet !== undefined) {
|
|
1245
|
-
var uipsOk =
|
|
1245
|
+
var uipsOk = intrinsic.isArray(opts.userInitialPolicySet) && opts.userInitialPolicySet.length > 0 &&
|
|
1246
1246
|
guard.list.allMatch(opts.userInitialPolicySet, function (p) { return typeof p === "string" && p.length > 0; });
|
|
1247
1247
|
if (!uipsOk) throw E("path/bad-input", "validate: opts.userInitialPolicySet must be a non-empty array of policy OID strings");
|
|
1248
1248
|
// Each entry is compared (indexOf) against canonical decoder output, so a
|
|
@@ -2643,7 +2643,7 @@ function assertAnchorConstraints(ta, checkPurpose) {
|
|
|
2643
2643
|
function resolvePurposeOpts(opts) {
|
|
2644
2644
|
var requiredEku = null;
|
|
2645
2645
|
if (opts.requiredEku !== undefined) {
|
|
2646
|
-
if (!
|
|
2646
|
+
if (!intrinsic.isArray(opts.requiredEku) || opts.requiredEku.length === 0) {
|
|
2647
2647
|
throw E("path/bad-input", "validate: opts.requiredEku must be a non-empty array of key-purpose OID names or dotted OID strings");
|
|
2648
2648
|
}
|
|
2649
2649
|
requiredEku = opts.requiredEku.map(function (p) {
|
|
@@ -2679,7 +2679,7 @@ function toAnchor(entry) {
|
|
|
2679
2679
|
// (name matching), publicKey bytes and the algorithm OID (the sec. 6.1.4 key
|
|
2680
2680
|
// hand-off). trustAnchors is a caller option, so a malformed tuple is a config
|
|
2681
2681
|
// error and fails closed at entry, not a downstream no-path / soft verdict.
|
|
2682
|
-
if (!
|
|
2682
|
+
if (!intrinsic.isArray(entry.name.rdns) || !Buffer.isBuffer(entry.publicKey) || typeof entry.algorithm !== "string") {
|
|
2683
2683
|
throw E("path/bad-input", "build: a trustAnchor tuple must be { name: { rdns: [...] }, publicKey: Buffer, algorithm: OID string }");
|
|
2684
2684
|
}
|
|
2685
2685
|
return entry;
|
|
@@ -2972,7 +2972,7 @@ async function build(leaf, opts) {
|
|
|
2972
2972
|
|
|
2973
2973
|
var poolInput = opts.candidates !== undefined ? opts.candidates : opts.intermediates;
|
|
2974
2974
|
if (poolInput === undefined) poolInput = [];
|
|
2975
|
-
if (!
|
|
2975
|
+
if (!intrinsic.isArray(poolInput)) throw E("path/bad-input", "build: opts.candidates must be an array of certificates");
|
|
2976
2976
|
// A pool larger than the absolute ceiling is rejected at entry (bounds the
|
|
2977
2977
|
// parse work on an untrusted bundle) -- distinct from the per-search tick
|
|
2978
2978
|
// budget below, which an operator may set lower to cap the DFS.
|
|
@@ -2987,7 +2987,7 @@ async function build(leaf, opts) {
|
|
|
2987
2987
|
var poolDerKeys = new Set();
|
|
2988
2988
|
for (var pdk = 0; pdk < pool.length; pdk++) intrinsic.setAdd(poolDerKeys, certDerKey(pool[pdk]));
|
|
2989
2989
|
|
|
2990
|
-
if (!
|
|
2990
|
+
if (!intrinsic.isArray(opts.trustAnchors) || opts.trustAnchors.length === 0) throw E("path/bad-input", "build: opts.trustAnchors must be a non-empty array of anchor tuples or root certificates");
|
|
2991
2991
|
var anchors = opts.trustAnchors.map(toAnchor);
|
|
2992
2992
|
|
|
2993
2993
|
guard.time.assertValid(opts.time, E, "path/bad-input", "build: opts.time (the validity-window check date, forwarded to validate)");
|
package/lib/pki-build.js
CHANGED
|
@@ -14,6 +14,10 @@ var asn1 = require("./asn1-der");
|
|
|
14
14
|
var schema = require("./schema-engine");
|
|
15
15
|
var compositeSig = require("./composite-sig");
|
|
16
16
|
var guard = require("./guard-all");
|
|
17
|
+
var intrinsic = require("./guard-intrinsic");
|
|
18
|
+
// Each of these doors refuses a caller argument that is not a list. Read live, a replacement
|
|
19
|
+
// answering true lets a non-list through the check written to stop it.
|
|
20
|
+
var _isArray = intrinsic.isArray;
|
|
17
21
|
var oid = require("./oid");
|
|
18
22
|
var ipUtils = require("./ip-utils");
|
|
19
23
|
var nodeCrypto = require("crypto");
|
|
@@ -83,7 +87,7 @@ function makeBuilder(ctx) {
|
|
|
83
87
|
function encodeName(spec) {
|
|
84
88
|
if (Buffer.isBuffer(spec)) { assertValidNameDer(spec); return spec; }
|
|
85
89
|
if (typeof spec === "string") spec = [{ commonName: spec }];
|
|
86
|
-
if (!
|
|
90
|
+
if (!_isArray(spec)) throw E("bad-name", "a name must be a string, an array of RDNs, or raw Name DER");
|
|
87
91
|
return b.sequence(spec.map(encodeRdn));
|
|
88
92
|
}
|
|
89
93
|
// Full validation of a raw Name DER: walk it through the exact RDNSequence parser the decoder uses.
|
|
@@ -161,7 +165,7 @@ function makeBuilder(ctx) {
|
|
|
161
165
|
|
|
162
166
|
// ---- extension-value encoders (the inverse of certExtensionDecoders) ----
|
|
163
167
|
function extKeyUsage(names) {
|
|
164
|
-
if (!
|
|
168
|
+
if (!_isArray(names) || !names.length) throw E("bad-input", "keyUsage must assert at least one bit (RFC 5280 sec. 4.2.1.3)");
|
|
165
169
|
var positions = names.map(function (n) {
|
|
166
170
|
var pos = KU_BIT[n];
|
|
167
171
|
if (pos == null) throw E("bad-input", "unknown keyUsage bit " + JSON.stringify(n));
|
|
@@ -187,7 +191,7 @@ function makeBuilder(ctx) {
|
|
|
187
191
|
throw E("bad-input", "unknown " + label + " " + JSON.stringify(n) + " (expected a registered name or a dotted-decimal OID)");
|
|
188
192
|
}
|
|
189
193
|
function extExtKeyUsage(names) {
|
|
190
|
-
if (!
|
|
194
|
+
if (!_isArray(names) || !names.length) throw E("bad-input", "extendedKeyUsage must list at least one KeyPurposeId");
|
|
191
195
|
return b.sequence(names.map(function (n) { return b.oid(_resolveOid(n, "extendedKeyUsage purpose")); }));
|
|
192
196
|
}
|
|
193
197
|
function validateBcSpec(bc) {
|
|
@@ -214,14 +218,14 @@ function makeBuilder(ctx) {
|
|
|
214
218
|
// RoleSyntax.roleAuthority [0], IetfAttrSyntax.policyAuthority [0]); the [n] node's children ARE the
|
|
215
219
|
// GeneralName members, no inner SEQUENCE wrapper.
|
|
216
220
|
function encodeGeneralNames(entries, implicitTag) {
|
|
217
|
-
if (!
|
|
221
|
+
if (!_isArray(entries) || !entries.length) throw E("bad-input", "a GeneralNames must carry at least one GeneralName");
|
|
218
222
|
var members = entries.map(encodeGeneralName);
|
|
219
223
|
if (implicitTag == null) return b.sequence(members);
|
|
220
224
|
return b.contextConstructed(implicitTag, Buffer.concat(members));
|
|
221
225
|
}
|
|
222
226
|
function extSan(entries) { return encodeGeneralNames(entries); }
|
|
223
227
|
function extCertPolicies(names) {
|
|
224
|
-
if (!
|
|
228
|
+
if (!_isArray(names) || !names.length) throw E("bad-input", "certificatePolicies must list at least one policy OID");
|
|
225
229
|
var seen = {};
|
|
226
230
|
return b.sequence(names.map(function (n) {
|
|
227
231
|
var pOid = _resolveOid(n, "certificate policy");
|
|
@@ -301,7 +305,7 @@ function makeBuilder(ctx) {
|
|
|
301
305
|
};
|
|
302
306
|
function requestedExtensions(extSpec, spki) {
|
|
303
307
|
var EXT_DECODERS = ctx.EXT_DECODERS;
|
|
304
|
-
if (
|
|
308
|
+
if (_isArray(extSpec)) {
|
|
305
309
|
if (!extSpec.length) throw E("bad-input", "the requested extensions list must carry at least one extension");
|
|
306
310
|
var seen = {};
|
|
307
311
|
return b.sequence(extSpec.map(function (e, i) {
|
package/lib/x509-sign.js
CHANGED
|
@@ -27,6 +27,11 @@ var oid = require("./oid");
|
|
|
27
27
|
var x509 = require("./schema-x509");
|
|
28
28
|
var signScheme = require("./sign-scheme");
|
|
29
29
|
var guard = require("./guard-all");
|
|
30
|
+
var intrinsic = require("./guard-intrinsic");
|
|
31
|
+
// This test picks which FORM the caller's extension spec is in, so it decides which fields are read
|
|
32
|
+
// and therefore what is signed into the certificate. Read live, a replacement answering true sends
|
|
33
|
+
// an object down the list arm and the extensions it carries are never seen.
|
|
34
|
+
var _isArray = intrinsic.isArray;
|
|
30
35
|
var frameworkError = require("./framework-error");
|
|
31
36
|
var schema = require("./schema-engine");
|
|
32
37
|
var pkix = require("./schema-pkix");
|
|
@@ -112,7 +117,7 @@ function _akiKeyId(val, ctx) {
|
|
|
112
117
|
// Extension DER buffers). Enforces the RFC 5280 CA cross-field gates, then emits a deterministic order.
|
|
113
118
|
function _buildExtensions(extSpec, ctx) {
|
|
114
119
|
if (extSpec == null) return [];
|
|
115
|
-
if (
|
|
120
|
+
if (_isArray(extSpec)) {
|
|
116
121
|
// Validate each pre-encoded extension, reject a duplicate extnID (RFC 5280 sec. 4.2 allows at most one
|
|
117
122
|
// instance of an extension), and decode basicConstraints + keyUsage to apply the same CA
|
|
118
123
|
// cross-field rules the object form enforces (below), so the array escape hatch cannot bypass them.
|
|
@@ -161,7 +166,7 @@ function _buildExtensions(extSpec, ctx) {
|
|
|
161
166
|
if (bc != null) _validateBcSpec(bc);
|
|
162
167
|
var caTrue = !!(bc && bc.cA === true);
|
|
163
168
|
var ku = extSpec.keyUsage;
|
|
164
|
-
var assertsKeyCertSign =
|
|
169
|
+
var assertsKeyCertSign = _isArray(ku) && ku.indexOf("keyCertSign") >= 0;
|
|
165
170
|
// RFC 5280 sec. 4.2.1.3 -- keyCertSign requires basicConstraints cA=TRUE.
|
|
166
171
|
if (assertsKeyCertSign && !caTrue) throw _err("x509/bad-input", "keyUsage keyCertSign requires basicConstraints cA=TRUE (RFC 5280 sec. 4.2.1.3)");
|
|
167
172
|
// RFC 5280 sec. 4.2.1.9 -- pathLenConstraint requires cA=TRUE AND keyCertSign.
|
|
@@ -217,7 +222,7 @@ function _assertIssuerIsCa(issuerCert) {
|
|
|
217
222
|
// pre-encoded array extensions form (used to honor a supplied issuer's pathLenConstraint).
|
|
218
223
|
function _issuedCaInfo(extSpec) {
|
|
219
224
|
if (extSpec == null) return { cA: false, pathLen: null };
|
|
220
|
-
if (!
|
|
225
|
+
if (!_isArray(extSpec)) {
|
|
221
226
|
var bc = extSpec.basicConstraints;
|
|
222
227
|
return { cA: !!(bc && bc.cA === true), pathLen: bc && bc.pathLen != null ? Number(bc.pathLen) : null };
|
|
223
228
|
}
|
|
@@ -234,7 +239,7 @@ function _issuedCaInfo(extSpec) {
|
|
|
234
239
|
// is decoded to look for an Extension whose extnID is subjectAltName with a TRUE critical flag.
|
|
235
240
|
function _hasCriticalSan(extSpec) {
|
|
236
241
|
if (extSpec == null) return false;
|
|
237
|
-
if (!
|
|
242
|
+
if (!_isArray(extSpec)) return !!extSpec.subjectAltName;
|
|
238
243
|
var sanOid = O("subjectAltName");
|
|
239
244
|
for (var i = 0; i < extSpec.length; i++) {
|
|
240
245
|
var n = asn1.decode(_reqDer(extSpec[i], "extension"));
|
|
@@ -362,17 +367,23 @@ function _sign(spec, issuer, opts) {
|
|
|
362
367
|
if (guard.time.instantOf(spec.notBefore) > guard.time.instantOf(spec.notAfter)) throw _err("x509/bad-input", "notBefore must not be after notAfter (RFC 5280 sec. 4.1.2.5)");
|
|
363
368
|
var validityDer = b.sequence([_timeDer(spec.notBefore, "notBefore"), _timeDer(spec.notAfter, "notAfter")]);
|
|
364
369
|
|
|
365
|
-
|
|
370
|
+
// Read ONCE. Three separate consumers test which form this spec is in -- the encoder, the
|
|
371
|
+
// empty-subject rule below and the path-length rule under it -- and each answering for its own
|
|
372
|
+
// read is three chances to answer differently about one certificate. The door deep-copies the
|
|
373
|
+
// spec, so today they cannot disagree; taking the value once means they still cannot if the
|
|
374
|
+
// shape of that door ever changes.
|
|
375
|
+
var extSpec = spec.extensions;
|
|
376
|
+
var exts = _buildExtensions(extSpec, { spki: spki, issuerSpki: issuerSpki, issuerCert: issuerCert, subjectEmpty: subjectEmpty });
|
|
366
377
|
// RFC 5280 sec. 4.1.2.6 -- an empty subject requires a critical subjectAltName (recognized in both the
|
|
367
378
|
// object form and a pre-encoded array-form extension).
|
|
368
|
-
if (subjectEmpty && !_hasCriticalSan(
|
|
379
|
+
if (subjectEmpty && !_hasCriticalSan(extSpec)) {
|
|
369
380
|
throw _err("x509/bad-input", "an empty subject requires a critical subjectAltName (RFC 5280 sec. 4.1.2.6)");
|
|
370
381
|
}
|
|
371
382
|
// Honor a supplied issuer's pathLenConstraint (RFC 5280 sec. 4.2.1.9): a CA certificate issued below
|
|
372
383
|
// it consumes one unit of path length, so pathLen=0 forbids issuing a CA below it, and an issued CA's
|
|
373
384
|
// own pathLenConstraint must leave room within the issuer's remaining depth.
|
|
374
385
|
if (issuerPathLen != null) {
|
|
375
|
-
var issued = _issuedCaInfo(
|
|
386
|
+
var issued = _issuedCaInfo(extSpec);
|
|
376
387
|
// A self-issued CA certificate (subject == issuer, e.g. a CA key rollover) does NOT consume path
|
|
377
388
|
// length (RFC 5280 sec. 6.1 counts only non-self-issued intermediates), so it is permitted even at
|
|
378
389
|
// pathLen 0. Compare canonically (sec. 7.1) via the parsed RDN sequences.
|
package/package.json
CHANGED
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:
|
|
5
|
+
"serialNumber": "urn:uuid:3735feb6-6447-4894-953a-28811d47f2c0",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-08-
|
|
8
|
+
"timestamp": "2026-08-21T03:44:09.554Z",
|
|
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.5.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.5.22",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.5.
|
|
25
|
+
"version": "0.5.22",
|
|
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.5.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.5.22",
|
|
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.5.
|
|
57
|
+
"ref": "@blamejs/pki@0.5.22",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|