@blamejs/pki 0.8.6 → 0.8.9
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 +143 -76
- package/README.md +4 -2
- package/index.js +11 -0
- package/lib/acme.js +15 -78
- package/lib/asn1-der.js +58 -6
- package/lib/cbor-det.js +11 -1
- package/lib/cmc-build.js +1 -1
- package/lib/cmc-verify.js +3 -3
- package/lib/cmp-session.js +1 -1
- package/lib/cms-sign.js +3 -3
- package/lib/cms-verify.js +5 -5
- package/lib/constants.js +11 -0
- package/lib/est.js +1 -1
- package/lib/framework-error.js +8 -0
- package/lib/guard-async.js +53 -2
- package/lib/guard-encoding.js +105 -1
- package/lib/guard-identifier.js +74 -1
- package/lib/guard-intrinsic.js +2 -0
- package/lib/guard-json.js +1 -6
- package/lib/guard-limits.js +40 -1
- package/lib/guard-list.js +25 -0
- package/lib/guard-name.js +134 -22
- package/lib/guard-text.js +33 -1
- package/lib/http-transport.js +39 -7
- package/lib/identity-match.js +561 -0
- package/lib/ip-utils.js +52 -1
- package/lib/key.js +250 -5
- package/lib/ocsp.js +125 -22
- package/lib/oid.js +1 -1
- package/lib/path-validate.js +199 -61
- package/lib/pkcs11-uri.js +646 -0
- package/lib/pkcs12-build.js +6 -1
- package/lib/revocation-fetch.js +772 -0
- package/lib/schema-all.js +124 -0
- package/lib/schema-attrcert.js +4 -2
- package/lib/schema-crmf.js +4 -2
- package/lib/schema-pkcs1.js +335 -0
- package/lib/schema-pkcs8.js +15 -0
- package/lib/schema-pkix.js +322 -40
- package/lib/schema-sec1.js +244 -0
- package/lib/schema-tsp.js +4 -2
- package/lib/schema-x509.js +4 -2
- package/lib/trust.js +2 -2
- package/lib/tsp-sign.js +1 -1
- package/lib/webauthn.js +4 -4
- package/lib/webcrypto.js +17 -5
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/guard-async.js
CHANGED
|
@@ -8,6 +8,13 @@ var _reject = _intrinsic.uncurry(_intrinsic.promiseReject);
|
|
|
8
8
|
var _apply = _intrinsic.apply;
|
|
9
9
|
var _String = _intrinsic.String;
|
|
10
10
|
var _Promise = Promise;
|
|
11
|
+
/** @internal Bound at load, like every other operation this module decides with: a replacement
|
|
12
|
+
* installed later would otherwise choose when a bound expires, or whether it ever does. */
|
|
13
|
+
var _setTimeout = setTimeout;
|
|
14
|
+
var _clearTimeout = clearTimeout;
|
|
15
|
+
/** @internal The losing side of a race is observed, not acted on: attaching this is what keeps an
|
|
16
|
+
* outcome nobody wants from becoming an unhandled rejection. */
|
|
17
|
+
function _ignore() { return undefined; }
|
|
11
18
|
// @internal
|
|
12
19
|
// @enforced-by behavioral -- the rule has no rename-proof code shape (it is the ABSENCE of a wrapper
|
|
13
20
|
function deferred(body) {
|
|
@@ -40,9 +47,53 @@ function invoked(fn, args, E, code, label) {
|
|
|
40
47
|
var out;
|
|
41
48
|
try { out = _apply(fn, undefined, args); }
|
|
42
49
|
catch (e) {
|
|
43
|
-
|
|
50
|
+
/** @internal The thrown value is the caller's, so describing it is a read of caller data: a
|
|
51
|
+
* `message` accessor that throws would leave this boundary by the one path it exists to close.
|
|
52
|
+
* @guard-via guard.text.describeThrown */
|
|
53
|
+
// allow:inline-require guard-text reaches guard-bytes, which reaches this module, so binding it at load leaves guard-bytes half-built for whichever of the two is required first; this line runs only after a producer has already thrown
|
|
54
|
+
throw E(code, label + " threw when it was called: " +
|
|
55
|
+
require("./guard-text").describeThrown(e), e);
|
|
44
56
|
}
|
|
45
57
|
return awaited(out, E, code, label);
|
|
46
58
|
}
|
|
47
59
|
|
|
48
|
-
|
|
60
|
+
// @internal
|
|
61
|
+
// @enforced-by behavioral -- a RED vector drives a never-settling caller operation through a client
|
|
62
|
+
/** @internal Await a caller-supplied operation under a bound this side controls. A request field
|
|
63
|
+
* naming a timeout is a request: an operation that does not implement it never settles, and the
|
|
64
|
+
* verb awaiting it never returns. The timer holds the event loop until it fires or is cleared,
|
|
65
|
+
* which is what keeps a process from exiting while a bound is still in flight and abandoning the
|
|
66
|
+
* await in silence. It is cleared on either outcome, so it holds the loop for at most `ms`. The
|
|
67
|
+
* losing side of the race is not cancelled, because a promise cannot be; the caller stops waiting.
|
|
68
|
+
*
|
|
69
|
+
* `ms` at or below zero is already past and refuses without awaiting anything. */
|
|
70
|
+
function bounded(value, ms, E, code, label) {
|
|
71
|
+
var settled = false;
|
|
72
|
+
return new _Promise(function (resolve, reject) {
|
|
73
|
+
if (!(ms > 0)) {
|
|
74
|
+
/** @internal The operation may already be running: a deadline can expire between the check
|
|
75
|
+
* that started it and the bound computed for it. Refusing without observing its promise
|
|
76
|
+
* leaves a rejection nobody handled, which ends the process on the default
|
|
77
|
+
* `unhandledRejection` behavior, and the caller catching this refusal does not help. */
|
|
78
|
+
_resolve(_Promise, value).then(_ignore, _ignore);
|
|
79
|
+
reject(E(code, label + " had no time left to run in"));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
var timer = _setTimeout(function () {
|
|
83
|
+
if (settled) return;
|
|
84
|
+
settled = true;
|
|
85
|
+
reject(E(code, label + " did not settle within " + ms + "ms"));
|
|
86
|
+
}, ms);
|
|
87
|
+
function done(fn) {
|
|
88
|
+
return function (x) {
|
|
89
|
+
if (settled) return;
|
|
90
|
+
settled = true;
|
|
91
|
+
_clearTimeout(timer);
|
|
92
|
+
fn(x);
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
_resolve(_Promise, value).then(done(resolve), done(reject));
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = _intrinsic.freeze({ deferred: deferred, awaited: awaited, invoked: invoked, bounded: bounded });
|
package/lib/guard-encoding.js
CHANGED
|
@@ -16,6 +16,9 @@ var _charCodeAt = _intrinsic.uncurry(String.prototype.charCodeAt);
|
|
|
16
16
|
var _strSlice = _intrinsic.uncurry(String.prototype.slice);
|
|
17
17
|
var _toLowerCase = _intrinsic.uncurry(String.prototype.toLowerCase);
|
|
18
18
|
var _isInteger = _intrinsic.isInteger;
|
|
19
|
+
var _charAt = _intrinsic.uncurry(String.prototype.charAt);
|
|
20
|
+
var _fromCharCode = String.fromCharCode;
|
|
21
|
+
var _guardBytes = require("./guard-bytes");
|
|
19
22
|
var _floor = _intrinsic.floor;
|
|
20
23
|
|
|
21
24
|
var UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", LOWER = "abcdefghijklmnopqrstuvwxyz", DIGITS = "0123456789";
|
|
@@ -76,4 +79,105 @@ function hex(text, maxBytes, E, code, label) {
|
|
|
76
79
|
return buf;
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
|
|
82
|
+
/**
|
|
83
|
+
* @internal The value of one hexadecimal digit, or -1 when the byte is not one. Every
|
|
84
|
+
* character-scanning reader of an escape (a DN string, a JSON string, a URI) asks the same
|
|
85
|
+
* question, and asking it in one place keeps the three ranges from drifting apart.
|
|
86
|
+
*
|
|
87
|
+
* @enforced-by guard-shape-reinlined
|
|
88
|
+
* @guard-shape c >= 0x30 && c <= 0x39\) return c - 0x30
|
|
89
|
+
*/
|
|
90
|
+
function hexNibble(c) {
|
|
91
|
+
if (c >= 0x30 && c <= 0x39) return c - 0x30;
|
|
92
|
+
if (c >= 0x41 && c <= 0x46) return c - 0x41 + 10;
|
|
93
|
+
if (c >= 0x61 && c <= 0x66) return c - 0x61 + 10;
|
|
94
|
+
return -1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @internal Whether a byte is an ASCII letter or digit. It is the base of every
|
|
99
|
+
* character-class a textual syntax defines on top of it, so the three ranges live here
|
|
100
|
+
* rather than being spelled again per reader.
|
|
101
|
+
*
|
|
102
|
+
* @enforced-by behavioral -- the ranges have no rename-proof shape distinct from a legitimate
|
|
103
|
+
* range test, and each consumer's own acceptance vector is the guard.
|
|
104
|
+
*/
|
|
105
|
+
function isAlphanumericByte(c) {
|
|
106
|
+
return (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || (c >= 0x30 && c <= 0x39);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
var _HEX_UPPER = "0123456789ABCDEF";
|
|
110
|
+
|
|
111
|
+
/** @internal Whether a byte is in RFC 3986 sec. 2.3's unreserved set: `ALPHA / DIGIT / "-" / "."
|
|
112
|
+
* / "_" / "~"`. Every other byte is written as a percent-triplet, which is what sec. 2.1 requires
|
|
113
|
+
* of a character that is not allowed where it sits. The set is deliberately the unreserved one
|
|
114
|
+
* and not the larger set a path segment also admits, so the output is safe wherever it is
|
|
115
|
+
* placed and a caller cannot reach a delimiter by choosing a component. */
|
|
116
|
+
function _isUnreservedByte(c) {
|
|
117
|
+
return (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || (c >= 0x30 && c <= 0x39) ||
|
|
118
|
+
c === 0x2d || c === 0x2e || c === 0x5f || c === 0x7e;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** @internal Percent-encode bytes to RFC 3986 sec. 2.1, with the unreserved set of sec. 2.3 passed
|
|
122
|
+
* through and every other byte written as `%` and two upper-case hex digits. sec. 2.1 states that
|
|
123
|
+
* the hexadecimal digits of a triplet may be either case and that uppercase is the canonical form
|
|
124
|
+
* a producer emits.
|
|
125
|
+
*
|
|
126
|
+
* This is the only escaping in `lib/`. `encodeURIComponent` reads a JavaScript string, so bytes
|
|
127
|
+
* reach it only after a decode this cannot assume, and it leaves `!`, `*`, `'`, `(` and `)`
|
|
128
|
+
* unescaped, which are sub-delims in sec. 2.2 and therefore delimiters in some components. */
|
|
129
|
+
// @enforced-by guard-shape-reinlined
|
|
130
|
+
// @guard-shape %[0-9A-F]{2}|toString\(16\)[\s\S]{0,80}?padStart\(\s*2
|
|
131
|
+
// @guard-via guard\.encoding\.(?:percentEncode|uriPathSegment)\(
|
|
132
|
+
function percentEncode(bytes, E, code, label) {
|
|
133
|
+
var buf = _guardBytes.source(bytes, _rawError(E), code, label || "value to percent-encode");
|
|
134
|
+
var out = "";
|
|
135
|
+
for (var i = 0; i < buf.length; i++) {
|
|
136
|
+
var c = buf[i];
|
|
137
|
+
if (_isUnreservedByte(c)) { out += _fromCharCode(c); continue; }
|
|
138
|
+
out += "%" + _charAt(_HEX_UPPER, (c >> 4) & 0x0f) + _charAt(_HEX_UPPER, c & 0x0f);
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** @internal One path segment of a URI, built from `text` read as its latin1 bytes. Every byte
|
|
144
|
+
* outside the unreserved set becomes a triplet, so a value carrying `/`, `?`, `#` or a percent of
|
|
145
|
+
* its own cannot reach past the segment it was placed in. */
|
|
146
|
+
function uriPathSegment(text, E, code, label) {
|
|
147
|
+
if (typeof text !== "string") {
|
|
148
|
+
throw _rawError(E)(code, (label || "URI path segment") + " must be a string");
|
|
149
|
+
}
|
|
150
|
+
return percentEncode(_bufferFrom(text, "latin1"), E, code, label);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** @internal The guards take a `(code, message)` factory. A caller that passes an error CLASS is a
|
|
154
|
+
* configuration fault and not an input one, so it is refused here instead of producing
|
|
155
|
+
* `undefined is not a constructor` at the first refusal. */
|
|
156
|
+
function _rawError(E) {
|
|
157
|
+
if (typeof E !== "function") throw new TypeError("guard.encoding: an error factory (code, message) is required");
|
|
158
|
+
return E;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** @internal Whether a string is already the form that goes on the wire. RFC 3986 sec. 2 builds a
|
|
162
|
+
* URI out of US-ASCII alone, so a string carrying anything above it is an IRI (RFC 3987) and the
|
|
163
|
+
* bytes a transport would send are not the characters counted here. Every length a specification
|
|
164
|
+
* states about a URL is a byte count, which makes this the check a boundary needs before measuring
|
|
165
|
+
* one: a code point in U+0080..U+07FF is one character and two UTF-8 bytes, so a rule measured in
|
|
166
|
+
* characters passes a URL the rule was meant to refuse.
|
|
167
|
+
*
|
|
168
|
+
* @enforced-by guard-shape-reinlined
|
|
169
|
+
* @guard-shape /charCodeAt\s*\(\s*[\w$]+\s*,\s*[\w$]+\s*\)\s*>\s*(?:0x7f|127)\b/ */
|
|
170
|
+
function isAsciiText(s) {
|
|
171
|
+
if (typeof s !== "string") return false;
|
|
172
|
+
for (var i = 0; i < s.length; i++) {
|
|
173
|
+
if (_charCodeAt(s, i) > 0x7f) return false;
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = _intrinsic.freeze({
|
|
179
|
+
base64url: base64url, base64: base64, hex: hex,
|
|
180
|
+
hexNibble: hexNibble, isAlphanumericByte: isAlphanumericByte,
|
|
181
|
+
isAsciiText: isAsciiText,
|
|
182
|
+
percentEncode: percentEncode, uriPathSegment: uriPathSegment,
|
|
183
|
+
});
|
package/lib/guard-identifier.js
CHANGED
|
@@ -401,7 +401,62 @@ function _settle(opts, E, code, label) {
|
|
|
401
401
|
throw E(code, label + " changes which options it carries while they are read, so no set of " +
|
|
402
402
|
"them can be checked; pass an object whose properties are plain values");
|
|
403
403
|
}
|
|
404
|
-
return opts;
|
|
404
|
+
return _withoutPlantedFields(opts, before);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/** @internal A language prototype, as opposed to one the caller built. A defaults object the
|
|
408
|
+
* caller put under their bag is theirs and sits below these in the chain. */
|
|
409
|
+
function _isLanguageProto(o) {
|
|
410
|
+
return o === intrinsic.ObjectProto || o === intrinsic.ArrayProto ||
|
|
411
|
+
o === intrinsic.BufferProto || o === intrinsic.FunctionProto;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function _holderOf(obj, k) {
|
|
415
|
+
var visited = new _Set();
|
|
416
|
+
for (var o = obj; o !== null && o !== undefined && !_setHas(visited, o); o = _getPrototypeOf(o)) {
|
|
417
|
+
_setAdd(visited, o);
|
|
418
|
+
if (_getOwnPropertyDescriptor(o, k)) return o;
|
|
419
|
+
}
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/** @internal An option is something the caller supplied, and a name whose descriptor sits on a
|
|
424
|
+
* language prototype was supplied by whoever wrote to that prototype. The verb reads its options
|
|
425
|
+
* with `opts.name`, which walks the chain, so such a name answers as though it had been passed:
|
|
426
|
+
* `historicalMode` treats a revocation as not yet in force and `softFail` waives an undetermined
|
|
427
|
+
* one, each turning a revoked certificate into a valid path. The unknown-key check catches a
|
|
428
|
+
* planted name that is not an option at all; one spelled like a real option passes it.
|
|
429
|
+
*
|
|
430
|
+
* What comes back reads `undefined` for those names and is otherwise the object that was passed:
|
|
431
|
+
* the same prototype chain, the same own fields, and one own `undefined` masking each planted
|
|
432
|
+
* name. So the caller's own field still wins, a defaults object under their bag still supplies its
|
|
433
|
+
* options whatever their type, the reported set of names does not change, and a verb in a polluted
|
|
434
|
+
* process still runs instead of failing on every call. With nothing planted the object is handed
|
|
435
|
+
* back untouched, which is every call outside a process that has been written to.
|
|
436
|
+
*
|
|
437
|
+
* The own fields are carried as OWN fields rather than left to be inherited from the object that
|
|
438
|
+
* was passed. A verb that forwards its options builds the forwarded set by copying own keys, and a
|
|
439
|
+
* bag whose fields had all become inherited arrives at the inner call carrying none of them. */
|
|
440
|
+
function _withoutPlantedFields(obj, names) {
|
|
441
|
+
var planted = null;
|
|
442
|
+
for (var i = 0; i < names.length; i++) {
|
|
443
|
+
if (!_isLanguageProto(_holderOf(obj, names[i]))) continue;
|
|
444
|
+
if (planted === null) planted = [];
|
|
445
|
+
_append(planted, names[i]);
|
|
446
|
+
}
|
|
447
|
+
if (planted === null) return obj;
|
|
448
|
+
var shadow = _create(_getPrototypeOf(obj));
|
|
449
|
+
var own = _ownKeys(obj);
|
|
450
|
+
for (var k = 0; k < own.length; k++) {
|
|
451
|
+
var d = _getOwnPropertyDescriptor(obj, own[k]);
|
|
452
|
+
if (!d) continue;
|
|
453
|
+
_defineProperty(shadow, own[k], d);
|
|
454
|
+
}
|
|
455
|
+
for (var j = 0; j < planted.length; j++) {
|
|
456
|
+
if (_getOwnPropertyDescriptor(obj, planted[j])) continue;
|
|
457
|
+
_defineProperty(shadow, planted[j], { value: undefined, writable: true, enumerable: false, configurable: true });
|
|
458
|
+
}
|
|
459
|
+
return shadow;
|
|
405
460
|
}
|
|
406
461
|
|
|
407
462
|
/** @internal An injectable callback option, refused unless it is callable. A value that is present
|
|
@@ -445,8 +500,26 @@ function assertCallableOption(opts, key, E, code, label, shape) {
|
|
|
445
500
|
return assertCallable(opts[key], E, code, label, shape);
|
|
446
501
|
}
|
|
447
502
|
|
|
503
|
+
/** @internal Whether a caller's object says something about itself, as opposed to inheriting the
|
|
504
|
+
* answer. A declaration is a promise the author of that object made, so it has to sit on the object
|
|
505
|
+
* they wrote: reading the flag through the object finds a value on `Function.prototype` that every
|
|
506
|
+
* function in the process carries, and a boundary reading it that way treats every caller as having
|
|
507
|
+
* promised whatever was planted there. The value is read from the descriptor rather than through
|
|
508
|
+
* the object, so an accessor further up the chain cannot answer either.
|
|
509
|
+
*
|
|
510
|
+
* @enforced-by guard-shape-reinlined
|
|
511
|
+
* @guard-shape /\.\s*blocksPrivateAddresses\s*===\s*true/ */
|
|
512
|
+
function declaresOwn(obj, name) {
|
|
513
|
+
if (obj === null || obj === undefined) return false;
|
|
514
|
+
var d;
|
|
515
|
+
try { d = _getOwnPropertyDescriptor(_Object(obj), name); }
|
|
516
|
+
catch (_e) { return false; }
|
|
517
|
+
return !!d && !d.get && !d.set && d.value === true;
|
|
518
|
+
}
|
|
519
|
+
|
|
448
520
|
module.exports = intrinsic.freeze({
|
|
449
521
|
assertCallable: assertCallable,
|
|
522
|
+
declaresOwn: declaresOwn,
|
|
450
523
|
assertCallableOption: assertCallableOption,
|
|
451
524
|
assertCanonicalOid: assertCanonicalOid,
|
|
452
525
|
assertKnownKeys: assertKnownKeys,
|
package/lib/guard-intrinsic.js
CHANGED
|
@@ -96,6 +96,7 @@ var _ObjectFn = Object;
|
|
|
96
96
|
var _ObjectProto = Object.prototype;
|
|
97
97
|
var _BufferProto = Buffer.prototype;
|
|
98
98
|
var _ArrayProto = Array.prototype;
|
|
99
|
+
var _FunctionProto = Function.prototype;
|
|
99
100
|
var _Set = Set;
|
|
100
101
|
var _Map = Map;
|
|
101
102
|
var _WeakMap = WeakMap;
|
|
@@ -216,6 +217,7 @@ module.exports = Object.freeze({
|
|
|
216
217
|
ObjectProto: _ObjectProto,
|
|
217
218
|
BufferProto: _BufferProto,
|
|
218
219
|
ArrayProto: _ArrayProto,
|
|
220
|
+
FunctionProto: _FunctionProto,
|
|
219
221
|
setAdd: _setAdd,
|
|
220
222
|
setHas: _setHas,
|
|
221
223
|
mapGet: _mapGet,
|
package/lib/guard-json.js
CHANGED
|
@@ -15,12 +15,7 @@ var _stringify = require("./guard-intrinsic").stringify;
|
|
|
15
15
|
var _append = require("./guard-list").append;
|
|
16
16
|
var limits = require("./guard-limits");
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
if (c >= 0x30 && c <= 0x39) return c - 0x30;
|
|
20
|
-
if (c >= 0x61 && c <= 0x66) return c - 0x61 + 10;
|
|
21
|
-
if (c >= 0x41 && c <= 0x46) return c - 0x41 + 10;
|
|
22
|
-
return -1;
|
|
23
|
-
}
|
|
18
|
+
var _hexVal = require("./guard-encoding").hexNibble;
|
|
24
19
|
|
|
25
20
|
// @enforced-by json-parse-not-via-guard
|
|
26
21
|
function parse(input, ErrorClass, spec) {
|
package/lib/guard-limits.js
CHANGED
|
@@ -7,6 +7,10 @@ var constants = require("./constants");
|
|
|
7
7
|
var _isInteger = require("./guard-intrinsic").isInteger;
|
|
8
8
|
var _freeze = require("./guard-intrinsic").freeze;
|
|
9
9
|
var _sizeOf = require("./guard-intrinsic").sizeOf;
|
|
10
|
+
var _isFinite = require("./guard-intrinsic").isFinite;
|
|
11
|
+
var _Number = require("./guard-intrinsic").Number;
|
|
12
|
+
var _hrtimeBigint = process.hrtime.bigint;
|
|
13
|
+
function _monotonicNow() { return _Number(_hrtimeBigint() / 1000000n); }
|
|
10
14
|
|
|
11
15
|
// @enforced-by behavioral -- a config-time cap validator has no rename-proof code
|
|
12
16
|
function cap(value, key, dflt, opts) {
|
|
@@ -109,7 +113,42 @@ function budget(limits) {
|
|
|
109
113
|
return _freeze(self);
|
|
110
114
|
}
|
|
111
115
|
|
|
116
|
+
/** @internal A wall-clock bound shared across several operations, where a per-operation timeout
|
|
117
|
+
* bounds each one and nothing bounds their sum. `now` is the clock, which a caller supplies so a
|
|
118
|
+
* test drives the bound without waiting; it defaults to the monotonic one, because a wall clock
|
|
119
|
+
* that steps backward would extend the deadline it was meant to enforce.
|
|
120
|
+
*
|
|
121
|
+
* `expired()` answers the question and `remaining()` gives the budget a per-operation timeout
|
|
122
|
+
* should be narrowed to, never below zero, so a caller that hands `remaining()` to a timeout
|
|
123
|
+
* cannot ask for one that outlives the deadline.
|
|
124
|
+
*/
|
|
125
|
+
// @enforced-by behavioral -- an elapsed-time comparison has no rename-proof shape that separates a deadline from any other subtraction; the vectors driving a checker to its bound are the guard
|
|
126
|
+
function deadline(totalMs, opts) {
|
|
127
|
+
opts = opts || {};
|
|
128
|
+
var now = typeof opts.now === "function" ? opts.now : _monotonicNow;
|
|
129
|
+
if (!_isInteger(totalMs) || totalMs < 0) {
|
|
130
|
+
throw new TypeError("guard.limits.deadline: the total must be a non-negative integer of milliseconds");
|
|
131
|
+
}
|
|
132
|
+
var started = now();
|
|
133
|
+
if (typeof started !== "number" || !_isFinite(started)) {
|
|
134
|
+
throw new TypeError("guard.limits.deadline: the clock must return a finite number of milliseconds");
|
|
135
|
+
}
|
|
136
|
+
var self = {
|
|
137
|
+
elapsed: function () {
|
|
138
|
+
var at = now();
|
|
139
|
+
return typeof at === "number" && _isFinite(at) && at > started ? at - started : 0;
|
|
140
|
+
},
|
|
141
|
+
remaining: function () {
|
|
142
|
+
var left = totalMs - self.elapsed();
|
|
143
|
+
return left > 0 ? left : 0;
|
|
144
|
+
},
|
|
145
|
+
expired: function () { return self.remaining() === 0; },
|
|
146
|
+
total: function () { return totalMs; },
|
|
147
|
+
};
|
|
148
|
+
return _freeze(self);
|
|
149
|
+
}
|
|
150
|
+
|
|
112
151
|
module.exports = _freeze({
|
|
113
152
|
cap: cap, depthCap: depthCap, counter: counter, byteCap: byteCap,
|
|
114
|
-
budget: budget, isBudgetExceeded: isBudgetExceeded,
|
|
153
|
+
budget: budget, isBudgetExceeded: isBudgetExceeded, deadline: deadline,
|
|
115
154
|
});
|
package/lib/guard-list.js
CHANGED
|
@@ -48,10 +48,35 @@ function allMatch(list, predicate) {
|
|
|
48
48
|
return true;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
// @enforced-by caller-array-copied-live
|
|
52
|
+
function snapshot(list) {
|
|
53
|
+
if (!_isArray(list)) throw new TypeError("guard.list.snapshot: the receiver must be an array");
|
|
54
|
+
var n = list.length;
|
|
55
|
+
var out = [];
|
|
56
|
+
for (var i = 0; i < n; i++) {
|
|
57
|
+
_defineProperty(out, i, { value: list[i], writable: true, enumerable: true, configurable: true });
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// @enforced-by caller-array-copied-live
|
|
63
|
+
function copyMap(list, fn) {
|
|
64
|
+
if (!_isArray(list)) throw new TypeError("guard.list.copyMap: the receiver must be an array");
|
|
65
|
+
if (typeof fn !== "function") throw new TypeError("guard.list.copyMap: the second argument must be a function");
|
|
66
|
+
var n = list.length;
|
|
67
|
+
var out = [];
|
|
68
|
+
for (var i = 0; i < n; i++) {
|
|
69
|
+
_defineProperty(out, i, { value: fn(list[i], i), writable: true, enumerable: true, configurable: true });
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
51
74
|
module.exports = _freeze({
|
|
52
75
|
append: append,
|
|
53
76
|
contains: contains,
|
|
54
77
|
containsAll: containsAll,
|
|
55
78
|
anyMatches: anyMatches,
|
|
56
79
|
allMatch: allMatch,
|
|
80
|
+
snapshot: snapshot,
|
|
81
|
+
copyMap: copyMap,
|
|
57
82
|
});
|
package/lib/guard-name.js
CHANGED
|
@@ -164,12 +164,7 @@ function escapeControlBytes(str) {
|
|
|
164
164
|
|
|
165
165
|
var DN_SPECIAL = intrinsic.assign(intrinsic.create(null), { 0x2c: 1, 0x2b: 1, 0x22: 1, 0x5c: 1, 0x3c: 1, 0x3e: 1, 0x3b: 1 });
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
if (c >= 0x30 && c <= 0x39) return c - 0x30;
|
|
169
|
-
if (c >= 0x41 && c <= 0x46) return c - 0x41 + 10;
|
|
170
|
-
if (c >= 0x61 && c <= 0x66) return c - 0x61 + 10;
|
|
171
|
-
return -1;
|
|
172
|
-
}
|
|
167
|
+
var _hexNibble = require("./guard-encoding").hexNibble;
|
|
173
168
|
function _isAttrTypeStart(c) { return (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a); }
|
|
174
169
|
function _isAttrTypeChar(c) { return _isAttrTypeStart(c) || (c >= 0x30 && c <= 0x39) || c === 0x2d; }
|
|
175
170
|
function _isDigit(c) { return c >= 0x30 && c <= 0x39; }
|
|
@@ -330,7 +325,6 @@ function _charTable(chars) {
|
|
|
330
325
|
for (var i = 0; i < chars.length; i++) t[_charCodeAt(chars, i)] = true;
|
|
331
326
|
return t;
|
|
332
327
|
}
|
|
333
|
-
var _FQDN_TABLE = _charTable("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-");
|
|
334
328
|
var _DIGDOT_TABLE = _charTable("0123456789.");
|
|
335
329
|
function _allCharsIn(s, table) {
|
|
336
330
|
if (s.length === 0) return false;
|
|
@@ -349,12 +343,18 @@ function uriSchemeIs(s, scheme) {
|
|
|
349
343
|
return lowerAscii(_strSlice(s, 0, scheme.length)) === scheme;
|
|
350
344
|
}
|
|
351
345
|
|
|
352
|
-
/** @internal A host name
|
|
353
|
-
*
|
|
346
|
+
/** @internal A host name that is not an address, which is the form a URI name constraint is
|
|
347
|
+
* compared by (RFC 5280 sec. 4.2.1.10). The clause holds a URI constraint to "a fully qualified
|
|
348
|
+
* domain name" and names in the same sentence what that excludes: a URI with no authority
|
|
349
|
+
* component, and one whose authority names an IP address. It states no label count, so a single
|
|
350
|
+
* label is a host like any other and `com` constrains a top-level domain. The host is read by its
|
|
351
|
+
* labels rather than by which characters it holds: a character test admits an empty label and a
|
|
352
|
+
* hyphen at a label edge, and the suffix comparison below would then match a base against a name
|
|
353
|
+
* that is not a name. */
|
|
354
354
|
function isFqdnHost(host) {
|
|
355
|
+
if (typeof host !== "string") return false;
|
|
355
356
|
var h = stripTrailingDot(host);
|
|
356
|
-
if (
|
|
357
|
-
if (!_allCharsIn(h, _FQDN_TABLE)) return false;
|
|
357
|
+
if (!_isHostName(h)) return false;
|
|
358
358
|
if (_allCharsIn(h, _DIGDOT_TABLE)) return false;
|
|
359
359
|
return true;
|
|
360
360
|
}
|
|
@@ -391,6 +391,112 @@ function _isHostName(s) {
|
|
|
391
391
|
return true;
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
+
/** @internal A host name written as labels separated by dots (RFC 1034 sec. 3.5, RFC 1123 sec. 2.1),
|
|
395
|
+
* with no trailing dot: the caller strips the one dot a comparison strips, so a second one is an
|
|
396
|
+
* empty label and is refused. */
|
|
397
|
+
// @enforced-by behavioral -- an LDH label walk has no rename-proof shape that separates it from any other per-character scan; the vectors refusing an empty label, a hyphen at a label edge and a name past 253 octets are the guard
|
|
398
|
+
function isHostName(s) { return typeof s === "string" && _isHostName(s); }
|
|
399
|
+
|
|
400
|
+
/** @internal The labels of `s`, in order, with no trailing-dot handling of its own: the caller
|
|
401
|
+
* normalizes the dot through `stripTrailingDot` so both sides of a comparison are normalized by
|
|
402
|
+
* one symbol. A name carrying an empty label yields an empty string at that position, which a
|
|
403
|
+
* label test then refuses, rather than being dropped. */
|
|
404
|
+
// @enforced-by behavioral -- splitting on a dot has no rename-proof shape; the vectors counting the labels of a name carrying an empty one are the guard
|
|
405
|
+
function hostLabels(s) {
|
|
406
|
+
var out = [], start = 0;
|
|
407
|
+
for (var i = 0; i <= s.length; i++) {
|
|
408
|
+
if (i === s.length || _charCodeAt(s, i) === 0x2e) {
|
|
409
|
+
_append(out, _strSlice(s, start, i));
|
|
410
|
+
start = i + 1;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function _isAlnumLower(c) { return (c >= 0x61 && c <= 0x7a) || (c >= 0x30 && c <= 0x39); }
|
|
417
|
+
|
|
418
|
+
/** @internal Whether `l` is a well-formed A-label (RFC 5890 sec. 2.3.2.1): the `xn--` prefix
|
|
419
|
+
* followed by groups of lower-case alphanumerics separated by single hyphens, each group non-empty.
|
|
420
|
+
* The label is not decoded, because RFC 5891 sec. 3.1 rule 2 compares a pair of A-labels as
|
|
421
|
+
* case-insensitive ASCII without validating either. */
|
|
422
|
+
// @enforced-by behavioral -- an `xn--` prefix scan has no rename-proof shape; the vectors refusing `xn--`, a trailing hyphen and an empty group are the guard
|
|
423
|
+
function isXnLabel(l) {
|
|
424
|
+
if (typeof l !== "string") return false;
|
|
425
|
+
var n = l.length;
|
|
426
|
+
if (!(n >= 4 && _charCodeAt(l, 0) === 0x78 && _charCodeAt(l, 1) === 0x6e &&
|
|
427
|
+
_charCodeAt(l, 2) === 0x2d && _charCodeAt(l, 3) === 0x2d)) return false;
|
|
428
|
+
var i = 4, g = 0;
|
|
429
|
+
while (i < n && _isAlnumLower(_charCodeAt(l, i))) { i++; g++; }
|
|
430
|
+
if (g < 1) return false;
|
|
431
|
+
while (i < n) {
|
|
432
|
+
if (_charCodeAt(l, i) !== 0x2d) return false;
|
|
433
|
+
i++;
|
|
434
|
+
var g2 = 0;
|
|
435
|
+
while (i < n && _isAlnumLower(_charCodeAt(l, i))) { i++; g2++; }
|
|
436
|
+
if (g2 < 1) return false;
|
|
437
|
+
}
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
var _SCHEME_TABLE = _charTable("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-");
|
|
442
|
+
|
|
443
|
+
/** @internal The scheme and the host of a URI, read to RFC 3986 sec. 3: the scheme up to its colon,
|
|
444
|
+
* and, when `//` follows that colon, the authority up to the first `/`, `?` or `#`, with userinfo
|
|
445
|
+
* and port removed and an IPv6 literal's brackets stripped. `hasAuthority` is false for a URI
|
|
446
|
+
* written without `//`, which RFC 3986 sec. 3.3 permits and which therefore has no host; a caller
|
|
447
|
+
* that needs a host decides what to do with that rather than being handed a guess. `host` is null
|
|
448
|
+
* when the authority carries none, and `bracketed` says the host arrived inside `[]`, which is the
|
|
449
|
+
* only place an IPv6 literal may appear. Percent-escapes are left as written, so a caller comparing
|
|
450
|
+
* the host decides whether to accept one. */
|
|
451
|
+
// @enforced-by behavioral -- a URI walk has no rename-proof shape that separates it from any other character scan; the vectors reading a scheme, refusing two userinfo separators and stripping a port are the guard
|
|
452
|
+
function uriParts(uri) {
|
|
453
|
+
if (typeof uri !== "string") return null;
|
|
454
|
+
var n = uri.length;
|
|
455
|
+
if (n === 0) return null;
|
|
456
|
+
var c0 = _charCodeAt(uri, 0);
|
|
457
|
+
if (!((c0 >= 0x41 && c0 <= 0x5a) || (c0 >= 0x61 && c0 <= 0x7a))) return null;
|
|
458
|
+
var p = 1;
|
|
459
|
+
while (p < n && _SCHEME_TABLE[_charCodeAt(uri, p)]) p += 1;
|
|
460
|
+
if (_charCodeAt(uri, p) !== 0x3a) return null;
|
|
461
|
+
var scheme = _strSlice(uri, 0, p);
|
|
462
|
+
if (_charCodeAt(uri, p + 1) !== 0x2f || _charCodeAt(uri, p + 2) !== 0x2f) {
|
|
463
|
+
return { scheme: scheme, hasAuthority: false, host: null, bracketed: false };
|
|
464
|
+
}
|
|
465
|
+
p += 3;
|
|
466
|
+
var authStart = p;
|
|
467
|
+
while (p < n) {
|
|
468
|
+
var d = _charCodeAt(uri, p);
|
|
469
|
+
if (d === 0x2f || d === 0x3f || d === 0x23) break;
|
|
470
|
+
p += 1;
|
|
471
|
+
}
|
|
472
|
+
var authority = _strSlice(uri, authStart, p);
|
|
473
|
+
var firstAt = _indexOf(authority, "@");
|
|
474
|
+
if (firstAt !== _lastIndexOf(authority, "@")) return null;
|
|
475
|
+
if (firstAt !== -1) authority = _strSlice(authority, firstAt + 1);
|
|
476
|
+
var host = _strSlice(authority, 0, _portStripIndex(authority));
|
|
477
|
+
if (host === "") return { scheme: scheme, hasAuthority: true, host: null, bracketed: false };
|
|
478
|
+
var hn = host.length;
|
|
479
|
+
if (hn >= 2 && _charCodeAt(host, 0) === 0x5b && _charCodeAt(host, hn - 1) === 0x5d) {
|
|
480
|
+
return { scheme: scheme, hasAuthority: true, host: _strSlice(host, 1, hn - 1), bracketed: true };
|
|
481
|
+
}
|
|
482
|
+
return { scheme: scheme, hasAuthority: true, host: host, bracketed: false };
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/** @internal Where a `:port` suffix starts, or the length when there is none. RFC 3986 sec. 3.2.3
|
|
486
|
+
* writes `port = *DIGIT`, so a colon with nothing after it is a port too, and leaving that colon on
|
|
487
|
+
* the host would make the authority compare as a name carrying a character no name carries. A
|
|
488
|
+
* bracketed IPv6 literal has colons of its own, so the scan reads digits back from the end and
|
|
489
|
+
* requires the colon immediately before them; the closing bracket is what stops it. */
|
|
490
|
+
function _portStripIndex(s) {
|
|
491
|
+
var i = s.length;
|
|
492
|
+
while (i > 0) {
|
|
493
|
+
var c = _charCodeAt(s, i - 1);
|
|
494
|
+
if (c >= 0x30 && c <= 0x39) i -= 1;
|
|
495
|
+
else break;
|
|
496
|
+
}
|
|
497
|
+
return (i > 0 && _charCodeAt(s, i - 1) === 0x3a) ? i - 1 : s.length;
|
|
498
|
+
}
|
|
499
|
+
|
|
394
500
|
var _ATEXT_TABLE = _charTable("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-/=?^_`{|}~");
|
|
395
501
|
|
|
396
502
|
/** @internal A quoted local part (RFC 5321 sec. 4.1.2 Quoted-string): printable ASCII between
|
|
@@ -539,18 +645,23 @@ function constraintBaseRefusal(tag, base) {
|
|
|
539
645
|
: "must be a host name, optionally written as a subtree with a leading dot";
|
|
540
646
|
}
|
|
541
647
|
/** @internal RFC 5280 sec. 4.2.1.10 says of a URI constraint: "The constraint MUST be specified as
|
|
542
|
-
* a fully qualified domain name and MAY specify a host or a domain
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
* `
|
|
648
|
+
* a fully qualified domain name and MAY specify a host or a domain", and the same sentence names
|
|
649
|
+
* what that excludes: a URI with no authority component, and one whose authority names an IP
|
|
650
|
+
* address. Its dNSName paragraph states no such requirement, so an address is a valid dNSName base
|
|
651
|
+
* and is not a valid URI base. Neither paragraph states a label count. A caller meets this rule at
|
|
652
|
+
* the door, whether through `pki.trust.anchor` or through the validator's subtree seeds. A base a
|
|
653
|
+
* certificate carries in its own extension never reaches a door, and `uriMatch` holds it to the
|
|
654
|
+
* same `isFqdnHost` this calls, so the door and the comparison cannot answer differently. */
|
|
549
655
|
if (tag === 6) {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
656
|
+
/** @internal Only the leading dot comes off here. `isFqdnHost` takes a host as a URI carries
|
|
657
|
+
* it and strips the one trailing dot that makes a name absolute, so stripping it here too
|
|
658
|
+
* would let a base ending in two dots through as one ending in none. */
|
|
659
|
+
var uriBody = _toLowerCase(base);
|
|
660
|
+
if (_charAt(uriBody, 0) === ".") uriBody = _strSlice(uriBody, 1);
|
|
661
|
+
if (isFqdnHost(uriBody)) return null;
|
|
662
|
+
return _allCharsIn(stripTrailingDot(uriBody), _DIGDOT_TABLE)
|
|
663
|
+
? "must be a domain name rather than an address, RFC 5280 sec. 4.2.1.10 naming an address in a URI's authority as what a URI constraint cannot be compared against"
|
|
664
|
+
: "must be a domain name, which RFC 5280 sec. 4.2.1.10 requires of a URI constraint, optionally written as a subtree with a leading dot";
|
|
554
665
|
}
|
|
555
666
|
return null;
|
|
556
667
|
}
|
|
@@ -661,6 +772,7 @@ module.exports = intrinsic.freeze({
|
|
|
661
772
|
isAttributeDescriptor: isAttributeDescriptor,
|
|
662
773
|
lowerAscii: lowerAscii, uriSchemeIs: uriSchemeIs,
|
|
663
774
|
stripTrailingDot: stripTrailingDot, isFqdnHost: isFqdnHost, splitMailbox: splitMailbox,
|
|
775
|
+
isHostName: isHostName, hostLabels: hostLabels, isXnLabel: isXnLabel, uriParts: uriParts,
|
|
664
776
|
constraintBaseRefusal: constraintBaseRefusal,
|
|
665
777
|
constraintBaseUnreadable: constraintBaseUnreadable,
|
|
666
778
|
});
|
package/lib/guard-text.js
CHANGED
|
@@ -50,4 +50,36 @@ function keyOf(v) {
|
|
|
50
50
|
return typeof v === "string" || typeof v === "number" ? v : undefined;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
/** @internal What a caught value says about itself, as a string, for a boundary that reports a
|
|
54
|
+
* fault it did not cause. The value came from caller code, so every step of reading it is a step
|
|
55
|
+
* the caller chose the behavior of: `message` may be an accessor that throws, a Symbol that
|
|
56
|
+
* refuses string conversion, or an object whose `toString` throws, and the value itself may be a
|
|
57
|
+
* Proxy that refuses every read. A boundary whose whole contract is to turn a fault into a verdict
|
|
58
|
+
* cannot afford any of those to leave it, which is what happens when the description is built by
|
|
59
|
+
* reading `e.message` in the open.
|
|
60
|
+
*
|
|
61
|
+
* Every read is attempted and none is required. A value that answers nothing readable is described
|
|
62
|
+
* by its type, which is what `showValue` would have said about it anyway.
|
|
63
|
+
*
|
|
64
|
+
* @enforced-by guard-shape-reinlined
|
|
65
|
+
* @guard-shape /([\w$]+)\s*&&\s*\1\.message\s*\?\s*\1\.message\s*:|([\w$]+)\.message\s*\|\|\s*\2\b/ */
|
|
66
|
+
function describeThrown(e) {
|
|
67
|
+
var m;
|
|
68
|
+
try { m = e === null || e === undefined ? undefined : e.message; }
|
|
69
|
+
catch (_readFailed) { return _thrownFallback(e); }
|
|
70
|
+
if (typeof m === "string" && m.length > 0) return m;
|
|
71
|
+
if (m === undefined || m === null) return _thrownFallback(e);
|
|
72
|
+
try {
|
|
73
|
+
if (typeof m === "symbol") return intrinsic.String(m);
|
|
74
|
+
return "" + m;
|
|
75
|
+
} catch (_convertFailed) { return _thrownFallback(e); }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function _thrownFallback(e) {
|
|
79
|
+
try { return showValue(e); }
|
|
80
|
+
catch (_showFailed) { return "a value that reports nothing readable"; }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = intrinsic.freeze({
|
|
84
|
+
decode: decode, showValue: showValue, keyOf: keyOf, describeThrown: describeThrown,
|
|
85
|
+
});
|