fix(net): stabilize shared wire graph audits
This commit is contained in:
@@ -64,8 +64,11 @@ function propertyPath(parent: string, key: string | symbol): string {
|
||||
}
|
||||
|
||||
function assertNoInheritedToJson(prototype: object | null, path: string): void {
|
||||
const visited = new Set<object>();
|
||||
let current = prototype;
|
||||
while (current !== null) {
|
||||
if (visited.has(current)) invalidEnvelope(path, 'prototype cycle is not allowed');
|
||||
visited.add(current);
|
||||
if (Object.getOwnPropertyDescriptor(current, 'toJSON')) {
|
||||
invalidEnvelope(path, 'inherited toJSON is not allowed');
|
||||
}
|
||||
@@ -165,6 +168,7 @@ function snapshotJsonValue(
|
||||
path: string,
|
||||
ancestors: Set<object>,
|
||||
audits: CapturedNode[],
|
||||
snapshots: Map<object, unknown>,
|
||||
): unknown {
|
||||
if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;
|
||||
if (typeof value === 'number') {
|
||||
@@ -178,6 +182,7 @@ function snapshotJsonValue(
|
||||
|
||||
const objectValue = value as object;
|
||||
if (ancestors.has(objectValue)) invalidEnvelope(path, 'cycle is not protocol JSON data');
|
||||
if (snapshots.has(objectValue)) return snapshots.get(objectValue);
|
||||
ancestors.add(objectValue);
|
||||
try {
|
||||
const captured = captureNode(objectValue, path, audits);
|
||||
@@ -192,6 +197,7 @@ function snapshotJsonValue(
|
||||
const allowedKeys = new Set<string>(['length']);
|
||||
const snapshot: unknown[] = [];
|
||||
Object.setPrototypeOf(snapshot, null);
|
||||
snapshots.set(objectValue, snapshot);
|
||||
for (let index = 0; index < (length as number); index++) {
|
||||
const key = String(index);
|
||||
const itemPath = `${path}[${index}]`;
|
||||
@@ -204,6 +210,7 @@ function snapshotJsonValue(
|
||||
itemPath,
|
||||
ancestors,
|
||||
audits,
|
||||
snapshots,
|
||||
);
|
||||
Object.defineProperty(snapshot, key, {
|
||||
configurable: true,
|
||||
@@ -225,6 +232,7 @@ function snapshotJsonValue(
|
||||
invalidEnvelope(path, 'expected plain object or null-prototype object');
|
||||
}
|
||||
const snapshot = Object.create(null) as Record<string, unknown>;
|
||||
snapshots.set(objectValue, snapshot);
|
||||
for (const { key } of captured.properties) {
|
||||
if (typeof key === 'symbol') invalidEnvelope(path, 'symbol key is not allowed');
|
||||
const nestedPath = childPath(path, key);
|
||||
@@ -233,6 +241,7 @@ function snapshotJsonValue(
|
||||
nestedPath,
|
||||
ancestors,
|
||||
audits,
|
||||
snapshots,
|
||||
);
|
||||
Object.defineProperty(snapshot, key, {
|
||||
configurable: true,
|
||||
@@ -252,6 +261,7 @@ function serializeOutbound(envelope: OutboundEnvelope): string {
|
||||
return invalidEnvelope('$', 'expected object');
|
||||
}
|
||||
const audits: CapturedNode[] = [];
|
||||
const snapshots = new Map<object, unknown>();
|
||||
const captured = captureNode(envelope, '$', audits);
|
||||
if (captured.prototype !== Object.prototype && captured.prototype !== null) {
|
||||
return invalidEnvelope('$', 'expected plain object or null-prototype object');
|
||||
@@ -272,13 +282,21 @@ function serializeOutbound(envelope: OutboundEnvelope): string {
|
||||
if (typeof rpc !== 'string' || rpc.length === 0) {
|
||||
invalidEnvelope('$.rpc', 'expected non-empty string');
|
||||
}
|
||||
const snapshot = Object.create(null) as Record<string, unknown>;
|
||||
snapshots.set(envelope, snapshot);
|
||||
const ancestors = new Set<object>([envelope]);
|
||||
let dataSnapshot: unknown;
|
||||
try {
|
||||
dataSnapshot = snapshotJsonValue(data, '$.data', ancestors, audits, snapshots);
|
||||
} finally {
|
||||
ancestors.delete(envelope);
|
||||
}
|
||||
const snapshotValues = new Map<string, unknown>([
|
||||
['app', app],
|
||||
['route', route],
|
||||
['rpc', rpc],
|
||||
['data', snapshotJsonValue(data, '$.data', new Set<object>(), audits)],
|
||||
['data', dataSnapshot],
|
||||
]);
|
||||
const snapshot = Object.create(null) as Record<string, unknown>;
|
||||
for (const { key } of captured.properties) {
|
||||
Object.defineProperty(snapshot, key as string, {
|
||||
configurable: true,
|
||||
|
||||
Reference in New Issue
Block a user