mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(code-runtime): validate arguments before worker dispatch
This commit is contained in:
@@ -190,7 +190,7 @@ describe('makeNamespaces', () => {
|
||||
await expect(tools['toString']?.({})).resolves.toBe('toString-ok')
|
||||
})
|
||||
|
||||
it('rejects a non-cloneable argument without leaking the pending entry', async () => {
|
||||
it('rejects a postMessage clone failure without leaking the pending entry', async () => {
|
||||
let firstCall = true
|
||||
const throwingPort: BootstrapPort = {
|
||||
// First call throws an Error (the real DataCloneError shape), the
|
||||
@@ -203,8 +203,8 @@ describe('makeNamespaces', () => {
|
||||
}
|
||||
const pending = new Map<number, PendingCall>()
|
||||
const [tools] = makeNamespaces({ namespaces: [{ global: 'tools', names: ['x'] }] }, throwingPort, pending, { value: 1 }) as [Record<string, (args: unknown) => Promise<unknown>>]
|
||||
const first = await rejectionOf(tools.x?.(() => 1) ?? Promise.resolve())
|
||||
const second = await rejectionOf(tools.x?.(() => 1) ?? Promise.resolve())
|
||||
const first = await rejectionOf(tools.x?.({ first: true }) ?? Promise.resolve())
|
||||
const second = await rejectionOf(tools.x?.({ second: true }) ?? Promise.resolve())
|
||||
expect(first).toMatchObject({ name: 'ToolCallError', toolName: 'x' })
|
||||
expect(second).toMatchObject({ name: 'ToolCallError', toolName: 'x' })
|
||||
expect(first).toBeInstanceOf(ToolCallError)
|
||||
@@ -214,6 +214,32 @@ describe('makeNamespaces', () => {
|
||||
expect(pending.size).toBe(0)
|
||||
})
|
||||
|
||||
it('rejects lossy arguments before posting or allocating a call id', async () => {
|
||||
let posts = 0
|
||||
const port: BootstrapPort = { postMessage: () => { posts += 1 }, on: () => {} }
|
||||
const pending = new Map<number, PendingCall>()
|
||||
const nextId = { value: 1 }
|
||||
const [tools] = makeNamespaces(
|
||||
{ namespaces: [{ global: 'tools', names: ['x'] }] }, port, pending, nextId,
|
||||
) as [Record<string, (args: unknown) => Promise<unknown>>]
|
||||
const decorated = [1]
|
||||
Object.defineProperty(decorated, 'extra', { value: true })
|
||||
const throwing = Object.defineProperty({}, 'value', {
|
||||
enumerable: true,
|
||||
get: () => { throw new Error('getter exploded') },
|
||||
})
|
||||
|
||||
for (const value of [() => 1, new Date(), decorated, throwing]) {
|
||||
const failure = await rejectionOf(tools.x?.(value) ?? Promise.resolve())
|
||||
expect(failure).toMatchObject({
|
||||
name: 'ToolCallError', toolName: 'x', message: 'binding arguments must be lossless JSON',
|
||||
})
|
||||
}
|
||||
expect(posts).toBe(0)
|
||||
expect(pending.size).toBe(0)
|
||||
expect(nextId.value).toBe(1)
|
||||
})
|
||||
|
||||
it('uses ordinary Error for non-tools namespace failures', async () => {
|
||||
const deniedPort = new FakePort()
|
||||
deniedPort.respond = message => message.type === 'call'
|
||||
@@ -226,9 +252,14 @@ describe('makeNamespaces', () => {
|
||||
expect(denied).toBeInstanceOf(Error)
|
||||
expect(denied).not.toBeInstanceOf(ToolCallError)
|
||||
|
||||
const invalid = await rejectionOf(helpers.x?.(() => 1) ?? Promise.resolve())
|
||||
expect(invalid).toBeInstanceOf(Error)
|
||||
expect(invalid).not.toBeInstanceOf(ToolCallError)
|
||||
expect((invalid as Error).message).toBe('binding arguments must be lossless JSON')
|
||||
|
||||
const clonePort: BootstrapPort = { postMessage: () => { throw new Error('clone failed') }, on: () => {} }
|
||||
const [cloneHelpers] = makeNamespaces({ namespaces: [{ global: 'helpers', names: ['x'] }] }, clonePort, new Map(), { value: 1 }) as [Record<string, (args: unknown) => Promise<unknown>>]
|
||||
const cloneFailure = await rejectionOf(cloneHelpers.x?.(() => 1) ?? Promise.resolve())
|
||||
const cloneFailure = await rejectionOf(cloneHelpers.x?.({}) ?? Promise.resolve())
|
||||
expect(cloneFailure).toBeInstanceOf(Error)
|
||||
expect(cloneFailure).not.toBeInstanceOf(ToolCallError)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user