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:
6 files changed
+107
-13
No files matched your search
@@ -31,6 +31,13 @@ export interface RunHookOptions {
|
||||
defaultTimeoutMs: number
|
||||
/** Whether to append a trailing newline to the stdin payload (CC yes, Codex no). */
|
||||
trailingNewline: boolean
|
||||
/**
|
||||
* The event this hook is firing for (e.g. `'PreToolUse'`). When set, a
|
||||
* structured `hookSpecificOutput` block whose `hookEventName` names a DIFFERENT
|
||||
* event is treated as malformed and its event-scoped fields are discarded (see
|
||||
* {@link parseHookOutput}). Omit it to apply any block as-is.
|
||||
*/
|
||||
expectedEventName?: string
|
||||
}
|
||||
|
||||
/** The {@link HookOutput} plus the wall-clock duration of the run (for `hook/result`). */
|
||||
@@ -75,7 +82,7 @@ export async function runHook(
|
||||
// `undefined` (a non-blocking error — no clean exit code to act on).
|
||||
const exitCode = result.exitCode ?? undefined
|
||||
return {
|
||||
output: parseHookOutput(exitCode, result.stdout.text, result.stderr.text),
|
||||
output: parseHookOutput(exitCode, result.stdout.text, result.stderr.text, options.expectedEventName),
|
||||
durationMs: now() - started,
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
|
||||
Reference in New Issue
Block a user