fix(code-runtime): capture worker JSON intrinsics

This commit is contained in:
Tianyi Cui
2026-07-23 03:04:43 +08:00
parent a6b769c427
commit 4afcb5c3c9
4 changed files with 265 additions and 75 deletions
@@ -68,4 +68,42 @@ describe('jsonValueBytesUpTo', () => {
expect(jsonValueBytesUpTo(value, 10_004)).toBe(10_004)
expect(jsonValueBytesUpTo(value, 10_003)).toBeUndefined()
})
it('uses module-captured intrinsics after model-visible globals are mutated', () => {
const value: CodeJsonValue = { payload: ['€', 42] }
const bytes = Buffer.byteLength(JSON.stringify(value), 'utf8')
const arrayIsArrayDescriptor = Object.getOwnPropertyDescriptor(Array, 'isArray')!
const arrayPopDescriptor = Object.getOwnPropertyDescriptor(Array.prototype, 'pop')!
const arrayPushDescriptor = Object.getOwnPropertyDescriptor(Array.prototype, 'push')!
const byteLengthDescriptor = Object.getOwnPropertyDescriptor(Buffer, 'byteLength')!
const objectKeysDescriptor = Object.getOwnPropertyDescriptor(Object, 'keys')!
const charCodeAtDescriptor = Object.getOwnPropertyDescriptor(String.prototype, 'charCodeAt')!
const codePointAtDescriptor = Object.getOwnPropertyDescriptor(String.prototype, 'codePointAt')!
const sliceDescriptor = Object.getOwnPropertyDescriptor(String.prototype, 'slice')!
let measured: number | undefined
let prefix = ''
try {
Array.isArray = () => false
Array.prototype.pop = () => { throw new Error('mutated pop') }
Array.prototype.push = () => { throw new Error('mutated push') }
Buffer.byteLength = () => 0
Object.keys = () => []
String.prototype.charCodeAt = () => { throw new Error('mutated charCodeAt') }
String.prototype.codePointAt = () => { throw new Error('mutated codePointAt') }
String.prototype.slice = () => { throw new Error('mutated slice') }
measured = jsonValueBytesUpTo(value, bytes)
prefix = truncateJsonStringBytes('€x', 5)
} finally {
Object.defineProperty(Array, 'isArray', arrayIsArrayDescriptor)
Object.defineProperty(Array.prototype, 'pop', arrayPopDescriptor)
Object.defineProperty(Array.prototype, 'push', arrayPushDescriptor)
Object.defineProperty(Buffer, 'byteLength', byteLengthDescriptor)
Object.defineProperty(Object, 'keys', objectKeysDescriptor)
Object.defineProperty(String.prototype, 'charCodeAt', charCodeAtDescriptor)
Object.defineProperty(String.prototype, 'codePointAt', codePointAtDescriptor)
Object.defineProperty(String.prototype, 'slice', sliceDescriptor)
}
expect(measured).toBe(bytes)
expect(prefix).toBe('€')
})
})
@@ -657,6 +657,41 @@ describe('WorkerCodeRuntime — hostile programs (real workers)', () => {
})
})
it('preserves binding and completion JSON after model code mutates boundary globals', async () => {
const { runtime } = await setup()
const result = await runtime.run({
program: `
const arrayPrototype = Array.prototype;
const objectPrototype = Object.prototype;
const setPrototype = Set.prototype;
const stringPrototype = String.prototype;
Array.isArray = () => false;
arrayPrototype.at = arrayPrototype.includes = arrayPrototype.pop = arrayPrototype.push = () => { throw new Error('mutated array method') };
Object.defineProperty = Object.getOwnPropertyDescriptor = Object.getPrototypeOf = Object.keys = () => { throw new Error('mutated object method') };
Object.hasOwn = () => false;
Object.is = () => true;
objectPrototype.propertyIsEnumerable = () => false;
Number.isFinite = Number.isSafeInteger = () => false;
Reflect.apply = Reflect.ownKeys = () => { throw new Error('mutated reflect method') };
setPrototype.add = setPrototype.delete = setPrototype.has = () => { throw new Error('mutated set method') };
stringPrototype.charCodeAt = stringPrototype.codePointAt = stringPrototype.slice = () => { throw new Error('mutated string method') };
Buffer.byteLength = () => 0;
Function.prototype.toString = () => 'mutated';
globalThis.Array = globalThis.Buffer = globalThis.Function = globalThis.Number = globalThis.Object = globalThis.Reflect = globalThis.Set = globalThis.String = undefined;
const echoed = await tools.echo({ request: ['€', 1] });
return { echoed, completion: { ok: true, amount: 42 } };
`,
bindings: tools({ echo: async args => args }),
})
expect(result).toEqual({
logs: [],
value: {
echoed: { request: ['€', 1] },
completion: { ok: true, amount: 42 },
},
})
})
it('rejects forged lossy binding arguments again at the host boundary', async () => {
const { runtime } = await setup()
let calls = 0