Skip to content

fix: TOCTOU allowlist bypass + exec.cwd silently ignored - #101

Merged
blaspat merged 3 commits into
mainfrom
fix/review-findings-go
Jul 20, 2026
Merged

fix: TOCTOU allowlist bypass + exec.cwd silently ignored#101
blaspat merged 3 commits into
mainfrom
fix/review-findings-go

Conversation

@blaspat

@blaspat blaspat commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes two bugs from the deep peer review of hermes-node.

Bug #1 — TOCTOU symlink-swap allowlist bypass

handler_fs.go:197,223,340 used the raw p.Path for Stat/ReadFile/WriteFile calls, but checkAllowed() returns a symlink-resolved canonical path. An attacker with filesystem write access could swap a symlink component between the allowlist check and the file operation, bypassing the allowlist.

Fix: Pass canonical (the resolved path from checkAllowed) instead of p.Path to all three file I/O operations.

Bug #2exec.cwd silently ignored

SessionAdapter.Run discarded the target/cwd parameter entirely. Commands ran in whatever directory the persistent bash session happened to be in, regardless of the cwd field in the exec payload.

Fix (two parts):

  1. handler_exec.go:249 — passes canonical (the resolved, validated working directory) instead of raw cwd to Shell.Run.
  2. adapter.goSessionAdapter.Run now prepends cd <target> when target is non-empty, so the command executes in the caller-intended directory.

Build: clean. Tests: 7/7 packages pass, no races.

blaspat added 3 commits July 20, 2026 13:05
Bug #1 — TOCTOU symlink-swap allowlist bypass:
handler_fs.go read/write operations used the raw p.Path instead of
the canonical (symlink-resolved) path returned by checkAllowed.
An attacker with filesystem write access could swap a symlink
component between the allowlist check and the file operation,
reading or writing files outside the permitted roots.
Fix: pass canonical to Stat, ReadFile, WriteFile.

Bug #2 — exec.cwd silently ignored:
handler_exec.go passed the original cwd (or the shell's current cwd)
to Shell.Run(callCtx, cwd, p.Command), but SessionAdapter.Run
discarded the target parameter entirely. Commands ran in whatever
directory the persistent bash session happened to be in.
Fix: pass canonical (validated/resolved path) from handler to
Shell.Run. SessionAdapter.Run now prepends  when
target is non-empty, so explicit cwd is honoured.

Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com>
Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com>
Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com>
@blaspat
blaspat merged commit 9505734 into main Jul 20, 2026
14 checks passed
@blaspat
blaspat deleted the fix/review-findings-go branch July 21, 2026 07:49
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