@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-bytes.js
CHANGED
|
@@ -7,6 +7,183 @@
|
|
|
7
7
|
// pki.cbor.decode, pki.ct.parseSctList, pki.webcrypto.*).
|
|
8
8
|
//
|
|
9
9
|
var async = require("./guard-async");
|
|
10
|
+
var identifier = require("./guard-identifier");
|
|
11
|
+
var time = require("./guard-time");
|
|
12
|
+
var util = require("util");
|
|
13
|
+
|
|
14
|
+
// isByteSource(x) -> boolean: is this one of the four forms the toolkit calls bytes?
|
|
15
|
+
//
|
|
16
|
+
// A Buffer, any typed-array view, a DataView, or a bare ArrayBuffer. Exported so a door deciding
|
|
17
|
+
// whether an argument is bytes asks the module that finally reads them, instead of restating the
|
|
18
|
+
// list. A restatement narrows without anyone noticing. An `x instanceof ArrayBuffer` at such a
|
|
19
|
+
// door turns away a real buffer from another realm, and the caller's container is then accepted
|
|
20
|
+
// at one door and refused at the next.
|
|
21
|
+
// @enforced-by behavioral -- a narrowed restatement of a list has no rename-proof code shape;
|
|
22
|
+
// the RED vector (a byte form accepted by one door and refused by the other) is the guard.
|
|
23
|
+
function isByteSource(x) {
|
|
24
|
+
return Buffer.isBuffer(x) || ArrayBuffer.isView(x) || _isArrayBuffer(x);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Is this value a byte store, asked of the internal slot rather than the prototype?
|
|
28
|
+
//
|
|
29
|
+
// `v instanceof ArrayBuffer` answers what a value inherits from, which is a different question
|
|
30
|
+
// from what it holds. `Object.create(ArrayBuffer.prototype)` inherits everything and holds no
|
|
31
|
+
// bytes, so it passes and the read that follows throws a raw TypeError out of a boundary whose
|
|
32
|
+
// contract is a typed error. The same test also misses a real buffer from another realm, whose
|
|
33
|
+
// prototype is that realm's. `util.types` reads the slot, so both answers come out right.
|
|
34
|
+
function _isArrayBuffer(v) {
|
|
35
|
+
return util.types.isArrayBuffer(v);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Where a view's bytes actually are, asked of the language rather than of the view.
|
|
39
|
+
//
|
|
40
|
+
// `v.buffer`, `v.byteOffset` and `v.byteLength` are accessors on the shared typed-array prototype,
|
|
41
|
+
// so `class Opts extends Uint8Array { get buffer() { return elsewhere; } }` answers all three
|
|
42
|
+
// questions itself. Two things followed from reading them off the value. A getter that throws left
|
|
43
|
+
// this module through the caller's own exception, from a boundary whose contract is a typed error.
|
|
44
|
+
// And a getter that lied substituted the memory: an array holding AA AA AA AA whose `buffer`
|
|
45
|
+
// returned another store had `view` and `snapshot` hand back that other store's bytes, so the
|
|
46
|
+
// bytes a verb went on to hash, sign or parse were not the bytes in the array it was given.
|
|
47
|
+
//
|
|
48
|
+
// The getters are taken off the prototypes the language installs them on and called against the
|
|
49
|
+
// view, which reaches the internal slot whatever the value's own class says. A subclass override
|
|
50
|
+
// is then what it is -- a property of the caller's object -- and answers no question asked here.
|
|
51
|
+
var _TYPED_ARRAY_PROTO = Object.getPrototypeOf(Uint8Array.prototype);
|
|
52
|
+
|
|
53
|
+
function _intrinsicGetter(proto, name, who) {
|
|
54
|
+
var d = Object.getOwnPropertyDescriptor(proto, name);
|
|
55
|
+
if (!d || typeof d.get !== "function") {
|
|
56
|
+
throw new TypeError("guard.bytes: this runtime has no intrinsic " + who + "." + name +
|
|
57
|
+
" accessor, so a view's backing store cannot be read without invoking the value's own");
|
|
58
|
+
}
|
|
59
|
+
return d.get;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
var _taBuffer = _intrinsicGetter(_TYPED_ARRAY_PROTO, "buffer", "%TypedArray%.prototype");
|
|
63
|
+
var _taByteOffset = _intrinsicGetter(_TYPED_ARRAY_PROTO, "byteOffset", "%TypedArray%.prototype");
|
|
64
|
+
var _taByteLength = _intrinsicGetter(_TYPED_ARRAY_PROTO, "byteLength", "%TypedArray%.prototype");
|
|
65
|
+
var _dvBuffer = _intrinsicGetter(DataView.prototype, "buffer", "DataView.prototype");
|
|
66
|
+
var _dvByteOffset = _intrinsicGetter(DataView.prototype, "byteOffset", "DataView.prototype");
|
|
67
|
+
var _dvByteLength = _intrinsicGetter(DataView.prototype, "byteLength", "DataView.prototype");
|
|
68
|
+
|
|
69
|
+
// A DataView carries its own accessors rather than the typed-array ones, so the pair is chosen by
|
|
70
|
+
// the value's slot. Either getter refuses a receiver with no slot behind it, which is the same
|
|
71
|
+
// refusal a detached store gets, and every caller here already types that.
|
|
72
|
+
function _storeOf(v) { return util.types.isDataView(v) ? _dvBuffer.call(v) : _taBuffer.call(v); }
|
|
73
|
+
function _offsetOf(v) { return util.types.isDataView(v) ? _dvByteOffset.call(v) : _taByteOffset.call(v); }
|
|
74
|
+
function _lengthOf(v) { return util.types.isDataView(v) ? _dvByteLength.call(v) : _taByteLength.call(v); }
|
|
75
|
+
|
|
76
|
+
// The Buffer over a view's own bytes. The single place this module turns a view into a Buffer, so
|
|
77
|
+
// the intrinsic reads above cannot be skipped at one of the two call sites and kept at the other.
|
|
78
|
+
function _reView(v) {
|
|
79
|
+
return Buffer.from(_storeOf(v), _offsetOf(v), _lengthOf(v));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Which concrete typed-array kind a value is, decided by its slot and answered with this realm's
|
|
83
|
+
// own constructor. `v.constructor` is an ordinary inherited property, so a subclass answers it and
|
|
84
|
+
// a copy built through it is whatever the caller said.
|
|
85
|
+
//
|
|
86
|
+
// The pairs are DERIVED from what this runtime offers, never written out. A hand-written list is
|
|
87
|
+
// complete on the day it is written and silently short afterwards: `Float16Array` landed on Node 24
|
|
88
|
+
// and a list drafted before it refused the kind outright, since the fallback that used to catch it
|
|
89
|
+
// was `v.constructor` -- which is the read this table exists to replace. Deriving covers the next
|
|
90
|
+
// kind on the day the runtime exposes its predicate, and covers nothing the runtime does not.
|
|
91
|
+
//
|
|
92
|
+
// `util.types` names each concrete kind's predicate `is<Constructor>`, so the global of that name
|
|
93
|
+
// is the constructor for it. `BYTES_PER_ELEMENT` on the CONSTRUCTOR is what separates a concrete
|
|
94
|
+
// kind from the umbrella predicates: `isTypedArray` has no `TypedArray` global to pair with, and
|
|
95
|
+
// `isArrayBufferView` would pair with none either.
|
|
96
|
+
// The predicate FUNCTION is held, never its name. A name is looked up afresh on every call, and
|
|
97
|
+
// `util.types` is an ordinary object: replacing a predicate on it after this module loads would
|
|
98
|
+
// hand the kind decision, and with it the constructor the copy is built from, to whoever did.
|
|
99
|
+
// Reading it once at load leaves nothing to replace.
|
|
100
|
+
var _CONCRETE_KINDS = Object.keys(util.types)
|
|
101
|
+
.filter(function (name) { return /^is[A-Za-z0-9]+Array$/.test(name); })
|
|
102
|
+
.map(function (name) { return [util.types[name], globalThis[name.slice(2)]]; })
|
|
103
|
+
.filter(function (row) {
|
|
104
|
+
return typeof row[0] === "function" && typeof row[1] === "function" &&
|
|
105
|
+
typeof row[1].BYTES_PER_ELEMENT === "number" && row[1].BYTES_PER_ELEMENT > 0;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
function _concreteKindOf(v) {
|
|
109
|
+
for (var i = 0; i < _CONCRETE_KINDS.length; i++) {
|
|
110
|
+
if (_CONCRETE_KINDS[i][0](v)) return _CONCRETE_KINDS[i][1];
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// A name for a refused value, for the message alone. Taken from what the value IS, since
|
|
116
|
+
// `v.constructor.name` is two ordinary property reads a caller answers: a getter under either one
|
|
117
|
+
// took the refusal below out through the caller's exception, in place of the typed error whose
|
|
118
|
+
// whole purpose here is to say why the value was refused.
|
|
119
|
+
var _NAMED_KINDS = [
|
|
120
|
+
["isNativeError", "Error"], ["isRegExp", "RegExp"], ["isPromise", "Promise"],
|
|
121
|
+
["isWeakMap", "WeakMap"], ["isWeakSet", "WeakSet"], ["isMap", "Map"], ["isSet", "Set"],
|
|
122
|
+
["isDate", "Date"], ["isProxy", "Proxy"]
|
|
123
|
+
].filter(function (row) { return typeof util.types[row[0]] === "function"; });
|
|
124
|
+
|
|
125
|
+
function _kindName(v) {
|
|
126
|
+
for (var i = 0; i < _NAMED_KINDS.length; i++) {
|
|
127
|
+
if (util.types[_NAMED_KINDS[i][0]](v)) return _NAMED_KINDS[i][1];
|
|
128
|
+
}
|
|
129
|
+
return typeof v === "function" ? "function" : "value";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function _article(name) { return /^[AEIOU]/i.test(name) ? "an" : "a"; }
|
|
133
|
+
|
|
134
|
+
// outputView(input, ErrorClass, code, label) -> a Buffer aliasing a caller's view, for WRITING.
|
|
135
|
+
//
|
|
136
|
+
// The same slot-based re-view as `view`, without the shared-memory refusal. That refusal answers a
|
|
137
|
+
// question about INPUT: bytes another thread can rewrite after they have been checked cannot be
|
|
138
|
+
// checked at all. A write target is the other direction. The caller is asking to be handed random
|
|
139
|
+
// bytes in memory they chose, and choosing shared memory is a decision they are entitled to make;
|
|
140
|
+
// `crypto.getRandomValues` on a `SharedArrayBuffer`-backed view is ordinary, and both the platform
|
|
141
|
+
// and this runtime fill it. Refusing it here turned a supported W3C call into an error.
|
|
142
|
+
//
|
|
143
|
+
// The bytes still come back through the intrinsic accessors, so a subclass cannot redirect the
|
|
144
|
+
// write to memory the caller does not own, and a detached store is still refused.
|
|
145
|
+
// @enforced-by behavioral -- an input door reaching for this instead of `view` has no rename-proof
|
|
146
|
+
// code shape; the RED vectors (a shared view filled here, and a detached one refused) are the guard.
|
|
147
|
+
function outputView(input, ErrorClass, code, label) {
|
|
148
|
+
if (!util.types.isUint8Array(input) && !ArrayBuffer.isView(input)) {
|
|
149
|
+
throw _raise(ErrorClass, code, label + ": expected a Buffer / TypedArray to write into");
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
return _reView(input);
|
|
153
|
+
} catch (e) {
|
|
154
|
+
throw _raise(ErrorClass, code, label + ": output is not a usable byte view (detached backing buffer?)", e);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// lengthOf(view) -> how many bytes a view holds, read the same way.
|
|
159
|
+
//
|
|
160
|
+
// For the boundaries that size a value rather than read it: a response-body cap, an emptiness
|
|
161
|
+
// test. `length` is an accessor on the shared typed-array prototype AND shadowable by an own
|
|
162
|
+
// property on any instance, so `Object.defineProperty(buf, "length", { get: () => 0 })` answers
|
|
163
|
+
// for a Buffer holding megabytes. A cap measured that way is a cap its caller sets.
|
|
164
|
+
// @enforced-by behavioral -- `x.length` is the ordinary spelling wherever the value is this
|
|
165
|
+
// toolkit's own, so the misuse has no rename-proof code shape; the RED vector (a planted `length`
|
|
166
|
+
// getting a body past a documented size cap) is the guard.
|
|
167
|
+
function lengthOf(view) {
|
|
168
|
+
return _lengthOf(view);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Shared memory is refused rather than accepted as bytes.
|
|
172
|
+
//
|
|
173
|
+
// This module exists to hand a verb bytes its caller can no longer change once they have been
|
|
174
|
+
// checked, and a SharedArrayBuffer is writable from another thread for as long as it lives, so
|
|
175
|
+
// no copy taken here can hold that promise for the value the caller passed. Copying it into a
|
|
176
|
+
// private ArrayBuffer would keep the bytes and change the kind, which is the same-kind contract
|
|
177
|
+
// broken quietly; refusing says so instead. The message names the shape, since "not a byte
|
|
178
|
+
// container" about a container that plainly holds bytes reads as the toolkit being wrong.
|
|
179
|
+
function _refuseShared(v, ErrorClass, code, label) {
|
|
180
|
+
if (util.types.isSharedArrayBuffer(v) ||
|
|
181
|
+
(ArrayBuffer.isView(v) && util.types.isSharedArrayBuffer(_storeOf(v)))) {
|
|
182
|
+
throw _raise(ErrorClass, code, label + ": shared memory cannot be used here, because another " +
|
|
183
|
+
"thread can rewrite it after it has been checked; pass a Buffer or a Uint8Array over " +
|
|
184
|
+
"memory this process owns");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
10
187
|
|
|
11
188
|
// guard-bytes -- fail-closed coercion of an untrusted byte-source input to a
|
|
12
189
|
// Buffer view. One of the enforced choke points of the guard family: a
|
|
@@ -49,12 +226,22 @@ function _raise(E, code, message, cause) {
|
|
|
49
226
|
// Accepts a Buffer / Uint8Array -- the DER / CBOR / CT / Merkle input contract.
|
|
50
227
|
// ErrorClass MUST be a withCause PkiError subclass (the raw detach failure is
|
|
51
228
|
// threaded as the cause).
|
|
229
|
+
// The kind is read from the slot. A prototype test turns away a real Uint8Array built in another
|
|
230
|
+
// realm, whose prototype is that realm's, and tells the caller their bytes are not bytes. It
|
|
231
|
+
// admits the mirror shape, `Object.create(Uint8Array.prototype)`, which holds none; that one comes
|
|
232
|
+
// back as this guard's own code either way, since the read below is wrapped, so reading the slot
|
|
233
|
+
// only moves the refusal to the door from the failing read.
|
|
52
234
|
// @enforced-by guard-shape-reinlined
|
|
53
|
-
//
|
|
235
|
+
// The shape covers the re-view however the view is constructed. It was anchored on `Buffer.from(`
|
|
236
|
+
// alone, and `Buffer.from(new Uint8Array(v.buffer, v.byteOffset, v.byteLength))` slipped past it
|
|
237
|
+
// into two format modules, where a caller's subclass could answer all three accessors and hand the
|
|
238
|
+
// copy a different store than the array held.
|
|
239
|
+
// @guard-shape (?:Buffer\.from|new\s+[A-Za-z_$][\w$]*Array)\(\s*(?:new\s+[A-Za-z_$][\w$]*Array\(\s*)?([A-Za-z_$][\w$]*)\.buffer\s*,\s*\1\.byteOffset
|
|
54
240
|
function view(input, ErrorClass, code, label) {
|
|
55
|
-
|
|
241
|
+
_refuseShared(input, ErrorClass, code, label);
|
|
242
|
+
if (util.types.isUint8Array(input)) {
|
|
56
243
|
try {
|
|
57
|
-
return
|
|
244
|
+
return _reView(input);
|
|
58
245
|
} catch (e) {
|
|
59
246
|
throw _raise(ErrorClass, code, label + ": input is not a usable byte view (detached backing buffer?)", e);
|
|
60
247
|
}
|
|
@@ -72,10 +259,11 @@ function view(input, ErrorClass, code, label) {
|
|
|
72
259
|
// copies. Throws on a detached backing store.
|
|
73
260
|
// @enforced-by guard-shape-reinlined (the re-view shape is declared on view above)
|
|
74
261
|
function source(input, ErrorClass, code, label) {
|
|
75
|
-
|
|
262
|
+
_refuseShared(input, ErrorClass, code, label);
|
|
263
|
+
var isAb = _isArrayBuffer(input);
|
|
76
264
|
if (isAb || ArrayBuffer.isView(input)) {
|
|
77
265
|
try {
|
|
78
|
-
return isAb ? Buffer.from(input) :
|
|
266
|
+
return isAb ? Buffer.from(input) : _reView(input);
|
|
79
267
|
} catch (e) {
|
|
80
268
|
throw _raise(ErrorClass, code, label + ": input is not a usable byte source (detached backing buffer?)", e);
|
|
81
269
|
}
|
|
@@ -168,13 +356,36 @@ function snapshotDeep(value, ErrorClass, code, label, opts) {
|
|
|
168
356
|
// platform's, or another implementation's) carries exactly these (its key material lives in an
|
|
169
357
|
// internal slot or a non-enumerable own property). A caller's options bag that happens to satisfy
|
|
170
358
|
// the structural predicate carries its own fields alongside, and those are what give it away.
|
|
171
|
-
|
|
359
|
+
// `Symbol.toStringTag` is the language's own name for what a value is, and a key handle carries it
|
|
360
|
+
// as a non-enumerable own property. It answers no named option, so it is surface rather than data,
|
|
361
|
+
// and naming it here is what lets the pass-through test below judge every key by the same rule it
|
|
362
|
+
// judges every other property by.
|
|
363
|
+
// A surface is a table of names, so it is built WITHOUT a prototype. An ordinary object literal
|
|
364
|
+
// answers `surface["toString"]` with a function it inherited, and every table is then read as
|
|
365
|
+
// though it listed `toString`, `constructor`, `valueOf`, `hasOwnProperty` and `__proto__` -- names
|
|
366
|
+
// a caller can write, on an object that is passed on by reference. Membership has to be a fact
|
|
367
|
+
// about the table rather than about what any object answers to a lookup.
|
|
368
|
+
//
|
|
369
|
+
// `Symbol.toStringTag` is on every surface: it is the language's own name for what a value is, a
|
|
370
|
+
// key handle carries it as a non-enumerable own property, and it answers no named option.
|
|
371
|
+
function _surface(names) {
|
|
372
|
+
var t = Object.create(null);
|
|
373
|
+
names.forEach(function (n) { t[n] = 1; });
|
|
374
|
+
t[Symbol.toStringTag] = 1;
|
|
375
|
+
return t;
|
|
376
|
+
}
|
|
377
|
+
// `_handle` is this engine's own state on its own key object, the counterpart of the
|
|
378
|
+
// `asymmetricKeyType` and `asymmetricKeyDetails` that Node puts on a KeyObject. What it holds
|
|
379
|
+
// varies by algorithm -- a KeyObject for a signing key, the raw bytes for an HMAC one -- and it is
|
|
380
|
+
// reachable only on a value already recognized as a key, whose material the caller holds anyway.
|
|
381
|
+
var _CRYPTO_KEY_SURFACE = _surface(["type", "extractable", "algorithm", "usages",
|
|
382
|
+
"asymmetricKeyType", "asymmetricKeyDetails", "_handle"]);
|
|
172
383
|
// The same, for the other kinds whose state cannot be copied: what belongs to the kind, so that
|
|
173
384
|
// anything else on the object is recognizably the caller's own and gets the refusal below.
|
|
174
|
-
var _ERROR_SURFACE =
|
|
175
|
-
var _REGEXP_SURFACE =
|
|
176
|
-
sticky
|
|
177
|
-
var _THENABLE_SURFACE =
|
|
385
|
+
var _ERROR_SURFACE = _surface(["message", "stack", "name", "cause"]);
|
|
386
|
+
var _REGEXP_SURFACE = _surface(["lastIndex", "source", "flags", "global", "ignoreCase", "multiline",
|
|
387
|
+
"sticky", "unicode", "unicodeSets", "hasIndices", "dotAll"]);
|
|
388
|
+
var _THENABLE_SURFACE = _surface(["then", "catch", "finally"]);
|
|
178
389
|
|
|
179
390
|
// Is this an object whose state this module cannot read, and therefore cannot copy?
|
|
180
391
|
//
|
|
@@ -190,10 +401,27 @@ var _THENABLE_SURFACE = { then: 1, catch: 1, finally: 1 };
|
|
|
190
401
|
function _opaqueSurface(v, ErrorClass, code, label) {
|
|
191
402
|
var proto = Object.getPrototypeOf(v);
|
|
192
403
|
if (proto === Object.prototype || proto === null) return null;
|
|
193
|
-
if (v
|
|
404
|
+
if (util.types.isWeakMap(v) || util.types.isWeakSet(v)) return {}; // deliberately not enumerable
|
|
194
405
|
if (v instanceof Error) return _ERROR_SURFACE;
|
|
195
|
-
if (v
|
|
196
|
-
|
|
406
|
+
if (util.types.isRegExp(v)) return _REGEXP_SURFACE;
|
|
407
|
+
// A node:crypto KeyObject, read off its internal slot. `pki.hpke` documents one as a key input
|
|
408
|
+
// and `pki.ct` names it too, and a copy of a key handle is not a key: its material lives behind
|
|
409
|
+
// the slot, so what the copy holds is the shape of a key and none of the key. Recognized here so
|
|
410
|
+
// it reaches the verb as itself, which is the same treatment a WebCrypto CryptoKey gets.
|
|
411
|
+
// `util.types.isKeyObject` reads the slot, so it answers for a handle from any realm and says no
|
|
412
|
+
// to `Object.create(KeyObject.prototype)`.
|
|
413
|
+
if (util.types.isKeyObject(v)) return _CRYPTO_KEY_SURFACE;
|
|
414
|
+
// Asking whether it is key-shaped reads its properties, and a value wearing a key's prototype
|
|
415
|
+
// without a key behind it answers with a getter that throws: `Object.create(KeyObject.prototype)`
|
|
416
|
+
// raises a raw TypeError from inside `type`. Typed here, like the `then` read below, so a
|
|
417
|
+
// question this module asked on its own behalf cannot escape as somebody else's error.
|
|
418
|
+
var keyLike;
|
|
419
|
+
try {
|
|
420
|
+
keyLike = require("./webcrypto").isCryptoKeyLike(v); // allow:inline-require -- circular load: webcrypto requires this module
|
|
421
|
+
} catch (e) {
|
|
422
|
+
throw _raise(ErrorClass, code, label + ": reading the key surface threw", e);
|
|
423
|
+
}
|
|
424
|
+
if (keyLike) return _CRYPTO_KEY_SURFACE;
|
|
197
425
|
// Asking whether it is a thenable reads a property, and a caller's accessor can throw: the
|
|
198
426
|
// same fault the copy itself types, so it is typed here too and never escapes raw from a
|
|
199
427
|
// question this module asked on its own behalf.
|
|
@@ -209,42 +437,100 @@ function _opaqueSurface(v, ErrorClass, code, label) {
|
|
|
209
437
|
// passing it through would leave that data the caller's to change after the checks have read it.
|
|
210
438
|
// Every reachable name is examined, not only the own ones; a verb reads a field by name and does
|
|
211
439
|
// not care where on the chain it sits.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
440
|
+
// Can this name answer differently after the checks have read it? That is the whole question for a
|
|
441
|
+
// value handed on BY REFERENCE, and it is a fact about the property rather than a guess about who
|
|
442
|
+
// wrote it. An accessor answers afresh every read. A writable or configurable data property can be
|
|
443
|
+
// assigned or redefined. A data property that is neither is settled: whatever the checks saw is
|
|
444
|
+
// what the verb gets.
|
|
445
|
+
//
|
|
446
|
+
// Judged where the property is found, walking up as a read does. A `getPrototypeOf` trap can hand
|
|
447
|
+
// back a fresh object forever, so the walk stops at one it has already visited, the same bound the
|
|
448
|
+
// name walk uses.
|
|
449
|
+
//
|
|
450
|
+
// One limit, stated rather than papered over: a caller can still swap the whole prototype after the
|
|
451
|
+
// call and change what an inherited name answers. That is true of every value passed by reference,
|
|
452
|
+
// and a platform object's prototype is the platform's.
|
|
453
|
+
function _canChangeAfterTheCheck(v, name) {
|
|
454
|
+
var visited = new Set();
|
|
455
|
+
for (var o = v; o && !visited.has(o); o = Object.getPrototypeOf(o)) {
|
|
456
|
+
visited.add(o);
|
|
457
|
+
var d = Object.getOwnPropertyDescriptor(o, name);
|
|
458
|
+
if (!d) continue;
|
|
459
|
+
if (d.get || d.set || d.writable || d.configurable) return true;
|
|
460
|
+
// Settled where it sits. Reached through the chain it is settled only while nothing can be put
|
|
461
|
+
// in front of it: an extensible receiver takes an own property of the same name at any time,
|
|
462
|
+
// and the read then answers with that one. Own on the value itself, or a value nothing can be
|
|
463
|
+
// added to, is the case where what was read is what stays.
|
|
464
|
+
return o !== v && Object.isExtensible(v);
|
|
224
465
|
}
|
|
225
|
-
return
|
|
466
|
+
return false;
|
|
226
467
|
}
|
|
227
468
|
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
469
|
+
// Is what a settled binding POINTS AT settled too? A property that cannot be reassigned still
|
|
470
|
+
// answers with whatever its value has become: a frozen `signingTime` holding a Date is a binding
|
|
471
|
+
// nobody can move and an instant anybody can move, and `pki.cms.sign` signed 2030 while the
|
|
472
|
+
// operator read 2040 off the same object afterwards. A primitive cannot change. Another handle is
|
|
473
|
+
// settled exactly when the same question about it comes back clean, which is this function one
|
|
474
|
+
// level down. Everything else is state the caller keeps, and this module would have copied it.
|
|
475
|
+
//
|
|
476
|
+
// Bounded, because the chain is the caller's to make as long as they like.
|
|
477
|
+
var _SETTLED_DEPTH = 4;
|
|
478
|
+
function _settledValueIsFixed(value, ErrorClass, code, label, depth) {
|
|
479
|
+
if (value === null || (typeof value !== "object" && typeof value !== "function")) return true;
|
|
480
|
+
if (depth >= _SETTLED_DEPTH) return false;
|
|
481
|
+
// A Proxy answers every question through a handler, so it is never settled and asking is the
|
|
482
|
+
// harm: reading its prototype runs a trap, and a trap that throws would leave this module over
|
|
483
|
+
// a question it asked on its own behalf. `util.types.isProxy` reads the slot and runs nothing.
|
|
484
|
+
if (util.types.isProxy(value)) return false;
|
|
485
|
+
var surface = _opaqueSurface(value, ErrorClass, code, label);
|
|
486
|
+
if (!surface) return false;
|
|
487
|
+
return _opaqueFieldOutsideSurface(value, surface, ErrorClass, code, label, depth + 1) === null;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function _opaqueFieldOutsideSurface(v, surface, ErrorClass, code, label, depth) {
|
|
491
|
+
var keys = _namesToCopy(v, ErrorClass, code, label);
|
|
492
|
+
for (var i = 0; i < keys.length; i++) {
|
|
493
|
+
// The kind's own surface, and nothing else. Enumerability says how a property was written,
|
|
494
|
+
// never who wrote it: `Object.defineProperty` is available to a caller as much as to a
|
|
495
|
+
// platform, so skipping the non-enumerable ones let a field ride in on a handle that is
|
|
496
|
+
// passed through BY REFERENCE and stays the caller's to change afterwards. A foreign RegExp
|
|
497
|
+
// carrying a non-enumerable `detached` reached pki.cms.sign that way, and flipping it after
|
|
498
|
+
// the call moved the content out of the message the call had already been asked to sign.
|
|
499
|
+
//
|
|
500
|
+
// The surfaces are what makes this affordable rather than a blanket refusal: each names what
|
|
501
|
+
// the real platform object of that kind carries, so the ordinary value passes and anything
|
|
502
|
+
// beyond it is the caller's. A callable value is no exemption either -- an option is read for
|
|
503
|
+
// what it is, and `signedAttributes` holding a function is neither false nor a method of
|
|
504
|
+
// anything.
|
|
505
|
+
if (surface[keys[i]]) continue;
|
|
506
|
+
// A symbol is not an option. Every verb reads its options by name, so nothing under a symbol
|
|
507
|
+
// key reaches one, and refusing a handle for carrying one buys nothing. What it costs is real:
|
|
508
|
+
// a CryptoKey from another WebCrypto implementation is a documented input, and an
|
|
509
|
+
// implementation that keeps its internals under a symbol -- written the ordinary way, by
|
|
510
|
+
// assignment -- would have its keys refused at every verb.
|
|
511
|
+
//
|
|
512
|
+
// The options door is unaffected and still refuses one. `assertKnownKeys` walks the bag a
|
|
513
|
+
// caller passed as options and reports a symbol there, because a caller who wrote one meant it
|
|
514
|
+
// as an option and no verb will ever read it. That is a different question from whether a key
|
|
515
|
+
// handle may be handed on.
|
|
516
|
+
if (typeof keys[i] === "symbol") continue;
|
|
517
|
+
// A name outside the surface is refused only if it can still move. Enumerability was the old
|
|
518
|
+
// test and it decided the wrong question: it describes how a property was written, and
|
|
519
|
+
// `Object.defineProperty` belongs to a caller as much as to a platform, so a hidden `detached`
|
|
520
|
+
// on a foreign RegExp rode into pki.cms.sign and moved the content out of the signed message
|
|
521
|
+
// after the call. Position was no better -- a key is an argument in its own right at
|
|
522
|
+
// `pki.csr.sign(spec, key)`, so refusing everything at the argument refused the key.
|
|
523
|
+
//
|
|
524
|
+
// Settled data is what an implementation's internals are: this engine writes `_handle` on a key
|
|
525
|
+
// with neither `writable` nor `configurable`, so it cannot be reassigned or redefined, and
|
|
526
|
+
// nothing the checks read about that key can change afterwards. A field a caller can still
|
|
527
|
+
// assign to, redefine, or answer with a getter is the case this refusal is for -- whatever
|
|
528
|
+
// kind of object it rides on, and wherever in the arguments that object sits.
|
|
529
|
+
if (!_canChangeAfterTheCheck(v, keys[i]) &&
|
|
530
|
+
_settledValueIsFixed(v[keys[i]], ErrorClass, code, label, depth || 0)) continue;
|
|
531
|
+
return keys[i];
|
|
246
532
|
}
|
|
247
|
-
return
|
|
533
|
+
return null;
|
|
248
534
|
}
|
|
249
535
|
|
|
250
536
|
// A private copy of a byte value of the same kind the caller passed. The kind is
|
|
@@ -260,15 +546,107 @@ function _copyBytesSameKind(v, ErrorClass, code, label, collect) {
|
|
|
260
546
|
// What `release` clears: a Buffer over the whole private store, whatever kind is handed back.
|
|
261
547
|
if (collect) collect.push(Buffer.from(owned, 0, src.length));
|
|
262
548
|
if (Buffer.isBuffer(v)) return Buffer.from(owned, 0, src.length);
|
|
263
|
-
if (v
|
|
264
|
-
if (v
|
|
265
|
-
|
|
549
|
+
if (_isArrayBuffer(v)) return owned;
|
|
550
|
+
if (util.types.isDataView(v)) return new DataView(owned);
|
|
551
|
+
// The kind comes from the value's slot, never from `v.constructor` or `v.BYTES_PER_ELEMENT`.
|
|
552
|
+
// Both are ordinary properties a subclass answers: a `constructor` that threw took this module
|
|
553
|
+
// out through the caller's own exception, and a `BYTES_PER_ELEMENT` that lied built the copy
|
|
554
|
+
// with a different element count and a different kind than the value it was copying.
|
|
555
|
+
var Ctor = _concreteKindOf(v);
|
|
556
|
+
if (!Ctor) {
|
|
557
|
+
throw _raise(ErrorClass, code, label + ": a " +
|
|
558
|
+
(util.types.isTypedArray(v) ? "typed array of a kind this runtime added" : "byte view") +
|
|
559
|
+
" cannot be copied while keeping its kind; pass a Buffer or a Uint8Array");
|
|
560
|
+
}
|
|
561
|
+
return new Ctor(owned, 0, src.length / Ctor.BYTES_PER_ELEMENT);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Is this value's prototype chain cyclic? A Proxy whose `getPrototypeOf` trap returns the proxy
|
|
565
|
+
// itself makes one, and the engine permits it while the target stays extensible.
|
|
566
|
+
//
|
|
567
|
+
// It has to be asked before anything else this module does with the value. Almost every question
|
|
568
|
+
// about a kind is an `instanceof`, and `instanceof` walks the prototype chain. On a cyclic chain
|
|
569
|
+
// it exhausts the stack, and a raw RangeError escapes from a guard whose whole contract is a
|
|
570
|
+
// typed error.
|
|
571
|
+
//
|
|
572
|
+
// Naming the shape is the fail-closed answer. An options bag cannot need a prototype chain that
|
|
573
|
+
// loops, and copying one has no meaning to fall back to.
|
|
574
|
+
function _protoChainIsCyclic(v) {
|
|
575
|
+
var visited = new Set();
|
|
576
|
+
for (var o = v; o; o = Object.getPrototypeOf(o)) {
|
|
577
|
+
if (visited.has(o)) return true;
|
|
578
|
+
visited.add(o);
|
|
579
|
+
}
|
|
580
|
+
return false;
|
|
266
581
|
}
|
|
267
582
|
|
|
583
|
+
// Is a Proxy anywhere on this chain? The copy reads names from the whole chain, so one sitting
|
|
584
|
+
// above a plain object decides what the copy takes while the original answers reads from its
|
|
585
|
+
// `get` trap. Testing only the value handed in leaves that one line of indirection open.
|
|
586
|
+
function _protoChainHasProxy(v) {
|
|
587
|
+
var visited = new Set();
|
|
588
|
+
for (var o = v; o && !visited.has(o); o = Object.getPrototypeOf(o)) {
|
|
589
|
+
visited.add(o);
|
|
590
|
+
if (util.types.isProxy(o)) return true;
|
|
591
|
+
}
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Give the copy of a collection the same prototype the original carries.
|
|
596
|
+
//
|
|
597
|
+
// The object branch does this with `Object.create(Object.getPrototypeOf(v))`, so an instance of a
|
|
598
|
+
// caller's class keeps its methods. A collection branch builds a plain `[]`, `Map` or `Set`, and
|
|
599
|
+
// a subclass of one holds its additions on a prototype above that. Copying the chain down onto a
|
|
600
|
+
// plain array turns `class Opts extends Array { describe() {} }` into an array with `describe` as
|
|
601
|
+
// an own function that nothing above supplies, which the check reads as a typed option and
|
|
602
|
+
// refuses, though the same bag passed that check before the copy.
|
|
603
|
+
//
|
|
604
|
+
// A subclass of a built-in is the caller's own class, so this is the same trade the object branch
|
|
605
|
+
// makes. The prototype is theirs and stays theirs, while every readable name is copied down so no
|
|
606
|
+
// value is re-read through it after the checks have run.
|
|
607
|
+
// The copy's class is this module's, never the caller's.
|
|
608
|
+
//
|
|
609
|
+
// A copy used to be given the original's prototype back, so that the walk which reports option
|
|
610
|
+
// names read the copy the way it read the original. It does that anyway: a method the caller's
|
|
611
|
+
// class defines is not a name that walk reports, so it is never copied across as one, and the two
|
|
612
|
+
// sides agree without the prototype. Measured, not assumed -- the vectors compare both.
|
|
613
|
+
//
|
|
614
|
+
// What restoring it cost was the whole point of copying. The prototype is part of the argument, so
|
|
615
|
+
// the copy went on dispatching into the caller's code: `list.map(...)` inside a signing verb
|
|
616
|
+
// reached an override on an Array subclass, `getUTCFullYear()` reached one on a Date subclass, and
|
|
617
|
+
// an override that threw surfaced the caller's own exception from inside a verb whose contract is
|
|
618
|
+
// a typed error. Worse, a prototype can be rewritten after the call has begun, so a copy taken to
|
|
619
|
+
// stop the argument changing under the verb changed with it.
|
|
620
|
+
function _plainCopy(copy) { return copy; }
|
|
621
|
+
|
|
268
622
|
function _deep(v, ErrorClass, code, label, cap, depth, collect) {
|
|
269
623
|
if (depth > cap) throw _raise(ErrorClass, code, label + " is nested too deeply to copy");
|
|
270
624
|
if (v == null || typeof v !== "object") return v;
|
|
271
|
-
|
|
625
|
+
_refuseShared(v, ErrorClass, code, label);
|
|
626
|
+
// The Proxy test comes FIRST, ahead of the cycle walk and every `instanceof` below, because
|
|
627
|
+
// both of those read the prototype chain and a `getPrototypeOf` trap runs the caller's code.
|
|
628
|
+
// A trap that throws then escapes as its own raw Error from a guard whose whole contract is a
|
|
629
|
+
// typed one. This test reaches `Object.getPrototypeOf` only on links it has already cleared,
|
|
630
|
+
// so no trap runs before the refusal that makes running it unnecessary.
|
|
631
|
+
//
|
|
632
|
+
// A Proxy answers `ownKeys` and `get` from two independent traps, so the copy takes the names
|
|
633
|
+
// one trap reports while the original answers reads from the other. One reporting no keys and
|
|
634
|
+
// returning "pw" for `password` copies to an object holding nothing, and the field the caller
|
|
635
|
+
// supplied is gone with no fault raised. Copying it is therefore not copying it, which is the
|
|
636
|
+
// case this refusal is for: an argument is copied or refused, never half-taken.
|
|
637
|
+
if (_protoChainHasProxy(v)) {
|
|
638
|
+
throw _raise(ErrorClass, code, label + ": a Proxy cannot be copied faithfully, because the " +
|
|
639
|
+
"keys it reports need not be the ones it answers; pass a plain object");
|
|
640
|
+
}
|
|
641
|
+
// A second line under the Proxy rule, and unreachable while that one holds: `setPrototypeOf`
|
|
642
|
+
// refuses to build a cycle out of ordinary objects, so a Proxy is the only way to have one and
|
|
643
|
+
// the refusal above already covers every such value. It stays because the cost is one walk and
|
|
644
|
+
// the failure it prevents is an unbounded loop rather than a wrong answer.
|
|
645
|
+
if (_protoChainIsCyclic(v)) {
|
|
646
|
+
throw _raise(ErrorClass, code, label + ": its prototype chain is a cycle, so it cannot be " +
|
|
647
|
+
"read or copied; pass a plain object");
|
|
648
|
+
}
|
|
649
|
+
if (Buffer.isBuffer(v) || ArrayBuffer.isView(v) || _isArrayBuffer(v)) {
|
|
272
650
|
var bytesCopy = _copyBytesSameKind(v, ErrorClass, code, label, collect);
|
|
273
651
|
// A byte value can carry named properties too, and a verb reads an option by name whatever the
|
|
274
652
|
// argument's type: `opts = new Uint8Array(0); opts.pem = true` is an options object as far as
|
|
@@ -284,16 +662,66 @@ function _deep(v, ErrorClass, code, label, cap, depth, collect) {
|
|
|
284
662
|
// added a field no door re-derives, so `AsProduced` is what is asked, not `isRecorded`.
|
|
285
663
|
// Required inline because guard-parsed requires this module; at call time it is fully loaded.
|
|
286
664
|
if (require("./guard-parsed").isRecordedAsProduced(v)) return v; // allow:inline-require -- circular load: guard-parsed requires this module
|
|
287
|
-
if (
|
|
665
|
+
if (util.types.isDate(v)) {
|
|
666
|
+
// A Date can carry named properties too, and a verb reads an option by name whatever the
|
|
667
|
+
// argument's type. Returning the instant alone dropped anything the caller had added, so the
|
|
668
|
+
// copy held nothing while the original still answered, and the check read the copy.
|
|
669
|
+
//
|
|
670
|
+
// The instant comes through the time guard's intrinsic read. `getTime` is an ordinary method
|
|
671
|
+
// a subclass answers, so reading it off the value put a different instant in the copy than the
|
|
672
|
+
// original holds, and the value validated and the value used were two different reads.
|
|
673
|
+
//
|
|
674
|
+
// What the copy carries is the caller's DATA. A name the language already supplies on a Date
|
|
675
|
+
// is that Date's behavior, and copying an own one down put a caller's function on an object
|
|
676
|
+
// whose whole purpose is to answer as a plain Date holding that instant: an own
|
|
677
|
+
// `getUTCFullYear` that throws rode onto the copy and threw out of pki.cms.sign when the
|
|
678
|
+
// signing time was encoded. Normalizing the prototype does not reach an own shadow, so the
|
|
679
|
+
// shadow is left behind here. The excluded set is read off `Date.prototype` rather than
|
|
680
|
+
// written out, so it cannot fall behind the language.
|
|
681
|
+
var dateCopy = new Date(time.instantOf(v));
|
|
682
|
+
_copyNamed(v, dateCopy, ErrorClass, code, label, cap, depth, collect, _DATE_BEHAVIOR);
|
|
683
|
+
return _plainCopy(dateCopy);
|
|
684
|
+
}
|
|
288
685
|
if (Array.isArray(v)) {
|
|
289
686
|
var arr = [];
|
|
290
|
-
|
|
687
|
+
// The indices a read on the array resolves, rather than a count from zero to `length`. An
|
|
688
|
+
// array is sparse whenever a caller sets a high index or assigns `length`, and
|
|
689
|
+
// `[].length = 4e9` is one statement: walking to `length` then does four billion iterations
|
|
690
|
+
// for an array holding nothing, inside a guard whose job includes keeping a caller's argument
|
|
691
|
+
// from costing unbounded work. Copying the indices that resolve costs what the array actually
|
|
692
|
+
// holds. `length` is carried across afterwards so trailing holes survive and the copy measures
|
|
693
|
+
// the same.
|
|
694
|
+
//
|
|
695
|
+
// Asked of guard-identifier, which owns the same question for names. Its own keys are not the
|
|
696
|
+
// answer: an array consults its prototype for a hole, so a length-one signer list holding its
|
|
697
|
+
// only signer on its prototype reports no own index while every consumer reads one element.
|
|
698
|
+
// Copying the own keys alone handed the verb an empty list.
|
|
699
|
+
var indexE = function (c, m) { return _raise(ErrorClass, c, m); };
|
|
700
|
+
var indices = identifier.readableIndices(v, indexE, code, label);
|
|
701
|
+
// An accessor is refused among the elements for the reason it is refused among the names:
|
|
702
|
+
// reading it once and writing what it returned as plain data leaves the later check nothing
|
|
703
|
+
// to find, since it sees a value and cannot tell an accessor was ever there. The refusal
|
|
704
|
+
// covered names only, so the same trick under an index passed -- and a list is exactly where
|
|
705
|
+
// it pays, an element answering as a trusted signer to the check and as another afterwards.
|
|
706
|
+
// The argument itself, not everything under it, matching the rule for names.
|
|
707
|
+
if (depth === 0) identifier.refuseAccessorFields(v, indices, indexE, code, label);
|
|
708
|
+
indices.forEach(function (k) {
|
|
709
|
+
// Reading an element can run a caller's accessor, the same as reading a named field: an
|
|
710
|
+
// index can carry a getter too, and one that threw escaped as itself from a boundary whose
|
|
711
|
+
// contract is a typed error. Typed here with the raw fault as its cause, as the named walk
|
|
712
|
+
// below does, so an element and a field fail the same way.
|
|
713
|
+
var element;
|
|
714
|
+
try { element = v[k]; }
|
|
715
|
+
catch (e) { throw _raise(ErrorClass, code, label + ": reading element " + k + " threw", e); }
|
|
716
|
+
arr[k] = _deep(element, ErrorClass, code, label, cap, depth + 1, collect);
|
|
717
|
+
});
|
|
718
|
+
arr.length = v.length;
|
|
291
719
|
// An array can carry named properties too, and a verb reads an option by name whatever the
|
|
292
720
|
// argument's type: `opts = []; opts.pem = true` is an options object as far as `opts.pem` is
|
|
293
721
|
// concerned. Copying only the indexed elements dropped those fields, which silently changed
|
|
294
722
|
// what the verb was asked to do instead of failing.
|
|
295
723
|
_copyNamed(v, arr, ErrorClass, code, label, cap, depth, collect);
|
|
296
|
-
return arr;
|
|
724
|
+
return _plainCopy(arr);
|
|
297
725
|
}
|
|
298
726
|
// The only kinds this module cannot read the state of. One carrying nothing of its own is passed
|
|
299
727
|
// through: there is no data on it to fix, and a copy would break it. One carrying its own
|
|
@@ -302,18 +730,33 @@ function _deep(v, ErrorClass, code, label, cap, depth, collect) {
|
|
|
302
730
|
// never half-handled, which is what keeps this from being another shape to be found later.
|
|
303
731
|
var surface = _opaqueSurface(v, ErrorClass, code, label);
|
|
304
732
|
if (surface) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
733
|
+
// The name that decided it, so the message says which field to move rather than leaving the
|
|
734
|
+
// caller to find it on an object whose whole problem is that its state cannot be read.
|
|
735
|
+
var carried = _opaqueFieldOutsideSurface(v, surface, ErrorClass, code, label);
|
|
736
|
+
if (carried === null) return v;
|
|
737
|
+
throw _raise(ErrorClass, code, label + ": " + _article(_kindName(v)) + " " + _kindName(v) +
|
|
738
|
+
" carrying its own field " + JSON.stringify(String(carried)) + " cannot be used here -- its " +
|
|
739
|
+
"state cannot be copied, so that field would stay changeable after it was checked; pass the " +
|
|
740
|
+
"fields as a plain object");
|
|
741
|
+
}
|
|
742
|
+
if (util.types.isMap(v)) {
|
|
743
|
+
return _plainCopy(_copyEntries(v, new Map(), ErrorClass, code, label, cap, depth, collect));
|
|
744
|
+
}
|
|
745
|
+
if (util.types.isSet(v)) {
|
|
746
|
+
return _plainCopy(_copyEntries(v, new Set(), ErrorClass, code, label, cap, depth, collect));
|
|
309
747
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
//
|
|
313
|
-
//
|
|
314
|
-
//
|
|
315
|
-
//
|
|
316
|
-
|
|
748
|
+
// A plain object, or a dictionary with no prototype at all -- never the caller's class.
|
|
749
|
+
//
|
|
750
|
+
// Carrying the class across was for its methods, and it cost what copying is for: the copy went
|
|
751
|
+
// on dispatching into the caller's code, and their class could be rewritten after the call had
|
|
752
|
+
// begun. The names the copy reports do not depend on it, which the vectors measure rather than
|
|
753
|
+
// assume, and no verb calls a method on an options bag.
|
|
754
|
+
//
|
|
755
|
+
// Having NO prototype is different and is kept. `Object.create(null)` and what `JSON.parse`
|
|
756
|
+
// produces inherit nothing, and a copy that gained `Object.prototype` would inherit whatever a
|
|
757
|
+
// runtime has planted there -- names the original never answered, reported against a caller who
|
|
758
|
+
// never wrote them.
|
|
759
|
+
var out = Object.create(Object.getPrototypeOf(v) === null ? null : Object.prototype);
|
|
317
760
|
_copyNamed(v, out, ErrorClass, code, label, cap, depth, collect);
|
|
318
761
|
return out;
|
|
319
762
|
}
|
|
@@ -329,25 +772,49 @@ function _deep(v, ErrorClass, code, label, cap, depth, collect) {
|
|
|
329
772
|
// allocated from). Every one of those is already correct on the copy, and reading them would
|
|
330
773
|
// copy things the caller never passed. Enumerating the accessors to skip is the wrong way round;
|
|
331
774
|
// what the caller added is exactly what is own and not an index.
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
775
|
+
//
|
|
776
|
+
// Which strings are indices is guard-identifier's to answer, for names and for elements alike. A
|
|
777
|
+
// second copy of that test here could drift from it, and the set of names that module passes over
|
|
778
|
+
// would then stop matching the set this one carries across as structure.
|
|
779
|
+
|
|
780
|
+
// The names to carry across, asked of the module that decides which names a caller supplied.
|
|
781
|
+
//
|
|
782
|
+
// One walk answers for both questions, because a second walk here drifts from it and the two then
|
|
783
|
+
// disagree about one value. A walk that stops at whatever object is identical to `Object.prototype`
|
|
784
|
+
// reads a foreign realm's root as caller data and copies `__proto__` down as a field. One that
|
|
785
|
+
// reads every level outside a collection gives `Object.create(Array.prototype)` an own `length`
|
|
786
|
+
// and `Symbol.unscopables`. Either way the copy holds a different set than the check accepted, and
|
|
787
|
+
// a bag that passes the check is refused once it has been copied.
|
|
788
|
+
//
|
|
789
|
+
// The prototype is kept on every branch, so a name skipped here still resolves through it, and the
|
|
790
|
+
// indices of an array are carried by that branch as the structure they are. What the check reads
|
|
791
|
+
// is therefore what the copy answers to.
|
|
792
|
+
function _namesToCopy(src, ErrorClass, code, label, atArgument) {
|
|
793
|
+
var E = function (c, m) { return _raise(ErrorClass, c, m); };
|
|
794
|
+
var names = identifier.readableNames(src, E, code, label);
|
|
795
|
+
// Refused here as well as at the options door, because this copy runs FIRST for the verbs that
|
|
796
|
+
// take a spec. Reading an accessor and writing what it returned as plain data leaves the later
|
|
797
|
+
// check nothing to find: it sees a value and cannot tell an accessor was ever there, so a field
|
|
798
|
+
// that answers differently on the next read passes as a settled one.
|
|
799
|
+
//
|
|
800
|
+
// The argument itself, not everything under it. A platform object nested in a spec carries its
|
|
801
|
+
// own accessors, and a Node KeyObject inside a signer holds `asymmetricKeyType` among others;
|
|
802
|
+
// those belong to the platform and refusing them turns an ordinary signer into a bad input.
|
|
803
|
+
if (atArgument) identifier.refuseAccessorFields(src, names, E, code, label);
|
|
804
|
+
return names;
|
|
343
805
|
}
|
|
344
806
|
|
|
345
807
|
// A Map or a Set is data the caller can still change, entry by entry, so it is copied like any
|
|
346
808
|
// other: entries first, then the named properties one can carry alongside them.
|
|
347
809
|
function _copyEntries(src, dst, ErrorClass, code, label, cap, depth, collect) {
|
|
348
|
-
|
|
810
|
+
// The walk uses the intrinsic `forEach`, called against the collection. `forEach` is an ordinary
|
|
811
|
+
// method on `Map.prototype` and `Set.prototype`, so an own one or a subclass's answers instead:
|
|
812
|
+
// one that threw took this module out through the caller's own exception, and one that yielded
|
|
813
|
+
// different entries filled the copy with something other than what the collection holds.
|
|
814
|
+
var walk = util.types.isSet(src) ? Set.prototype.forEach : Map.prototype.forEach;
|
|
815
|
+
walk.call(src, function (value, key) {
|
|
349
816
|
var copiedValue = _deep(value, ErrorClass, code, label, cap, depth + 1, collect);
|
|
350
|
-
if (dst
|
|
817
|
+
if (util.types.isSet(dst)) dst.add(copiedValue);
|
|
351
818
|
else dst.set(_deep(key, ErrorClass, code, label, cap, depth + 1, collect), copiedValue);
|
|
352
819
|
});
|
|
353
820
|
_copyNamed(src, dst, ErrorClass, code, label, cap, depth, collect);
|
|
@@ -362,8 +829,20 @@ function _copyEntries(src, dst, ErrorClass, code, label, cap, depth, collect) {
|
|
|
362
829
|
// copy is what makes an inherited value stop tracking the caller's prototype. A method is left
|
|
363
830
|
// where it is, since copying a function would only move it, and it is the prototype's behavior,
|
|
364
831
|
// not the caller's data.
|
|
365
|
-
|
|
366
|
-
|
|
832
|
+
// The names `Date.prototype` supplies, read off the language rather than written out, so a method
|
|
833
|
+
// the language gains later is behavior here on the day it lands.
|
|
834
|
+
var _DATE_BEHAVIOR = (function () {
|
|
835
|
+
var t = Object.create(null);
|
|
836
|
+
Reflect.ownKeys(Date.prototype).forEach(function (n) { t[n] = 1; });
|
|
837
|
+
return t;
|
|
838
|
+
})();
|
|
839
|
+
|
|
840
|
+
// `behavior`, when given, names what belongs to the destination's KIND rather than to the caller.
|
|
841
|
+
// An own property under one of those names is a shadow of the kind's own method, and copying it
|
|
842
|
+
// down would make the copy answer with the caller's function where the original answered with the
|
|
843
|
+
// language's. The copy is meant to be that kind holding the caller's data.
|
|
844
|
+
function _copyNamed(src, dst, ErrorClass, code, label, cap, depth, collect, behavior) {
|
|
845
|
+
var keys = _namesToCopy(src, ErrorClass, code, label, depth === 0);
|
|
367
846
|
for (var k = 0; k < keys.length; k++) {
|
|
368
847
|
var value;
|
|
369
848
|
// Reading a caller's property can run a caller's accessor, and one that throws is a bad input
|
|
@@ -371,6 +850,12 @@ function _copyNamed(src, dst, ErrorClass, code, label, cap, depth, collect) {
|
|
|
371
850
|
// and never escapes as itself from inside a verb the caller called for something else.
|
|
372
851
|
try { value = src[keys[k]]; }
|
|
373
852
|
catch (e) { throw _raise(ErrorClass, code, label + ": reading " + JSON.stringify(keys[k]) + " threw", e); }
|
|
853
|
+
// A name the destination's KIND already defines, holding a FUNCTION, is a shadow of that
|
|
854
|
+
// kind's own method: copying it down would make the copy answer with the caller's code where
|
|
855
|
+
// the original answered with the language's. The value decides, not the name -- a plain
|
|
856
|
+
// `date.getTime = "unused"` is a field, and dropping it took the caller's typo out of reach of
|
|
857
|
+
// the unknown-option check, which is the silence this release exists to end.
|
|
858
|
+
if (behavior && behavior[keys[k]] && typeof value === "function") continue;
|
|
374
859
|
// A function is carried across by reference, since there is nothing in it to copy, but it is
|
|
375
860
|
// still carried. Dropping it changed what the caller passed: an unknown spec
|
|
376
861
|
// field whose value happened to be a function stopped reaching the verb's own key check, so a
|
|
@@ -459,5 +944,6 @@ function fixedCall(ErrorClass, code, args, body) {
|
|
|
459
944
|
|
|
460
945
|
module.exports = {
|
|
461
946
|
view: view, source: source, snapshot: snapshot, snapshotSource: snapshotSource,
|
|
947
|
+
isByteSource: isByteSource, lengthOf: lengthOf, outputView: outputView,
|
|
462
948
|
snapshotDeep: snapshotDeep, fixArguments: fixArguments, fixedCall: fixedCall,
|
|
463
949
|
};
|