fix(hooks): reject invalid matcher regexes
This commit is contained in:
@@ -38,12 +38,13 @@ function writeHooks(dir: string, hooks: unknown): void {
|
||||
writeFileSync(join(dir, 'hooks.json'), JSON.stringify({ hooks }))
|
||||
}
|
||||
|
||||
async function harness(dir: string, adapter: MockAdapter): Promise<Context> {
|
||||
async function harness(dir: string, adapter: MockAdapter, beforeHooks?: (ctx: Context) => void): Promise<Context> {
|
||||
const ctx = new Context()
|
||||
await mountAgentLoopTestDependencies(ctx)
|
||||
await ctx.plugin(AgentLoop, { agents: [] })
|
||||
await ctx.plugin(LocalSubprocessService)
|
||||
await ctx.plugin(LocalBashExecutor, { timeoutMs: 10_000 })
|
||||
beforeHooks?.(ctx)
|
||||
await ctx.plugin(HooksCodex, { configPath: join(dir, 'hooks.json'), model: 'test-model' })
|
||||
ctx.llm.registerAdapter(['mock'], adapter)
|
||||
return ctx
|
||||
@@ -150,6 +151,26 @@ describe('hooks-codex bridge', () => {
|
||||
expect(adapter.requests).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('an invalid regex matcher is reported and registers no hooks', async () => {
|
||||
const dir = configDir()
|
||||
writeHooks(dir, {
|
||||
UserPromptSubmit: [{ hooks: [{ type: 'command', command: 'exit 2' }] }],
|
||||
Stop: [{ matcher: '[', hooks: [{ type: 'command', command: 'exit 2' }] }],
|
||||
})
|
||||
const adapter = new MockAdapter([textResponse('ok')])
|
||||
const warn = vi.fn()
|
||||
const ctx = await harness(dir, adapter, (ctx) => { ctx.logger.warn = warn as never })
|
||||
const agent = ctx.agentLoop.create(SessionId('invalid-codex-matcher'), { provider: 'mock', model: 'mock' })
|
||||
agent.followup({ content: [{ type: 'text', text: 'go' }], source: { kind: 'user' } })
|
||||
await waitForIdle(ctx, agent)
|
||||
expect(adapter.requests).toHaveLength(1)
|
||||
expect(events(agent).some(event => event.type === 'hook/invoked')).toBe(false)
|
||||
|
||||
expect(warn).toHaveBeenCalledWith(expect.stringContaining(
|
||||
'invalid codex regex matcher "[" on event "Stop"',
|
||||
))
|
||||
})
|
||||
|
||||
it('disposing the bridge fiber removes its listeners (HMR safety)', async () => {
|
||||
const dir = configDir()
|
||||
// A leaked listener would let this blocking hook veto the prompt and log an invocation; a
|
||||
|
||||
@@ -65,4 +65,10 @@ describe('parseCodexConfig', () => {
|
||||
const { config } = parseCodexConfig({ PreToolUse: [{ matcher: '^Bash$', hooks: [{ type: 'command', command: 'b.sh' }] }] })
|
||||
expect(config.PreToolUse![0]!.matcher).toBe('^Bash$')
|
||||
})
|
||||
|
||||
it('rejects an invalid regex matcher with its event name', () => {
|
||||
expect(() => parseCodexConfig({
|
||||
Stop: [{ matcher: '[', hooks: [{ type: 'command', command: 's.sh' }] }],
|
||||
})).toThrow('invalid codex regex matcher "[" on event "Stop"')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user