Normalize shell command stdout and stderr before the prompt-shell path and shared tool-result mappers use string operations. This prevents /security-review from crashing when a shell tool returns null output fields and adds regression coverage for both direct mapper calls and prompt generation. Fixes #165 Co-authored-by: Claude <noreply@anthropic.com>
78 lines
2.0 KiB
TypeScript
78 lines
2.0 KiB
TypeScript
import { afterEach, expect, test } from 'bun:test'
|
|
import { getEmptyToolPermissionContext } from '../Tool.js'
|
|
import { BashTool } from '../tools/BashTool/BashTool.js'
|
|
import { executeShellCommandsInPrompt } from './promptShellExecution.js'
|
|
|
|
const originalCall = BashTool.call
|
|
const originalMapToolResultToToolResultBlockParam =
|
|
BashTool.mapToolResultToToolResultBlockParam
|
|
|
|
afterEach(() => {
|
|
BashTool.call = originalCall
|
|
BashTool.mapToolResultToToolResultBlockParam =
|
|
originalMapToolResultToToolResultBlockParam
|
|
})
|
|
|
|
test('executeShellCommandsInPrompt normalizes null shell output', async () => {
|
|
let normalizedResult:
|
|
| { stdout: string; stderr: string; interrupted: boolean }
|
|
| undefined
|
|
|
|
BashTool.call = (async () => ({
|
|
data: {
|
|
stdout: null,
|
|
stderr: null,
|
|
interrupted: false,
|
|
},
|
|
})) as unknown as typeof BashTool.call
|
|
|
|
BashTool.mapToolResultToToolResultBlockParam = (result, toolUseID) => {
|
|
normalizedResult = result as {
|
|
stdout: string
|
|
stderr: string
|
|
interrupted: boolean
|
|
}
|
|
return originalMapToolResultToToolResultBlockParam(result, toolUseID)
|
|
}
|
|
|
|
await executeShellCommandsInPrompt(
|
|
'```!\ngit status\n```',
|
|
{
|
|
abortController: new AbortController(),
|
|
options: {
|
|
commands: [],
|
|
debug: false,
|
|
mainLoopModel: 'sonnet',
|
|
tools: new Map(),
|
|
verbose: false,
|
|
thinkingConfig: { type: 'disabled' },
|
|
mcpClients: [],
|
|
mcpResources: {},
|
|
isNonInteractiveSession: false,
|
|
agentDefinitions: {
|
|
systemDefinitions: [],
|
|
projectDefinitions: [],
|
|
userDefinitions: [],
|
|
},
|
|
},
|
|
readFileState: new Map(),
|
|
getAppState() {
|
|
return {
|
|
toolPermissionContext: {
|
|
...getEmptyToolPermissionContext(),
|
|
alwaysAllowRules: { command: ['Bash(*)'] },
|
|
},
|
|
}
|
|
},
|
|
setAppState() {},
|
|
} as never,
|
|
'security-review',
|
|
)
|
|
|
|
expect(normalizedResult).toEqual({
|
|
stdout: '',
|
|
stderr: '',
|
|
interrupted: false,
|
|
})
|
|
})
|