@blamejs/pki 0.8.55 → 0.8.56
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 +66 -0
- package/lib/acme.js +2 -2
- package/lib/asn1-der.js +17 -5
- package/lib/attrcert-verify.js +1 -1
- package/lib/cmc-verify.js +1 -1
- package/lib/cmp-session.js +2 -2
- package/lib/cmp-verify.js +2 -2
- package/lib/cms-decrypt.js +4 -1
- package/lib/cms-encrypt.js +4 -1
- package/lib/cms-sign.js +26 -12
- package/lib/cms-verify.js +1 -1
- package/lib/composite-kem.js +30 -12
- package/lib/composite-sig.js +42 -23
- package/lib/constants.js +7 -2
- package/lib/crl-verify.js +1 -1
- package/lib/crmf-sign.js +72 -43
- package/lib/crmf-verify.js +1 -1
- package/lib/csr-verify.js +1 -1
- package/lib/ct.js +2 -2
- package/lib/edwards-point.js +1 -1
- package/lib/est.js +3 -3
- package/lib/guard-async.js +22 -1
- package/lib/guard-bytes.js +6 -3
- package/lib/guard-identifier.js +1 -1
- package/lib/guard-intrinsic.js +474 -0
- package/lib/http-digest.js +146 -62
- package/lib/http-transport.js +82 -17
- package/lib/identity-match.js +2 -2
- package/lib/merkle.js +2 -2
- package/lib/ocsp-request-verify.js +1 -1
- package/lib/ocsp.js +46 -22
- package/lib/path-validate.js +169 -76
- package/lib/pbes2.js +14 -7
- package/lib/schema-attrcert.js +2 -2
- package/lib/schema-cmc.js +2 -2
- package/lib/schema-cmp.js +2 -2
- package/lib/schema-cms.js +2 -2
- package/lib/schema-crmf.js +2 -2
- package/lib/schema-csr.js +2 -2
- package/lib/schema-pkix.js +3 -3
- package/lib/schema-x509.js +2 -2
- package/lib/sigstore.js +1 -1
- package/lib/tlog.js +7 -4
- package/lib/trust.js +187 -104
- package/lib/tsp-sign.js +1 -1
- package/lib/webauthn-mds.js +257 -121
- package/lib/webauthn.js +327 -164
- package/lib/webcrypto.js +10 -3
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/http-digest.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
var crypto = require("crypto");
|
|
7
7
|
var constants = require("./constants");
|
|
8
8
|
var intrinsic = require("./guard-intrinsic");
|
|
9
|
+
var guardIdentifier = require("./guard-identifier");
|
|
9
10
|
var _hasOwn = intrinsic.hasOwn;
|
|
10
11
|
/** @internal The URL parser, taken at load: the origin it reports is what a challenge is bound to.
|
|
11
12
|
* Its accessors are taken with it, since the constructor's capture does not cover the prototype the
|
|
@@ -16,6 +17,22 @@ var _urlPathname = intrinsic.urlPathname;
|
|
|
16
17
|
var _urlSearch = intrinsic.urlSearch;
|
|
17
18
|
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
18
19
|
var _strSlice = intrinsic.uncurry(String.prototype.slice);
|
|
20
|
+
/** @internal The operations the challenge is parsed and classified through. This module decides which
|
|
21
|
+
* algorithm and which qop a credential is built for, and whether a weak algorithm is refused at all,
|
|
22
|
+
* so every one of them is a decision: measured, a replaced global `String` rewriting an `MD5`
|
|
23
|
+
* challenge as `SHA-256` turned `transport/proxy-digest-weak-algorithm` into an accepted credential. */
|
|
24
|
+
var _strTrim = intrinsic.uncurry(String.prototype.trim);
|
|
25
|
+
var _strSplit = intrinsic.uncurry(String.prototype.split);
|
|
26
|
+
/** @internal The LEXER's character reads, captured because the lexer copies the challenge one
|
|
27
|
+
* character at a time and a replacement therefore chooses the token the parse assembles. Measured,
|
|
28
|
+
* rewriting `algorithm=MD5` to `algorithm=SHA-256` as the split copies it does NOT reach an accepted
|
|
29
|
+
* credential: the policy still refuses, as `proxy-digest-unsupported-algorithm` rather than
|
|
30
|
+
* `proxy-digest-weak-algorithm`. What is steerable is the REASON, so the capture is what keeps a
|
|
31
|
+
* refusal naming the fault it actually found. A parser is captured at its characters or not at all. */
|
|
32
|
+
var _charAt = intrinsic.uncurry(String.prototype.charAt);
|
|
33
|
+
var _strNormalize = intrinsic.uncurry(String.prototype.normalize);
|
|
34
|
+
var _arrIndexOf = intrinsic.uncurry(Array.prototype.indexOf);
|
|
35
|
+
var _arrJoin = intrinsic.uncurry(Array.prototype.join);
|
|
19
36
|
var _fromCharCode = intrinsic.fromCharCode;
|
|
20
37
|
var pkix = require("./schema-pkix");
|
|
21
38
|
|
|
@@ -29,10 +46,17 @@ var DEFAULT_CODES = intrinsic.assign(intrinsic.create(null), {
|
|
|
29
46
|
noQop: "digest/no-qop", badChallenge: "digest/bad-challenge",
|
|
30
47
|
});
|
|
31
48
|
|
|
32
|
-
|
|
49
|
+
/** @internal The hash the credential is computed with, taken at load along with the methods of the
|
|
50
|
+
* object it returns. Every Digest response value is this function's output, so a replacement chooses
|
|
51
|
+
* what is sent in place of the password-derived proof. */
|
|
52
|
+
var _createHash = crypto.createHash;
|
|
53
|
+
var _randomBytes = crypto.randomBytes;
|
|
54
|
+
var _hashUpdate = intrinsic.uncurry(crypto.createHash("sha256").update);
|
|
55
|
+
var _hashDigest = intrinsic.uncurry(crypto.createHash("sha256").digest);
|
|
56
|
+
function H(hash, s) { return _hashDigest(_hashUpdate(_createHash(hash), s, "latin1"), "hex"); }
|
|
33
57
|
function KD(hash, secret, data) { return H(hash, secret + ":" + data); }
|
|
34
58
|
function _qstr(v) {
|
|
35
|
-
var s = String(v), out = "";
|
|
59
|
+
var s = intrinsic.String(v), out = "";
|
|
36
60
|
for (var i = 0; i < s.length; i++) { var c = _charCodeAt(s, i); if (c === 34 || c === 92) out += "\\"; out += _fromCharCode(c); }
|
|
37
61
|
return "\"" + out + "\"";
|
|
38
62
|
}
|
|
@@ -40,20 +64,20 @@ function _qstr(v) {
|
|
|
40
64
|
function _commaSplitOutsideQuotes(s) {
|
|
41
65
|
var out = [], buf = "", inQ = false, esc = false;
|
|
42
66
|
for (var i = 0; i < s.length; i++) {
|
|
43
|
-
var c = s
|
|
67
|
+
var c = _charAt(s, i);
|
|
44
68
|
if (esc) { buf += c; esc = false; continue; }
|
|
45
69
|
if (inQ && c === "\\") { buf += c; esc = true; continue; }
|
|
46
70
|
if (c === "\"") { inQ = !inQ; buf += c; continue; }
|
|
47
|
-
if (c === "," && !inQ) {
|
|
71
|
+
if (c === "," && !inQ) { intrinsic.append(out, buf); buf = ""; continue; }
|
|
48
72
|
buf += c;
|
|
49
73
|
}
|
|
50
|
-
|
|
74
|
+
intrinsic.append(out, buf);
|
|
51
75
|
return out;
|
|
52
76
|
}
|
|
53
77
|
function _firstEqOutsideQuotes(s) {
|
|
54
78
|
var inQ = false, esc = false;
|
|
55
79
|
for (var i = 0; i < s.length; i++) {
|
|
56
|
-
var c = s
|
|
80
|
+
var c = _charAt(s, i);
|
|
57
81
|
if (esc) { esc = false; continue; }
|
|
58
82
|
if (inQ && c === "\\") { esc = true; continue; }
|
|
59
83
|
if (c === "\"") { inQ = !inQ; continue; }
|
|
@@ -67,10 +91,10 @@ function _unq(s) {
|
|
|
67
91
|
return out + _strSlice(s, last, n - 1);
|
|
68
92
|
}
|
|
69
93
|
function _closedQuote(s) {
|
|
70
|
-
if (s.length < 2 || s
|
|
94
|
+
if (s.length < 2 || _charAt(s, 0) !== "\"") return false;
|
|
71
95
|
var esc = false;
|
|
72
96
|
for (var i = 1; i < s.length; i++) {
|
|
73
|
-
var c = s
|
|
97
|
+
var c = _charAt(s, i);
|
|
74
98
|
if (esc) { esc = false; continue; }
|
|
75
99
|
if (c === "\\") { esc = true; continue; }
|
|
76
100
|
if (c === "\"") return i === s.length - 1;
|
|
@@ -106,12 +130,12 @@ function _splitWhitespace(s) {
|
|
|
106
130
|
var out = [], i = 0, n = s.length, ps = 0;
|
|
107
131
|
while (i < n) {
|
|
108
132
|
if (pkix.isJsWhitespace(_charCodeAt(s, i))) {
|
|
109
|
-
out
|
|
133
|
+
intrinsic.append(out, _strSlice(s, ps, i));
|
|
110
134
|
while (i < n && pkix.isJsWhitespace(_charCodeAt(s, i))) i += 1;
|
|
111
135
|
ps = i;
|
|
112
136
|
} else i += 1;
|
|
113
137
|
}
|
|
114
|
-
out
|
|
138
|
+
intrinsic.append(out, _strSlice(s, ps));
|
|
115
139
|
return out;
|
|
116
140
|
}
|
|
117
141
|
|
|
@@ -119,29 +143,37 @@ function _splitChallenges(s) {
|
|
|
119
143
|
var segs = _commaSplitOutsideQuotes(s);
|
|
120
144
|
var out = [], cur = null;
|
|
121
145
|
for (var i = 0; i < segs.length; i++) {
|
|
122
|
-
|
|
146
|
+
/** @internal The OUTER trim is captured as well as the inner ones. It controls the complete
|
|
147
|
+
* auth-param before anything slices it, so a replacement mapping the segment `algorithm=MD5` to
|
|
148
|
+
* `algorithm=SHA-256` classifies a weak challenge as a strong one and the refusal never fires:
|
|
149
|
+
* capturing only the key and value slices left the whole parameter steerable. */
|
|
150
|
+
var seg = _strTrim(segs[i]);
|
|
123
151
|
if (seg === "") continue;
|
|
124
152
|
var eq = _firstEqOutsideQuotes(seg);
|
|
125
|
-
var pre = (eq < 0 ? seg : _strSlice(seg, 0, eq))
|
|
153
|
+
var pre = _strTrim(eq < 0 ? seg : _strSlice(seg, 0, eq));
|
|
126
154
|
var scheme = _matchTokenRest(pre);
|
|
127
|
-
if (eq < 0) { cur = { scheme: seg, paramText: "" };
|
|
128
|
-
else if (scheme !== null) { cur = { scheme: scheme, paramText: _strSlice(seg, _stripTokenIndex(seg)) };
|
|
155
|
+
if (eq < 0) { cur = { scheme: seg, paramText: "" }; intrinsic.append(out, cur); }
|
|
156
|
+
else if (scheme !== null) { cur = { scheme: scheme, paramText: _strSlice(seg, _stripTokenIndex(seg)) }; intrinsic.append(out, cur); }
|
|
129
157
|
else if (cur) { cur.paramText = cur.paramText ? cur.paramText + "," + seg : seg; }
|
|
130
158
|
}
|
|
131
159
|
return out;
|
|
132
160
|
}
|
|
161
|
+
/** @internal The map this returns IS the challenge the policy is applied to, so the construction is
|
|
162
|
+
* captured along with the lexer that fills it. A replaced construction hands back a map already
|
|
163
|
+
* carrying an `algorithm` entry, and a challenge that names no algorithm then reads as the strong one
|
|
164
|
+
* instead of the MD5 default that `allowMD5: false` refuses. */
|
|
133
165
|
function _parseParams(paramText, E, code) {
|
|
134
|
-
var segs = _commaSplitOutsideQuotes(paramText), map =
|
|
166
|
+
var segs = _commaSplitOutsideQuotes(paramText), map = intrinsic.create(null);
|
|
135
167
|
for (var i = 0; i < segs.length; i++) {
|
|
136
|
-
var seg = segs[i]
|
|
168
|
+
var seg = _strTrim(segs[i]);
|
|
137
169
|
if (seg === "") continue;
|
|
138
170
|
var eq = _firstEqOutsideQuotes(seg);
|
|
139
|
-
if (eq < 0) throw E(code, "malformed Digest auth-param (no '='): " +
|
|
140
|
-
var key = _strSlice(seg, 0, eq)
|
|
141
|
-
var rawVal = _strSlice(seg, eq + 1)
|
|
142
|
-
var quoted = rawVal
|
|
171
|
+
if (eq < 0) throw E(code, "malformed Digest auth-param (no '='): " + intrinsic.stringify(seg));
|
|
172
|
+
var key = intrinsic.toLowerCase(_strTrim(_strSlice(seg, 0, eq)));
|
|
173
|
+
var rawVal = _strTrim(_strSlice(seg, eq + 1));
|
|
174
|
+
var quoted = _charCodeAt(rawVal, 0) === 34;
|
|
143
175
|
if (quoted && !_closedQuote(rawVal)) throw E(code, "an unterminated or trailing-garbage Digest quoted-string (RFC 7230 sec. 3.2.6)");
|
|
144
|
-
if (_hasOwn(map, key)) throw E(code, "repeated Digest auth-param " +
|
|
176
|
+
if (_hasOwn(map, key)) throw E(code, "repeated Digest auth-param " + intrinsic.stringify(key));
|
|
145
177
|
var value = quoted ? _unq(rawVal) : rawVal;
|
|
146
178
|
if (_hasCtl(value)) throw E(code, "a Digest auth-param value contains a control character (RFC 7230 sec. 3.2.6)");
|
|
147
179
|
map[key] = { value: value, quoted: quoted };
|
|
@@ -156,42 +188,50 @@ function _validateDigest(paramText, E, code) {
|
|
|
156
188
|
var qop = [];
|
|
157
189
|
if (p.qop) {
|
|
158
190
|
if (!p.qop.quoted) throw E(code, "the Digest qop must be a quoted list (RFC 7616 sec. 3.3)");
|
|
159
|
-
|
|
191
|
+
/** @internal The predicate that drops an empty member is written out rather than handed the
|
|
192
|
+
* global conversion. It is what leaves `qop=""` with no members, which is the refusal below, so
|
|
193
|
+
* a replaced conversion answering true for the empty string returned a challenge whose quality
|
|
194
|
+
* of protection is a member the scheme does not define. */
|
|
195
|
+
qop = intrinsic.selectList(intrinsic.mapList(intrinsic.splitChar(p.qop.value, 44), function (x) {
|
|
196
|
+
return intrinsic.toLowerCase(_strTrim(x));
|
|
197
|
+
}), function (x) { return x !== ""; });
|
|
160
198
|
if (qop.length === 0) throw E(code, "a present Digest qop directive must list at least one value (RFC 7616 sec. 3.3)");
|
|
161
199
|
}
|
|
162
200
|
if (p.domain && !p.domain.quoted) throw E(code, "the Digest domain must be a quoted-string (RFC 7616 sec. 3.3)");
|
|
163
|
-
var domain = (p.domain && p.domain.value
|
|
164
|
-
? _splitWhitespace(p.domain.value
|
|
165
|
-
if (p.charset && (p.charset.quoted || String(p.charset.value)
|
|
201
|
+
var domain = (p.domain && _strTrim(p.domain.value) !== "")
|
|
202
|
+
? _splitWhitespace(_strTrim(p.domain.value)) : null;
|
|
203
|
+
if (p.charset && (p.charset.quoted || intrinsic.toUpperCase(intrinsic.String(p.charset.value)) !== "UTF-8")) throw E(code, "the Digest charset must be the unquoted token UTF-8 (RFC 7616 sec. 3.3)");
|
|
166
204
|
if (p.stale) {
|
|
167
|
-
var sv = String(p.stale.value)
|
|
205
|
+
var sv = intrinsic.toLowerCase(intrinsic.String(p.stale.value));
|
|
168
206
|
if (p.stale.quoted || (sv !== "true" && sv !== "false")) throw E(code, "the Digest stale directive must be the unquoted token true or false (RFC 7616 sec. 3.3)");
|
|
169
207
|
}
|
|
170
208
|
if (p.userhash) {
|
|
171
|
-
var uhv = String(p.userhash.value)
|
|
209
|
+
var uhv = intrinsic.toLowerCase(intrinsic.String(p.userhash.value));
|
|
172
210
|
if (p.userhash.quoted || (uhv !== "true" && uhv !== "false")) throw E(code, "the Digest userhash directive must be the unquoted token true or false (RFC 7616 sec. 3.3)");
|
|
173
211
|
}
|
|
174
212
|
if (p.opaque && !p.opaque.quoted) throw E(code, "the Digest opaque directive must be a quoted-string (RFC 7616 sec. 3.3)");
|
|
175
213
|
return {
|
|
176
214
|
scheme: "Digest", realm: p.realm.value, nonce: p.nonce.value, qop: qop, domain: domain,
|
|
177
|
-
algorithm: p.algorithm ? p.algorithm.value
|
|
215
|
+
algorithm: p.algorithm ? intrinsic.toUpperCase(p.algorithm.value) : "MD5",
|
|
178
216
|
opaque: p.opaque ? p.opaque.value : null,
|
|
179
|
-
stale: !!(p.stale && String(p.stale.value)
|
|
180
|
-
userhash: !!(p.userhash && String(p.userhash.value)
|
|
217
|
+
stale: !!(p.stale && intrinsic.toLowerCase(intrinsic.String(p.stale.value)) === "true"),
|
|
218
|
+
userhash: !!(p.userhash && intrinsic.toLowerCase(intrinsic.String(p.userhash.value)) === "true"),
|
|
181
219
|
charset: p.charset ? p.charset.value : null,
|
|
182
220
|
};
|
|
183
221
|
}
|
|
184
222
|
|
|
185
223
|
function parseChallenge(www, E, code, policy) {
|
|
186
|
-
|
|
224
|
+
/** @internal An own-key copy with no prototype, for the reason the answer path states: every policy
|
|
225
|
+
* field is read off this record, so an inherited read is a way to supply one. */
|
|
226
|
+
var pol = guardIdentifier.ownOptions(policy);
|
|
187
227
|
var codes = pol.codes || DEFAULT_CODES;
|
|
188
228
|
var preferStale = !!pol.preferStale;
|
|
189
|
-
var raw = String(www == null ? "" : www);
|
|
229
|
+
var raw = intrinsic.String(www == null ? "" : www);
|
|
190
230
|
if (raw.length > constants.LIMITS.HTTP_AUTH_HEADER_MAX_BYTES) throw E(code, "the WWW-Authenticate header exceeds the " + constants.LIMITS.HTTP_AUTH_HEADER_MAX_BYTES + "-byte cap");
|
|
191
231
|
var challenges = _splitChallenges(raw);
|
|
192
232
|
var best = null, bestUsable = false, bestApplicable = false, bestStale = false, bestRank = -1, sawDigest = false;
|
|
193
233
|
for (var i = 0; i < challenges.length; i++) {
|
|
194
|
-
if (challenges[i].scheme
|
|
234
|
+
if (intrinsic.toLowerCase(challenges[i].scheme) !== "digest") continue;
|
|
195
235
|
sawDigest = true;
|
|
196
236
|
var parsed;
|
|
197
237
|
try { parsed = _validateDigest(challenges[i].paramText, E, code); }
|
|
@@ -216,19 +256,22 @@ function parseChallenge(www, E, code, policy) {
|
|
|
216
256
|
return null;
|
|
217
257
|
}
|
|
218
258
|
|
|
259
|
+
/** @internal The bytes a credential is computed over, so the conversion and the encoding both come
|
|
260
|
+
* from the captures: what this returns is hashed, and a replacement changes the input to the proof. */
|
|
219
261
|
function _octets(s, charset) {
|
|
220
|
-
var v = s == null ? "" : String(s);
|
|
221
|
-
return (charset && String(charset)
|
|
262
|
+
var v = s == null ? "" : intrinsic.String(s);
|
|
263
|
+
return (charset && intrinsic.toUpperCase(intrinsic.String(charset)) === "UTF-8")
|
|
264
|
+
? intrinsic.bufToString(intrinsic.bufferFrom(v, "utf8"), "latin1") : v;
|
|
222
265
|
}
|
|
223
266
|
|
|
224
267
|
function _hasNonAscii(s) {
|
|
225
|
-
s = String(s == null ? "" : s);
|
|
268
|
+
s = intrinsic.String(s == null ? "" : s);
|
|
226
269
|
for (var i = 0; i < s.length; i++) { if (_charCodeAt(s, i) > 0x7F) return true; }
|
|
227
270
|
return false;
|
|
228
271
|
}
|
|
229
272
|
|
|
230
273
|
function _pctEncodeUtf8(s) {
|
|
231
|
-
var bytes =
|
|
274
|
+
var bytes = intrinsic.bufferFrom(intrinsic.String(s == null ? "" : s), "utf8");
|
|
232
275
|
var out = "";
|
|
233
276
|
for (var i = 0; i < bytes.length; i++) {
|
|
234
277
|
var b = bytes[i];
|
|
@@ -237,7 +280,7 @@ function _pctEncodeUtf8(s) {
|
|
|
237
280
|
b === 0x2E || b === 0x5E || b === 0x5F || b === 0x60 || b === 0x7C || b === 0x7E) {
|
|
238
281
|
out += _fromCharCode(b);
|
|
239
282
|
} else {
|
|
240
|
-
out += "%" + (b < 0x10 ? "0" : "") +
|
|
283
|
+
out += "%" + (b < 0x10 ? "0" : "") + intrinsic.toUpperCase(intrinsic.numberToString(b, 16));
|
|
241
284
|
}
|
|
242
285
|
}
|
|
243
286
|
return out;
|
|
@@ -249,25 +292,48 @@ function _rejection(challenge, pol, codes) {
|
|
|
249
292
|
if (alg.hash === "md5" && !pol.allowMD5) return { code: codes.weakAlgorithm, msg: "MD5 Digest refused by default (RFC 7616 sec. 3.4 discourages it); set opts.auth.allowMD5 for legacy interop" };
|
|
250
293
|
if (challenge.qop.length === 0) {
|
|
251
294
|
if (!pol.allowLegacyQop) return { code: codes.noQop, msg: "a no-qop (RFC 2069) Digest challenge is refused by default; set opts.auth.allowLegacyQop for legacy interop" };
|
|
252
|
-
|
|
295
|
+
/** @internal The qop membership tests come from the capture. This arm REFUSES a challenge whose
|
|
296
|
+
* qop offers nothing this client supports, and the one below SELECTS which qop the credential is
|
|
297
|
+
* computed for, so a replaced `indexOf` either skips the refusal or changes the computation. */
|
|
298
|
+
} else if (_arrIndexOf(challenge.qop, "auth") === -1 && _arrIndexOf(challenge.qop, "auth-int") === -1) {
|
|
253
299
|
return { code: codes.badChallenge, msg: "the Digest qop offered no member this client supports (auth / auth-int)" };
|
|
254
300
|
}
|
|
255
301
|
return null;
|
|
256
302
|
}
|
|
257
303
|
|
|
258
304
|
function answer(challenge, params, E) {
|
|
259
|
-
|
|
305
|
+
/** @internal The whole bag is taken as an OWN-key copy with no prototype, once, and every field
|
|
306
|
+
* below is read off that copy. Each field is a separate way to supply a value the caller did not:
|
|
307
|
+
* `Object.prototype.policy` answered for a caller who passed no policy and `Object.prototype
|
|
308
|
+
* .allowMD5` for one who passed an empty one, either turning the default MD5 refusal into an
|
|
309
|
+
* issued credential; and `Object.prototype.rng` answered for the client nonce, which makes every
|
|
310
|
+
* credential this function issues carry the same cnonce and so replayable. The policy is copied
|
|
311
|
+
* the same way for the same reason, since it is a record of its own. */
|
|
312
|
+
params = guardIdentifier.ownOptions(params);
|
|
313
|
+
var pol = guardIdentifier.ownOptions(_hasOwn(params, "policy") ? params.policy : null);
|
|
260
314
|
var codes = pol.codes || DEFAULT_CODES;
|
|
261
315
|
var rej = _rejection(challenge, pol, codes);
|
|
262
316
|
if (rej) throw E(rej.code, rej.msg);
|
|
263
317
|
var alg = ALGS[challenge.algorithm];
|
|
264
|
-
var useQop = challenge.qop
|
|
265
|
-
|
|
318
|
+
var useQop = _arrIndexOf(challenge.qop, "auth") !== -1 ? "auth"
|
|
319
|
+
: (_arrIndexOf(challenge.qop, "auth-int") !== -1 ? "auth-int" : null);
|
|
320
|
+
/** @internal The client nonce comes from the captured random source. Read off the module export it
|
|
321
|
+
* is replaceable, and a predictable cnonce is a replayable response: the whole point of the value
|
|
322
|
+
* is that the server cannot have seen it before. A caller's own `rng` still wins, as before. */
|
|
323
|
+
/** @internal The ENCODING of the random bytes is captured too. Capturing the random source alone
|
|
324
|
+
* left the value it produced readable through a replaced `Buffer.prototype.toString`, which can
|
|
325
|
+
* answer with a fixed string: the unpredictability is a property of what reaches the header, not of
|
|
326
|
+
* where the bytes came from. A caller's own `rng` still wins. */
|
|
327
|
+
var cnonce = intrinsic.String((params.rng || function () {
|
|
328
|
+
return intrinsic.bufToString(_randomBytes(18), "base64");
|
|
329
|
+
})());
|
|
266
330
|
var realm = challenge.realm, nonce = challenge.nonce;
|
|
267
|
-
var isUtf8 = String(challenge.charset || "")
|
|
268
|
-
var pUser = params.username == null ? "" : String(params.username);
|
|
269
|
-
var pPass = params.password == null ? "" : String(params.password);
|
|
270
|
-
|
|
331
|
+
var isUtf8 = intrinsic.toUpperCase(intrinsic.String(challenge.charset || "")) === "UTF-8";
|
|
332
|
+
var pUser = params.username == null ? "" : intrinsic.String(params.username);
|
|
333
|
+
var pPass = params.password == null ? "" : intrinsic.String(params.password);
|
|
334
|
+
/** @internal The credentials are normalized through the capture: what this produces is hashed, so a
|
|
335
|
+
* replaced `normalize` changes the input to the proof the server checks. */
|
|
336
|
+
if (isUtf8) { pUser = _strNormalize(pUser, "NFC"); pPass = _strNormalize(pPass, "NFC"); }
|
|
271
337
|
var user = _octets(pUser, challenge.charset);
|
|
272
338
|
var pass = _octets(pPass, challenge.charset);
|
|
273
339
|
var HA1 = H(alg.hash, user + ":" + realm + ":" + pass);
|
|
@@ -276,8 +342,8 @@ function answer(challenge, params, E) {
|
|
|
276
342
|
? params.method + ":" + params.uri + ":" + H(alg.hash, params.body == null ? "" : params.body)
|
|
277
343
|
: params.method + ":" + params.uri;
|
|
278
344
|
var HA2 = H(alg.hash, A2);
|
|
279
|
-
var ncNum = (typeof params.nc === "number" && params.nc >= 1) ?
|
|
280
|
-
var nc = _strSlice("0000000" +
|
|
345
|
+
var ncNum = (typeof params.nc === "number" && params.nc >= 1) ? intrinsic.floor(params.nc) : 1;
|
|
346
|
+
var nc = _strSlice("0000000" + intrinsic.numberToString(ncNum, 16), -8);
|
|
281
347
|
var response = useQop
|
|
282
348
|
? KD(alg.hash, HA1, nonce + ":" + nc + ":" + cnonce + ":" + useQop + ":" + HA2)
|
|
283
349
|
: KD(alg.hash, HA1, nonce + ":" + HA2);
|
|
@@ -288,34 +354,52 @@ function answer(challenge, params, E) {
|
|
|
288
354
|
var sentUser = challenge.userhash ? H(alg.hash, _octets(pUser, challenge.charset) + ":" + realm) : _octets(pUser, challenge.charset);
|
|
289
355
|
parts = ["username=" + _qstr(sentUser)];
|
|
290
356
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
357
|
+
/** @internal The header is assembled through the captures. These decide which parameters reach the
|
|
358
|
+
* server: a dropped `cnonce` or `nc` is a credential the server reads under different rules than
|
|
359
|
+
* the one it was computed under. */
|
|
360
|
+
/** @internal Appended with the operation that writes an OWN property. `push` writes through Set,
|
|
361
|
+
* which walks the prototype for a numeric setter, so an accessor installed at `Array.prototype[0]`
|
|
362
|
+
* takes the value and the index reads back as whatever its getter answers: a dropped `cnonce` or
|
|
363
|
+
* nonce count, or a substituted `qop`, is a credential the server reads under different rules than
|
|
364
|
+
* the one it was computed under. */
|
|
365
|
+
intrinsic.append(parts, "realm=" + _qstr(realm));
|
|
366
|
+
intrinsic.append(parts, "nonce=" + _qstr(nonce));
|
|
367
|
+
intrinsic.append(parts, "uri=" + _qstr(params.uri));
|
|
368
|
+
intrinsic.append(parts, "algorithm=" + challenge.algorithm);
|
|
369
|
+
if (useQop) { intrinsic.append(parts, "qop=" + useQop); intrinsic.append(parts, "nc=" + nc); }
|
|
370
|
+
if (useQop || alg.sess) { intrinsic.append(parts, "cnonce=" + _qstr(cnonce)); }
|
|
371
|
+
intrinsic.append(parts, "response=" + _qstr(response));
|
|
372
|
+
if (challenge.opaque != null) intrinsic.append(parts, "opaque=" + _qstr(challenge.opaque));
|
|
373
|
+
if (challenge.userhash) intrinsic.append(parts, "userhash=true");
|
|
374
|
+
return "Digest " + _arrJoin(parts, ", ");
|
|
298
375
|
}
|
|
299
376
|
|
|
377
|
+
/** @internal The protection-space entries are parsed through the captures. They decide whether a
|
|
378
|
+
* request is inside the space a credential was issued for, so an entry read differently is a
|
|
379
|
+
* credential sent to an origin the challenge did not cover. */
|
|
300
380
|
function _domainEntry(d) {
|
|
301
|
-
d = String(d == null ? "" : d);
|
|
302
|
-
if (d
|
|
303
|
-
try { var u = new _URL(d); return { origin: _urlOrigin(u)
|
|
381
|
+
d = intrinsic.String(d == null ? "" : d);
|
|
382
|
+
if (_charAt(d, 0) === "/") return { origin: null, full: d };
|
|
383
|
+
try { var u = new _URL(d); return { origin: intrinsic.toLowerCase(_urlOrigin(u)), full: (_urlPathname(u) || "/") + (_urlSearch(u) || "") }; }
|
|
304
384
|
catch (_e) { return null; }
|
|
305
385
|
}
|
|
306
386
|
|
|
307
387
|
function inProtectionSpace(challenge, requestOrigin, requestPathAndSearch) {
|
|
308
388
|
var domain = challenge && challenge.domain;
|
|
309
389
|
if (!domain || !domain.length) return true;
|
|
310
|
-
|
|
311
|
-
|
|
390
|
+
/** @internal This answers whether a credential may be sent to this request's origin, so every
|
|
391
|
+
* operation it compares through is captured: `true` here is what lets the credential go out. */
|
|
392
|
+
var origin = intrinsic.toLowerCase(intrinsic.String(requestOrigin == null ? "" : requestOrigin));
|
|
393
|
+
var full = intrinsic.String(requestPathAndSearch == null ? "" : requestPathAndSearch);
|
|
312
394
|
for (var i = 0; i < domain.length; i++) {
|
|
313
395
|
var e = _domainEntry(domain[i]);
|
|
314
396
|
if (e === null) continue;
|
|
315
397
|
if (e.origin !== null && e.origin !== origin) continue;
|
|
316
|
-
if (
|
|
398
|
+
if (intrinsic.stringIndexOf(full, e.full) === 0) return true;
|
|
317
399
|
}
|
|
318
400
|
return false;
|
|
319
401
|
}
|
|
320
402
|
|
|
321
|
-
|
|
403
|
+
/** @internal Frozen, so a consumer that reads one of these off this object at the call cannot be
|
|
404
|
+
* handed a replacement in place of it. */
|
|
405
|
+
module.exports = intrinsic.freeze({ parseChallenge: parseChallenge, answer: answer, inProtectionSpace: inProtectionSpace });
|
package/lib/http-transport.js
CHANGED
|
@@ -67,7 +67,21 @@
|
|
|
67
67
|
var nodeHttps = require("node:https");
|
|
68
68
|
var nodeHttp = require("node:http");
|
|
69
69
|
var nodeNet = require("node:net");
|
|
70
|
+
/** @internal The address-family classification, captured at load. It is consulted BEFORE any of the
|
|
71
|
+
* string operations in `_isBlockedIp`, and the export is writable: answering `0` for `127.0.0.1`
|
|
72
|
+
* makes the function fall through to "not blocked", so a request under `blockPrivateAddresses`
|
|
73
|
+
* reaches the private address no matter how carefully the operations after it are captured. */
|
|
74
|
+
var _isIP = nodeNet.isIP;
|
|
75
|
+
/** @internal The TLS identity check, captured at load. It RETURNS A VERDICT about whether a
|
|
76
|
+
* certificate belongs to the host that was asked for, and the export is writable: a replacement
|
|
77
|
+
* answering with no error is the server-authentication step reporting success for a certificate
|
|
78
|
+
* issued to someone else. Every route that asks the question reads this one reference. */
|
|
70
79
|
var nodeTls = require("node:tls");
|
|
80
|
+
/** @internal The TLS identity check, captured where `nodeTls` is in scope. It RETURNS A VERDICT about
|
|
81
|
+
* whether a certificate belongs to the host that was asked for, and the export is writable: a
|
|
82
|
+
* replacement answering with no error is the server-authentication step reporting success for a
|
|
83
|
+
* certificate issued to someone else. Every route that asks the question reads this one reference. */
|
|
84
|
+
var _checkServerIdentity = nodeTls.checkServerIdentity;
|
|
71
85
|
var nodeDns = require("node:dns");
|
|
72
86
|
var nodeUtil = require("node:util");
|
|
73
87
|
var constants = require("./constants");
|
|
@@ -84,6 +98,11 @@ var _Promise = intrinsic.Promise;
|
|
|
84
98
|
* with it, since a replacement answers with a host of its own. */
|
|
85
99
|
var _URL = intrinsic.URL;
|
|
86
100
|
var _stringOf = intrinsic.String;
|
|
101
|
+
/** @internal The split an address literal is classified by. Read off the live prototype, it decides
|
|
102
|
+
* which octets the blocklist sees, and a replacement answering with a public address's octets is
|
|
103
|
+
* admitted: measured, `127.0.0.1` split into `93.184.216.34` cleared the private-address refusal and
|
|
104
|
+
* the request went out. */
|
|
105
|
+
var _strSplit = intrinsic.uncurry(String.prototype.split);
|
|
87
106
|
/** @internal The parts of that destination are read through the accessors captured at load, not off the
|
|
88
107
|
* prototype the parsed value reads through: a replaced `URL.prototype.hostname` names the host this
|
|
89
108
|
* module then opens a socket to. */
|
|
@@ -201,9 +220,22 @@ function _systemCa() {
|
|
|
201
220
|
}
|
|
202
221
|
|
|
203
222
|
function _isBlockedIp(ip) {
|
|
204
|
-
|
|
223
|
+
/** @internal The argument has to BE a string, and what cannot be classified is blocked. `isIP`
|
|
224
|
+
* converts its argument, so a value whose conversion answers a loopback literal to the family
|
|
225
|
+
* check and a public one to the octet scan was classified twice and the second answer decided:
|
|
226
|
+
* measured, such a value came back NOT blocked while the family check had seen `127.0.0.1`. A
|
|
227
|
+
* caller-supplied `lookup` reaches this with whatever it yields, and `pki.transport.isBlockedIp`
|
|
228
|
+
* takes the value directly. A non-IP STRING is still not an address literal and answers false. */
|
|
229
|
+
if (typeof ip !== "string") return true;
|
|
230
|
+
var fam = _isIP(ip);
|
|
205
231
|
if (fam === 4) {
|
|
206
|
-
|
|
232
|
+
/** @internal The literal is scanned on the delimiter CHARACTER rather than split on a separator
|
|
233
|
+
* string. `String.prototype.split` is specified to look the separator's `Symbol.split` method up
|
|
234
|
+
* and call it, so the separator's prototype chain is part of the operation and capturing `split`
|
|
235
|
+
* does not close it; hard rule 11 bans `split` in lib/ for that reason. MEASURED on Node 24.21,
|
|
236
|
+
* the supported runtime: a hook answering with a public address's octets made this refusal clear
|
|
237
|
+
* for `127.0.0.1` and a request to loopback proceeded. */
|
|
238
|
+
var o = intrinsic.splitChar(ip, 46), a = +o[0], b = +o[1], c = +o[2];
|
|
207
239
|
return a === 0 || a === 10 || a === 127 || a >= 224 ||
|
|
208
240
|
(a === 100 && b >= 64 && b <= 127) ||
|
|
209
241
|
(a === 169 && b === 254) ||
|
|
@@ -216,12 +248,22 @@ function _isBlockedIp(ip) {
|
|
|
216
248
|
(a === 203 && b === 0 && c === 113);
|
|
217
249
|
}
|
|
218
250
|
if (fam === 6) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
251
|
+
/** @internal The fold and the index read come from the load-time captures. This classification
|
|
252
|
+
* ADMITS anything inside global unicast, so a replaced `toLowerCase` answering with a
|
|
253
|
+
* global-unicast spelling for a private address turns a refusal into a connection attempt:
|
|
254
|
+
* measured, `fc00::1` folded to `2606:4700::1` was admitted past this blocklist and the request
|
|
255
|
+
* went out. The substitute has to be an address this function admits, or the refusal comes from
|
|
256
|
+
* another rule and the probe measures nothing. */
|
|
257
|
+
var l = intrinsic.toLowerCase(ip);
|
|
258
|
+
if (intrinsic.stringIndexOf(l, "::ffff:") === 0) return true;
|
|
259
|
+
var parts = intrinsic.splitChar(l, 58);
|
|
260
|
+
/** @internal The numeric conversion is captured too. The fold and the split were converted first
|
|
261
|
+
* and this was still live, so `parseInt("fc00", 16)` answering `0x2606` put the address inside
|
|
262
|
+
* global unicode and the refusal was skipped: converting the operations that produce the hextets
|
|
263
|
+
* without the one that reads them leaves the gate exactly as open. */
|
|
264
|
+
var h = intrinsic.parseInt(parts[0], 16);
|
|
223
265
|
if (!(h >= 0x2000 && h <= 0x3fff)) return true;
|
|
224
|
-
var h2 = parts[1] ? parseInt(parts[1], 16) : 0;
|
|
266
|
+
var h2 = parts[1] ? intrinsic.parseInt(parts[1], 16) : 0;
|
|
225
267
|
if (h === 0x2002) return true;
|
|
226
268
|
if (h === 0x2001 && h2 < 0x0200) return true;
|
|
227
269
|
if (h === 0x2001 && h2 === 0x0db8) return true;
|
|
@@ -380,7 +422,7 @@ function _validateProxy(request, defaults, E, C, originHost, originStatedPort, o
|
|
|
380
422
|
if (scheme !== "digest") throw E(C("bad-proxy"), "opts.proxy.auth." + kn + " applies only to Digest authentication");
|
|
381
423
|
if (typeof kv !== "boolean") throw E(C("bad-proxy"), "opts.proxy.auth." + kn + " must be a boolean, got " + guard.text.showValue(kv));
|
|
382
424
|
}
|
|
383
|
-
if (scheme === "basic" && proxy.auth.username
|
|
425
|
+
if (scheme === "basic" && intrinsic.stringIndexOf(proxy.auth.username, ":") !== -1) throw E(C("bad-proxy"), "a Basic proxy user-id must not contain a colon (RFC 7617 sec. 2)");
|
|
384
426
|
auth = { scheme: scheme, username: proxy.auth.username, password: proxy.auth.password,
|
|
385
427
|
allowMD5: proxy.auth.allowMD5 === true, allowLegacyQop: proxy.auth.allowLegacyQop === true };
|
|
386
428
|
}
|
|
@@ -394,6 +436,10 @@ function _validateProxy(request, defaults, E, C, originHost, originStatedPort, o
|
|
|
394
436
|
guard.identifier.assertKnownKeys(ptls, PROXY_TLS_KEYS, E, C("bad-proxy"), "opts.proxy.tls has an unknown option ");
|
|
395
437
|
if (ptls.servername !== undefined && ptls.servername !== null && typeof ptls.servername !== "string") throw E(C("bad-proxy"), "opts.proxy.tls.servername must be a string");
|
|
396
438
|
}
|
|
439
|
+
/** @internal Taken here, so the value the tunnel is opened under is the one typed above rather
|
|
440
|
+
* than a later read of the caller's record. The door refuses an accessor and refuses a Proxy, so
|
|
441
|
+
* the two reads cannot differ today; holding the value keeps that true without depending on it. */
|
|
442
|
+
var pServername = (ptls && ptls.servername) || null;
|
|
397
443
|
var pAnchors = ptls ? ptls.anchors : undefined;
|
|
398
444
|
if (pAnchors !== undefined && pAnchors !== null && typeof pAnchors === "object" && nodeUtil.types.isProxy(pAnchors)) throw E(C("bad-proxy"), "opts.proxy.tls.anchors must not be a Proxy");
|
|
399
445
|
var pUseSystem = ptls ? ptls.useSystemStore === true : false;
|
|
@@ -412,10 +458,10 @@ function _validateProxy(request, defaults, E, C, originHost, originStatedPort, o
|
|
|
412
458
|
var pCa = [];
|
|
413
459
|
if (pHasAnchors) pCa = pCa.concat(_pemifyAnchors(pAnchors));
|
|
414
460
|
if (pUseSystem) pCa = pCa.concat(_systemCa());
|
|
415
|
-
proxyTls = { ca: pCa.length ? pCa : undefined, minVersion: pMinVersion, servername:
|
|
461
|
+
proxyTls = { ca: pCa.length ? pCa : undefined, minVersion: pMinVersion, servername: pServername };
|
|
416
462
|
}
|
|
417
463
|
|
|
418
|
-
var originAuthHost =
|
|
464
|
+
var originAuthHost = _isIP(originHost) === 6 ? "[" + originHost + "]" : originHost;
|
|
419
465
|
var phost = _stripIpv6Brackets(_urlHostname(purl));
|
|
420
466
|
/** @internal The CONNECT target is the origin's own authority, so an origin URL that states no
|
|
421
467
|
* port takes the default for the origin's scheme, not the proxy's. Defaulting it to 443 opens
|
|
@@ -435,7 +481,9 @@ function _basicProxyHeader(auth) {
|
|
|
435
481
|
* the request line carries (RFC 9112 sec. 3.2.3), which is the pair the proxy checks it against. */
|
|
436
482
|
function _digestProxyCredential(headers, pinfo, C, E) {
|
|
437
483
|
var httpDigest = require("./http-digest"); // allow:inline-require -- circular load: http-digest -> schema-pkix -> ct -> this module
|
|
438
|
-
|
|
484
|
+
/** @internal Captured: this is the challenge the digest credential is computed against, so the
|
|
485
|
+
* conversion decides which parameters, and which algorithm, the response is built for. */
|
|
486
|
+
var offered = intrinsic.String((headers && headers["proxy-authenticate"]) || "");
|
|
439
487
|
var policy = {
|
|
440
488
|
allowMD5: pinfo.auth.allowMD5, allowLegacyQop: pinfo.auth.allowLegacyQop,
|
|
441
489
|
codes: {
|
|
@@ -579,7 +627,13 @@ function httpsTransport(defaults) {
|
|
|
579
627
|
var callerCsi = typeof reqTls.checkServerIdentity === "function" ? reqTls.checkServerIdentity
|
|
580
628
|
: (typeof tlsDefaults.checkServerIdentity === "function" ? tlsDefaults.checkServerIdentity : null);
|
|
581
629
|
var sni = (reqTls.servername !== undefined ? reqTls.servername : tlsDefaults.servername) || host;
|
|
582
|
-
|
|
630
|
+
/** @internal Typed here, the way `opts.proxy.tls.servername` is typed on the proxy arm. The value
|
|
631
|
+
* is read three times after this point and each read is a separate conversion: once to decide it
|
|
632
|
+
* is not an address literal, once by the TLS layer for the name sent in the handshake, and once
|
|
633
|
+
* as `idHost` for the name the server certificate is matched against. A value answering a
|
|
634
|
+
* different string per read sends one name and verifies another. */
|
|
635
|
+
if (typeof sni !== "string") throw E(C("bad-input"), "tls.servername must be a string, got " + guard.text.showValue(sni));
|
|
636
|
+
if (sni && !_isIP(sni)) options.servername = sni;
|
|
583
637
|
var caList = [];
|
|
584
638
|
if (hasAnchors) caList = caList.concat(_pemifyAnchors(anchors));
|
|
585
639
|
if (useSystem) caList = caList.concat(_systemCa());
|
|
@@ -588,10 +642,18 @@ function httpsTransport(defaults) {
|
|
|
588
642
|
if (key) options.key = key;
|
|
589
643
|
if (callerCsi) {
|
|
590
644
|
options.checkServerIdentity = function (host, cert2) {
|
|
591
|
-
var baseErr =
|
|
645
|
+
var baseErr = _checkServerIdentity(host, cert2);
|
|
592
646
|
if (baseErr) return baseErr;
|
|
593
647
|
return callerCsi(host, cert2);
|
|
594
648
|
};
|
|
649
|
+
} else {
|
|
650
|
+
/** @internal With no caller hook, the option is set to the CAPTURE rather than left out. Left
|
|
651
|
+
* out, node's own TLS stack looks the function up on its module export when the handshake
|
|
652
|
+
* completes, so a replacement there answers the identity question and a certificate issued to
|
|
653
|
+
* another host is accepted. Measured: with this absent, a request asking for a name the
|
|
654
|
+
* server's certificate does not carry was admitted. The function is node's own default, taken
|
|
655
|
+
* at load, so the verdict is the one it would have reached anyway. */
|
|
656
|
+
options.checkServerIdentity = _checkServerIdentity;
|
|
595
657
|
}
|
|
596
658
|
var proxyInfo;
|
|
597
659
|
try { proxyInfo = _validateProxy(request, defaults, E, C, host, statedPort, scheme); }
|
|
@@ -649,7 +711,10 @@ function httpsTransport(defaults) {
|
|
|
649
711
|
var client = prep.plaintext ? nodeHttp : nodeHttps;
|
|
650
712
|
req = client.request(options, function (res) {
|
|
651
713
|
var tlsInfo = _tlsInfo(res.socket);
|
|
652
|
-
|
|
714
|
+
/** @internal Captured for consistency with the rest of the file, not because this gates
|
|
715
|
+
* anything: an understated `content-length` only skips this EARLY refusal, and the cap is
|
|
716
|
+
* enforced below on the bytes actually counted as they arrive. */
|
|
717
|
+
var declared = intrinsic.parseInt((res.headers || {})["content-length"], 10);
|
|
653
718
|
if (Number.isFinite(declared) && declared > prep.maxBytes) { fail(C("response-too-large"), "the declared content-length " + declared + " exceeds the " + prep.maxBytes + "-byte cap (RFC 7030 sec. 6)"); return; }
|
|
654
719
|
var buf = Buffer.allocUnsafe(0);
|
|
655
720
|
var len = 0;
|
|
@@ -727,8 +792,8 @@ function httpsTransport(defaults) {
|
|
|
727
792
|
connectOpts.rejectUnauthorized = true;
|
|
728
793
|
connectOpts.minVersion = pinfo.proxyTls.minVersion;
|
|
729
794
|
var pIdentity = pinfo.proxyTls.servername || pinfo.host;
|
|
730
|
-
connectOpts.servername = (pIdentity && !
|
|
731
|
-
connectOpts.checkServerIdentity = function (host, cert) { return
|
|
795
|
+
connectOpts.servername = (pIdentity && !_isIP(pIdentity)) ? pIdentity : "";
|
|
796
|
+
connectOpts.checkServerIdentity = function (host, cert) { return _checkServerIdentity(pIdentity, cert); };
|
|
732
797
|
cr = nodeHttps.request(connectOpts);
|
|
733
798
|
} else {
|
|
734
799
|
cr = nodeHttp.request(connectOpts);
|
|
@@ -775,7 +840,7 @@ function httpsTransport(defaults) {
|
|
|
775
840
|
minVersion: prep.options.minVersion,
|
|
776
841
|
rejectUnauthorized: true,
|
|
777
842
|
checkServerIdentity: function (host, cert) {
|
|
778
|
-
var baseErr =
|
|
843
|
+
var baseErr = _checkServerIdentity(idHost, cert);
|
|
779
844
|
if (baseErr) return baseErr;
|
|
780
845
|
return prep.callerCsi ? prep.callerCsi(idHost, cert) : undefined;
|
|
781
846
|
},
|
package/lib/identity-match.js
CHANGED
|
@@ -212,7 +212,7 @@ var _SNAPSHOTS = new WeakSet();
|
|
|
212
212
|
/** @internal Whether this list is one `snapshotReferences` produced. The record is kept off the
|
|
213
213
|
* value, so a caller cannot make an arbitrary array pass by writing a field on it. */
|
|
214
214
|
function _isSnapshot(v) {
|
|
215
|
-
return _isArray(v) &&
|
|
215
|
+
return _isArray(v) && intrinsic.weakSetHas(_SNAPSHOTS, v);
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
/** @internal The reference identities, read and copied in one pass, so a caller that mutates its
|
|
@@ -233,7 +233,7 @@ function snapshotReferences(references) {
|
|
|
233
233
|
intrinsic.freeze(parsed[i]);
|
|
234
234
|
}
|
|
235
235
|
intrinsic.freeze(parsed);
|
|
236
|
-
|
|
236
|
+
intrinsic.weakSetAdd(_SNAPSHOTS, parsed);
|
|
237
237
|
return parsed;
|
|
238
238
|
}
|
|
239
239
|
|
package/lib/merkle.js
CHANGED
|
@@ -516,7 +516,7 @@ function consistencyProof(opts) {
|
|
|
516
516
|
return _subproof(leaves, 0, n, m, true);
|
|
517
517
|
}
|
|
518
518
|
|
|
519
|
-
module.exports = {
|
|
519
|
+
module.exports = intrinsic.freeze({
|
|
520
520
|
leafHash: leafHash,
|
|
521
521
|
nodeHash: nodeHash,
|
|
522
522
|
emptyRootHash: emptyRootHash,
|
|
@@ -525,4 +525,4 @@ module.exports = {
|
|
|
525
525
|
verifyConsistency: verifyConsistency,
|
|
526
526
|
inclusionProof: inclusionProof,
|
|
527
527
|
consistencyProof: consistencyProof,
|
|
528
|
-
};
|
|
528
|
+
});
|
|
@@ -12,4 +12,4 @@ function verifyRequestSignature(signatureAlgorithm, signature, signerSpkiBytes,
|
|
|
12
12
|
return _seam.verify(signatureAlgorithm, signature, signerSpkiBytes, tbsRequestBytes);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
module.exports = { setEngine: _seam.setEngine, verifyRequestSignature: verifyRequestSignature };
|
|
15
|
+
module.exports = Object.freeze({ setEngine: _seam.setEngine, verifyRequestSignature: verifyRequestSignature });
|