fix(file-safety): block auth.json read via TERMINAL_CWD relative path

read_file_tool resolves relative paths against TERMINAL_CWD (or the
task's live terminal cwd), but the prior call passed the original
unresolved string to get_read_block_error. That function's own
resolve() is anchored at the Python process cwd, so when a task's
TERMINAL_CWD pointed at HERMES_HOME and the agent issued read_file
on the relative path "auth.json", the credential-store denylist was
never reached and the file was read normally.

Pass the already-resolved absolute path string at the file_tools call
site, document the contract on get_read_block_error, and add a
read_file_tool-level regression test that pins the relative-path
case under TERMINAL_CWD == HERMES_HOME.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
briandevans 2026-04-29 17:09:41 -07:00 committed by Teknium
parent 056e00a77e
commit 567ea61298
3 changed files with 50 additions and 3 deletions

View file

@ -474,8 +474,13 @@ def read_file_tool(path: str, offset: int = 1, limit: int = 500, task_id: str =
})
# ── Hermes internal path guard ────────────────────────────────
# Prevent prompt injection via catalog or hub metadata files.
block_error = get_read_block_error(path)
# Prevent prompt injection via catalog or hub metadata files,
# and block credential stores under HERMES_HOME. Pass the
# already-resolved path so a relative-path read against
# TERMINAL_CWD == HERMES_HOME (e.g. "auth.json") still hits the
# denylist — get_read_block_error's own resolve() runs against
# the Python process cwd, which can differ.
block_error = get_read_block_error(str(_resolved))
if block_error:
return json.dumps({"error": block_error})