fix(code-runtime): capture worker JSON intrinsics
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user