Hardening read_file device path blocking bypass
NousResearch/hermes-agent · cluster 10141 · agent
Data as of
Nothing open on GitHub. Fix merged in #50221. Check it covers your case.
The short answer
- Known?
- Yes. 4 related items grouped here, 2 issues already closed.
- Fix event
- A fix was merged to main in PR #50221. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
partially resolved, deciding PR #50221. That is separate from the fix event. - Still open
- Nothing open on GitHub.
These issues and pull requests address a security vulnerability where the _is_blocked_device check in the read_file tool could be bypassed using device path aliases or symlinks due to use of expanduser instead of proper path normalization. The work involves tightening the path resolution logic to prevent circumvention of the device blocklist.
Open issues0
Open PRs0
Fixes on record
-
#50221 fix(file): harden read_file device alias blocking
files:
tests/tools/test_file_read_guards.py,tools/file_tools.pyShow the diff
-
tests/tools/test_file_read_guards.py@@ -109,6 +109,10 @@ def test_proc_legitimate_files_not_blocked(self): for path in ("/proc/cpuinfo", "/proc/meminfo", "/proc/uptime", "/proc/version"): self.assertFalse(_is_blocked_device(path), f"{path} should not be blocked") + def test_normpath_alias_to_blocked_device_is_blocked(self): + self.assertTrue(_is_blocked_device("/dev/../dev/zero")) + self.assertTrue(_is_blocked_device("/dev/./urandom")) + def test_normal_files_not_blocked(self): self.assertFalse(_is_blocked_device("/tmp/test.py")) self.assertFalse(_is_blocked_device("/home/user/.bashrc")) @@ -134,6 +138,17 @@ def test_symlink_to_regular_file_not_blocked(self): self.skipTest(f"symlink unavailable: {exc}") self.assertFalse(_is_blocked_device(link_path)) + def test_symlink_to_blocked_alias_is_blocked_before_realpath(self): + if not os.path.exists("/dev/stdin"): + self.skipTest("/dev/stdin is not available on this platform") + with tempfile.TemporaryDirectory() as tmpdir: + link_path = os.path.join(tmpdir, "stdin-link") + try: + os.symlink("/dev/../dev/stdin", link_path) + except OSError as exc: + self.skipTest(f"symlink unavailable: {exc}") + self.assertTrue(_is_blocked_device(link_path)) + def test_read_file_tool_rejects_device(self): """read_file_tool returns an error without any file I/O.""" result = json.loads(read_file_tool("/dev/zero", task_id="dev_test")) -
tools/file_tools.py@@ -285,7 +285,7 @@ def _path_resolution_warning(filepath: str, resolved: Path, task_id: str = "defa def _is_blocked_device_path(path: str) -> bool: """Return True for concrete device/fd paths that can hang reads.""" - normalized = os.path.expanduser(path) + normalized = os.path.normpath(os.path.expanduser(path)) if normalized in _BLOCKED_DEVICE_PATHS: return True # /proc/self/fd/0-2 and /proc/<pid>/fd/0-2 are Linux aliases for stdio @@ -306,17 +306,35 @@ def _is_blocked_device(filepath: str) -> bool: """Return True if the path would hang the process (infinite output or blocking input). Check the literal path first so aliases like /dev/stdin are caught before - they resolve to terminal-specific paths. Then check the resolved path so a - workspace symlink to /dev/zero cannot bypass the guard. + they resolve to terminal-specific paths. Then check each symlink hop before + the final resolved path so aliases to devices cannot bypass the guard. """ - normalized = os.path.expanduser(filepath) + normalized = os.path.normpath(os.path.expanduser(filepath)) if _is_blocked_device_path(normalized): return True + + seen: set[str] = set() + current = normalized + for _ in range(20): + try: + target = os.readlink(current) + except OSError: + break + if not os.path.isabs(target): + target = os.path.join(os.path.dirname(current), target) + target = os.path.normpath(target) + if _is_blocked_device_path(target): + return True + if target in seen: + break + seen.add(target) + current = target + try: - resolved = os.path.realpath(normalized) + resolved = os.path.normpath(os.path.realpath(normalized)) except (OSError, ValueError): return False - if resolved != normalized and _is_blocked_device_path(resolved): + if _is_blocked_device_path(resolved): return True return False
-
1 PR closed without merging
From the close comments: merged via another change 1
-
#34466 fix(file): harden read_file device alias blocking · merged via another change →
9078b4b,#50221
Closed issues · 2
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.73: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Subsystem
- agent
- Merged PRs
- #50221
- Closed issues
- 2
- Attempts
- 1 PR closed without merging
- Fix link checked
- 21 Jun 2026