From 71e0eeac40231c943490c328bd71be3fafdadc9b Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Wed, 29 Jul 2026 09:28:50 +0800 Subject: [PATCH] fix(code-runtime): contain hostile boundary failures --- .../code-runtime-worker/src/runtime-host.ts | 6 +++++- .../code-runtime-worker/tests/runtime.spec.ts | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/code-runtime/code-runtime-worker/src/runtime-host.ts b/packages/code-runtime/code-runtime-worker/src/runtime-host.ts index 334bd82e38..474a058e9f 100644 --- a/packages/code-runtime/code-runtime-worker/src/runtime-host.ts +++ b/packages/code-runtime/code-runtime-worker/src/runtime-host.ts @@ -49,7 +49,11 @@ export type RuntimeBindingReply = * @returns the caller-facing diagnostic text. */ export function runtimeErrorMessage(error: unknown): string { - return error instanceof Error ? error.message : String(error) + try { + return error instanceof Error ? error.message : String(error) + } catch { + return 'binding rejected with an unrenderable value' + } } /** diff --git a/packages/code-runtime/code-runtime-worker/tests/runtime.spec.ts b/packages/code-runtime/code-runtime-worker/tests/runtime.spec.ts index 54f58eb414..db2c167354 100644 --- a/packages/code-runtime/code-runtime-worker/tests/runtime.spec.ts +++ b/packages/code-runtime/code-runtime-worker/tests/runtime.spec.ts @@ -597,6 +597,27 @@ describe('WorkerCodeRuntime — hostile programs (real workers)', () => { expect(result.value).toEqual({ name: 'ToolCallError', toolName: 'bad', message: 'binding resolution must be lossless JSON' }) }) + it('contains binding rejections whose thrown values cannot be rendered', async () => { + const { runtime } = await setup() + const result = await runtime.run({ + program: 'try { await tools.bad({}) } catch (error) { return { name: error.name, toolName: error.toolName, message: error.message } }', + bindings: tools({ + bad: async () => { + const hostile = new Error('hidden') + Object.defineProperty(hostile, 'message', { + get() { throw new Error('message getter failed') }, + }) + throw hostile + }, + }), + }) + expect(result.value).toEqual({ + name: 'ToolCallError', + toolName: 'bad', + message: 'binding rejected with an unrenderable value', + }) + }) + it('rejects lossy binding arguments in the worker before invoking the host binding', async () => { const { runtime } = await setup() let calls = 0