@blamejs/pki 0.5.15 → 0.5.17

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/lib/acme.js +1 -0
  3. package/lib/asn1-der.js +1 -0
  4. package/lib/attrcert-sign.js +1 -0
  5. package/lib/cbor-det.js +1 -0
  6. package/lib/cmc-verify.js +1 -0
  7. package/lib/cmp-build.js +1 -0
  8. package/lib/cmp-verify.js +425 -82
  9. package/lib/cms-decrypt.js +58 -29
  10. package/lib/cms-encrypt.js +45 -24
  11. package/lib/cms-sign.js +3 -3
  12. package/lib/cms-verify.js +5 -1
  13. package/lib/constants.js +1 -0
  14. package/lib/crl-sign.js +1 -0
  15. package/lib/crmf-sign.js +1 -0
  16. package/lib/csr-sign.js +1 -0
  17. package/lib/ct.js +1 -0
  18. package/lib/est.js +1 -0
  19. package/lib/framework-error.js +1 -0
  20. package/lib/guard-all.js +2 -0
  21. package/lib/guard-async.js +14 -2
  22. package/lib/guard-bytes.js +183 -93
  23. package/lib/guard-compress.js +32 -12
  24. package/lib/guard-der.js +30 -7
  25. package/lib/guard-encoding.js +28 -12
  26. package/lib/guard-header.js +18 -7
  27. package/lib/guard-identifier.js +146 -79
  28. package/lib/guard-intrinsic.js +318 -0
  29. package/lib/guard-json.js +39 -12
  30. package/lib/guard-limits.js +15 -6
  31. package/lib/guard-name.js +72 -23
  32. package/lib/guard-parsed.js +71 -34
  33. package/lib/guard-range.js +17 -4
  34. package/lib/guard-secret.js +60 -2
  35. package/lib/guard-text.js +35 -6
  36. package/lib/guard-time.js +13 -4
  37. package/lib/hpke.js +6 -2
  38. package/lib/http-transport.js +1 -0
  39. package/lib/inspect.js +1 -0
  40. package/lib/jose.js +1 -0
  41. package/lib/key.js +2 -1
  42. package/lib/lint.js +1 -0
  43. package/lib/merkle.js +1 -0
  44. package/lib/ocsp.js +1 -0
  45. package/lib/oid.js +1 -0
  46. package/lib/path-validate.js +1 -0
  47. package/lib/pbes2.js +16 -8
  48. package/lib/pkcs12-build.js +2 -1
  49. package/lib/schema-all.js +1 -0
  50. package/lib/schema-c509.js +1 -0
  51. package/lib/schema-csrattrs.js +1 -0
  52. package/lib/schema-engine.js +1 -0
  53. package/lib/schema-pkcs8.js +1 -0
  54. package/lib/schema-smime.js +1 -0
  55. package/lib/schema-tsp.js +1 -0
  56. package/lib/schema-x509.js +1 -0
  57. package/lib/shbs.js +1 -0
  58. package/lib/sigstore.js +1 -0
  59. package/lib/smime.js +1 -0
  60. package/lib/tls-cert-compress.js +1 -0
  61. package/lib/trust.js +1 -0
  62. package/lib/tsp-sign.js +1 -0
  63. package/lib/webauthn.js +1 -0
  64. package/lib/webcrypto.js +11 -8
  65. package/lib/x509-sign.js +1 -0
  66. package/package.json +1 -1
  67. package/sbom.cdx.json +6 -6
@@ -0,0 +1,318 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) blamejs contributors
3
+ "use strict";
4
+ //
5
+ // @internal -- no operator-facing namespace. The documented surface is every guard that composes
6
+ // this one, which is every guard that performs its work with a captured intrinsic: guard-bytes
7
+ // (the byte-source door), guard-time (the evaluation instant), guard-name (distinguished-name
8
+ // identity), guard-text (the byte-to-string decode), guard-secret (the wipe).
9
+ //
10
+ // guard-intrinsic -- invoke a captured method against a receiver without reading any property of
11
+ // the captured function.
12
+ //
13
+ // Capturing a method at module load closes half of the hole. `var _fill =
14
+ // Uint8Array.prototype.fill` freezes which function the guard will run, so a later
15
+ // `Uint8Array.prototype.fill = noop` changes nothing. Then `_fill.call(view, 0)` reads `call` off
16
+ // that function object, and the object is the one still sitting on the prototype where any caller
17
+ // can reach it. Assigning `Uint8Array.prototype.fill.call = function () {}` installs an own
18
+ // property that shadows `Function.prototype.call`, and the guard runs the caller's function with
19
+ // the same result the un-captured code had. The wipe clears nothing. A distinguished name compares
20
+ // equal to an unrelated one. A decode returns a string the caller chose. Every check around it
21
+ // still runs and still passes.
22
+ //
23
+ // The uncurried form performs no property read at the call site. `_fill(view, 0)` invokes a
24
+ // function object no caller holds a reference to, so nothing on it is left to poison. The binding
25
+ // happens here, at load, while `Function.prototype.call` and `.bind` are still the language's own.
26
+ //
27
+ // The scope of the defense is replacement that happens after this module loads. Code running
28
+ // before it is already inside the trust boundary and can reach further than any single method, so
29
+ // there is no defense here to add for that case. What capture-then-uncurry buys is that a caller
30
+ // who receives control later, through an option accessor or while a verb is suspended on an await,
31
+ // cannot change what a guard does.
32
+
33
+ var _utilTypes = require("util").types;
34
+
35
+ var _call = Function.prototype.call;
36
+ var _bind = Function.prototype.bind;
37
+ // The invocation primitive `uncurry` itself is built out of. Binding as `_bind.call(_call, fn)`
38
+ // would read `call` off the captured bind function, which is the very shape this module exists to
39
+ // remove -- and `uncurry` is exported, so it can run long after load, while another guard
40
+ // initializes. `Reflect.apply` is invoked directly, so no property of it is consulted either.
41
+ var _reflectApply = Reflect.apply;
42
+
43
+ // isArray -- the array test, snapshotted for the same reason as the predicates below. It decides
44
+ // whether a value is walked as a list at all, so one answering `false` makes an index scan report
45
+ // nothing and every rule keyed on what that scan found silently applies to nothing.
46
+ var _isArray = Array.isArray;
47
+
48
+ // The Buffer constructors, snapshotted for the same reason. `Buffer.from` is a writable property
49
+ // of the `Buffer` global and it is how this toolkit turns a backing store into something it can
50
+ // read or clear; one returning a buffer over DIFFERENT memory hands every later step a decoy, so a
51
+ // wipe zeroes the decoy while the plaintext it was aimed at stays readable.
52
+ var _bufferFrom = Buffer.from;
53
+ var _bufferAlloc = Buffer.alloc;
54
+ var _bufferConcat = Buffer.concat;
55
+ var _bufferIsBuffer = Buffer.isBuffer;
56
+ var _bufferByteLength = Buffer.byteLength;
57
+ var _bufferCompare = Buffer.compare;
58
+
59
+ // The rest of what a guard reads from the runtime while it is deciding something. These are
60
+ // grouped here rather than captured file by file so the set is enumerable in one place and a guard
61
+ // added later inherits it: a reader asking "what can a caller still reach" has one list to read.
62
+ //
63
+ // The reflective operations are the load-bearing ones. `getOwnPropertyDescriptor` is how the
64
+ // identifier guard learns whether a field is an accessor, `getPrototypeOf` and `ownKeys` are how it
65
+ // walks a chain honestly, and `isView` is how the byte guard recognizes the shape whose backing
66
+ // store it must refuse when shared. A replacement for any of them answers the question the guard
67
+ // asked on the caller's behalf.
68
+ var _isView = ArrayBuffer.isView;
69
+ var _isInteger = Number.isInteger;
70
+ var _isSafeInteger = Number.isSafeInteger;
71
+ var _fromCharCode = String.fromCharCode;
72
+ var _objectCreate = Object.create;
73
+ var _getPrototypeOf = Object.getPrototypeOf;
74
+ var _setPrototypeOf = Object.setPrototypeOf;
75
+ var _getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
76
+ var _getOwnPropertyNames = Object.getOwnPropertyNames;
77
+ var _defineProperty = Object.defineProperty;
78
+ var _objectKeys = Object.keys;
79
+ var _objectAssign = Object.assign;
80
+ var _objectFreeze = Object.freeze;
81
+ var _isExtensible = Object.isExtensible;
82
+ var _ownKeys = Reflect.ownKeys;
83
+ var _jsonStringify = JSON.stringify;
84
+ var _mathFloor = Math.floor;
85
+ var _mathCeil = Math.ceil;
86
+ var _mathMin = Math.min;
87
+ var _promiseResolve = Promise.resolve;
88
+ var _promiseReject = Promise.reject;
89
+
90
+ // The array operations, uncurried at load. These run on arrays the TOOLKIT owns -- an `ownKeys`
91
+ // result, a local accumulator -- so the receiver is never a caller's object; what is caller-
92
+ // reachable is `Array.prototype` itself. A `forEach` replaced with a no-op makes a scan over
93
+ // perfectly real keys report nothing, and a check keyed on what that scan found then passes
94
+ // vacuously, which is the same fail-open shape as an index test that answers `false`.
95
+ var _arrForEach = uncurry(Array.prototype.forEach);
96
+ var _arrMap = uncurry(Array.prototype.map);
97
+ var _arrFilter = uncurry(Array.prototype.filter);
98
+ var _arrEvery = uncurry(Array.prototype.every);
99
+ var _arrSome = uncurry(Array.prototype.some);
100
+ var _arrIndexOf = uncurry(Array.prototype.indexOf);
101
+ var _arrSort = uncurry(Array.prototype.sort);
102
+ var _arrConcat = uncurry(Array.prototype.concat);
103
+ var _arrSlice = uncurry(Array.prototype.slice);
104
+ var _arrJoin = uncurry(Array.prototype.join);
105
+
106
+ // The collection operations, uncurried. A guard's cycle-safe chain walk is a `Set` it owns, and
107
+ // `has` is what stops it: one answering `true` ends the walk at the first prototype, so a scan that
108
+ // should have found an accessor two levels up reports nothing and the rule keyed on it passes. The
109
+ // registry lookups are the same shape from the other side -- a `get` that answers for a key never
110
+ // stored hands a guard a member list for an object it has never seen.
111
+ var _setAdd = uncurry(Set.prototype.add);
112
+ var _setHas = uncurry(Set.prototype.has);
113
+ var _mapGet = uncurry(Map.prototype.get);
114
+ var _mapSet = uncurry(Map.prototype.set);
115
+ var _mapHas = uncurry(Map.prototype.has);
116
+ var _weakGet = uncurry(WeakMap.prototype.get);
117
+ var _weakSet = uncurry(WeakMap.prototype.set);
118
+ var _weakHas = uncurry(WeakMap.prototype.has);
119
+ var _taSet = uncurry(Uint8Array.prototype.set);
120
+ // Own-membership, which is how this toolkit asks whether a key is really in a table rather than
121
+ // inherited from a prototype somebody extended. Spelled `Object.prototype.hasOwnProperty.call(t, k)`
122
+ // it reads two replaceable properties to answer one question, and either of them answering yes puts
123
+ // a name into an allowlist that never contained it.
124
+ var _hasOwn = uncurry(Object.prototype.hasOwnProperty);
125
+ // The conversions, which are globals and so ordinary writable properties of globalThis. They read
126
+ // like language rather than like code, which is why they were the last of these to be noticed: an
127
+ // index test is `String(Number(k)) === k`, a narrow is `Number(v)`, and a header value is
128
+ // `String(value)` before it is scanned for injection. A replacement decides all three.
129
+ var _String = String;
130
+ var _Number = Number;
131
+ var _Boolean = Boolean;
132
+ var _BigInt = BigInt;
133
+ var _Date = Date;
134
+ var _isFinite = globalThis.isFinite;
135
+ // The byte-view constructors, for the same reason: a private copy is only private if the store and
136
+ // the view over it are the ones this code asked for. A replaced constructor hands back a view over
137
+ // storage of its own choosing, while the cleanup still tracks the store that was allocated.
138
+ var _ArrayBuffer = ArrayBuffer;
139
+ var _Uint8Array = Uint8Array;
140
+ var _DataView = DataView;
141
+ // Boxing a primitive, which decides what a reflection scan then sees.
142
+ var _ObjectFn = Object;
143
+ // The prototype OBJECTS, used as identity sentinels: "is this a plain object", "is this a Buffer",
144
+ // "has the chain walk reached the top". Each is reached as a property of a replaceable global, so
145
+ // after a replacement the sentinel is a different object and every comparison against it is false.
146
+ var _ObjectProto = Object.prototype;
147
+ var _BufferProto = Buffer.prototype;
148
+ var _ArrayProto = Array.prototype;
149
+ var _Set = Set;
150
+ var _Map = Map;
151
+ var _WeakMap = WeakMap;
152
+
153
+ // types -- the slot predicates, snapshotted at load.
154
+ //
155
+ // `util.types` is an ordinary object on an ordinary module export, so `util.types.isDate = () =>
156
+ // true` reaches every guard that asks it at call time. These predicates are how this toolkit
157
+ // answers "what IS this value", in preference to a prototype test a caller can satisfy, so a
158
+ // caller who gets to answer them instead has replaced the one question a lookalike cannot lie
159
+ // about. Every own function property is carried across rather than a written-out list, because a
160
+ // list is a thing to get wrong and a predicate added by a later runtime would silently be missing
161
+ // from it.
162
+ var _types = Object.create(null);
163
+ var _typeNames = Object.getOwnPropertyNames(_utilTypes);
164
+ for (var _i = 0; _i < _typeNames.length; _i++) {
165
+ if (typeof _utilTypes[_typeNames[_i]] === "function") {
166
+ _types[_typeNames[_i]] = _utilTypes[_typeNames[_i]];
167
+ }
168
+ }
169
+ Object.freeze(_types);
170
+
171
+ // uncurry(fn) -> function (receiver, ...args) { return fn.call(receiver, ...args); }
172
+ // fn : the intrinsic captured from a prototype, or an intrinsic getter function.
173
+ //
174
+ // The returned function is private to whichever module called `uncurry`, so no property of it is
175
+ // reachable from outside.
176
+ //
177
+ // A `.call` on a captured intrinsic is the shape this replaces. It reads as safe while being as
178
+ // reachable as the un-captured method was, so it is flagged anywhere in lib/, including a guard
179
+ // not yet written. The safe spelling is also the tripwire for the next one.
180
+ // @enforced-by guard-shape-reinlined
181
+ // @guard-shape \b_[A-Za-z][A-Za-z0-9_$]*\.(?:call|apply)\s*\(
182
+ function uncurry(fn) {
183
+ if (typeof fn !== "function") {
184
+ throw new TypeError("guard.intrinsic.uncurry: expects the captured function, got " + typeof fn);
185
+ }
186
+ return _reflectApply(_bind, _call, [fn]);
187
+ }
188
+
189
+ // getter(proto, name, who) -> the prototype's own accessor, uncurried.
190
+ // proto : the intrinsic prototype the accessor lives on.
191
+ // name : the accessor's property name.
192
+ // who : how the prototype is named in the refusal, for a runtime that has no such accessor.
193
+ //
194
+ // A view's size, offset and backing store are accessors on a prototype, which means they are
195
+ // replaceable, which means every rule keyed on one is answerable by whoever replaced it. Taking the
196
+ // descriptor's `get` at load and uncurrying it reads the value's own slot and consults nothing.
197
+ // A runtime without the accessor is refused rather than fallen back from: a fallback here would be
198
+ // the value's own `length`, which is the property this exists to stop reading.
199
+ // @enforced-by behavioral -- reading a descriptor's `get` is not distinguishable from testing one
200
+ // for presence (`!!d.get`, which four guards do legitimately), so a lexical detector would
201
+ // false-fire on every accessor-shape check in the tree; the vectors that replace an accessor and
202
+ // drive the composing guard are the guard. The `.call` half of the shape is covered by uncurry's
203
+ // guard-shape-reinlined detector.
204
+ function getter(proto, name, who) {
205
+ var d = _getOwnPropertyDescriptor(proto, name);
206
+ if (!d || typeof d.get !== "function") {
207
+ throw new TypeError("guard.intrinsic.getter: this runtime has no intrinsic " + who + "." +
208
+ name + " accessor, so the value's own would have to be invoked instead");
209
+ }
210
+ return uncurry(d.get);
211
+ }
212
+
213
+ var _taByteLength = getter(_getPrototypeOf(Uint8Array.prototype), "byteLength", "%TypedArray%.prototype");
214
+ var _dvByteLength = getter(DataView.prototype, "byteLength", "DataView.prototype");
215
+
216
+ // sizeOf(value) -> how large the value is: a string's character count, a view's byte count.
217
+ //
218
+ // The size of an input decides a cap, a bound, an emptiness gate and a loop end all over this
219
+ // toolkit, and `value.length` asks the prototype for it. On a view that property is a replaceable
220
+ // accessor: one reporting a small number for a large buffer disables every size cap at once, and
221
+ // one reporting a positive number for an empty buffer passes an empty secret, an empty DER element
222
+ // and an empty name through the gates that exist to refuse them. So a view is measured from its
223
+ // slot, and a DataView carries its own accessor rather than the typed-array one.
224
+ //
225
+ // A primitive string's `length` is a data property the language maintains and no code can redefine,
226
+ // so the string arm reads it directly. The two arms are one function because a size comparison
227
+ // should have one spelling: a second way to measure is a second thing to get wrong, and the arm
228
+ // that gets skipped is the one that was replaceable. Anything that is neither is refused rather
229
+ // than measured, since its `length` would be whatever its author wrote.
230
+ // @enforced-by behavioral -- `x.length` on a byte view is the same three tokens as on a string or
231
+ // an array, where the read is safe and ubiquitous, and the receiver's kind is not knowable
232
+ // statically in this codebase's style; a detector would flag every array bound in the tree. The
233
+ // over-cap and control-byte vectors under a replaced accessor are the guard.
234
+ function sizeOf(value) {
235
+ if (typeof value === "string") return value.length;
236
+ if (_types.isDataView(value)) return _dvByteLength(value);
237
+ if (_types.isTypedArray(value)) return _taByteLength(value);
238
+ throw new TypeError("guard.intrinsic.sizeOf: expects a string or a byte view, got " +
239
+ (value === null ? "null" : typeof value));
240
+ }
241
+
242
+ module.exports = {
243
+ uncurry: uncurry,
244
+ getter: getter,
245
+ sizeOf: sizeOf,
246
+ types: _types,
247
+ isArray: _isArray,
248
+ bufferFrom: _bufferFrom,
249
+ bufferAlloc: _bufferAlloc,
250
+ bufferConcat: _bufferConcat,
251
+ isBuffer: _bufferIsBuffer,
252
+ byteLength: _bufferByteLength,
253
+ compare: _bufferCompare,
254
+ isView: _isView,
255
+ isInteger: _isInteger,
256
+ isSafeInteger: _isSafeInteger,
257
+ fromCharCode: _fromCharCode,
258
+ create: _objectCreate,
259
+ getPrototypeOf: _getPrototypeOf,
260
+ setPrototypeOf: _setPrototypeOf,
261
+ getOwnPropertyDescriptor: _getOwnPropertyDescriptor,
262
+ getOwnPropertyNames: _getOwnPropertyNames,
263
+ defineProperty: _defineProperty,
264
+ keys: _objectKeys,
265
+ assign: _objectAssign,
266
+ freeze: _objectFreeze,
267
+ isExtensible: _isExtensible,
268
+ ownKeys: _ownKeys,
269
+ stringify: _jsonStringify,
270
+ floor: _mathFloor,
271
+ ceil: _mathCeil,
272
+ min: _mathMin,
273
+ promiseResolve: _promiseResolve,
274
+ promiseReject: _promiseReject,
275
+ forEach: _arrForEach,
276
+ map: _arrMap,
277
+ filter: _arrFilter,
278
+ every: _arrEvery,
279
+ some: _arrSome,
280
+ indexOf: _arrIndexOf,
281
+ sort: _arrSort,
282
+ concat: _arrConcat,
283
+ arraySlice: _arrSlice,
284
+ join: _arrJoin,
285
+ hasOwn: _hasOwn,
286
+ // Invocation itself. `fn.apply(...)` reads `apply` off Function.prototype, so a replacement
287
+ // decides whether a verb's body runs at all and what it appears to return; `Reflect.apply` is
288
+ // invoked directly and consults no property of the function.
289
+ apply: _reflectApply,
290
+ // And the cleanup half of a promise chain: `.finally` is where a copied secret is released, so a
291
+ // replacement that drops its callback leaves the plaintext copy alive for the process's lifetime.
292
+ promiseFinally: uncurry(Promise.prototype["finally"]),
293
+ String: _String,
294
+ Number: _Number,
295
+ Boolean: _Boolean,
296
+ BigInt: _BigInt,
297
+ Date: _Date,
298
+ isFinite: _isFinite,
299
+ ArrayBuffer: _ArrayBuffer,
300
+ Uint8Array: _Uint8Array,
301
+ DataView: _DataView,
302
+ Object: _ObjectFn,
303
+ ObjectProto: _ObjectProto,
304
+ BufferProto: _BufferProto,
305
+ ArrayProto: _ArrayProto,
306
+ setAdd: _setAdd,
307
+ setHas: _setHas,
308
+ mapGet: _mapGet,
309
+ mapSet: _mapSet,
310
+ mapHas: _mapHas,
311
+ weakGet: _weakGet,
312
+ weakSet: _weakSet,
313
+ weakHas: _weakHas,
314
+ typedArraySet: _taSet,
315
+ Set: _Set,
316
+ Map: _Map,
317
+ WeakMap: _WeakMap,
318
+ };
package/lib/guard-json.js CHANGED
@@ -23,6 +23,31 @@
23
23
  // grammar (no leading zero, a fraction/exponent needs a digit, no bare "-").
24
24
  //
25
25
  var text = require("./guard-text");
26
+ // Taken at load. `defineProperty` is how a decoded member lands on the result without consulting a
27
+ // setter, and `fromCharCode` is how an escape becomes the character it names; a replacement for
28
+ // either decides what this parser produces. See guard-intrinsic for the whole captured set.
29
+ var _defineProperty = require("./guard-intrinsic").defineProperty;
30
+ var _fromCharCode = require("./guard-intrinsic").fromCharCode;
31
+ // The character-code read the whole scanner is built on: whitespace, escapes and the control-byte
32
+ // reject all consult it, so one that lies decides what this parser sees in the input.
33
+ var _charCodeAt = require("./guard-intrinsic").uncurry(String.prototype.charCodeAt);
34
+ // The scanner has already found where a number literal starts and ends; this is the step that cuts
35
+ // it out. A replacement hands `Number` a different span than the one the parser validated, so the
36
+ // value the document is understood to carry is not the value its bytes spell.
37
+ var _strSlice = require("./guard-intrinsic").uncurry(String.prototype.slice);
38
+ // The duplicate-member gate below asks whether a key is already present. Spelled
39
+ // `Object.prototype.hasOwnProperty.call(...)` it reads two replaceable properties to answer that,
40
+ // and either of them answering wrongly lets a document carry the same member twice.
41
+ var _hasOwn = require("./guard-intrinsic").hasOwn;
42
+ // The conversion that turns a validated number literal into the value the document is understood to
43
+ // carry. A replacement returns something else for text this parser has already checked.
44
+ var _Number = require("./guard-intrinsic").Number;
45
+ // The gate that refuses a numeric token that overflowed to Infinity. A replacement answering true
46
+ // admits it, and the document is understood to carry a value its bytes cannot spell.
47
+ var _isFinite = require("./guard-intrinsic").isFinite;
48
+ var _stringify = require("./guard-intrinsic").stringify;
49
+ // Appended by index rather than through `push`, so the growth reads no prototype method at all.
50
+ function _append(arr, v) { _defineProperty(arr, arr.length, { value: v, writable: true, enumerable: true, configurable: true }); }
26
51
  var limits = require("./guard-limits");
27
52
 
28
53
  // One hex digit's value, or -1. Written out because the three ranges ARE the rule.
@@ -55,7 +80,7 @@ function parse(input, ErrorClass, spec) {
55
80
  charset: "utf-8", fatal: true, tooLarge: spec.tooLarge, badDecode: spec.badJson, badInput: spec.badInput, label: spec.label,
56
81
  });
57
82
  var i = 0, n = str.length;
58
- function ws() { while (i < n) { var c = str.charCodeAt(i); if (c === 0x20 || c === 0x09 || c === 0x0a || c === 0x0d) i++; else break; } }
83
+ function ws() { while (i < n) { var c = _charCodeAt(str, i); if (c === 0x20 || c === 0x09 || c === 0x0a || c === 0x0d) i++; else break; } }
59
84
  function fail(msg) { throw E(spec.badJson, "invalid JSON at offset " + i + ": " + msg); }
60
85
  function value(depth) {
61
86
  if (depth > maxDepth) throw E(spec.tooDeep, "JSON nesting exceeds the depth cap");
@@ -66,9 +91,11 @@ function parse(input, ErrorClass, spec) {
66
91
  if (c === "[") return array(depth);
67
92
  if (c === "\"") return string();
68
93
  if (c === "-" || (c >= "0" && c <= "9")) return number();
69
- if (str.substr(i, 4) === "true") { i += 4; return true; }
70
- if (str.substr(i, 5) === "false") { i += 5; return false; }
71
- if (str.substr(i, 4) === "null") { i += 4; return null; }
94
+ // Through the captured slice, not `substr`: these three comparisons ARE the literal grammar, so
95
+ // one returning something else accepts a token the document does not spell.
96
+ if (_strSlice(str, i, i + 4) === "true") { i += 4; return true; }
97
+ if (_strSlice(str, i, i + 5) === "false") { i += 5; return false; }
98
+ if (_strSlice(str, i, i + 4) === "null") { i += 4; return null; }
72
99
  fail("unexpected token");
73
100
  return undefined;
74
101
  }
@@ -81,13 +108,13 @@ function parse(input, ErrorClass, spec) {
81
108
  ws();
82
109
  if (str[i] !== "\"") fail("expected a string key");
83
110
  var key = string();
84
- if (Object.prototype.hasOwnProperty.call(out, key)) throw E(spec.duplicateMember, "duplicate JSON member " + JSON.stringify(key));
111
+ if (_hasOwn(out, key)) throw E(spec.duplicateMember, "duplicate JSON member " + _stringify(key));
85
112
  ws();
86
113
  if (str[i] !== ":") fail("expected ':'");
87
114
  i++;
88
115
  // Own data property (not out[key]=...): a "__proto__" key becomes a normal
89
116
  // member, never a prototype mutation nor a silent duplicate-gate bypass.
90
- Object.defineProperty(out, key, { value: value(depth + 1), writable: true, enumerable: true, configurable: true });
117
+ _defineProperty(out, key, { value: value(depth + 1), writable: true, enumerable: true, configurable: true });
91
118
  ws();
92
119
  if (str[i] === ",") { i++; continue; }
93
120
  if (str[i] === "}") { i++; return out; }
@@ -100,7 +127,7 @@ function parse(input, ErrorClass, spec) {
100
127
  ws();
101
128
  if (str[i] === "]") { i++; return out; }
102
129
  for (;;) {
103
- out.push(value(depth + 1));
130
+ _append(out, value(depth + 1));
104
131
  ws();
105
132
  if (str[i] === ",") { i++; continue; }
106
133
  if (str[i] === "]") { i++; return out; }
@@ -132,14 +159,14 @@ function parse(input, ErrorClass, spec) {
132
159
  var cp = 0;
133
160
  if (i + 4 > n) fail("bad \\u escape");
134
161
  for (var h = 0; h < 4; h++) {
135
- var d = _hexVal(str.charCodeAt(i + h));
162
+ var d = _hexVal(_charCodeAt(str, i + h));
136
163
  if (d < 0) fail("bad \\u escape");
137
164
  cp = (cp << 4) | d;
138
165
  }
139
- s += String.fromCharCode(cp);
166
+ s += _fromCharCode(cp);
140
167
  i += 4;
141
168
  } else fail("bad escape");
142
- } else if (c.charCodeAt(0) < 0x20) {
169
+ } else if (_charCodeAt(c, 0) < 0x20) {
143
170
  fail("control character in string");
144
171
  } else s += c;
145
172
  }
@@ -172,8 +199,8 @@ function parse(input, ErrorClass, spec) {
172
199
  while (i < n && str[i] >= "0" && str[i] <= "9") i++;
173
200
  if (i === expStart) fail("malformed number"); // "1e" has no exponent
174
201
  }
175
- var v = Number(str.slice(start, i));
176
- if (!isFinite(v)) fail("bad number");
202
+ var v = _Number(_strSlice(str, start, i));
203
+ if (!_isFinite(v)) fail("bad number");
177
204
  return v;
178
205
  }
179
206
  var result = value(0);
@@ -23,6 +23,14 @@
23
23
  // that actually holds is the bound at the parser.
24
24
 
25
25
  var constants = require("./constants");
26
+ // The integer test, taken at load. Every bound below is only a bound while this answers honestly:
27
+ // one returning true admits a NaN or fractional cap, which makes the comparison it guards always
28
+ // false. See guard-intrinsic for the whole captured set.
29
+ var _isInteger = require("./guard-intrinsic").isInteger;
30
+ // And the size measurement, for the same reason one step further in: a cap compares against a
31
+ // length, and on a byte view `length` is a replaceable accessor. One reporting a small number for a
32
+ // large buffer leaves every byteCap below always-false, which is the size defense silently dead.
33
+ var _sizeOf = require("./guard-intrinsic").sizeOf;
26
34
 
27
35
  // cap(value, key, dflt, opts) -> a validated bounded integer, or dflt when value
28
36
  // is undefined. A non-integer / non-finite / negative value fails closed
@@ -49,10 +57,10 @@ function cap(value, key, dflt, opts) {
49
57
  // compares false against every value, silently disabling the check (a NaN min
50
58
  // even REPLACES the default >= 0 floor). Always a config-time TypeError --
51
59
  // the bound wiring is broken regardless of the value currency opts.E selects.
52
- if (!Number.isInteger(min) || (opts.max !== undefined && !Number.isInteger(opts.max))) {
60
+ if (!_isInteger(min) || (opts.max !== undefined && !_isInteger(opts.max))) {
53
61
  throw new TypeError("guard.limits.cap: the min/max bounds for " + key + " must be integers");
54
62
  }
55
- if (!Number.isInteger(value) || value < min || (opts.max !== undefined && value > opts.max)) {
63
+ if (!_isInteger(value) || value < min || (opts.max !== undefined && value > opts.max)) {
56
64
  var want = "an integer >= " + min + (opts.max !== undefined ? " and <= " + opts.max : "");
57
65
  if (min === 0 && opts.max === undefined) want = "a non-negative integer";
58
66
  if (opts.E) throw opts.E(opts.code, (opts.label || key) + " must be " + want);
@@ -91,7 +99,7 @@ function counter(max, E, code, label) {
91
99
  // The ceiling is an authoring input: an undefined / NaN / fractional max
92
100
  // builds a counter whose `n > max` never fires, leaving a silently dead fanout
93
101
  // defense. Reject at construction (config-time TypeError).
94
- if (!Number.isInteger(max) || max < 0) {
102
+ if (!_isInteger(max) || max < 0) {
95
103
  throw new TypeError("guard.limits.counter: max must be a non-negative integer");
96
104
  }
97
105
  var n = 0;
@@ -119,11 +127,12 @@ function byteCap(buf, max, E, code, label) {
119
127
  // The ceiling is an authoring input: an undefined / NaN / fractional / negative
120
128
  // max makes `length > max` never fire, and the size defense is then silently
121
129
  // dead. Reject at config time (TypeError), regardless of the value currency E selects.
122
- if (!Number.isInteger(max) || max < 0) {
130
+ if (!_isInteger(max) || max < 0) {
123
131
  throw new TypeError("guard.limits.byteCap: max must be a non-negative integer");
124
132
  }
125
- if (buf.length > max) {
126
- throw E(code, (label || "input") + " is " + buf.length + " bytes, over the " + max + "-byte cap");
133
+ var size = _sizeOf(buf);
134
+ if (size > max) {
135
+ throw E(code, (label || "input") + " is " + size + " bytes, over the " + max + "-byte cap");
127
136
  }
128
137
  return buf;
129
138
  }
package/lib/guard-name.js CHANGED
@@ -25,6 +25,35 @@
25
25
  // match, or (the mirror risk) a name constraint is escaped. Every DN identity
26
26
  // decision routes through the one canonical comparison here.
27
27
 
28
+ var intrinsic = require("./guard-intrinsic");
29
+
30
+ // The string operations this module is built out of, taken from the prototypes at module load and
31
+ // uncurried so no call site reads a property of them. Every value compared here is a primitive
32
+ // string, so each call below would otherwise dispatch to whatever `String.prototype` holds at the
33
+ // moment it runs, and those are ordinary writable properties. A `toLowerCase` that returns a
34
+ // constant makes every DN compare equal to every other, which is the whole of this guard's job
35
+ // answered by the caller: chaining accepts an unrelated issuer, a revocation entry matches a
36
+ // certificate it was never written for, and a name constraint stops excluding anything. The same
37
+ // reasoning covers the escaping helpers, where a replaced `charAt` decides what a report line says
38
+ // a subject was. See guard-intrinsic for why the capture alone is not enough.
39
+ var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
40
+ var _charAt = intrinsic.uncurry(String.prototype.charAt);
41
+ var _toLowerCase = intrinsic.uncurry(String.prototype.toLowerCase);
42
+ var _toUpperCase = intrinsic.uncurry(String.prototype.toUpperCase);
43
+ var _strSlice = intrinsic.uncurry(String.prototype.slice);
44
+ var _lastIndexOf = intrinsic.uncurry(String.prototype.lastIndexOf);
45
+ var _numToString = intrinsic.uncurry(Number.prototype.toString);
46
+ var _create = intrinsic.create;
47
+ var _hasOwn = intrinsic.hasOwn;
48
+ // Already a plain function rather than a method, so it is invoked directly and has no receiver.
49
+ var _fromCharCode = String.fromCharCode;
50
+ var _String = String;
51
+ // The shape test that decides whether a comparison can be performed at all. Refusing is the
52
+ // fail-closed direction, so replacing this turns comparisons into refusals rather than into false
53
+ // matches, and it is captured for the same reason as the rest: what a guard does is settled before
54
+ // a caller runs, or it is settled by the caller.
55
+ var _isArray = Array.isArray;
56
+
28
57
  // assertNoControlBytes(str, E, code, label) -> str | throws E(code, ...)
29
58
  // DirectoryString policy (a DN attribute value): reject NUL and C0 control bytes
30
59
  // in a DECODED name string. TAB (0x09) is exempt; printable non-ASCII (a
@@ -35,7 +64,7 @@
35
64
  // (a DN string with an embedded NUL / control byte rejects) are the guard.
36
65
  function assertNoControlBytes(str, E, code, label) {
37
66
  for (var i = 0; i < str.length; i++) {
38
- var c = str.charCodeAt(i);
67
+ var c = _charCodeAt(str, i);
39
68
  if (c === 0 || (c < 0x20 && c !== 0x09)) {
40
69
  throw E(code, label + " contains an embedded control byte (CVE-2009-2408)");
41
70
  }
@@ -51,7 +80,10 @@ function assertNoControlBytes(str, E, code, label) {
51
80
  // code shape distinct from the ASN.1 IA5 reader; the CVE-2009-2408 RED vectors
52
81
  // (a SAN with a control byte rejects) are the guard.
53
82
  function assertPrintableIa5(buf, E, code, label) {
54
- for (var i = 0; i < buf.length; i++) {
83
+ // The scan end comes from the slot, not from `buf.length`. A shorter answer stops the walk before
84
+ // the control byte, which is the whole defense skipped on a name that still carries it.
85
+ var n = intrinsic.sizeOf(buf);
86
+ for (var i = 0; i < n; i++) {
55
87
  if (buf[i] < 0x20 || buf[i] > 0x7e) {
56
88
  throw E(code, label + " must be a printable IA5String (no control bytes)");
57
89
  }
@@ -78,12 +110,12 @@ function _canonAttrValue(v, E, code, label) {
78
110
  assertNoControlBytes(v, E, code, label);
79
111
  var out = "", lastWasSpace = true; // true so leading whitespace is dropped
80
112
  for (var i = 0; i < v.length; i++) {
81
- if (_isSpace(v.charCodeAt(i))) { lastWasSpace = true; continue; }
113
+ if (_isSpace(_charCodeAt(v, i))) { lastWasSpace = true; continue; }
82
114
  if (lastWasSpace && out.length) out += " ";
83
115
  lastWasSpace = false;
84
- out += v.charAt(i);
116
+ out += _charAt(v, i);
85
117
  }
86
- return out.toLowerCase();
118
+ return _toLowerCase(out);
87
119
  }
88
120
  // rdnEqual(a, b, E, code, label) -> boolean. Canonical comparison of a single
89
121
  // RelativeDistinguishedName (an unordered SET of type/value pairs, compared as a
@@ -100,15 +132,30 @@ function _canonAttrValue(v, E, code, label) {
100
132
  // performed refuses, and the caller learns at the call site instead of trusting a fabricated
101
133
  // match. (A caller holding a parsed Name passes its `.rdns`.)
102
134
  function _assertSequence(a, b, E, code, label, what) {
103
- if (!Array.isArray(a) || !Array.isArray(b)) {
135
+ if (!_isArray(a) || !_isArray(b)) {
104
136
  throw E(code, "cannot compare " + label + ": " + what + " comparison requires the RDN sequence on both sides (pass name.rdns, not the parsed Name)");
105
137
  }
138
+ // A hole is refused here rather than read below. Indexing a sparse array walks to
139
+ // `Array.prototype`, so a numeric property planted there supplies an attribute at the missing
140
+ // index and the comparison decides identity on a value the name does not carry. A real RDN
141
+ // sequence has no holes, so refusing costs nothing and there is no value to guess at.
142
+ if (_hasHole(a) || _hasHole(b)) {
143
+ throw E(code, "cannot compare " + label + ": " + what + " comparison requires a dense sequence on both sides");
144
+ }
145
+ }
146
+
147
+ function _hasHole(arr) {
148
+ for (var i = 0; i < arr.length; i++) { if (!_hasOwn(arr, i)) return true; }
149
+ return false;
106
150
  }
107
151
  // @enforced-by guard-shape-reinlined (shares the canonicalization shape declared on dnEqual)
108
152
  function rdnEqual(a, b, E, code, label) {
109
153
  _assertSequence(a, b, E, code, label, "an RDN");
110
154
  if (a.length !== b.length) return false;
111
- var used = [];
155
+ // A null-prototype accumulator rather than an array: a numeric property planted on
156
+ // `Array.prototype` reads back as "already matched" for an index nothing has matched, which skips
157
+ // a candidate attribute and reports two equal RDNs as different.
158
+ var used = _create(null);
112
159
  for (var i = 0; i < a.length; i++) {
113
160
  var found = false;
114
161
  for (var j = 0; j < b.length; j++) {
@@ -150,10 +197,12 @@ function dnEqual(rdnsA, rdnsB, E, code, label) {
150
197
  // @enforced-by guard-shape-reinlined
151
198
  // @guard-shape < 0x20 \|\| \w+ === 0x7f
152
199
  function escapeControlBytes(str) {
153
- var s = String(str), out = "";
200
+ var s = _String(str), out = "";
154
201
  for (var i = 0; i < s.length; i++) {
155
- var c = s.charCodeAt(i);
156
- out += (c < 0x20 || c === 0x7f) ? "\\x" + (c < 16 ? "0" : "") + c.toString(16).toUpperCase() : s.charAt(i);
202
+ var c = _charCodeAt(s, i);
203
+ out += (c < 0x20 || c === 0x7f)
204
+ ? "\\x" + (c < 16 ? "0" : "") + _toUpperCase(_numToString(c, 16))
205
+ : _charAt(s, i);
157
206
  }
158
207
  return out;
159
208
  }
@@ -177,17 +226,17 @@ var DN_SPECIAL = { 0x2c: 1, 0x2b: 1, 0x22: 1, 0x5c: 1, 0x3c: 1, 0x3e: 1, 0x3b: 1
177
226
  // from ordinary string building; the guard-name RED vectors + the schema-pkix DN
178
227
  // round-trip vectors (a comma / plus / leading '#' renders backslash-escaped) are the guard.
179
228
  function escapeDnValue(v) {
180
- var s = String(v), out = "";
229
+ var s = _String(v), out = "";
181
230
  // A NUL / control octet becomes '\' + two hex digits, so an embedded CR / LF / NUL
182
231
  // in a decoded DN value can never forge a report line when displayed.
183
232
  for (var i = 0; i < s.length; i++) {
184
- var c = s.charCodeAt(i);
185
- if (c < 0x20 || c === 0x7f) out += "\\" + (c < 16 ? "0" : "") + c.toString(16).toUpperCase();
186
- else if (DN_SPECIAL[c] === 1) out += "\\" + s.charAt(i);
187
- else out += s.charAt(i);
233
+ var c = _charCodeAt(s, i);
234
+ if (c < 0x20 || c === 0x7f) out += "\\" + (c < 16 ? "0" : "") + _toUpperCase(_numToString(c, 16));
235
+ else if (DN_SPECIAL[c] === 1) out += "\\" + _charAt(s, i);
236
+ else out += _charAt(s, i);
188
237
  }
189
- if (out.length && out.charAt(out.length - 1) === " ") out = out.slice(0, -1) + "\\ ";
190
- if (out.charAt(0) === "#" || out.charAt(0) === " ") out = "\\" + out;
238
+ if (out.length && _charAt(out, out.length - 1) === " ") out = _strSlice(out, 0, -1) + "\\ ";
239
+ if (_charAt(out, 0) === "#" || _charAt(out, 0) === " ") out = "\\" + out;
191
240
  return out;
192
241
  }
193
242
 
@@ -218,10 +267,10 @@ function emailEqual(a, b) {
218
267
  if (typeof a !== "string" || typeof b !== "string") return "not-comparable";
219
268
  // Split on the LAST "@": a quoted local-part may legally contain one, and taking the
220
269
  // first would move part of the mailbox into the domain and compare the wrong halves.
221
- var ai = a.lastIndexOf("@"), bi = b.lastIndexOf("@");
270
+ var ai = _lastIndexOf(a, "@"), bi = _lastIndexOf(b, "@");
222
271
  if (ai <= 0 || bi <= 0 || ai === a.length - 1 || bi === b.length - 1) return "not-comparable";
223
- var aLocal = a.slice(0, ai), bLocal = b.slice(0, bi);
224
- var aHost = a.slice(ai + 1), bHost = b.slice(bi + 1);
272
+ var aLocal = _strSlice(a, 0, ai), bLocal = _strSlice(b, 0, bi);
273
+ var aHost = _strSlice(a, ai + 1), bHost = _strSlice(b, bi + 1);
225
274
  if (!_asciiHost(aHost) || !_asciiHost(bHost)) return "not-comparable";
226
275
  if (aLocal !== bLocal) return "no-match";
227
276
  return _lowerAscii(aHost) === _lowerAscii(bHost) ? "match" : "no-match";
@@ -239,7 +288,7 @@ function _asciiHost(h) {
239
288
  // an internationalized domain, including one whose address matches exactly. The case an
240
289
  // IDNA transform WOULD be needed for is an A-label against a U-label, and the U-label
241
290
  // side is non-ASCII, so it is refused here anyway.
242
- for (var i = 0; i < h.length; i++) if (h.charCodeAt(i) > 0x7f) return false;
291
+ for (var i = 0; i < h.length; i++) if (_charCodeAt(h, i) > 0x7f) return false;
243
292
  return true;
244
293
  }
245
294
 
@@ -250,8 +299,8 @@ function _asciiHost(h) {
250
299
  function _lowerAscii(s) {
251
300
  var out = "";
252
301
  for (var i = 0; i < s.length; i++) {
253
- var c = s.charCodeAt(i);
254
- out += (c >= 0x41 && c <= 0x5a) ? String.fromCharCode(c + 32) : s.charAt(i);
302
+ var c = _charCodeAt(s, i);
303
+ out += (c >= 0x41 && c <= 0x5a) ? _fromCharCode(c + 32) : _charAt(s, i);
255
304
  }
256
305
  return out;
257
306
  }