Просмотр исходного кода

fix(session): identify intrinsic JSON prototypes

Tianyi Cui 1 месяц назад
Родитель
Сommit
87f4e0b28e
2 измененных файлов с 26 добавлено и 5 удалено
  1. 20 5
      packages/core/session/src/json.ts
  2. 6 0
      packages/core/session/tests/json.spec.ts

+ 20 - 5
packages/core/session/src/json.ts

@@ -12,20 +12,35 @@
  */
 export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue }
 
+/** Whether a realm-owned intrinsic prototype names and points back to its constructor. */
+function hasIntrinsicConstructor(prototype: object, name: 'Array' | 'Object'): boolean {
+  const descriptor = Object.getOwnPropertyDescriptor(prototype, 'constructor')
+  const constructor: unknown = descriptor?.value
+  return typeof constructor === 'function'
+    && constructor.name === name
+    && constructor.prototype === prototype
+}
+
+/** Whether a candidate is one realm's intrinsic `Object.prototype`. */
+function isIntrinsicObjectPrototype(value: object): boolean {
+  return Object.getPrototypeOf(value) === null && hasIntrinsicConstructor(value, 'Object')
+}
+
 /** Whether an array uses one realm's intrinsic `Array.prototype`, not a subclass or forged prototype. */
 function hasPlainArrayPrototype(value: unknown[]): boolean {
   const prototype: unknown = Object.getPrototypeOf(value)
-  if (!Array.isArray(prototype)) return false
+  if (!Array.isArray(prototype) || !hasIntrinsicConstructor(prototype, 'Array')) return false
   const objectPrototype: unknown = Object.getPrototypeOf(prototype)
-  return objectPrototype !== null
-    && !Array.isArray(objectPrototype)
-    && Object.getPrototypeOf(objectPrototype) === null
+  return typeof objectPrototype === 'object'
+    && objectPrototype !== null
+    && isIntrinsicObjectPrototype(objectPrototype)
 }
 
 /** Whether an object is a plain or null-prototype record from any JavaScript realm. */
 function hasPlainObjectPrototype(value: object): boolean {
   const prototype: unknown = Object.getPrototypeOf(value)
-  return prototype === null || Object.getPrototypeOf(prototype) === null
+  return prototype === null
+    || typeof prototype === 'object' && isIntrinsicObjectPrototype(prototype)
 }
 
 /** Return every JSON-visible object key, or reject own data JSON would discard. */

+ 6 - 0
packages/core/session/tests/json.spec.ts

@@ -94,6 +94,8 @@ describe('snapshotJsonValue', () => {
     Object.defineProperty(symbolDecorated, Symbol('extra'), { value: true })
     const hiddenObject = Object.defineProperty({}, 'hidden', { value: true })
     const symbolObject = { [Symbol('extra')]: true }
+    const customPrototype = Object.create(null) as Record<string, unknown>
+    const customPrototypeObject = Object.assign(Object.create(customPrototype) as Record<string, unknown>, { value: 1 })
     const forgedPrototype: unknown[] = []
     Object.setPrototypeOf(forgedPrototype, null)
     const forgedArray = [1]
@@ -117,6 +119,7 @@ describe('snapshotJsonValue', () => {
     expect(snapshotJsonValue(symbolDecorated)).toBeUndefined()
     expect(snapshotJsonValue(hiddenObject)).toBeUndefined()
     expect(snapshotJsonValue(symbolObject)).toBeUndefined()
+    expect(snapshotJsonValue(customPrototypeObject)).toBeUndefined()
     expect(snapshotJsonValue(forgedArray)).toBeUndefined()
     expect(snapshotJsonValue(cyclic)).toBeUndefined()
     expect(snapshotJsonValue([undefined])).toBeUndefined()
@@ -188,6 +191,8 @@ describe('isJsonValue', () => {
     Object.defineProperty(symbolDecorated, Symbol('extra'), { value: true })
     const hiddenObject = Object.defineProperty({}, 'hidden', { value: true })
     const symbolObject = { [Symbol('extra')]: true }
+    const customPrototype = Object.create(null) as Record<string, unknown>
+    const customPrototypeObject = Object.assign(Object.create(customPrototype) as Record<string, unknown>, { value: 1 })
     const forgedPrototype: unknown[] = []
     Object.setPrototypeOf(forgedPrototype, null)
     const forgedArray = [1]
@@ -201,6 +206,7 @@ describe('isJsonValue', () => {
     expect(isJsonValue(symbolDecorated)).toBe(false)
     expect(isJsonValue(hiddenObject)).toBe(false)
     expect(isJsonValue(symbolObject)).toBe(false)
+    expect(isJsonValue(customPrototypeObject)).toBe(false)
     expect(isJsonValue(forgedArray)).toBe(false)
     expect(isJsonValue(new ExoticArray(1))).toBe(false)
     expect(isJsonValue([undefined])).toBe(false)