fix: strict type check for cross realms (#21669)

Deno v1.39 introduces `vm.runInNewContext`. This may cause problems when
using `Object.prototype.isPrototypeOf` to check built-in types.

```js
import vm from "node:vm";

const err = new Error();
const crossErr = vm.runInNewContext(`new Error()`);

console.assert( !(crossErr instanceof Error) );
console.assert( Object.getPrototypeOf(err) !== Object.getPrototypeOf(crossErr) );
```

This PR changes to check using internal slots solves them.

---

current: 

```
> import vm from "node:vm";
undefined
> vm.runInNewContext(`new Error("message")`)
Error {}
> vm.runInNewContext(`new Date("2018-12-10T02:26:59.002Z")`)
Date {}
```

this PR:

```
> import vm from "node:vm";
undefined
> vm.runInNewContext(`new Error("message")`)
Error: message
    at <anonymous>:1:1
> vm.runInNewContext(`new Date("2018-12-10T02:26:59.002Z")`)
2018-12-10T02:26:59.002Z
```

---------

Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
Kenta Moriuchi 2024-01-04 13:12:38 +09:00 committed by GitHub
parent 4855674857
commit b2cd254c35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 532 additions and 906 deletions

View file

@ -17,23 +17,26 @@ const {
DataViewPrototypeGetBuffer,
DataViewPrototypeGetByteLength,
DataViewPrototypeGetByteOffset,
ObjectPrototypeIsPrototypeOf,
PromiseReject,
PromiseResolve,
// TODO(lucacasonato): add SharedArrayBuffer to primordials
// SharedArrayBufferPrototype
// SharedArrayBufferPrototype,
StringPrototypeCharCodeAt,
StringPrototypeSlice,
SymbolFor,
TypedArrayPrototypeSubarray,
TypedArrayPrototypeGetBuffer,
TypedArrayPrototypeGetByteLength,
TypedArrayPrototypeGetByteOffset,
TypedArrayPrototypeGetSymbolToStringTag,
Uint8Array,
ObjectPrototypeIsPrototypeOf,
ArrayBufferIsView,
TypedArrayPrototypeSubarray,
Uint32Array,
Uint8Array,
} = primordials;
const {
isDataView,
isSharedArrayBuffer,
isTypedArray,
} = core;
class TextDecoder {
/** @type {string} */
@ -111,51 +114,37 @@ class TextDecoder {
try {
/** @type {ArrayBufferLike} */
let buffer = input;
if (ArrayBufferIsView(input)) {
if (TypedArrayPrototypeGetSymbolToStringTag(input) !== undefined) {
// TypedArray
buffer = TypedArrayPrototypeGetBuffer(
/** @type {Uint8Array} */ (input),
);
} else {
// DataView
buffer = DataViewPrototypeGetBuffer(/** @type {DataView} */ (input));
}
if (isTypedArray(input)) {
buffer = TypedArrayPrototypeGetBuffer(
/** @type {Uint8Array} */ (input),
);
} else if (isDataView(input)) {
buffer = DataViewPrototypeGetBuffer(/** @type {DataView} */ (input));
}
// Note from spec: implementations are strongly encouraged to use an implementation strategy that avoids this copy.
// When doing so they will have to make sure that changes to input do not affect future calls to decode().
if (
ObjectPrototypeIsPrototypeOf(
// deno-lint-ignore prefer-primordials
SharedArrayBuffer.prototype,
buffer,
)
) {
if (isSharedArrayBuffer(buffer)) {
// We clone the data into a non-shared ArrayBuffer so we can pass it
// to Rust.
// `input` is now a Uint8Array, and calling the TypedArray constructor
// with a TypedArray argument copies the data.
if (ArrayBufferIsView(input)) {
if (TypedArrayPrototypeGetSymbolToStringTag(input) !== undefined) {
// TypedArray
input = new Uint8Array(
buffer,
TypedArrayPrototypeGetByteOffset(
/** @type {Uint8Array} */ (input),
),
TypedArrayPrototypeGetByteLength(
/** @type {Uint8Array} */ (input),
),
);
} else {
// DataView
input = new Uint8Array(
buffer,
DataViewPrototypeGetByteOffset(/** @type {DataView} */ (input)),
DataViewPrototypeGetByteLength(/** @type {DataView} */ (input)),
);
}
if (isTypedArray(input)) {
input = new Uint8Array(
buffer,
TypedArrayPrototypeGetByteOffset(
/** @type {Uint8Array} */ (input),
),
TypedArrayPrototypeGetByteLength(
/** @type {Uint8Array} */ (input),
),
);
} else if (isDataView(input)) {
input = new Uint8Array(
buffer,
DataViewPrototypeGetByteOffset(/** @type {DataView} */ (input)),
DataViewPrototypeGetByteLength(/** @type {DataView} */ (input)),
);
} else {
input = new Uint8Array(buffer);
}