mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(code-runtime): bound hostile output accounting
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { truncateJsonStringBytes } from '../src/output-json.ts'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { jsonStringBytesUpTo, jsonValueBytesUpTo, truncateJsonStringBytes } from '../src/output-json.ts'
|
||||
|
||||
describe('truncateJsonStringBytes', () => {
|
||||
it('returns a fitting string whole and rejects budgets without JSON quotes', () => {
|
||||
expect(truncateJsonStringBytes('fits', 6)).toBe('fits')
|
||||
expect(truncateJsonStringBytes('x', 1)).toBe('')
|
||||
expect(jsonStringBytesUpTo('fits', 6)).toBe(6)
|
||||
expect(jsonStringBytesUpTo('fits', 5)).toBeUndefined()
|
||||
})
|
||||
|
||||
it('accounts every JSON escape and cuts only between complete code points', () => {
|
||||
@@ -15,4 +17,43 @@ describe('truncateJsonStringBytes', () => {
|
||||
expect(truncateJsonStringBytes(text, budget)).toBe(prefix)
|
||||
expect(Buffer.byteLength(JSON.stringify(truncateJsonStringBytes(text, budget)), 'utf8')).toBe(budget)
|
||||
})
|
||||
|
||||
it('bounds hostile strings without materializing their complete escaped form', () => {
|
||||
const stringify = vi.spyOn(JSON, 'stringify').mockImplementation(() => { throw new Error('must not stringify') })
|
||||
try {
|
||||
expect(jsonStringBytesUpTo('"'.repeat(10_000), 32)).toBeUndefined()
|
||||
expect(truncateJsonStringBytes('"'.repeat(10_000), 32)).toBe('"'.repeat(15))
|
||||
} finally {
|
||||
stringify.mockRestore()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('jsonValueBytesUpTo', () => {
|
||||
it('matches JSON serialization for every lossless value branch and stops at the cap', () => {
|
||||
const value = {
|
||||
empty: {},
|
||||
nil: null,
|
||||
yes: true,
|
||||
no: false,
|
||||
number: 1.5,
|
||||
text: '"\n😀',
|
||||
array: [1, 'x'],
|
||||
}
|
||||
const bytes = Buffer.byteLength(JSON.stringify(value), 'utf8')
|
||||
|
||||
expect(jsonValueBytesUpTo(value, bytes)).toBe(bytes)
|
||||
expect(jsonValueBytesUpTo(value, bytes - 1)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo({}, 1)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo(null, 3)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo(10, 1)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo(false, 4)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo(new Array<never>(1), 10)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo([null], 5)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo([0, 0], 3)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo({ a: null, b: null }, 10)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo({ long: null }, 2)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo({ '': null }, 4)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo({ a: null }, 9)).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -401,6 +401,22 @@ describe('WorkerCodeRuntime — hostile programs (real workers)', () => {
|
||||
expect(Buffer.byteLength(JSON.stringify(result.logs), 'utf8')).toBeLessThan(200)
|
||||
})
|
||||
|
||||
it('bounds one oversized forged log while retaining its fitting escaped prefix', async () => {
|
||||
const { runtime } = await setup({ maxOutputBytes: 96 })
|
||||
const result = await runtime.run({
|
||||
program: `
|
||||
const { parentPort } = await import('node:worker_threads');
|
||||
parentPort.postMessage({ type: 'log', text: '"'.repeat(1_000_000) });
|
||||
for (;;) {}
|
||||
`,
|
||||
bindings: [],
|
||||
})
|
||||
expect(result.error).toEqual({ kind: 'output-limit', message: 'outer output exceeded 96 bytes' })
|
||||
expect(result.logs).toHaveLength(1)
|
||||
expect(result.logs[0]).toMatch(/^"+$/)
|
||||
expect(Buffer.byteLength(JSON.stringify(result.logs), 'utf8') + Buffer.byteLength(JSON.stringify('outer output exceeded 96 bytes'), 'utf8')).toBeLessThanOrEqual(96)
|
||||
})
|
||||
|
||||
it('drops a malformed forged done carrying both value and error', async () => {
|
||||
const { runtime } = await setup()
|
||||
const result = await runtime.run({
|
||||
|
||||
Reference in New Issue
Block a user