From b7b8bec8001ffd4ab56f3227d93eada72aada243 Mon Sep 17 00:00:00 2001 From: Dakota Secula-Rosell <115513861+dsr-restyn@users.noreply.github.com> Date: Mon, 25 May 2026 08:07:31 -0400 Subject: [PATCH] fix(security): block /proc/*/environ, cmdline, maps from file read (#4609) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The read_file tool and terminal cat can access /proc/self/environ to recover all process env vars including secrets stripped by the subprocess blocklist. Output redaction partially mitigates (catches known-format tokens) but misses custom/proprietary key formats, especially when values are printed without their key names. Add /proc/*/environ, /proc/*/cmdline, and /proc/*/maps to the blocked device paths in _is_blocked_device(): - /proc/*/environ: leaks full process env (API keys, tokens) - /proc/*/cmdline: leaks command-line args (may contain passwords) - /proc/*/maps: leaks memory layout (ASLR bypass for exploitation) Legitimate /proc reads (cpuinfo, meminfo, uptime, version) remain accessible — the check only blocks per-pid pseudo-files with known sensitive suffixes. Complements PR #4432 (PID namespace isolation for child processes) which prevents children from reading the parent's /proc, but does not prevent the parent process itself from being read via file tools. Partially addresses #4427 Changes: tools/file_tools.py | +6 tests/tools/test_file_read_guards.py | +18 -1 Co-authored-by: dsr-restyn --- tests/tools/test_file_read_guards.py | 18 +++++++++++++++++- tools/file_tools.py | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/tools/test_file_read_guards.py b/tests/tools/test_file_read_guards.py index ccb82daa734..8f38aab7b07 100644 --- a/tests/tools/test_file_read_guards.py +++ b/tests/tools/test_file_read_guards.py @@ -78,7 +78,23 @@ class TestDevicePathBlocking(unittest.TestCase): def test_proc_fd_other_not_blocked(self): self.assertFalse(_is_blocked_device("/proc/self/fd/3")) - self.assertFalse(_is_blocked_device("/proc/self/maps")) + + def test_proc_sensitive_pseudo_files_blocked(self): + """environ/cmdline/maps under /proc/ must be blocked (issue #4427).""" + for path in ( + "/proc/self/environ", + "/proc/12345/environ", + "/proc/self/cmdline", + "/proc/99/cmdline", + "/proc/self/maps", + "/proc/1/maps", + ): + self.assertTrue(_is_blocked_device(path), f"{path} should be blocked") + + def test_proc_legitimate_files_not_blocked(self): + """Top-level /proc files like cpuinfo and meminfo must remain accessible.""" + 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_normal_files_not_blocked(self): self.assertFalse(_is_blocked_device("/tmp/test.py")) diff --git a/tools/file_tools.py b/tools/file_tools.py index 9ff433ef939..27b768e93cf 100644 --- a/tools/file_tools.py +++ b/tools/file_tools.py @@ -143,6 +143,12 @@ def _is_blocked_device(filepath: str) -> bool: ("/fd/0", "/fd/1", "/fd/2") ): return True + # /proc/*/environ, /proc/*/cmdline, /proc/*/maps can leak secrets, + # command-line args, and memory layout from the host process (issue #4427) + if normalized.startswith("/proc/") and normalized.endswith( + ("/environ", "/cmdline", "/maps") + ): + return True return False