@blamejs/pki 0.8.31 → 0.8.49
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 +401 -0
- package/README.md +66 -15
- package/bin/pki.js +716 -30
- package/examples/issue-a-certificate.js +90 -0
- package/examples/post-quantum.js +66 -0
- package/examples/read-a-certificate.js +56 -0
- package/examples/revoke-and-check.js +86 -0
- package/examples/sign-and-verify-cms.js +63 -0
- package/index.js +18 -5
- package/lib/acme.js +223 -74
- package/lib/alt-sig.js +441 -0
- package/lib/asn1-der.js +48 -25
- package/lib/attrcert-sign.js +27 -19
- package/lib/cbor-det.js +24 -14
- package/lib/cmc-build.js +9 -2
- package/lib/cmc-verify.js +14 -3
- package/lib/cmp-build.js +17 -5
- package/lib/cmp-session.js +24 -11
- package/lib/cmp-verify.js +28 -19
- package/lib/cms-compress.js +3 -1
- package/lib/cms-decrypt.js +102 -7
- package/lib/cms-encrypt.js +59 -7
- package/lib/cms-sign.js +31 -9
- package/lib/cms-verify.js +22 -18
- package/lib/composite-kem.js +30 -18
- package/lib/composite-sig.js +29 -14
- package/lib/constants.js +26 -0
- package/lib/crl-sign.js +111 -19
- package/lib/crmf-sign.js +167 -15
- package/lib/csr-sign.js +117 -6
- package/lib/ct.js +1008 -58
- package/lib/est.js +29 -16
- package/lib/framework-error.js +12 -0
- package/lib/guard-async.js +1 -1
- package/lib/guard-bytes.js +103 -20
- package/lib/guard-crypto.js +205 -3
- package/lib/guard-encoding.js +6 -1
- package/lib/guard-identifier.js +55 -8
- package/lib/guard-intrinsic.js +43 -0
- package/lib/guard-json.js +122 -9
- package/lib/guard-limits.js +12 -1
- package/lib/guard-list.js +63 -8
- package/lib/guard-name.js +167 -20
- package/lib/guard-range.js +43 -5
- package/lib/guard-text.js +41 -0
- package/lib/guard-verdict.js +39 -8
- package/lib/hpke.js +688 -42
- package/lib/http-transport.js +156 -4
- package/lib/identity-match.js +4 -4
- package/lib/inspect.js +33 -6
- package/lib/jose.js +93 -25
- package/lib/key.js +18 -13
- package/lib/lint.js +165 -1
- package/lib/merkle.js +233 -27
- package/lib/ocsp.js +27 -14
- package/lib/oid.js +14 -4
- package/lib/path-validate.js +143 -61
- package/lib/pkcs12-build.js +7 -2
- package/lib/pki-build.js +31 -7
- package/lib/possession.js +575 -0
- package/lib/related-cert.js +708 -0
- package/lib/revocation-fetch.js +6 -6
- package/lib/scep.js +15 -5
- package/lib/schema-c509.js +5 -2
- package/lib/schema-cmc.js +1 -1
- package/lib/schema-cmp.js +8 -8
- package/lib/schema-cms.js +2 -15
- package/lib/schema-csr.js +69 -0
- package/lib/schema-engine.js +10 -3
- package/lib/schema-pkix.js +228 -2
- package/lib/schema-tsp.js +7 -6
- package/lib/sign-scheme.js +22 -9
- package/lib/sigstore.js +684 -168
- package/lib/sleep.js +8 -1
- package/lib/smime.js +22 -1
- package/lib/tlog.js +1235 -0
- package/lib/tls-cert-compress.js +25 -8
- package/lib/tsp-sign.js +120 -35
- package/lib/tuf.js +1395 -0
- package/lib/validator-tpm.js +4 -1
- package/lib/verify-seam.js +7 -1
- package/lib/webauthn-mds.js +41 -4
- package/lib/webauthn.js +43 -25
- package/lib/webcrypto.js +48 -46
- package/lib/x509-sign.js +225 -19
- package/package.json +2 -1
- package/sbom.cdx.json +6 -6
package/lib/guard-list.js
CHANGED
|
@@ -6,15 +6,43 @@
|
|
|
6
6
|
var _freeze = require("./guard-intrinsic").freeze;
|
|
7
7
|
var _defineProperty = require("./guard-intrinsic").defineProperty;
|
|
8
8
|
var _isArray = require("./guard-intrinsic").isArray;
|
|
9
|
+
var _arrSort = require("./guard-intrinsic").sort;
|
|
10
|
+
var _isSafeInteger = require("./guard-intrinsic").isSafeInteger;
|
|
9
11
|
|
|
10
12
|
var MAX_ARRAY_LENGTH = 4294967295;
|
|
11
13
|
|
|
14
|
+
/** @internal The receiver's length, NARROWED to a number rather than merely captured. `Array.isArray`
|
|
15
|
+
* is true for a Proxy whose target is an array, and a `get` trap can answer `length` with an OBJECT:
|
|
16
|
+
* capturing an object captures no value, so every later use coerces it again and each coercion may
|
|
17
|
+
* answer differently. MEASURED through `pki.merkle.root`, whose captured count was handed to the leaf
|
|
18
|
+
* cap as 0 and then walked as 4, returning a four-leaf root the cap had approved as empty and clearing
|
|
19
|
+
* `MERKLE_MAX_LEAVES` the same way. A length that is not already a non-negative safe integer describes
|
|
20
|
+
* no array any of these walks can take, so it is refused rather than coerced. Every walk below takes
|
|
21
|
+
* its count from here, so none of them can hold a value that changes between two reads.
|
|
22
|
+
*
|
|
23
|
+
* Exported, because the walks in `guard-bytes` and `guard-identifier` take a caller's list too, and one
|
|
24
|
+
* of them is a DOOR: a Proxy over `["a", "secret"]` whose length coerced to 2 and then to 0 walked
|
|
25
|
+
* `refuseAccessorFields` past the accessor named `secret` and returned as though it had checked it. A
|
|
26
|
+
* door that can be walked past is not a door, so the three families share this one narrow.
|
|
27
|
+
*
|
|
28
|
+
* @enforced-by behavioral -- `x.length` has no rename-proof shape that separates a caller's list from
|
|
29
|
+
* a local array, and the RED vectors are the shrinking and object-valued lengths each walk is driven
|
|
30
|
+
* with.
|
|
31
|
+
*/
|
|
32
|
+
function _count(list, verb) {
|
|
33
|
+
var n = list.length;
|
|
34
|
+
if (typeof n !== "number" || !_isSafeInteger(n) || n < 0) {
|
|
35
|
+
throw new TypeError("guard.list." + verb + ": the receiver's length is not a non-negative integer");
|
|
36
|
+
}
|
|
37
|
+
return n;
|
|
38
|
+
}
|
|
39
|
+
|
|
12
40
|
// @enforced-by guard-shape-reinlined
|
|
13
41
|
// @guard-shape defineProperty\(\s*\w+\s*,\s*\w+\.length\s*,\s*\{\s*value:
|
|
14
42
|
// @guard-via guard\.list\.append\(
|
|
15
43
|
function append(list, value) {
|
|
16
44
|
if (!_isArray(list)) throw new TypeError("guard.list.append: the receiver must be an array");
|
|
17
|
-
var n = list
|
|
45
|
+
var n = _count(list, "append");
|
|
18
46
|
if (n >= MAX_ARRAY_LENGTH) throw new RangeError("guard.list.append: the array is at its maximum length");
|
|
19
47
|
_defineProperty(list, n, { value: value, writable: true, enumerable: true, configurable: true });
|
|
20
48
|
return list;
|
|
@@ -23,35 +51,35 @@ function append(list, value) {
|
|
|
23
51
|
// @enforced-by behavioral -- `.indexOf(x) !== -1` has no rename-proof form that separates a
|
|
24
52
|
function contains(list, value) {
|
|
25
53
|
if (!list) return false;
|
|
26
|
-
for (var i = 0, n = list
|
|
54
|
+
for (var i = 0, n = _count(list, "contains"); i < n; i++) if (list[i] === value) return true;
|
|
27
55
|
return false;
|
|
28
56
|
}
|
|
29
57
|
|
|
30
58
|
// @enforced-by behavioral -- the composition of contains() has no rename-proof shape of its own;
|
|
31
59
|
function containsAll(list, values) {
|
|
32
60
|
if (!values) return true;
|
|
33
|
-
for (var i = 0, n = values
|
|
61
|
+
for (var i = 0, n = _count(values, "containsAll"); i < n; i++) if (!contains(list, values[i])) return false;
|
|
34
62
|
return true;
|
|
35
63
|
}
|
|
36
64
|
|
|
37
65
|
// @enforced-by behavioral -- `.some(` has legitimate non-rule siblings (building a list, reporting
|
|
38
66
|
function anyMatches(list, predicate) {
|
|
39
67
|
if (!list) return false;
|
|
40
|
-
for (var i = 0, n = list
|
|
68
|
+
for (var i = 0, n = _count(list, "anyMatches"); i < n; i++) if (predicate(list[i], i)) return true;
|
|
41
69
|
return false;
|
|
42
70
|
}
|
|
43
71
|
|
|
44
72
|
// @enforced-by behavioral -- see anyMatches; the RED vector (an untrusted signer keeps
|
|
45
73
|
function allMatch(list, predicate) {
|
|
46
74
|
if (!list) return true;
|
|
47
|
-
for (var i = 0, n = list
|
|
75
|
+
for (var i = 0, n = _count(list, "allMatch"); i < n; i++) if (!predicate(list[i], i)) return false;
|
|
48
76
|
return true;
|
|
49
77
|
}
|
|
50
78
|
|
|
51
79
|
// @enforced-by caller-array-copied-live
|
|
52
80
|
function snapshot(list) {
|
|
53
81
|
if (!_isArray(list)) throw new TypeError("guard.list.snapshot: the receiver must be an array");
|
|
54
|
-
var n = list
|
|
82
|
+
var n = _count(list, "snapshot");
|
|
55
83
|
var out = [];
|
|
56
84
|
for (var i = 0; i < n; i++) {
|
|
57
85
|
_defineProperty(out, i, { value: list[i], writable: true, enumerable: true, configurable: true });
|
|
@@ -59,11 +87,36 @@ function snapshot(list) {
|
|
|
59
87
|
return out;
|
|
60
88
|
}
|
|
61
89
|
|
|
90
|
+
/** @internal A sorted COPY, taken and ordered through the captured operations. A DER SET OF is ordered
|
|
91
|
+
* by its encoded bytes, so the sort decides the bytes emitted and the comparison decides the order:
|
|
92
|
+
* MEASURED, replacing either `Array.prototype.slice` or `Array.prototype.sort` made a certification
|
|
93
|
+
* request's attribute set encode a member other than the one that had just been validated, on a path
|
|
94
|
+
* whose output is then signed. The snapshot comes first, so the caller's array is never reordered.
|
|
95
|
+
*
|
|
96
|
+
* @enforced-by behavioral -- `x.slice().sort(cmp)` has no rename-proof shape of its own beyond the two
|
|
97
|
+
* method names, and the conformance vector is the SET-OF ordering one each format already carries.
|
|
98
|
+
*/
|
|
99
|
+
function sortedCopy(list, compare) {
|
|
100
|
+
var out = snapshot(list);
|
|
101
|
+
return _arrSort(out, compare);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** @internal `onCount`, where a caller passes one, is handed the count this walk will use, before any
|
|
105
|
+
* slot is read. It is how a caller caps the length WITHOUT reading it a second time. A caller that read
|
|
106
|
+
* `list.length` itself to cap it and then called this had two reads of one quantity, and they can
|
|
107
|
+
* disagree: `Array.isArray` is true for a Proxy whose target is an array, and a `get` trap answers
|
|
108
|
+
* "length" afresh each time, so MEASURED, a cap that saw 0 passed while the walk below saw 1 and the
|
|
109
|
+
* caller committed to a one-element result the cap had approved as empty. The caller keeps its own typed
|
|
110
|
+
* error and its own message; only the number is the guard's to hand over. */
|
|
62
111
|
// @enforced-by caller-array-copied-live
|
|
63
|
-
function copyMap(list, fn) {
|
|
112
|
+
function copyMap(list, fn, onCount) {
|
|
64
113
|
if (!_isArray(list)) throw new TypeError("guard.list.copyMap: the receiver must be an array");
|
|
65
114
|
if (typeof fn !== "function") throw new TypeError("guard.list.copyMap: the second argument must be a function");
|
|
66
|
-
|
|
115
|
+
if (onCount !== undefined && typeof onCount !== "function") {
|
|
116
|
+
throw new TypeError("guard.list.copyMap: onCount must be a function when supplied");
|
|
117
|
+
}
|
|
118
|
+
var n = _count(list, "copyMap");
|
|
119
|
+
if (onCount !== undefined) onCount(n);
|
|
67
120
|
var out = [];
|
|
68
121
|
for (var i = 0; i < n; i++) {
|
|
69
122
|
_defineProperty(out, i, { value: fn(list[i], i), writable: true, enumerable: true, configurable: true });
|
|
@@ -72,7 +125,9 @@ function copyMap(list, fn) {
|
|
|
72
125
|
}
|
|
73
126
|
|
|
74
127
|
module.exports = _freeze({
|
|
128
|
+
count: _count,
|
|
75
129
|
append: append,
|
|
130
|
+
sortedCopy: sortedCopy,
|
|
76
131
|
contains: contains,
|
|
77
132
|
containsAll: containsAll,
|
|
78
133
|
anyMatches: anyMatches,
|
package/lib/guard-name.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// @internal
|
|
5
5
|
|
|
6
6
|
var intrinsic = require("./guard-intrinsic");
|
|
7
|
-
var
|
|
7
|
+
var guardList = require("./guard-list");
|
|
8
|
+
var _append = guardList.append;
|
|
8
9
|
var _assertCanonicalOid = require("./guard-identifier").assertCanonicalOid;
|
|
9
10
|
var _ipUtils = require("./ip-utils");
|
|
10
11
|
var _isIPv4 = _ipUtils.isIPv4;
|
|
@@ -33,6 +34,12 @@ var _getOwnPropertyDescriptor = intrinsic.getOwnPropertyDescriptor;
|
|
|
33
34
|
|
|
34
35
|
// @enforced-by behavioral -- the control-byte reject has no rename-proof code
|
|
35
36
|
function assertNoControlBytes(str, E, code, label) {
|
|
37
|
+
/** @internal A PRIMITIVE string, required rather than assumed. Every lib caller passes one, but this
|
|
38
|
+
* is exported, and on anything else the scan reads a `length` and a character through hooks that can
|
|
39
|
+
* answer differently on each of the two reads per position: a scan that is handed an allowed
|
|
40
|
+
* character at each position it tests never examines one stable value. A primitive's length and
|
|
41
|
+
* characters cannot change, which is what makes every read below the same read. */
|
|
42
|
+
if (typeof str !== "string") throw E(code, label + " must be a string to be scanned for control bytes");
|
|
36
43
|
for (var i = 0; i < str.length; i++) {
|
|
37
44
|
var c = _charCodeAt(str, i);
|
|
38
45
|
if (c === 0 || (c < 0x20 && c !== 0x09)) {
|
|
@@ -46,7 +53,11 @@ function assertNoControlBytes(str, E, code, label) {
|
|
|
46
53
|
function assertPrintableIa5(buf, E, code, label) {
|
|
47
54
|
var n = intrinsic.sizeOf(buf);
|
|
48
55
|
for (var i = 0; i < n; i++) {
|
|
49
|
-
|
|
56
|
+
/** @internal ONE read per position. Written `buf[i] < 0x20 || buf[i] > 0x7e` the byte was read
|
|
57
|
+
* twice whenever the first test was false, so two answers could pass both tests although neither
|
|
58
|
+
* is printable: a high answer clears the low bound and a low answer clears the high one. */
|
|
59
|
+
var byte = buf[i];
|
|
60
|
+
if (byte < 0x20 || byte > 0x7e) {
|
|
50
61
|
throw E(code, label + " must be a printable IA5String (no control bytes)");
|
|
51
62
|
}
|
|
52
63
|
}
|
|
@@ -66,29 +77,51 @@ function _canonAttrValue(v, E, code, label) {
|
|
|
66
77
|
}
|
|
67
78
|
return _toLowerCase(out);
|
|
68
79
|
}
|
|
80
|
+
/** @internal Both lengths are NARROWED where they are taken, and every test and walk below asks the
|
|
81
|
+
* local. `Array.isArray` is true for a Proxy whose target is an array and a `get` trap answers `length`
|
|
82
|
+
* afresh on each read, so the equality test and the walk that follows it could see two different counts:
|
|
83
|
+
* a length answering equal to its partner and then shorter had two names that differ in a later element
|
|
84
|
+
* compare EQUAL, and this comparison is what chains a certificate to its issuer, matches a CRL to its
|
|
85
|
+
* scope and applies a name constraint. Returned as a pair so the caller holds one count per side. */
|
|
69
86
|
function _assertSequence(a, b, E, code, label, what) {
|
|
70
87
|
if (!_isArray(a) || !_isArray(b)) {
|
|
71
88
|
throw E(code, "cannot compare " + label + ": " + what + " comparison requires the RDN sequence on both sides (pass name.rdns, not the parsed Name)");
|
|
72
89
|
}
|
|
73
|
-
|
|
90
|
+
var na = guardList.count(a, "name comparison");
|
|
91
|
+
var nb = guardList.count(b, "name comparison");
|
|
92
|
+
if (_hasHole(a, na) || _hasHole(b, nb)) {
|
|
74
93
|
throw E(code, "cannot compare " + label + ": " + what + " comparison requires a dense sequence on both sides");
|
|
75
94
|
}
|
|
95
|
+
return { a: na, b: nb };
|
|
76
96
|
}
|
|
77
97
|
|
|
78
|
-
function _hasHole(arr) {
|
|
79
|
-
for (var i = 0; i <
|
|
98
|
+
function _hasHole(arr, n) {
|
|
99
|
+
for (var i = 0; i < n; i++) { if (!_hasOwn(arr, i)) return true; }
|
|
80
100
|
return false;
|
|
81
101
|
}
|
|
82
102
|
// @enforced-by guard-shape-reinlined (shares the canonicalization shape declared on dnEqual)
|
|
83
103
|
function rdnEqual(a, b, E, code, label) {
|
|
84
|
-
_assertSequence(a, b, E, code, label, "an RDN");
|
|
85
|
-
if (a
|
|
104
|
+
var n = _assertSequence(a, b, E, code, label, "an RDN");
|
|
105
|
+
if (n.a !== n.b) return false;
|
|
86
106
|
var used = _create(null);
|
|
87
|
-
for (var i = 0; i < a
|
|
107
|
+
for (var i = 0; i < n.a; i++) {
|
|
108
|
+
/** @internal ONE read of the element, and one of each field, into locals the comparison then asks.
|
|
109
|
+
* Written `a[i].type === b[j].type && canon(a[i].value) === canon(b[j].value)` the element was read
|
|
110
|
+
* TWICE per side, so a Proxy answering a different attribute on each index read had the type come
|
|
111
|
+
* from one attribute and the value from another: measured, an RDN whose first read answered
|
|
112
|
+
* `{2.5.4.3, "evil"}` and whose second answered `{2.5.4.10, "good"}` compared EQUAL to
|
|
113
|
+
* `{2.5.4.3, "good"}`, matching neither. A name that equals a name it does not hold is this
|
|
114
|
+
* comparison's one unacceptable answer, since it is what chains a certificate to its issuer. */
|
|
115
|
+
var ea = a[i];
|
|
116
|
+
var typeA = ea == null ? undefined : ea.type;
|
|
117
|
+
var valueA = _canonAttrValue(ea == null ? undefined : ea.value, E, code, label);
|
|
88
118
|
var found = false;
|
|
89
|
-
for (var j = 0; j < b
|
|
119
|
+
for (var j = 0; j < n.b; j++) {
|
|
90
120
|
if (used[j]) continue;
|
|
91
|
-
|
|
121
|
+
var eb = b[j];
|
|
122
|
+
var typeB = eb == null ? undefined : eb.type;
|
|
123
|
+
if (typeA !== typeB) continue;
|
|
124
|
+
if (valueA === _canonAttrValue(eb == null ? undefined : eb.value, E, code, label)) {
|
|
92
125
|
used[j] = true; found = true; break;
|
|
93
126
|
}
|
|
94
127
|
}
|
|
@@ -99,9 +132,9 @@ function rdnEqual(a, b, E, code, label) {
|
|
|
99
132
|
// @enforced-by guard-shape-reinlined
|
|
100
133
|
// @guard-shape replace\(/\\s\+/g,
|
|
101
134
|
function dnEqual(rdnsA, rdnsB, E, code, label) {
|
|
102
|
-
_assertSequence(rdnsA, rdnsB, E, code, label, "a distinguished-name");
|
|
103
|
-
if (
|
|
104
|
-
for (var i = 0; i <
|
|
135
|
+
var n = _assertSequence(rdnsA, rdnsB, E, code, label, "a distinguished-name");
|
|
136
|
+
if (n.a !== n.b) return false;
|
|
137
|
+
for (var i = 0; i < n.a; i++) {
|
|
105
138
|
if (!rdnEqual(rdnsA[i], rdnsB[i], E, code, label)) return false;
|
|
106
139
|
}
|
|
107
140
|
return true;
|
|
@@ -331,6 +364,11 @@ function _allCharsIn(s, table) {
|
|
|
331
364
|
return true;
|
|
332
365
|
}
|
|
333
366
|
|
|
367
|
+
/** @internal The one trailing dot a host name may be written with, removed before it is compared.
|
|
368
|
+
*
|
|
369
|
+
* @enforced-by behavioral -- reading the last character and slicing it off is the shape of any string
|
|
370
|
+
* trim; the guard is the vector that a base written with the dot and a name written without it reach the
|
|
371
|
+
* same verdict, and that a name of one dot alone does not become the empty string */
|
|
334
372
|
function stripTrailingDot(s) { return _charAt(s, s.length - 1) === "." ? _strSlice(s, 0, -1) : s; }
|
|
335
373
|
|
|
336
374
|
/** @internal Whether `s` opens with `scheme` followed by a colon, the scheme compared without
|
|
@@ -349,7 +387,11 @@ function uriSchemeIs(s, scheme) {
|
|
|
349
387
|
* label is a host like any other and `com` constrains a top-level domain. The host is read by its
|
|
350
388
|
* labels rather than by which characters it holds: a character test admits an empty label and a
|
|
351
389
|
* hyphen at a label edge, and the suffix comparison below would then match a base against a name
|
|
352
|
-
* that is not a name.
|
|
390
|
+
* that is not a name.
|
|
391
|
+
*
|
|
392
|
+
* @enforced-by behavioral -- the rule is which strings are host names and which are addresses, which no
|
|
393
|
+
* code shape states; the guard is the vector set driving a dotted-quad, a single label, an empty label and
|
|
394
|
+
* a hyphen at a label edge through a URI name constraint */
|
|
353
395
|
function isFqdnHost(host) {
|
|
354
396
|
if (typeof host !== "string") return false;
|
|
355
397
|
var h = stripTrailingDot(host);
|
|
@@ -636,6 +678,88 @@ function uriEqual(a, b) {
|
|
|
636
678
|
return pa === pb ? "match" : "no-match";
|
|
637
679
|
}
|
|
638
680
|
|
|
681
|
+
/** @internal Whether two dNSName values name the same host. RFC 4343 makes DNS comparison
|
|
682
|
+
* case-insensitive, and RFC 5280 sec. 4.2.1.6 puts a dNSName in the preferred name syntax, so the value
|
|
683
|
+
* is ASCII and folding its case is the whole comparison. A value carrying a byte above 0x7f is not that
|
|
684
|
+
* syntax and is "not-comparable" rather than compared as written.
|
|
685
|
+
*
|
|
686
|
+
* Case is the only thing folded. A trailing dot is a different encoded value and stays one here, unlike in
|
|
687
|
+
* the name-constraint readers, where `stripTrailingDot` removes the dot an operator writes a base with:
|
|
688
|
+
* this compares what two artifacts encode, and folding the dot would report a match their bytes do not
|
|
689
|
+
* support.
|
|
690
|
+
*
|
|
691
|
+
* @enforced-by behavioral -- the case fold is `lowerAscii`, whose own shape detector catches a re-inlined
|
|
692
|
+
* 0x41..0x5a arithmetic anywhere in lib/; what this adds is applying it to a whole dNSName, which no shape
|
|
693
|
+
* separates from any other string comparison, and the guard is the pair differing only in case that
|
|
694
|
+
* matches beside the pair differing in a label that does not */
|
|
695
|
+
function dnsNameEqual(a, b) {
|
|
696
|
+
if (typeof a !== "string" || typeof b !== "string") return "not-comparable";
|
|
697
|
+
if (!_asciiHost(a) || !_asciiHost(b)) return "not-comparable";
|
|
698
|
+
return lowerAscii(a) === lowerAscii(b) ? "match" : "no-match";
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/** @internal Whether two decoded GeneralName entries name the same thing, each form under its own rule.
|
|
702
|
+
* Takes the entries `pki.schema.x509.decodeExtensions` puts in a subjectAltName's `names`, keyed by the
|
|
703
|
+
* context tag number RFC 5280 sec. 4.2.1.6 assigns the form.
|
|
704
|
+
*
|
|
705
|
+
* Rendering every form to a string and comparing the renderings reads two names as different that name
|
|
706
|
+
* one entity, and two as the same that do not. A dNSName is case-insensitive, an rfc822Name is
|
|
707
|
+
* case-insensitive in its host and case-sensitive in its local part, a URI needs the sec. 7.4 preparation
|
|
708
|
+
* before an exact match, a directoryName needs the sec. 7.1 attribute-by-attribute comparison, and an
|
|
709
|
+
* iPAddress is bytes. Comparing renderings answers all five the same way, so `EXAMPLE.com` and
|
|
710
|
+
* `example.com` read as two hosts while a re-encoded but equivalent DN reads as two names.
|
|
711
|
+
*
|
|
712
|
+
* Returns "match", "no-match", or "not-comparable" for a form whose values this cannot hold to its rule,
|
|
713
|
+
* which the caller reports rather than resolving either way. A form with no comparison rule of its own,
|
|
714
|
+
* including otherName, compares by its encoded bytes: two such entries are equal when they are the same
|
|
715
|
+
* bytes and otherwise unequal, which is the only verdict available without knowing the form's semantics.
|
|
716
|
+
*
|
|
717
|
+
* `E`, `code` and `label` are the caller's, for the directoryName arm, which holds a malformed rdns
|
|
718
|
+
* sequence to the caller's own domain.
|
|
719
|
+
*
|
|
720
|
+
* @enforced-by behavioral -- a switch over GeneralName tag numbers is the shape of every reader of the
|
|
721
|
+
* type, so a pattern matching it cannot tell a comparison from a decode; the guard is the matrix of
|
|
722
|
+
* thirteen vectors, one pair per form in each direction, where the pair differing only where its rule folds
|
|
723
|
+
* MATCHES and the pair differing where its rule is exact does NOT. A single arm would pass for a
|
|
724
|
+
* comparison that folded case everywhere, which is the opposite defect.
|
|
725
|
+
*/
|
|
726
|
+
function generalNameEqual(a, b, E, code, label) {
|
|
727
|
+
if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return "not-comparable";
|
|
728
|
+
/** @internal Each member is read ONCE, and the tag, the dispatch and every comparison below ask the
|
|
729
|
+
* local. Read again per arm, the tag that was MATCHED need not be the tag that was DISPATCHED on: a
|
|
730
|
+
* getter reporting 8 to the equality test and 2 to the switch had two otherName values compared under
|
|
731
|
+
* the case-insensitive dNSName rule, which is looser than the rule their own tag names. The same
|
|
732
|
+
* shape sat on `value` and `bytes` in four arms, where the form check and the comparison were two
|
|
733
|
+
* reads: the check saw a Buffer and the comparison got whatever answered next. This verb decides
|
|
734
|
+
* whether a name matches a name constraint, so a match it reports is a subtree a certificate is
|
|
735
|
+
* allowed to speak for. */
|
|
736
|
+
var tagA = a.tagNumber, tagB = b.tagNumber;
|
|
737
|
+
if (tagA !== tagB) return "no-match";
|
|
738
|
+
var valueA = a.value, valueB = b.value;
|
|
739
|
+
switch (tagA) {
|
|
740
|
+
case 1: return emailEqual(valueA, valueB);
|
|
741
|
+
case 2: return dnsNameEqual(valueA, valueB);
|
|
742
|
+
case 4: {
|
|
743
|
+
var ra = valueA == null ? undefined : valueA.rdns;
|
|
744
|
+
var rb = valueB == null ? undefined : valueB.rdns;
|
|
745
|
+
if (!_isArray(ra) || !_isArray(rb)) return "not-comparable";
|
|
746
|
+
return dnEqual(ra, rb, E, code, label) ? "match" : "no-match";
|
|
747
|
+
}
|
|
748
|
+
case 6: return uriEqual(valueA, valueB);
|
|
749
|
+
case 7:
|
|
750
|
+
if (!_isBuffer(valueA) || !_isBuffer(valueB)) return "not-comparable";
|
|
751
|
+
return _bufferEquals(valueA, valueB) ? "match" : "no-match";
|
|
752
|
+
case 8:
|
|
753
|
+
if (typeof valueA !== "string" || typeof valueB !== "string") return "not-comparable";
|
|
754
|
+
return valueA === valueB ? "match" : "no-match";
|
|
755
|
+
default: {
|
|
756
|
+
var bytesA = a.bytes, bytesB = b.bytes;
|
|
757
|
+
if (!_isBuffer(bytesA) || !_isBuffer(bytesB)) return "not-comparable";
|
|
758
|
+
return _bufferEquals(bytesA, bytesB) ? "match" : "no-match";
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
639
763
|
var _ATEXT_TABLE = _charTable("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-/=?^_`{|}~");
|
|
640
764
|
|
|
641
765
|
/** @internal A quoted local part (RFC 5321 sec. 4.1.2 Quoted-string): printable ASCII between
|
|
@@ -693,7 +817,11 @@ function _isAddressLiteral(s) {
|
|
|
693
817
|
|
|
694
818
|
/** @internal The two halves of a mailbox, `null` when it carries no separator and the string
|
|
695
819
|
* "ambiguous" when it carries more than one. This is the split the rfc822Name comparison runs, so a
|
|
696
|
-
* constraint the comparison cannot separate is one this answers the same way.
|
|
820
|
+
* constraint the comparison cannot separate is one this answers the same way.
|
|
821
|
+
*
|
|
822
|
+
* @enforced-by behavioral -- splitting on a separator has no shape distinct from any other split; the
|
|
823
|
+
* guard is the vector that an address carrying two at-signs answers "ambiguous" rather than splitting on
|
|
824
|
+
* one of them, which is what a boundary using `indexOf` alone would do */
|
|
697
825
|
function splitMailbox(addr) {
|
|
698
826
|
var first = _indexOf(addr, "@");
|
|
699
827
|
if (first === -1) return null;
|
|
@@ -761,7 +889,11 @@ function constraintBaseUnreadable(tag, base) {
|
|
|
761
889
|
* form its tag names is not a narrower namespace: an empty dNSName base matches every name, and a
|
|
762
890
|
* base holding characters no host name can hold matches none, so the same value that reads as a
|
|
763
891
|
* restriction either lifts one or applies to nothing. The comparisons this mirrors are in
|
|
764
|
-
* lib/path-validate.js, and a base refused here is one they would answer with no verdict.
|
|
892
|
+
* lib/path-validate.js, and a base refused here is one they would answer with no verdict.
|
|
893
|
+
*
|
|
894
|
+
* @enforced-by behavioral -- a per-tag switch over name forms is the shape of every GeneralName reader in
|
|
895
|
+
* the toolkit, so no pattern tells a refusal from a decode; the guard is the vector set proving each tag's
|
|
896
|
+
* unenforceable base is refused where an anchor is built, an empty dNSName base among them */
|
|
765
897
|
function constraintBaseRefusal(tag, base) {
|
|
766
898
|
if (typeof base !== "string") return "must be a string for tag " + tag;
|
|
767
899
|
if (tag === 1) {
|
|
@@ -811,7 +943,12 @@ var _ATV_KEYS = ["type", "value", "name"];
|
|
|
811
943
|
* descriptors only. A Proxy answers its own reads and an accessor runs caller code, so either one
|
|
812
944
|
* at any level is refused before anything is copied: a name that decides what it says while it is
|
|
813
945
|
* being read can show a narrow value to a check and a wide one to the use, and where the name is a
|
|
814
|
-
* namespace restriction that is the difference between a certificate being refused and admitted.
|
|
946
|
+
* namespace restriction that is the difference between a certificate being refused and admitted.
|
|
947
|
+
*
|
|
948
|
+
* @enforced-by behavioral -- `getOwnPropertyDescriptor` is read legitimately across lib/, so a shape built
|
|
949
|
+
* on it would fire on every honest reader; the guard is the vector set handing an anchor builder a name
|
|
950
|
+
* whose attribute is an accessor, one inheriting from a Proxy, and one carrying an index hole, each refused
|
|
951
|
+
* before a value is copied */
|
|
815
952
|
function copyDnRdns(rdns, E, code, who) {
|
|
816
953
|
assertPlainDnRdns(rdns, E, code, who);
|
|
817
954
|
/** @internal A name of no relative names is a prefix of every name, so as a constraint it reaches
|
|
@@ -857,7 +994,13 @@ function copyDnRdns(rdns, E, code, who) {
|
|
|
857
994
|
/** @internal The shape check the copy above relies on. */
|
|
858
995
|
function assertPlainDnRdns(rdns, E, code, who) {
|
|
859
996
|
if (intrinsic.types.isProxy(rdns)) throw E(code, who + " must be a plain array, not a Proxy");
|
|
860
|
-
|
|
997
|
+
/** @internal An ARRAY, and its count narrowed where it is taken. The Proxy refusal above covers a
|
|
998
|
+
* Proxy; a plain object carrying a `length` GETTER is neither a Proxy nor an array, and it answered
|
|
999
|
+
* one count to this validation pass and another to the copy that follows, so the two passes covered
|
|
1000
|
+
* different elements and the copy carried what was never checked. */
|
|
1001
|
+
if (!_isArray(rdns)) throw E(code, who + " must be an array of relative distinguished names");
|
|
1002
|
+
var n = guardList.count(rdns, "assertPlainDnRdns");
|
|
1003
|
+
for (var i = 0; i < n; i++) {
|
|
861
1004
|
var rd = intrinsic.getOwnPropertyDescriptor(rdns, i);
|
|
862
1005
|
if (rd === undefined) continue;
|
|
863
1006
|
if (!_hasOwn(rd, "value")) throw E(code, who + " entry " + i + " must be a data property, not an accessor");
|
|
@@ -885,7 +1028,11 @@ function assertPlainDnRdns(rdns, E, code, who) {
|
|
|
885
1028
|
/** @internal The `rdns` of a directoryName base, captured the same way. An inherited Proxy answers
|
|
886
1029
|
* `in` through its has trap, which is caller code running inside a read meant to be getter-free, so
|
|
887
1030
|
* the prototype chain is walked before the property is asked for at all. Returns undefined when the
|
|
888
|
-
* base carries no rdns array, which the caller reads as "not a directoryName base".
|
|
1031
|
+
* base carries no rdns array, which the caller reads as "not a directoryName base".
|
|
1032
|
+
*
|
|
1033
|
+
* @enforced-by behavioral -- walking a prototype chain before a read has no shape distinct from any other
|
|
1034
|
+
* chain walk; the guard is the vector that a base inheriting from a Proxy is refused rather than having its
|
|
1035
|
+
* has trap answer the `in` test, which is the read this ordering exists to keep getter-free */
|
|
889
1036
|
function capturedRdns(base, E, code, who) {
|
|
890
1037
|
if (intrinsic.types.isProxy(base)) throw E(code, who + " must not be a Proxy");
|
|
891
1038
|
var baseProto = intrinsic.getPrototypeOf(base);
|
|
@@ -912,7 +1059,7 @@ module.exports = intrinsic.freeze({
|
|
|
912
1059
|
lowerAscii: lowerAscii, uriSchemeIs: uriSchemeIs,
|
|
913
1060
|
stripTrailingDot: stripTrailingDot, isFqdnHost: isFqdnHost, splitMailbox: splitMailbox,
|
|
914
1061
|
isHostName: isHostName, hostLabels: hostLabels, isXnLabel: isXnLabel, uriParts: uriParts,
|
|
915
|
-
uriEqual: uriEqual,
|
|
1062
|
+
uriEqual: uriEqual, dnsNameEqual: dnsNameEqual, generalNameEqual: generalNameEqual,
|
|
916
1063
|
constraintBaseRefusal: constraintBaseRefusal,
|
|
917
1064
|
constraintBaseUnreadable: constraintBaseUnreadable,
|
|
918
1065
|
});
|
package/lib/guard-range.js
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
// @internal
|
|
5
5
|
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
6
|
+
var intrinsic = require("./guard-intrinsic");
|
|
7
|
+
var _isSafeInteger = intrinsic.isSafeInteger;
|
|
8
|
+
var _freeze = intrinsic.freeze;
|
|
9
|
+
var _Number = intrinsic.Number;
|
|
10
|
+
var _BigInt = intrinsic.BigInt;
|
|
11
|
+
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
10
12
|
|
|
11
13
|
var UINT31_MAX = 2147483647n;
|
|
12
14
|
var SAFE_MAX = 9007199254740991n;
|
|
@@ -56,8 +58,43 @@ function uint64(value, E, code, label) {
|
|
|
56
58
|
return v;
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
var ASCII_ZERO = 48;
|
|
62
|
+
var ASCII_NINE = 57;
|
|
63
|
+
var UINT64_MAX_DIGITS = 20;
|
|
64
|
+
|
|
65
|
+
/** @internal An ASCII decimal integer arriving as TEXT: a C2SP checkpoint's tree-size line, or
|
|
66
|
+
* protojson, which encodes int64 and uint64 as JSON strings. `BigInt(s)` is the wrong tool here
|
|
67
|
+
* and silently converts "", " 12 ", "007", "+7", "-0", "0x10", "0b11" and "0o17"; the empty string
|
|
68
|
+
* becoming 0n turns a missing size into a claim about an empty tree. The scan accepts ASCII digits
|
|
69
|
+
* and nothing else, so a radix or sign notation added later is refused without being enumerated.
|
|
70
|
+
* UINT64_MAX_DIGITS is the width of 18446744073709551615, so a longer run is out of range before
|
|
71
|
+
* a value is built and a hostile run of digits never becomes a BigInt. */
|
|
72
|
+
// @enforced-by guard-shape-reinlined
|
|
73
|
+
// @guard-shape \* 10n \+
|
|
74
|
+
function decimalUint64(value, E, code, label) {
|
|
75
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
76
|
+
throw E(code, label + " must be a non-empty string holding an ASCII decimal integer");
|
|
77
|
+
}
|
|
78
|
+
if (value.length > 1 && _charCodeAt(value, 0) === ASCII_ZERO) {
|
|
79
|
+
throw E(code, label + " must not carry a leading zero");
|
|
80
|
+
}
|
|
81
|
+
if (value.length > UINT64_MAX_DIGITS) {
|
|
82
|
+
throw E(code, label + " must be a uint64 in [0, 2^64)");
|
|
83
|
+
}
|
|
84
|
+
var v = 0n;
|
|
85
|
+
for (var i = 0; i < value.length; i++) {
|
|
86
|
+
var c = _charCodeAt(value, i);
|
|
87
|
+
if (c < ASCII_ZERO || c > ASCII_NINE) {
|
|
88
|
+
throw E(code, label + " must be ASCII decimal digits with no sign, radix prefix or whitespace");
|
|
89
|
+
}
|
|
90
|
+
v = v * 10n + _BigInt(c - ASCII_ZERO);
|
|
91
|
+
}
|
|
92
|
+
if (v > UINT64_MAX) throw E(code, label + " must be a uint64 in [0, 2^64)");
|
|
93
|
+
return v;
|
|
94
|
+
}
|
|
95
|
+
|
|
59
96
|
// @enforced-by guard-shape-reinlined
|
|
60
|
-
// @guard-shape Number\.isSafeInteger\(\s*[A-Za-z_$][\w$]*\s*\)\s*\)\s*return BigInt
|
|
97
|
+
// @guard-shape Number\.isSafeInteger\(\s*[A-Za-z_$][\w$]*\s*\)\s*\)\s*return BigInt|typeof\s+[A-Za-z_$][\w$.\[\]]*\s*===\s*""\s*\?\s*[A-Za-z_$][\w$.\[\]]*\s*:\s*_?BigInt\(
|
|
61
98
|
function authoredInteger(value, E, code, label) {
|
|
62
99
|
if (typeof value === "bigint") return value;
|
|
63
100
|
if (typeof value === "number" && _isSafeInteger(value)) return _BigInt(value);
|
|
@@ -66,5 +103,6 @@ function authoredInteger(value, E, code, label) {
|
|
|
66
103
|
|
|
67
104
|
module.exports = _freeze({
|
|
68
105
|
int: int, uint31: uint31, positiveInt31: positiveInt31, uint64: uint64,
|
|
106
|
+
decimalUint64: decimalUint64,
|
|
69
107
|
authoredInteger: authoredInteger,
|
|
70
108
|
});
|
package/lib/guard-text.js
CHANGED
|
@@ -7,6 +7,7 @@ var bytes = require("./guard-bytes");
|
|
|
7
7
|
var intrinsic = require("./guard-intrinsic");
|
|
8
8
|
|
|
9
9
|
var _toString = intrinsic.uncurry(Buffer.prototype.toString);
|
|
10
|
+
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
10
11
|
var _decode = intrinsic.uncurry(TextDecoder.prototype.decode);
|
|
11
12
|
var _byteLength = Buffer.byteLength;
|
|
12
13
|
var _isInteger = Number.isInteger;
|
|
@@ -37,6 +38,45 @@ function decode(input, maxBytes, ErrorClass, spec) {
|
|
|
37
38
|
throw new ErrorClass(spec.badInput, spec.label + " expects a string or a byte source (Buffer / TypedArray / DataView / ArrayBuffer)");
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
/** A string is well-formed UTF-16 before it is converted to UTF-8 bytes. An unpaired surrogate has no
|
|
42
|
+
* UTF-8 encoding, and every conversion in the platform substitutes U+FFFD for it rather than failing:
|
|
43
|
+
* `Buffer.from(s, "utf8")`, `TextEncoder.encode` and `Buffer.byteLength` all do. So two strings that
|
|
44
|
+
* differ only in that code unit produce the SAME bytes, and wherever those bytes are signed, hashed or
|
|
45
|
+
* compared the substitution is a collision: a signature made over one verifies over the other, and the
|
|
46
|
+
* value a caller reads back is the other one. The string is scanned by code unit, per the project rule
|
|
47
|
+
* against regular expressions in lib/, and a well-formed pair is left alone, being an ordinary code
|
|
48
|
+
* point above the BMP.
|
|
49
|
+
*
|
|
50
|
+
* `label` names the string in the fault. The caller supplies its own ErrorClass and code so each
|
|
51
|
+
* boundary keeps its own domain/reason.
|
|
52
|
+
*
|
|
53
|
+
* The shape is a surrogate-boundary constant followed, inside the same function, by a throw: a range
|
|
54
|
+
* test that REFUSES. A function that tests the same range to MEASURE a string rather than to reject it
|
|
55
|
+
* is not this rule and is not matched, which is why the throw is part of the shape rather than the
|
|
56
|
+
* constants alone. Bounded by the function-closing brace rather than a character count, so it does not
|
|
57
|
+
* stop matching as a body grows.
|
|
58
|
+
*
|
|
59
|
+
* @enforced-by guard-shape-reinlined
|
|
60
|
+
* @guard-shape 0x[dD](?:[bB][fF][fF]|[cC]00)(?:(?!\n\})[\s\S])*?throw
|
|
61
|
+
*/
|
|
62
|
+
function assertWellFormedUtf16(s, ErrorClass, code, label) {
|
|
63
|
+
for (var i = 0; i < s.length; i++) {
|
|
64
|
+
var u = _charCodeAt(s, i);
|
|
65
|
+
if (u < 0xd800 || u > 0xdfff) continue;
|
|
66
|
+
if (u > 0xdbff) {
|
|
67
|
+
throw new ErrorClass(code, label + " has an unpaired low surrogate at index " + i +
|
|
68
|
+
"; it has no UTF-8 encoding and would be converted to U+FFFD, colliding with other strings");
|
|
69
|
+
}
|
|
70
|
+
var next = i + 1 < s.length ? _charCodeAt(s, i + 1) : -1;
|
|
71
|
+
if (next < 0xdc00 || next > 0xdfff) {
|
|
72
|
+
throw new ErrorClass(code, label + " has an unpaired high surrogate at index " + i +
|
|
73
|
+
"; it has no UTF-8 encoding and would be converted to U+FFFD, colliding with other strings");
|
|
74
|
+
}
|
|
75
|
+
i += 1;
|
|
76
|
+
}
|
|
77
|
+
return s;
|
|
78
|
+
}
|
|
79
|
+
|
|
40
80
|
// @enforced-by behavioral -- a safe error-message formatter has no rename-proof code shape; the RED vector (a BigInt or cyclic caller value renders to a typed error, never a native TypeError out of a raw JSON.stringify) is the guard.
|
|
41
81
|
function showValue(v) {
|
|
42
82
|
if (typeof v === "string") return intrinsic.stringify(v);
|
|
@@ -82,4 +122,5 @@ function _thrownFallback(e) {
|
|
|
82
122
|
|
|
83
123
|
module.exports = intrinsic.freeze({
|
|
84
124
|
decode: decode, showValue: showValue, keyOf: keyOf, describeThrown: describeThrown,
|
|
125
|
+
assertWellFormedUtf16: assertWellFormedUtf16,
|
|
85
126
|
});
|
package/lib/guard-verdict.js
CHANGED
|
@@ -20,17 +20,45 @@ function _copyOwn(out, src) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** @internal Resolving a promise reads `then` off the value, so an inherited accessor would run with
|
|
24
|
+
* the verdict as its receiver and could rewrite a field or hand the caller a different object. An own
|
|
25
|
+
* `then` ends that lookup, and is non-enumerable so keys, JSON, a spread and a deep-equality
|
|
26
|
+
* comparison do not see it.
|
|
27
|
+
*
|
|
28
|
+
* `shield` adds that sentinel to the object it is given. It is the form for a result whose SHAPE must
|
|
29
|
+
* survive: a parse result carrying nested structure, a row inside an array a caller indexes, an
|
|
30
|
+
* object whose prototype is null. `of` copies, which is right for a verdict assembled from a literal
|
|
31
|
+
* and wrong for anything whose non-enumerable members, accessors or identity a caller reads.
|
|
32
|
+
*
|
|
33
|
+
* @enforced-by behavioral -- the defect is the ABSENCE of this call at a construction site, which has
|
|
34
|
+
* no code shape of its own. The guard is the conformance vector per verb: a verdict-shaped object
|
|
35
|
+
* returned to a caller while an inherited `Object.prototype.then` is installed must still report the
|
|
36
|
+
* toolkit's answer.
|
|
37
|
+
*/
|
|
38
|
+
function shield(target) {
|
|
39
|
+
if (target === null || typeof target !== "object") return target;
|
|
40
|
+
/** @internal An own `then` the object already carries is left alone when it is not callable: the
|
|
41
|
+
* lookup ends there already, and overwriting it would DELETE a member. A parsed JSON document may
|
|
42
|
+
* carry a `then` of its own, and `{"then":42,"other":1}` round-tripped as `{"other":1}` when this
|
|
43
|
+
* replaced it unconditionally. A CALLABLE own `then` is the thenable itself rather than data, and
|
|
44
|
+
* JSON cannot produce one, so it is replaced. */
|
|
45
|
+
if (_hasOwn(target, "then")) {
|
|
46
|
+
var d = _getOwnPropertyDescriptor(target, "then");
|
|
47
|
+
/** @internal A DATA property only. An own accessor is replaced without being read: it answers each
|
|
48
|
+
* read separately, so one that returns a non-function now can return a function when the promise
|
|
49
|
+
* machinery looks, and reading it to decide would be the one read that does not count. */
|
|
50
|
+
if (d && _hasOwn(d, "value") && typeof d.value !== "function") return target;
|
|
51
|
+
}
|
|
52
|
+
_defineProperty(target, "then", { value: undefined, enumerable: false, configurable: true, writable: true });
|
|
53
|
+
return target;
|
|
54
|
+
}
|
|
55
|
+
|
|
23
56
|
// @enforced-by behavioral -- the defect is the ABSENCE of this call at a construction site, not a
|
|
24
57
|
function of(src, extras) {
|
|
25
58
|
var out = {};
|
|
26
59
|
_copyOwn(out, src);
|
|
27
60
|
if (extras != null) _copyOwn(out, extras);
|
|
28
|
-
|
|
29
|
-
* with the verdict as its receiver and could rewrite a field or hand the caller a different
|
|
30
|
-
* object. An own `then` ends that lookup, and is non-enumerable so keys, JSON, a spread and a
|
|
31
|
-
* deep-equality comparison do not see it. */
|
|
32
|
-
_defineProperty(out, "then", { value: undefined, enumerable: false, configurable: true, writable: true });
|
|
33
|
-
return out;
|
|
61
|
+
return shield(out);
|
|
34
62
|
}
|
|
35
63
|
|
|
36
64
|
// @enforced-by behavioral -- Object.hasOwn and hasOwnProperty are replaceable at any time, and a
|
|
@@ -41,7 +69,10 @@ function carries(obj, key) {
|
|
|
41
69
|
// @enforced-by behavioral -- for a target that cannot be rebuilt, an Error carrying a field a
|
|
42
70
|
function set(target, key, value) {
|
|
43
71
|
_defineProperty(target, key, { value: value, enumerable: true, configurable: true, writable: true });
|
|
44
|
-
|
|
72
|
+
/** @internal The target leaves here carrying the sentinel as well. A field set on an object is the
|
|
73
|
+
* same kind of value a verdict is, and a target that reached a promise resolution without one would
|
|
74
|
+
* hand the lookup to Object.prototype however it was assembled. */
|
|
75
|
+
return shield(target);
|
|
45
76
|
}
|
|
46
77
|
|
|
47
|
-
module.exports = _freeze({ of: of, carries: carries, set: set });
|
|
78
|
+
module.exports = _freeze({ of: of, shield: shield, carries: carries, set: set });
|