Skip to content

fix(hooks): eliminate TOCTOU race in stop-feedback.sh plugin root lookup#151

Open
xiaolai wants to merge 1 commit intotanweai:mainfrom
xiaolai:fix/nlpm-tmp-plugin-root-toctou
Open

fix(hooks): eliminate TOCTOU race in stop-feedback.sh plugin root lookup#151
xiaolai wants to merge 1 commit intotanweai:mainfrom
xiaolai:fix/nlpm-tmp-plugin-root-toctou

Conversation

@xiaolai
Copy link
Copy Markdown

@xiaolai xiaolai commented Apr 26, 2026

Automated audit: This PR was generated by NLPM, a natural language programming linter, running via claude-code-action. Please evaluate the diff on its merits.

What's broken

hooks/stop-feedback.sh resolves the plugin root path and writes it to a temporary file:

printf '%s' "$_PLUGIN_ROOT" > /tmp/pua-plugin-root

Then, inside a heredoc, it instructs Claude to execute:

bash "$(cat /tmp/pua-plugin-root)/hooks/sanitize-session.sh" ...

There is a time window between when the hook writes /tmp/pua-plugin-root and when Claude's bash execution reads it back. On a system where /tmp/ is world-writable, a local user can replace the file contents between those two moments, redirecting the bash call to an arbitrary script. This is a classic TOCTOU (Time-of-Check-Time-of-Use) race.

Fix

Remove the temp-file indirection entirely. CLAUDE_PLUGIN_ROOT is set in the hook's execution environment by Claude Code and is also available in Claude's bash context. Use it directly:

bash "${CLAUDE_PLUGIN_ROOT}/hooks/sanitize-session.sh" "$(cat /tmp/pua-session-path)"

The _PLUGIN_ROOT computation block (the if [[ -n "${CLAUDE_PLUGIN_ROOT:-}" ]] block) is removed as dead code since it was only used to produce the now-eliminated temp file.

This is a 9-line removal and a 1-word change — fully mechanical with no behavioral change in the normal code path.

The hook wrote the plugin root path to /tmp/pua-plugin-root, then the
heredoc instructed Claude to execute:
  bash "$(cat /tmp/pua-plugin-root)/hooks/sanitize-session.sh" ...

Between the write and Claude's bash execution, a local user controlling
/tmp/ could replace the file to redirect execution to an arbitrary script.

Fix: remove the temp-file indirection entirely. CLAUDE_PLUGIN_ROOT is
already set in the hook environment (and in Claude's bash context) by
Claude Code — use it directly. The _PLUGIN_ROOT computation block is
also removed as it is now dead code.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants