fix(cli): stop leaking API keys to CLI app subprocesses

Installed CLI apps were started with os.environ.copy(), so provider keys
from the parent process were visible to untrusted binaries. Use a minimal
allowlist env matching the shell tool.

Fixes #4783
This commit is contained in:
LHMQ878
2026-08-12 02:57:48 +09:00
committed by Xubin Ren
parent 72d3ce6b23
commit ec3dfb21ba
2 changed files with 85 additions and 2 deletions
+31 -2
View File
@@ -990,7 +990,36 @@ class CliAppManager:
return None
raise CliAppError("this CLI app uses an unsupported install strategy")
def _run_argv(self, argv: list[str], *, timeout: int) -> subprocess.CompletedProcess[str]:
def _subprocess_env(self) -> dict[str, str]:
"""Minimal env for CLI app subprocesses — no API keys or secrets.
Mirrors the shell tool's allowlist so installed apps cannot read
provider credentials from the parent process environment.
"""
if sys.platform == "win32":
sr = os.environ.get("SYSTEMROOT", r"C:\Windows")
env = {
"SYSTEMROOT": sr,
"COMSPEC": os.environ.get("COMSPEC", f"{sr}\\system32\\cmd.exe"),
"USERPROFILE": os.environ.get("USERPROFILE", ""),
"HOMEDRIVE": os.environ.get("HOMEDRIVE", "C:"),
"HOMEPATH": os.environ.get("HOMEPATH", "\\"),
"TEMP": os.environ.get("TEMP", f"{sr}\\Temp"),
"TMP": os.environ.get("TMP", f"{sr}\\Temp"),
"PATHEXT": os.environ.get("PATHEXT", ".COM;.EXE;.BAT;.CMD"),
"PATH": os.environ.get("PATH", f"{sr}\\system32;{sr}"),
"PYTHONUNBUFFERED": "1",
}
return {k: v for k, v in env.items() if v is not None}
return {
"HOME": os.environ.get("HOME", "/tmp"),
"LANG": os.environ.get("LANG", "C.UTF-8"),
"TERM": os.environ.get("TERM", "dumb"),
"PATH": os.environ.get("PATH", "/usr/bin:/bin"),
"PYTHONUNBUFFERED": "1",
}
def _run_argv(self, argv: list[str], *, timeout: int) -> subprocess.CompletedProcess[str]:
command = subprocess.list2cmdline(argv)
logger.info("CLI Apps: running {}", command)
result = subprocess.run(
@@ -1431,7 +1460,7 @@ Use the `run_cli_app` tool with `name="{name}"` for command execution. Do not in
encoding="utf-8",
errors="replace",
timeout=effective_timeout,
env=os.environ.copy(),
env=self._subprocess_env(),
)
except subprocess.TimeoutExpired:
return f"CLI app '{name}' timed out after {effective_timeout}s"