Skip to content

PreToolUse denial appends raw command or patch after sanitized hook reason #32573

Description

@TadFuji

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:

  1. The tool must remain blocked before execution.
  2. The visible result should contain the sanitized denial reason supplied by the hook.
  3. The original tool arguments must not be appended automatically, or there must be a documented hook/config option that suppresses them end to end.
  4. The behavior should be consistent for shell, custom/freeform apply_patch, and function-call functions.apply_patch.

Reproduction

  1. Configure a PreToolUse hook for shell or edit tools.
  2. Return permissionDecision=deny with a reason code and a fingerprint only.
  3. Attempt a harmless read of a nonexistent dummy secret path, or a harmless patch targeting a nonexistent dummy protected path.
  4. Observe that execution is blocked.
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIbugSomething isn't workinghooksIssues related to event hookswindows-osIssues related to Codex on Windows systems

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions