@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.
- package/CHANGELOG.md +49 -0
- package/lib/acme.js +4 -4
- package/lib/asn1-der.js +1 -1
- package/lib/attrcert-sign.js +1 -1
- package/lib/cbor-det.js +4 -4
- package/lib/cmc-verify.js +7 -4
- package/lib/cms-sign.js +3 -5
- package/lib/cms-verify.js +7 -4
- package/lib/crl-sign.js +1 -1
- package/lib/crmf-sign.js +1 -1
- package/lib/ct.js +8 -5
- package/lib/est.js +17 -11
- package/lib/guard-bytes.js +564 -78
- package/lib/guard-identifier.js +691 -3
- package/lib/guard-parsed.js +9 -5
- package/lib/guard-time.js +65 -4
- package/lib/inspect.js +8 -3
- package/lib/key.js +48 -6
- package/lib/lint.js +14 -7
- package/lib/ocsp-verify.js +10 -4
- package/lib/ocsp.js +34 -5
- package/lib/path-validate.js +94 -17
- package/lib/schema-c509.js +3 -3
- package/lib/sigstore.js +6 -2
- package/lib/trust.js +2 -2
- package/lib/tsp-sign.js +2 -2
- package/lib/validator-attcert.js +3 -3
- package/lib/webauthn-mds.js +5 -5
- package/lib/webauthn.js +1 -1
- package/lib/webcrypto.js +15 -6
- package/lib/x509-sign.js +1 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/guard-identifier.js
CHANGED
|
@@ -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
|
-
|
|
108
|
-
if (
|
|
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 = {
|
|
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
|
+
};
|