Summary
On Codex CLI 0.144.1 for Windows, a PreToolUse hook can correctly deny a shell command or patch with a sanitized permissionDecisionReason, but the runtime then appends the original raw command or patch to the visible denial message.
This defeats hook-side redaction. It can expose a sensitive path, dummy marker, command argument, or patch body even when the hook deliberately returns only a reason code and a 12-character SHA-256 fingerprint.
A related routing problem for function-call apply_patch is tracked in #26751. This report focuses on the end-to-end denial display and raw-input disclosure.
Environment
- Codex CLI:
0.144.1
- Platform: Windows 11, PowerShell
- Hook event:
PreToolUse
- Affected tool paths observed: shell and
functions.apply_patch
Hook response
The hook exits successfully and returns only sanitized JSON:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "reason=secret-read.shell; command_hash=0123456789ab"
}
}
The response contains no raw command, path, or patch text.
Observed behavior
Codex blocks before execution, but the visible result has this shape:
Command blocked by PreToolUse hook: reason=...; command_hash=.... Command: <original raw command or patch>
For functions.apply_patch, the hook can also receive an input shape it does not recognize and return reason=missing-path; Codex then appends the full raw patch. That routing aspect appears related to #26751.
Expected behavior
When a PreToolUse hook denies a tool call:
- The tool must remain blocked before execution.
- The visible result should contain the sanitized denial reason supplied by the hook.
- The original tool arguments must not be appended automatically, or there must be a documented hook/config option that suppresses them end to end.
- The behavior should be consistent for shell, custom/freeform
apply_patch, and function-call functions.apply_patch.
Reproduction
- Configure a
PreToolUse hook for shell or edit tools.
- Return
permissionDecision=deny with a reason code and a fingerprint only.
- Attempt a harmless read of a nonexistent dummy secret path, or a harmless patch targeting a nonexistent dummy protected path.
- Observe that execution is blocked.
- Observe that Codex appends the original raw command or patch after the sanitized reason.
No real secret or credential is needed to reproduce this.
Verification performed
- The hook output was tested directly and contains only the sanitized reason and fingerprint.
- The live tool call was blocked before execution.
- The nonexistent dummy file was not created or read.
- Repeated local hook tests pass.
- The disclosure appears only after the hook response reaches the Codex runtime/UI layer.
Security impact
Hook authors cannot guarantee that a deny response redacts sensitive tool input from the user-visible transcript. A security hook can prevent execution while still allowing the runtime to disclose the exact input that triggered the block.
Summary
On Codex CLI 0.144.1 for Windows, a
PreToolUsehook can correctly deny a shell command or patch with a sanitizedpermissionDecisionReason, but the runtime then appends the original raw command or patch to the visible denial message.This defeats hook-side redaction. It can expose a sensitive path, dummy marker, command argument, or patch body even when the hook deliberately returns only a reason code and a 12-character SHA-256 fingerprint.
A related routing problem for function-call
apply_patchis tracked in #26751. This report focuses on the end-to-end denial display and raw-input disclosure.Environment
0.144.1PreToolUsefunctions.apply_patchHook response
The hook exits successfully and returns only sanitized JSON:
{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "reason=secret-read.shell; command_hash=0123456789ab" } }The response contains no raw command, path, or patch text.
Observed behavior
Codex blocks before execution, but the visible result has this shape:
For
functions.apply_patch, the hook can also receive an input shape it does not recognize and returnreason=missing-path; Codex then appends the full raw patch. That routing aspect appears related to #26751.Expected behavior
When a
PreToolUsehook denies a tool call:apply_patch, and function-callfunctions.apply_patch.Reproduction
PreToolUsehook for shell or edit tools.permissionDecision=denywith a reason code and a fingerprint only.No real secret or credential is needed to reproduce this.
Verification performed
Security impact
Hook authors cannot guarantee that a deny response redacts sensitive tool input from the user-visible transcript. A security hook can prevent execution while still allowing the runtime to disclose the exact input that triggered the block.