Skip to content

fix(security): replace /tmp/pua-plugin-root TOCTOU race with CLAUDE_PLUGIN_ROOT#155

Open
xiaolai wants to merge 1 commit intotanweai:mainfrom
xiaolai:fix/nlpm-stop-feedback-tmp-race
Open

fix(security): replace /tmp/pua-plugin-root TOCTOU race with CLAUDE_PLUGIN_ROOT#155
xiaolai wants to merge 1 commit intotanweai:mainfrom
xiaolai:fix/nlpm-stop-feedback-tmp-race

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.

Security Issue (Medium)

hooks/stop-feedback.sh contains a TOCTOU (time-of-check/time-of-use) race condition in the plugin root path lookup.

The vulnerable pattern:

# Shell writes path to /tmp at hook start
printf '%s' "$_PLUGIN_ROOT" > /tmp/pua-plugin-root

# Claude later executes this instruction from the heredoc:
bash "$(cat /tmp/pua-plugin-root)/hooks/sanitize-session.sh" ...

On a shared or multi-user system, an attacker with write access to /tmp/ can overwrite /tmp/pua-plugin-root between the write and the exec, redirecting Claude to run an arbitrary script. The window is small but real — Claude has to process the hook output and present a question before executing the bash block, giving an attacker several seconds.

Fix

Replace the temp file with ${CLAUDE_PLUGIN_ROOT}, the authoritative env var that hooks.json already uses:

bash "${CLAUDE_PLUGIN_ROOT:-$(ls -td ~/.claude/plugins/cache/pua-skills/pua/*/ | head -1)}/hooks/sanitize-session.sh" ...

This reads the plugin root directly from the process environment at exec time — no writable file involved, no race window. The ls fallback preserves behaviour in environments where CLAUDE_PLUGIN_ROOT is not set.

The dead code that computed _PLUGIN_ROOT and wrote it to the temp file is also removed.

…_ROOT

The hook wrote the plugin root path to /tmp/pua-plugin-root and later
instructed Claude to read it back with $(cat /tmp/pua-plugin-root). On a
shared or multi-user system, an attacker with write access to /tmp/ could
overwrite that file between the write (line 60) and the exec (line 105),
redirecting Claude to run an arbitrary script.

Replace the temp file lookup with ${CLAUDE_PLUGIN_ROOT:-<glob fallback>},
which is already the authoritative env var used by hooks.json. This
eliminates the TOCTOU window entirely.

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.

1 participant