fix(tools): flag bun/deno inline script execution for approval

This commit is contained in:
liuhao1024
2026-09-19 18:48:10 +08:00
committed by Teknium
parent c1281eb46d
commit 886df27c6e
2 changed files with 77 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
"""Bun and Deno evaluate inline scripts through forms the interpreter flag
tables did not know: ``bun -e/--eval``, and Deno's bare ``eval`` subcommand
(``deno eval "code"``). They must reach the same approval classification as
``node -e`` / ``python -c``. See issue #116012.
"""
from tools.approval import detect_dangerous_command
class TestBunDenoInlineScriptExecution:
def test_inline_eval_detected(self):
for cmd in (
'bun -e "console.log(1)"',
'bun --eval "console.log(1)"',
'deno eval "console.log(1)"',
'deno -e "console.log(1)"',
'deno --eval "console.log(1)"',
# Command-position wrappers must not shield the interpreter.
'sudo bun -e "console.log(1)"',
'sudo deno eval "console.log(1)"',
):
dangerous, key, _ = detect_dangerous_command(cmd)
assert dangerous is True, cmd
assert key == "script execution via -e/-c flag", cmd
def test_windows_executable_spelling_detected(self):
# Windows resolves executable names case-insensitively; `_interpreter_family`
# lowercases the basename, so BUN.EXE / Deno.exe must be classified too.
for cmd in (
'BUN.EXE -e "console.log(1)"',
'Deno.exe eval "console.log(1)"',
):
dangerous, key, _ = detect_dangerous_command(cmd)
assert dangerous is True, cmd
assert key == "script execution via -e/-c flag", cmd
def test_interpreter_heredoc_detected(self):
for cmd in (
'bun << "EOF"\nconsole.log("pwned")\nEOF',
"deno << 'EOF'\nconsole.log(\"pwned\")\nEOF",
):
dangerous, key, _ = detect_dangerous_command(cmd)
assert dangerous is True, cmd
assert key == "script execution via heredoc", cmd
def test_malformed_quoting_fails_closed(self):
# An unterminated quote on a bun/deno inline script must fail closed like the
# other interpreter families, not silently skip the family.
dangerous, key, _ = detect_dangerous_command('bun -e "console.log(1)')
assert dangerous is True
assert key == "command parser limit or malformed executable payload"
def test_plain_bun_deno_invocations_not_flagged(self):
"""Everyday subcommands that run project files must stay safe."""
for cmd in (
"bun install",
"bun run build",
"deno run server.ts",
"deno test",
"deno lint",
# A file literally named eval.ts is a `run` operand, not the eval subcommand.
"deno run eval.ts",
):
dangerous, _, _ = detect_dangerous_command(cmd)
assert dangerous is False, cmd
def test_quoted_prose_about_deno_eval_not_flagged(self):
# `deno eval` inside quotes is data for echo, not a command position.
dangerous, _, _ = detect_dangerous_command("echo 'use deno eval for that'")
assert dangerous is False

View File

@@ -581,10 +581,12 @@ _INTERPRETER_NAME_RES = tuple((family, re.compile(pattern)) for family, pattern
("python", r"py(?:\.exe)?|python[23]?(?:\.\d+)*(?:\.exe)?"), ("node", r"node(?:js)?(?:\.exe)?"),
("perl", r"perl[0-9]*(?:\.\d+)*(?:\.exe)?"), ("ruby", r"ruby[0-9.]*(?:\.exe)?"), ("php", r"php(?:\.exe)?"),
("powershell", r"powershell(?:\.exe)?|pwsh(?:\.exe)?"),
("bun", r"bun(?:\.exe)?"), ("deno", r"deno(?:\.exe)?"),
))
_INTERPRETER_EXEC_FLAGS = {
"python": {"-c"}, "node": {"-e", "--eval", "-p", "--print"}, "perl": {"-e", "--eval"}, "ruby": {"-e"},
"php": {"-r"}, "powershell": {"-command", "-c", "-file", "-f"},
"bun": {"-e", "--eval"}, "deno": {"eval", "-e", "--eval"},
}
_INTERPRETER_WITH_ARG = {
"python": {"-W", "-X", "--check-hash-based-pycs"},
@@ -594,6 +596,7 @@ _INTERPRETER_WITH_ARG = {
"php": {"-c", "-d", "-z"},
"powershell": {"-configurationname", "-custompipename", "-executionpolicy", "-inputformat", "-outputformat",
"-settingsfile", "-version", "-windowstyle", "-workingdirectory"},
"bun": {"--config", "--cwd", "--env-file", "--preload", "--require"}, "deno": set(),
}
_READ_TOOL_EXEC_FLAGS = {
"sort": {"--compress-program"}, "rg": {"--pre", "--hostname-bin"}, "ag": {"--pager"},
@@ -840,6 +843,10 @@ def _iter_top_level_shell_segments(command: str):
def _interpreter_exec_flag(family: str, args: list[str]) -> str | None:
"""Return an execution-bearing interpreter option, if present."""
flags, with_arg = _INTERPRETER_EXEC_FLAGS[family], _INTERPRETER_WITH_ARG[family]
# Deno evaluates inline scripts via a bare `eval` subcommand rather than a dash flag, and
# only as the first argument; a later positional `eval` stays data.
if family == "deno" and args and args[0].lower() == "eval":
return "eval"
powershell = family == "powershell"
skip_value = False
for token in args: