fix(security): refuse find's fprint family under a bare allowlist entry

_DANGEROUS_FLAGS gates the flags that turn a search/list tool into an execution,
deletion, or file-writing tool, so an allowlisted command can never auto-run them.
It listed -fprintf but omitted -fprint, -fprint0, and -fls, which write find's
output to an arbitrary path: with allowed_commands = ["find"], a plain name search
auto-ran as intended but "find . -fprint /tmp/exfil.txt" also auto-ran, writing
outside the workspace with no approval prompt.

readonly.py's _FIND_BAD already carries the complete set for its own classifier;
the allowlist denylist was the incomplete copy. Add the three flags so the two
stay in step, and add a regression test beside the allowlist tests.

Tests: an allowlisted name search still auto-runs; the fprint/fls forms now
require approval.
This commit is contained in:
Shakti Prasad Mohapatra
2026-09-25 12:51:12 +05:30
parent 5870585321
commit e6bc432949
2 changed files with 19 additions and 2 deletions
+5 -2
View File
@@ -39,8 +39,11 @@ _INTERPRETERS = {
"sh", "bash", "zsh", "dash", "ksh", "fish", "powershell", "pwsh", "cmd",
"python", "python3", "node", "deno", "bun", "ruby", "perl", "php",
}
# Flags that turn a search/list tool into an execution or deletion tool.
_DANGEROUS_FLAGS = {"-exec", "-execdir", "-delete", "-ok", "-okdir", "-fprintf"}
# Flags that turn a search/list tool into an execution, deletion, or file-writing tool.
# The fprint family writes find's output to an arbitrary path; omitting it let a bare
# `find` allowlist entry auto-run `find . -fprint /tmp/exfil.txt`. readonly.py's
# `_FIND_BAD` is the complete copy of this set, so keep the two in step.
_DANGEROUS_FLAGS = {"-exec", "-execdir", "-delete", "-ok", "-okdir", "-fprintf", "-fprint", "-fprint0", "-fls"}
def _split_commands(command: str) -> list[str]:
+14
View File
@@ -108,6 +108,20 @@ def test_exec_uses_command_allowlist(tmp_path):
assert not asked.allowed and asked.needs_user
def test_allowlisted_find_refuses_output_writing_flags(tmp_path):
# `find`'s fprint family writes to an arbitrary path. A bare `find` allowlist entry
# must not auto-run it, exactly as it already refuses -exec/-delete/-fprintf.
eng = PermissionEngine(workspace_root=tmp_path, allowed_commands=["find"])
assert eng.evaluate("run_shell", {"command": "find . -name '*.py'"}, None).allowed
for command in (
"find . -fprint /tmp/exfil.txt",
"find . -fprint0 /tmp/exfil.txt",
"find . -fls /tmp/exfil.txt",
):
d = eng.evaluate("run_shell", {"command": command}, None)
assert not d.allowed and d.needs_user, command
@pytest.mark.parametrize(
"command",
[