@blamejs/pki 0.5.10 → 0.5.11

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.
@@ -1,6 +1,7 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // Copyright (c) blamejs contributors
3
3
  "use strict";
4
+ var util = require("util");
4
5
  //
5
6
  // @internal -- no operator-facing namespace. The documented surface is the
6
7
  // consumers whose identifier-string integrity composes this guard (pki.oid
@@ -97,16 +98,703 @@ function assertCanonicalOid(str, E, code, label, boundsCode) {
97
98
  // to, and a function (key) -> string builds the whole sentence, for the many callers that name the
98
99
  // key mid-sentence and follow it with the hint that says what to pass instead.
99
100
  //
101
+ // `known` and `message` are the module's own, and `obj` is the only argument here that a caller
102
+ // supplies. Questions asked of `obj` are typed, since a caller can answer them with a trap or a
103
+ // getter that throws; questions asked of the other two are not, because a table or a wording that
104
+ // throws is this toolkit's own wiring mistake. Handing either one a value off a caller would put
105
+ // an untyped exception on a boundary that promises a typed one, so neither takes one.
106
+ //
100
107
  // @enforced-by guard-shape-reinlined
101
108
  // The shape requires the throw: an identical walk whose body filters on the same table (copying the
102
109
  // recognized keys onward instead of rejecting the unrecognized ones) is a different operation with
103
110
  // no fail-open risk, and must not be dragged through a guard that only knows how to reject.
104
111
  // @guard-shape Object\.keys\(\w+(?:\.\w+)*\)\.forEach\(function \((\w+)\) \{\s*if \(![\w.]+\[\1\]\) (?:\{\s*)?throw
105
112
  function assertKnownKeys(obj, known, E, code, message) {
113
+ _refuseUnenumerable(obj, E, code, "the options");
106
114
  var describe = typeof message === "function" ? message : function (k) { return message + JSON.stringify(k); };
107
- Object.keys(obj).forEach(function (k) {
108
- if (!Object.prototype.hasOwnProperty.call(known, k)) throw E(code, describe(k));
115
+ _readableNames(obj).forEach(function (k) {
116
+ if (Object.prototype.hasOwnProperty.call(known, k)) return;
117
+ // A Symbol reaches the message as its description, before either form of `message` sees it.
118
+ // JSON.stringify returns undefined for one, so a caller's own builder (which stringifies the
119
+ // key itself) would name the refused option "undefined" and leave nothing to search for.
120
+ // Normalizing here rather than in the default builder keeps both forms reading the same.
121
+ throw E(code, describe(typeof k === "symbol" ? String(k) : k));
122
+ });
123
+ }
124
+
125
+ // What Object.prototype carries, per ECMA-262, written out rather than read from the runtime.
126
+ //
127
+ // Stopping the walk at Object.prototype excludes the whole object, so a runtime whose prototype
128
+ // carries `Object.prototype.password = "pw"` answers `opts.password` on a plain `{}` and
129
+ // `pki.key.export` hands back an unprotected key for a bag holding nothing. Naming the members
130
+ // instead reports anything else there.
131
+ //
132
+ // Reading the names off the live Object.prototype at load looks equivalent and is not: it is the
133
+ // runtime under attack answering the question, so a name planted before this module loads is
134
+ // captured as a built-in and is thereafter invisible. The spec list holds whoever loaded first.
135
+ // A guard-identifier test asserts it against the live object, so an engine that adds a member
136
+ // fails there rather than silently reporting it to every caller as an unknown option.
137
+ //
138
+ // The residual is a planted function REPLACING one of these twelve. No verb reads `opts.toString`
139
+ // as an option, so that smuggles nothing; it breaks the runtime at large, not this check.
140
+ var _PRISTINE_OBJECT_PROTO = (function () {
141
+ var s = Object.create(null);
142
+ ["constructor", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString",
143
+ "toString", "valueOf", "__defineGetter__", "__defineSetter__", "__lookupGetter__",
144
+ "__lookupSetter__", "__proto__"].forEach(function (k) { s[k] = 1; });
145
+ return s;
146
+ })();
147
+
148
+ // Is this descriptor shaped like a member of Object.prototype rather than a planted value? Every
149
+ // real one is a data property holding a function, or the `__proto__` accessor. Nothing there is a
150
+ // data property holding a string, a number or an object, so a planted `password: "pw"` fails this
151
+ // test whatever the snapshot above says about its name.
152
+ function _looksBuiltIn(d) {
153
+ return !!d && (!!d.get || !!d.set || typeof d.value === "function");
154
+ }
155
+
156
+ // Can this object be asked honestly what it holds?
157
+ //
158
+ // A Proxy answers `ownKeys` and `get` from two independent traps, so the two need not agree. One
159
+ // reporting no keys while its `get` returns "pw" for `password` presents an object that every
160
+ // enumeration calls empty and every read calls populated. No walk can close that: the readable
161
+ // names are whatever the trap decides at the moment it is asked, and asking again may differ.
162
+ // So the enumeration is not made more thorough, the object is refused. That is the same rule
163
+ // guard-bytes states for a value it cannot copy: there are two honest outcomes for a caller's
164
+ // argument, and "read it and hope the enumeration was truthful" is not one of them.
165
+ //
166
+ // The check is by identity through `util.types.isProxy`, never by behavior. Probing traps to
167
+ // catch a liar is a race a liar wins: it can answer consistently until the check is over.
168
+ // The WHOLE chain, not the object. `obj.password` resolves through every prototype above it, so a
169
+ // plain object inheriting from a lying Proxy answers the read while enumerating empty and is not
170
+ // itself a Proxy. Testing only the object handed in leaves that one line of indirection open.
171
+ // The error factory is validated before it is needed, for the same reason a guard taking a bound
172
+ // validates the bound: the one moment it is called is the moment something has already gone
173
+ // wrong, and a factory that is not one turns the refusal into a TypeError from inside the guard.
174
+ // This is a caller's wiring mistake rather than untrusted input, so it throws plainly.
175
+ function _requireFactory(E) {
176
+ if (typeof E !== "function") {
177
+ throw new TypeError("guard.identifier needs an error factory called as E(code, message); " +
178
+ "a class is not one, so adapt it at the call site");
179
+ }
180
+ }
181
+
182
+ function _refuseUnenumerable(v, E, code, label) {
183
+ _requireFactory(E);
184
+ if (v === null || typeof v !== "object") return;
185
+ var visited = new Set();
186
+ for (var o = v; o && !visited.has(o); o = Object.getPrototypeOf(o)) {
187
+ visited.add(o);
188
+ if (!util.types.isProxy(o)) continue;
189
+ throw E(code, label + (o === v ? " is" : " inherits from") + " a Proxy, whose reported keys " +
190
+ "need not match what it answers, so an option it holds cannot be found; pass a plain object");
191
+ }
192
+ }
193
+
194
+ // Does the prototype chain above `from` supply `k` as a data property holding `fn`? That is what
195
+ // makes `k` a method. Asking it by value identity is what makes the answer survive
196
+ // guard.bytes.snapshotDeep.
197
+ //
198
+ // The snapshot copies every readable name onto the copy and keeps the prototype, so a class's
199
+ // method arrives as an own property while the identical function is still reachable above it. A
200
+ // rule that asks "is this name own?" therefore calls the same method an option on the copy and
201
+ // behavior on the original, and every check running after a snapshot refuses an options bag that
202
+ // is an instance of the caller's own class.
203
+ //
204
+ // The walk stops at an object already seen, since a prototype chain is finite only while it is
205
+ // acyclic and a Proxy can make it otherwise. See the note on _readableNames.
206
+ function _isMethodOf(from, k, fn) {
207
+ var visited = new Set();
208
+ for (var o = from; o && !visited.has(o); o = Object.getPrototypeOf(o)) {
209
+ visited.add(o);
210
+ var d = Object.getOwnPropertyDescriptor(o, k);
211
+ if (!d) continue;
212
+ return !d.get && !d.set && d.value === fn;
213
+ }
214
+ return false;
215
+ }
216
+
217
+ // Every name that can supply a value to a later `obj.foo`. `Object.keys` reports a subset of
218
+ // those: own enumerable names only. Four ordinary object shapes answer the lookup while showing
219
+ // that enumeration nothing. A value on the prototype (`Object.create({ password: "pw" })`). A
220
+ // non-enumerable own value (`Object.defineProperty(o, "password", { enumerable: false })`). A
221
+ // class getter, which class syntax also defines non-enumerable. And a polluted
222
+ // `Object.prototype`. On each of them `pki.key.export` accepts the bag, ignores the password it
223
+ // can read, and returns the private key in the clear, which is the case the refusal exists to
224
+ // prevent.
225
+ //
226
+ // @enforced-by behavioral -- a property-surface walk has no rename-proof code shape. The RED
227
+ // vectors are the guard, and they are the shapes that got past it in turn: a prototype data
228
+ // property, a non-enumerable own property, a class getter, a polluted Object.prototype, and a
229
+ // prototype method that must still be allowed through.
230
+ //
231
+ // So the walk covers the whole chain and skips one thing: a method, meaning a data property
232
+ // holding a function that the chain above it also supplies under that name. A method is behavior
233
+ // the bag's class defines. This repository has settled that an instance of a caller's own class
234
+ // is a legitimate options bag, since guard.bytes.snapshotDeep copies one and keeps its prototype
235
+ // so its methods resolve, and flagging them would reject calls that work today.
236
+ //
237
+ // Neither enumerability nor ownness decides that test. Each records how the name arrived, which
238
+ // is a different question from what it means. Enumerability records how a method was written,
239
+ // and the two ways differ: class syntax defines methods non-enumerable while `Proto.m = fn`
240
+ // defines them enumerable, so a rule keyed on it accepts one style and rejects the other.
241
+ // Ownness records whether the object has been through a snapshot, which flips the same method
242
+ // from inherited to own, so a rule keyed on it calls that method behavior at one check site and
243
+ // an unknown option at the next. Value identity against the chain above answers the same on both
244
+ // sides, which is what lets a check sit before or after a snapshot and reach one verdict.
245
+ //
246
+ // An accessor is never a method, whichever way it was written. A getter exists to answer with a
247
+ // value, which is exactly what an option does.
248
+ //
249
+ // One case is traded away, and it is written down here rather than left implicit. An unknown
250
+ // option supplied as a method is not reported: its value would have to be a function, and the
251
+ // prototype chain would have to supply that same function under that name. So `class Options {
252
+ // typo() {} }` passes, and `opts.typo` does resolve. The function-valued options here
253
+ // (revocationChecker, verifier, transport) are passed as own properties, so reaching the hole
254
+ // needs a caller to misspell one AND define it as a method rather than assign it. That is the
255
+ // price of keeping class instances usable, which this repository has settled, and it is the one
256
+ // gap in "every name a later obj.foo can resolve is reported".
257
+ // Is this string an array index, as the language defines one: an integer in [0, 2^32 - 2] whose
258
+ // canonical decimal spelling is the string itself?
259
+ //
260
+ // `String(Number(k)) === k` looks like that test and is not. It also admits "-1", "1.5", "NaN",
261
+ // "Infinity" and "4294967295" (one past the last index), every one of which is an ordinary named
262
+ // property on an array rather than an element. Treating them as structure means an unknown option
263
+ // under one of those names is passed over here and dropped by the copy in guard-bytes, so it
264
+ // reaches neither the check nor the verb. A leading zero is excluded by the same comparison,
265
+ // since "01" is a named property too.
266
+ function _isArrayIndex(k) {
267
+ var n = Number(k);
268
+ return Number.isInteger(n) && n >= 0 && n < 4294967295 && String(n) === k;
269
+ }
270
+
271
+ // Is this value one whose own `length` and indices are structure rather than supplied names?
272
+ //
273
+ // The value itself, never anything it inherits from. An object merely sitting over an array is a
274
+ // plain object: a `0` or a `length` of its own is a field its caller wrote, and reading the kind
275
+ // off the chain suppressed exactly those. What such a bag inherits from the array beneath it is a
276
+ // separate question, answered by the level test in the walk.
277
+ //
278
+ // A DataView is a byte view and is not one of these. It reads its bytes through methods and has no
279
+ // integer-indexed elements and no `length`, so `dv.length = 9` and `dv[0] = "x"` are ordinary
280
+ // properties that stay set and answer on a later read. Counting it as indexed drops both from the
281
+ // names reported and from the copy the verb is handed.
282
+ function _isIndexedKind(v) {
283
+ return Array.isArray(v) || (ArrayBuffer.isView(v) && !util.types.isDataView(v));
284
+ }
285
+
286
+ // The names the built-in collection kinds define on their prototypes, written out rather than
287
+ // read from the runtime, for the same reason the root's twelve are: reading a prototype to learn
288
+ // what belongs on it asks the polluted object to vouch for itself.
289
+ //
290
+ // One set covering every kind, because a LEVEL is what gets read and a level's kind cannot be
291
+ // established without asking a prototype who it is. `Object.create(Map.prototype)` holds no map
292
+ // and inherits `size`; a foreign array's prototype is not this realm's. Both are answered by
293
+ // asking whether the NAME is one a kind defines, which no realm and no lookalike changes.
294
+ //
295
+ // Being on the list is not enough on its own: the member also has to be shaped like one, so a
296
+ // planted `Array.prototype.password` is reported while `Array.prototype.length` is not.
297
+ // One list per prototype, holding exactly what THAT prototype defines. Sharing a list across
298
+ // kinds that merely resemble each other exempts a name its holder never had, which is a caller's
299
+ // option passed over in silence: a weak collection counts nothing and has no `size`, a
300
+ // SharedArrayBuffer is `growable` where an ArrayBuffer is `resizable` and `detached`, a DataView
301
+ // has no `length`, and `BYTES_PER_ELEMENT` belongs to a concrete typed array rather than the
302
+ // prototype they share.
303
+ var _ARRAY_PROTO_MEMBERS = ["length", Symbol.unscopables];
304
+ var _REGEXP_PROTO_MEMBERS = ["source", "flags", "global", "ignoreCase", "multiline",
305
+ "dotAll", "unicode", "unicodeSets", "sticky", "hasIndices"];
306
+ var _SIZED_PROTO_MEMBERS = ["size", Symbol.toStringTag];
307
+ var _WEAK_PROTO_MEMBERS = [Symbol.toStringTag];
308
+ var _ARRAYBUFFER_PROTO_MEMBERS = ["byteLength", "maxByteLength", "resizable", "detached",
309
+ Symbol.toStringTag];
310
+ var _SHARED_PROTO_MEMBERS = ["byteLength", "maxByteLength", "growable", Symbol.toStringTag];
311
+ var _DATAVIEW_PROTO_MEMBERS = ["buffer", "byteLength", "byteOffset", Symbol.toStringTag];
312
+ var _TYPED_PROTO_MEMBERS = ["buffer", "byteLength", "byteOffset", "length", Symbol.toStringTag];
313
+ var _CONCRETE_TYPED_MEMBERS = ["BYTES_PER_ELEMENT"];
314
+ // Node puts these two on Buffer.prototype, beneath the ECMAScript typed-array prototype.
315
+ var _BUFFER_PROTO_MEMBERS = ["parent", "offset"];
316
+
317
+ // What a whole kind carries, as against what one prototype defines. This is what answers for a
318
+ // value whose chain holds no prototype of this realm, where the level cannot be recognized and
319
+ // the kind read from the value's own slot is all there is.
320
+ // A kind carries what its own prototypes hold and nothing a neighboring kind holds. Answering a
321
+ // buffer with both buffer kinds' members exempts `growable` on an ArrayBuffer and `detached` on a
322
+ // SharedArrayBuffer, neither of which the language defines there, so a caller's getter under
323
+ // either name is passed over.
324
+ var _ARRAY_MEMBERS = _ARRAY_PROTO_MEMBERS;
325
+ var _REGEXP_MEMBERS = ["lastIndex"].concat(_REGEXP_PROTO_MEMBERS);
326
+ var _VIEW_MEMBERS = _TYPED_PROTO_MEMBERS.concat(_CONCRETE_TYPED_MEMBERS);
327
+ var _BUFFER_KIND_MEMBERS = _VIEW_MEMBERS.concat(_BUFFER_PROTO_MEMBERS);
328
+ var _DATAVIEW_MEMBERS = _DATAVIEW_PROTO_MEMBERS;
329
+ var _ARRAYBUFFER_MEMBERS = _ARRAYBUFFER_PROTO_MEMBERS;
330
+ var _SHARED_MEMBERS = _SHARED_PROTO_MEMBERS;
331
+
332
+ // Which of those a value carries, taken from its internal slot.
333
+ //
334
+ // The value's own kind, not the union, because a name belongs to a kind and a caller's accessor
335
+ // can borrow one that a different kind defines. `class Opts extends Array { get buffer() {} }` is
336
+ // an array, `buffer` is a byte view's member and not an array's, so that getter is the caller's
337
+ // option and gets reported.
338
+ //
339
+ // A value with no collection slot answers with nothing. `Object.create(Map.prototype)` holds no
340
+ // map and still inherits `size`, and that is settled by the level test in the walk, which reads
341
+ // the members off the prototype the name was found on.
342
+ // The prototypes this realm installs a kind's members on. A level that IS one carries the
343
+ // language's members whatever the value in front of it turns out to be, which is how
344
+ // `Object.create(Map.prototype)` keeps `size` while holding no map.
345
+ //
346
+ // Typed arrays are recognized through the prototype they all share rather than by naming each
347
+ // constructor, so a kind the list never mentioned is covered: `Float16Array` arrived on Node 24
348
+ // and an enumeration would have gone on reporting its `BYTES_PER_ELEMENT` as a caller's option.
349
+ // Each one paired with the members IT defines, never the union of every kind's. A level carries
350
+ // only its own: `size` on `Array.prototype` is nothing the language put there, so exempting it
351
+ // because some other kind has a member by that name hands back the hole this walk exists to
352
+ // close, for anyone who plants it in a shape that reads as built-in.
353
+ var _TYPED_ARRAY_PROTO = Object.getPrototypeOf(Uint8Array.prototype);
354
+ var _INTRINSIC_HOLDERS = (function () {
355
+ var m = new Map([[_TYPED_ARRAY_PROTO, _TYPED_PROTO_MEMBERS]]);
356
+ function pair(ctor, members) { if (ctor && ctor.prototype) m.set(ctor.prototype, members); }
357
+ pair(Array, _ARRAY_PROTO_MEMBERS);
358
+ pair(RegExp, _REGEXP_PROTO_MEMBERS);
359
+ [Map, Set].forEach(function (c) { pair(c, _SIZED_PROTO_MEMBERS); });
360
+ [WeakMap, WeakSet].forEach(function (c) { pair(c, _WEAK_PROTO_MEMBERS); });
361
+ pair(ArrayBuffer, _ARRAYBUFFER_PROTO_MEMBERS);
362
+ pair(typeof SharedArrayBuffer === "function" ? SharedArrayBuffer : null, _SHARED_PROTO_MEMBERS);
363
+ pair(DataView, _DATAVIEW_PROTO_MEMBERS);
364
+ pair(Buffer, _BUFFER_PROTO_MEMBERS);
365
+ // Every concrete typed-array kind the runtime actually provides, found by asking it rather than
366
+ // by naming them, so a kind added later is covered the day it lands -- Float16Array was.
367
+ //
368
+ // Registered as identities. Recognizing them by their PARENT instead admitted anything a caller
369
+ // built over `%TypedArray%.prototype`, and a bag inheriting `BYTES_PER_ELEMENT` from such an
370
+ // object then read as an intrinsic member rather than as the unknown option it was.
371
+ //
372
+ // Reading a global can run a getter, and a deprecated one can throw; a fault here would break
373
+ // this module's load over a name it does not need.
374
+ Object.getOwnPropertyNames(globalThis).forEach(function (n) {
375
+ var C;
376
+ try { C = globalThis[n]; }
377
+ catch (_e) { /* allow:swallow-unverified this runs once at load over whatever globals the host defines, and no global in a supported runtime throws on read, so no test drives it; it is here so an embedder's throwing accessor cannot stop this module loading over a name it does not need */ return; }
378
+ if (typeof C !== "function" || typeof C.BYTES_PER_ELEMENT !== "number") return;
379
+ if (!C.prototype || Object.getPrototypeOf(C.prototype) !== _TYPED_ARRAY_PROTO) return;
380
+ m.set(C.prototype, _CONCRETE_TYPED_MEMBERS);
109
381
  });
382
+ return m;
383
+ })();
384
+
385
+ // The members a chain level defines, or null where the level is not one the language installs
386
+ // members on. Membership is by identity: every prototype the language installs members on is in
387
+ // the table above, the concrete typed-array ones included, so an object that merely sits over one
388
+ // of them is a caller's and its names are theirs.
389
+ function _holderMembers(o) {
390
+ return _INTRINSIC_HOLDERS.has(o) ? _INTRINSIC_HOLDERS.get(o) : null;
391
+ }
392
+
393
+ // Is this one of Node's Buffers? A byte view whose chain reaches `Buffer.prototype`, asked by
394
+ // walking it rather than through `Buffer.isBuffer`.
395
+ //
396
+ // `Buffer.isBuffer` is an `instanceof`, and `Buffer` is an ordinary extensible function, so
397
+ // defining `Buffer[Symbol.hasInstance]` on it hands the answer to whoever did. That is the same
398
+ // pollution this walk exists to survive on `Object.prototype` and `Array.prototype`, and it can
399
+ // throw as well as lie: a caller's argument then leaves a door whose contract is a typed error
400
+ // carrying somebody else's exception. A prototype identity is not writable from outside.
401
+ function _isNodeBuffer(v) {
402
+ if (!util.types.isUint8Array(v)) return false;
403
+ var visited = new Set();
404
+ for (var o = v; o && !visited.has(o); o = Object.getPrototypeOf(o)) {
405
+ visited.add(o);
406
+ if (o === Buffer.prototype) return true;
407
+ }
408
+ return false;
409
+ }
410
+
411
+ function _kindMembers(v) {
412
+ // The value's own slot, never a kind it merely inherits from. Whatever a real intrinsic
413
+ // prototype in the chain supplies is handled by the level test at the walk; a name that only
414
+ // looks like a member, on a class of the caller's, is the option they wrote.
415
+ if (v === null || v === undefined) return [];
416
+ if (Array.isArray(v)) return _ARRAY_MEMBERS;
417
+ if (_isNodeBuffer(v)) return _BUFFER_KIND_MEMBERS;
418
+ if (util.types.isDataView(v)) return _DATAVIEW_MEMBERS;
419
+ if (ArrayBuffer.isView(v)) return _VIEW_MEMBERS;
420
+ if (util.types.isArrayBuffer(v)) return _ARRAYBUFFER_MEMBERS;
421
+ if (util.types.isSharedArrayBuffer(v)) return _SHARED_MEMBERS;
422
+ if (util.types.isRegExp(v)) return _REGEXP_MEMBERS;
423
+ if (util.types.isWeakMap(v) || util.types.isWeakSet(v)) return _WEAK_PROTO_MEMBERS;
424
+ if (util.types.isMap(v) || util.types.isSet(v)) return _SIZED_PROTO_MEMBERS;
425
+ return [];
426
+ }
427
+
428
+ // The names a kind puts on the value itself rather than on its prototype, which are structure the
429
+ // same way an array's `length` is: `opts.lastIndex` on a RegExp answers with the match position
430
+ // whatever a caller meant by it, so no option can be supplied under that name.
431
+ function _isOwnStructuralName(v, k) {
432
+ return util.types.isRegExp(v) && k === "lastIndex";
433
+ }
434
+
435
+ // The shape a member the language put there has, and a planted one does not: an accessor, a
436
+ // function, or a data property the language pinned. `Symbol.toStringTag` and `BYTES_PER_ELEMENT`
437
+ // are non-writable; `Array.prototype.length` is writable and non-configurable. A name added by
438
+ // assignment or by an ordinary defineProperty is neither, so it stays reported.
439
+ function _looksIntrinsic(d) {
440
+ return !!d && (!!d.get || !!d.set || typeof d.value === "function" ||
441
+ d.writable === false || d.configurable === false);
442
+ }
443
+
444
+ // The member descriptors this realm's collection prototypes actually carry, taken at load and
445
+ // keyed by the accessor or value they hold.
446
+ //
447
+ // The name and the shape together are not enough on their own: a caller's class can extend a
448
+ // collection and define one of its own members over the top, and
449
+ // `class Opts extends Map { get size() {} }` is then an accessor named `size` on a value that is
450
+ // a Map. What tells the two apart is where the name sits, and that answers in any realm: the
451
+ // language installs a member once, so an intrinsic one is the only level of the chain carrying
452
+ // that name, while an override shadows the intrinsic still sitting above it. A name found twice
453
+ // is a caller's.
454
+ //
455
+ // Reading it that way also settles a kind this list would have to be extended for. `Float16Array`
456
+ // is on Node 24 and was not among the constructors named here, and the moment a typed array of an
457
+ // unlisted kind arrived its `BYTES_PER_ELEMENT` read as an option its caller never wrote.
458
+ function _shadowsAnother(v, k, holder) {
459
+ var visited = new Set();
460
+ var past = false;
461
+ for (var o = v; o !== null && o !== undefined && !visited.has(o); o = Object.getPrototypeOf(o)) {
462
+ visited.add(o);
463
+ if (o === holder) { past = true; continue; }
464
+ if (past && Object.getOwnPropertyDescriptor(o, k)) return true;
465
+ }
466
+ return false;
467
+ }
468
+
469
+ function _readableNames(obj) {
470
+ var seen = Object.create(null);
471
+ var out = [];
472
+ // A primitive supplies no option names, and `Reflect.ownKeys` refuses one outright. The walk
473
+ // starts above it rather than on it, so a caller who hands a number or a string where an
474
+ // options bag belongs gets their own verb's refusal for the wrong type from the door that
475
+ // checks types, instead of a raw TypeError from in here about a reflection method they never
476
+ // called. `null` and `undefined` have no chain at all and stop immediately.
477
+ var o = (obj === null || obj === undefined) ? null : Object(obj);
478
+ // Whether the value's own `length` and indices are structure. That is true of an array and a
479
+ // byte view and of nothing else: a Map, a Set and an ArrayBuffer have no own `length`, so
480
+ // skipping the name there would discard one the caller had put on themselves.
481
+ //
482
+ // A verb reads an option by name whatever the argument's type, and `opts = []; opts.pem = true`
483
+ // is an options bag as far as `opts.pem` is concerned, so these kinds have to work. Their
484
+ // prototypes carry names the caller did not supply, and the per-level test below is what
485
+ // handles those. Refusing to read the chain at all leaves a bag whose chain holds nothing
486
+ // intrinsic unread.
487
+ var indexedSelf = _isIndexedKind(o);
488
+ var members = _kindMembers(o);
489
+ // A prototype chain is finite only while it is acyclic, and a Proxy can make it otherwise.
490
+ // While its target stays extensible a `getPrototypeOf` trap may return the proxy itself. The
491
+ // walk therefore stops at an object it has already visited. A second lap over the same objects
492
+ // yields no name the first did not, so the reported names stay complete. Without the test a
493
+ // caller's argument hangs the verb before a single option has been read.
494
+ var visited = new Set();
495
+ while (o && !visited.has(o)) {
496
+ visited.add(o);
497
+ var inherited = (o !== obj);
498
+ var above = Object.getPrototypeOf(o);
499
+ // The root of the chain is the last level before null, which is what `Object.prototype` is.
500
+ // Its position holds in every realm. A value built in a `vm` context or a worker thread
501
+ // carries that realm's root above it, so an identity test against the local object misses it
502
+ // and the foreign `__proto__` accessor reads as a name the caller supplied.
503
+ //
504
+ // The level has to be an inherited one. A caller's own `Object.create(null)` has no prototype
505
+ // either, and treating the bag itself as a root skips the names they set on it.
506
+ var atObjectProto = inherited && above === null;
507
+ // A kind's own prototype gets no exemption as a level. Passing one over whole hides anything
508
+ // added to it, and `Array.prototype.password = "pw"` is then invisible while `opts.password`
509
+ // resolves, which handed `pki.key.export` an unprotected private key for `[]` as the bag.
510
+ // Object.prototype was already read for that reason; every level is now read the same way,
511
+ // and what a kind puts on its prototype is passed over by name below.
512
+ //
513
+ // Reflect.ownKeys, because Object.getOwnPropertyNames never returns a Symbol key and a
514
+ // caller can write one. Such a name cannot answer `opts.password`, so it reaches no verb,
515
+ // but it is still an option supplied and not read, which is the whole thing being refused.
516
+ Reflect.ownKeys(o).forEach(function (k) {
517
+ if (seen[k]) return;
518
+ // The indices of an array or a byte view are its structure, whichever level they are read
519
+ // from. Both kinds answer an integer-keyed read from their elements before any prototype is
520
+ // consulted, so no caller can supply an option under such a name.
521
+ if (indexedSelf && typeof k === "string" && _isArrayIndex(k)) return;
522
+ // `length` is structure only where the language puts it on the value itself, which an array
523
+ // does and a byte view does not. On an array it is an own non-configurable property that no
524
+ // prototype can shadow, so `opts.length` is always the count. A byte view carries it as a
525
+ // prototype accessor, and `class Opts extends Uint8Array { get length() {} }` shadows that
526
+ // one: `opts.length` then answers with what the caller wrote, so it is an option they
527
+ // supplied. Passing the name over for both kinds hid exactly that. Left to the level test
528
+ // below, which exempts the accessor where the language installed it and reports an override.
529
+ if (indexedSelf && k === "length" && !inherited) return;
530
+ if (_isOwnStructuralName(obj, k)) return;
531
+ var d = Object.getOwnPropertyDescriptor(o, k);
532
+ // A name a built-in kind defines, on a level above the value and shaped the way the
533
+ // language leaves it. Own is deliberately excluded: an own `detached` on a buffer shadows
534
+ // the intrinsic getter, so `opts.detached` answers with what the caller wrote, and
535
+ // pki.cms.sign reads an option under exactly that name.
536
+ // A member of the value's own kind, or one sitting on a prototype the language installs
537
+ // members on. The second is what carries `Object.create(Map.prototype)`, which holds no map
538
+ // and inherits `size` all the same. Neither alone is enough: without the level test a class
539
+ // of the caller's could name a getter after any kind's member and have it passed over.
540
+ var here = _holderMembers(o);
541
+ if (inherited && _looksIntrinsic(d) && !_shadowsAnother(obj, k, o) &&
542
+ (here === null ? members.indexOf(k) !== -1 : here.indexOf(k) !== -1)) return;
543
+ // Object.prototype is decided here and nowhere else. A member is one of the twelve the spec
544
+ // names, still shaped like one: a data property holding a function, or the `__proto__`
545
+ // accessor. Anything else on it was planted and is reported.
546
+ //
547
+ // The method rule below must not run on it. That rule exempts an inherited function so a
548
+ // caller's class keeps its methods, and a planted `password` function is inherited and a
549
+ // function, so the rule would rescue exactly the value this check exists to find. A class
550
+ // defines its methods on its own prototype, and nothing legitimate puts one on the root.
551
+ if (atObjectProto) {
552
+ if (_PRISTINE_OBJECT_PROTO[k] && _looksBuiltIn(d)) return;
553
+ seen[k] = 1;
554
+ out.push(k);
555
+ return;
556
+ }
557
+ // A method: a data property holding a function that is either inherited, where a class
558
+ // defines one, or also supplied under that name by the chain above, where a snapshot has
559
+ // copied one down onto the object. Both readings are needed. The first alone calls a
560
+ // promoted method an option. The second alone misses a method at the level that defines
561
+ // it, where nothing above carries the same function. An accessor is never a method,
562
+ // whichever way it was written, since a getter exists to answer with a value.
563
+ if (d && !d.get && !d.set && typeof d.value === "function" &&
564
+ (inherited || _isMethodOf(above, k, d.value))) return;
565
+ seen[k] = 1;
566
+ out.push(k);
567
+ });
568
+ o = above;
569
+ }
570
+ return out;
571
+ }
572
+
573
+ // The exported form of the property walk, which takes the caller's error factory like every
574
+ // other guard function.
575
+ //
576
+ // The internal walk reads the prototype chain, and reading a chain runs a `getPrototypeOf` trap,
577
+ // so a hostile one throws its own raw error out of a module whose whole contract is a typed one.
578
+ // `assertKnownKeys` and `optionsObject` refuse a Proxy before they walk; this door has to do the
579
+ // same, because a caller reaching for the names directly is exactly the caller who has not
580
+ // already checked. Taking `(E, code, label)` is what makes that possible, and it puts the one
581
+ // guard function that did not follow the family convention back inside it.
582
+ //
583
+ // @enforced-by behavioral -- a missing refusal has no rename-proof code shape; the RED vector
584
+ // (a hostile trap escaping as its own Error from this door) is the guard.
585
+ function readableNames(obj, E, code, label) {
586
+ _refuseUnenumerable(obj, E, code, label);
587
+ return _readableNames(obj);
588
+ }
589
+
590
+ // The indices an ordinary read on an array resolves, ascending, own and inherited.
591
+ //
592
+ // The name walk above drops every index of an indexed kind, since an index is structure rather
593
+ // than a name a caller supplied. Something still has to answer which indices there ARE, and the
594
+ // answer is not `Reflect.ownKeys`: an array consults its prototype chain for a hole, so
595
+ // `Object.setPrototypeOf([,], {0: signer})` reads `[signer]` to a `for` loop, to `forEach`, and to
596
+ // every consumer of the value, while its own keys report an empty array. A copy built from the own
597
+ // keys alone holds a hole where the original answers, which is the same divergence between the
598
+ // value checked and the value used that this family exists to close - here it silently shortens a
599
+ // signer list rather than lengthening it.
600
+ //
601
+ // Arrays only. A typed array answers a canonical index from its elements and never consults the
602
+ // chain, in bounds or out, so its indices are exactly its own. A DataView is an ordinary object
603
+ // whose numeric keys ARE caller-supplied names, and the name walk above already reports them.
604
+ //
605
+ // `length` is read off the own descriptor, which for an array is the non-configurable count the
606
+ // language maintains and no prototype can shadow. Inherited indices at or above it are left out:
607
+ // a read past `length` resolves, but no length-bounded traversal reaches it, and carrying one
608
+ // across would lengthen the copy past what the caller passed.
609
+ //
610
+ // The work stays bounded by what the value and its chain actually hold, so a caller who writes
611
+ // `a.length = 4e9` costs what a sparse array costs and not four billion steps. The walk stops at
612
+ // an object already visited for the reason the name walk does: a `getPrototypeOf` trap can hand
613
+ // back a fresh proxy forever, and a chain that never ends hangs the verb.
614
+ //
615
+ // @enforced-by behavioral -- an enumeration gap has no rename-proof code shape; the RED vector
616
+ // (a length-one array holding its only element on its prototype, copied to a hole) is the guard.
617
+ function readableIndices(obj, E, code, label) {
618
+ _refuseUnenumerable(obj, E, code, label);
619
+ if (!Array.isArray(obj)) return [];
620
+ var own = Object.getOwnPropertyDescriptor(obj, "length");
621
+ var limit = own ? own.value : 0;
622
+ var seen = Object.create(null);
623
+ var out = [];
624
+ var visited = new Set();
625
+ for (var o = obj; o && !visited.has(o); o = Object.getPrototypeOf(o)) {
626
+ visited.add(o);
627
+ Reflect.ownKeys(o).forEach(function (k) {
628
+ if (typeof k !== "string" || !_isArrayIndex(k) || seen[k]) return;
629
+ if (Number(k) >= limit) return;
630
+ seen[k] = 1;
631
+ out.push(k);
632
+ });
633
+ }
634
+ return out.sort(function (a, b) { return Number(a) - Number(b); });
635
+ }
636
+
637
+ // The object kinds that are never an options bag, named so the refusal says which mistake was
638
+ // made. Buffer is handled beside the type check, being the argument-order slip.
639
+ //
640
+ // A boxed primitive is the one that has to be here rather than left to the unknown-option check.
641
+ // `new Number(0)` and `new Boolean(false)` carry no readable name at all, so nothing is reported
642
+ // and they pass as a bag with no options in it: the same silence `0` and `false` used to buy,
643
+ // one wrapper away. The rest carry names the language installed, so each is refused already; what
644
+ // they gain is a message that names the argument rather than one of the engine's own fields.
645
+ //
646
+ // Every one read off the value's slot, so a lookalike built over the prototype does not answer for
647
+ // one, a real one from a `vm` context or a worker thread does, and the value is asked nothing.
648
+ //
649
+ // That last part is the reason none of these is a type tag. `Object.prototype.toString` reads
650
+ // `Symbol.toStringTag`, which a caller can back with a getter that throws, so classifying a bag
651
+ // that way put the caller's own exception on a boundary whose contract is a typed error, and a
652
+ // caller could name any object an arguments object by writing the tag themselves.
653
+ function _notAnOptionsBag(v) {
654
+ if (util.types.isBoxedPrimitive(v)) return "a boxed primitive";
655
+ if (util.types.isNativeError(v)) return "an Error";
656
+ if (util.types.isPromise(v)) return "a Promise";
657
+ if (util.types.isArgumentsObject(v)) return "an arguments object";
658
+ return null;
659
+ }
660
+
661
+ // Normalize a verb's options argument and return it. The order of the two steps decides whether
662
+ // the check runs on the caller's value, and returning is what makes the safe order the easy one.
663
+ //
664
+ // The idiom this replaces was `opts = opts || {}` followed by a type check. That reads as though
665
+ // it validates, and does not: `||` treats `false`, `0`, `""` and `NaN` as absent too, so those
666
+ // four were rewritten to `{}` before the check could see them and sailed through as valid
667
+ // options. Every entry point that reached for the idiom inherited the hole, including three that
668
+ // had shipped with it.
669
+ //
670
+ // Only `null` and `undefined` mean "no options given". Anything else non-object is a caller
671
+ // error and says so. A Buffer is excluded because it is an object and is never an options bag --
672
+ // passing one is an argument-order mistake, which is worth naming rather than treating as empty.
673
+ //
674
+ // @enforced-by behavioral -- the defect is an ordering, which has no rename-proof code shape; the
675
+ // RED vector (a verb handed `false` accepting it as `{}`) is the guard.
676
+ function optionsObject(opts, E, code, label) {
677
+ // No options supplied gets a bag with NO prototype, so it inherits nothing. A plain `{}` here
678
+ // would inherit whatever Object.prototype carries, and on a runtime where that has been
679
+ // polluted the caller is then refused for a name they never wrote. A caller who does pass `{}`
680
+ // is a different case and keeps the refusal: `opts.password` answers on their object, so the
681
+ // option is readable and unread, which is exactly what is being refused.
682
+ if (opts === null || opts === undefined) return Object.create(null);
683
+ // `typeof` asks the value nothing, so it is safe on any argument. Every question after it comes
684
+ // once the value has been refused as a Proxy, because each one walks the prototype chain and so
685
+ // runs a `getPrototypeOf` trap, which took this door out through the caller's own exception
686
+ // rather than the typed one it promises.
687
+ if (typeof opts !== "object") throw E(code, label + " must be an object");
688
+ _refuseUnenumerable(opts, E, code, label);
689
+ if (_isNodeBuffer(opts)) throw E(code, label + " must be an object");
690
+ var wrong = _notAnOptionsBag(opts);
691
+ if (wrong) throw E(code, label + ": " + wrong + " is not an options bag; pass a plain object");
692
+ return _settle(opts, E, code, label);
693
+ }
694
+
695
+ // The caller's bag, once its option names have been shown to hold still while they are read.
696
+ //
697
+ // Reading an option can run a caller's getter, and a getter can add a name to the bag it was read
698
+ // from. `{ get format() { this.password = "pw"; return "der"; } }` shows only `format` while the
699
+ // unknown-option check runs, and grows `password` when the verb reads `format` afterwards, so the
700
+ // names that were checked are not the names the verb ends up carrying. `pki.key.export` took such
701
+ // a bag and returned a private key in the clear, which is the case the check exists to prevent.
702
+ //
703
+ // Every readable name is therefore read here, before anything is checked, and the names are taken
704
+ // again afterwards. A bag that grew or dropped one is refused, because a bag that rewrites itself
705
+ // as it is read has no set of names that can be checked at all.
706
+ //
707
+ // The bag itself is handed back, never a copy of it. Which names a caller supplied is decided by
708
+ // where they sit: own is theirs, inherited is machinery unless the chain says otherwise. Copying
709
+ // the readable ones onto a new object destroys exactly that, and every attempt to keep it turned
710
+ // into its own defect: a bare copy dropped `pki.path.build`'s inherited `transport` and sent the
711
+ // verb to the network the caller meant to replace, and a copy built over the original inherited
712
+ // native accessors onto a receiver with no slot behind them, so an `ArrayBuffer` options bag
713
+ // raised a raw TypeError from `opts.detached`.
714
+ //
715
+ // An accessor cannot be one, and that is the whole rule: a getter answers a different question
716
+ // every time it is asked, so a name checked once says nothing about what the verb reads next.
717
+ //
718
+ // Every narrower attempt to keep them was measured and each carried its own defect. Reading once
719
+ // and re-taking the names catches a getter that mutates immediately and misses one that waits for
720
+ // a later read. Freezing the bag closes that and then throws a raw TypeError on a non-empty typed
721
+ // array, which cannot be frozen at all, while still leaving a mutable PROTOTYPE for an inherited
722
+ // getter to grow. Copying the bag instead loses which names were the caller's own, drops an
723
+ // inherited option a verb reads, and leaves a native accessor on a receiver with no slot for it.
724
+ //
725
+ // Refusing is the one rule with nothing behind it. A plain object, a class instance with methods,
726
+ // a collection, a bag from another realm are all still options bags; what is asked is that the
727
+ // options themselves be values. The names are read once even so, since an accessor can hide on a
728
+ // prototype and this is where that gets found.
729
+ // The descriptor a name resolves to, from wherever on the chain it is found. Only the first
730
+ // level carrying it matters, since that is the one a read reaches.
731
+ function _descriptorHolder(v, k) {
732
+ var visited = new Set();
733
+ for (var o = v; o !== null && o !== undefined && !visited.has(o); o = Object.getPrototypeOf(o)) {
734
+ visited.add(o);
735
+ var d = Object.getOwnPropertyDescriptor(o, k);
736
+ if (d) return d;
737
+ }
738
+ return null;
739
+ }
740
+
741
+ // refuseAccessorFields(obj, names, E, code, label) -> undefined | throws E(code, msg)
742
+ //
743
+ // Every door that decides what a caller supplied asks this, because a door that reads an accessor
744
+ // before another door checks for one leaves nothing behind to find: guard.bytes.snapshotDeep runs
745
+ // a spec's getters while copying and writes what they returned as plain data, so the option check
746
+ // that follows sees values and cannot tell there was ever an accessor there.
747
+ //
748
+ // @enforced-by behavioral -- reading an accessor before it is refused has no rename-proof code
749
+ // shape; the RED vectors (a getter on an options bag and on a signing spec) are the guard.
750
+ function refuseAccessorFields(obj, names, E, code, label) {
751
+ for (var i = 0; i < names.length; i++) {
752
+ var holder = _descriptorHolder(obj, names[i]);
753
+ if (holder && (holder.get || holder.set)) {
754
+ throw E(code, label + " supplies " +
755
+ JSON.stringify(typeof names[i] === "symbol" ? String(names[i]) : names[i]) +
756
+ " through an accessor, whose value can differ between the check and the read; pass an " +
757
+ "object whose fields are plain values");
758
+ }
759
+ }
760
+ }
761
+
762
+ function _settle(opts, E, code, label) {
763
+ // A caller's accessor can throw, and that fault gets this boundary's typed code rather than
764
+ // escaping as itself from a verb the caller called for something else.
765
+ function readAll(names) {
766
+ for (var i = 0; i < names.length; i++) {
767
+ try { void opts[names[i]]; }
768
+ catch (_e) {
769
+ throw E(code, label + ": reading " +
770
+ JSON.stringify(typeof names[i] === "symbol" ? String(names[i]) : names[i]) + " threw");
771
+ }
772
+ }
773
+ }
774
+ var before = _readableNames(opts);
775
+ refuseAccessorFields(opts, before, E, code, label);
776
+ readAll(before);
777
+ var after = _readableNames(opts);
778
+ var same = after.length === before.length;
779
+ for (var j = 0; same && j < after.length; j++) same = before.indexOf(after[j]) !== -1;
780
+ if (!same) {
781
+ throw E(code, label + " changes which options it carries while they are read, so no set of " +
782
+ "them can be checked; pass an object whose properties are plain values");
783
+ }
784
+ return opts;
110
785
  }
111
786
 
112
- module.exports = { assertCanonicalOid: assertCanonicalOid, assertKnownKeys: assertKnownKeys };
787
+ module.exports = {
788
+ assertCanonicalOid: assertCanonicalOid,
789
+ assertKnownKeys: assertKnownKeys,
790
+ optionsObject: optionsObject,
791
+ refuseAccessorFields: refuseAccessorFields,
792
+ // Exported so a caller that forwards an options bag copies the same surface this module
793
+ // accepts. A narrower enumeration such as `Object.keys` admits a name here and then drops
794
+ // it on the way, which is worse than refusing it. The option is reported valid and then
795
+ // does nothing at the place it was meant to act.
796
+ readableNames: readableNames,
797
+ // Exported for the same reason as its sibling: a caller copying an array has to reach the
798
+ // elements a read reaches, and the own keys are not that set.
799
+ readableIndices: readableIndices
800
+ };