fix(hook-protocol): discard a hookSpecificOutput block whose hookEventName mismatches the firing event
The reference schemas key the `hookSpecificOutput` block by `hookEventName`, so a block naming a DIFFERENT event than the one firing is malformed — a hook emitting `hookSpecificOutput.hookEventName: "PreToolUse"` on a `Stop` event must not deny the Stop. The codec surfaced `hookEventName` for a bridge to compare but never enforced the discard, so both bridges pushed every parsed output into the merge unconditionally. parseHookOutput now takes an optional `expectedEventName`; when the block's `hookEventName` names a different event, its event-scoped fields (permissionDecision/permissionDecisionReason/additionalContext/updatedInput) are discarded (the discriminator is still surfaced for the log, and the event-agnostic top-level decision/continue/etc. are unaffected). runHook threads it via RunHookOptions.expectedEventName; a caller that omits it opts out. Codex review finding on the bridges PR (PR-F); fixed here on the codec that owns the fold and knows field provenance, then flows down to both bridges.
This commit is contained in:
@@ -131,4 +131,17 @@ describe('runHook — outcome decoding + duration', () => {
|
||||
const { output } = await runHook(bash, { command: 'h' }, { payload: {}, defaultTimeoutMs: 1000, trailingNewline: true }, clock())
|
||||
expect(output.stderr).toBe('plain string fault')
|
||||
})
|
||||
|
||||
it('threads expectedEventName so a mismatched hookSpecificOutput block is discarded', async () => {
|
||||
const { bash } = recordingBash(async () => result({
|
||||
exitCode: 0,
|
||||
stdout: { text: JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny' } }), truncated: false },
|
||||
}))
|
||||
const { output } = await runHook(bash, { command: 'h' }, {
|
||||
payload: {}, defaultTimeoutMs: 1000, trailingNewline: true, expectedEventName: 'Stop',
|
||||
}, clock())
|
||||
// A PreToolUse block on a Stop hook is malformed → its decision is discarded.
|
||||
expect(output.hookEventName).toBe('PreToolUse')
|
||||
expect(output.decision).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user