Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
**Vulnerability:** The CLI and TUI Gateway executed user-defined `quick_commands` and arbitrary shell commands (`shell.exec`) using `subprocess.run(..., shell=True)` without sanitizing the environment variables passed to the child process.
**Learning:** This exposed sensitive API keys and credentials contained in the main Hermes process environment to these child processes, allowing for easy credential exfiltration by a malicious config or user interaction.
**Prevention:** Always use `tools.environments.local._sanitize_subprocess_env` to filter the environment before passing it to `subprocess` execution mechanisms when executing untrusted or user-supplied shell commands.

## 2024-05-24 - [Sanitize Subprocess Environments in Local Transcription Tools]
**Vulnerability:** Command injection and credential leakage vulnerability in `tools/transcription_tools.py` via `_transcribe_local_command`.
**Learning:** `subprocess.run(command, shell=True)` was used to run configured local STT commands (which can be modified) without sanitizing the environment variables passed to the child process. This means sensitive API keys available in the parent process's environment were being passed down to an untrusted local script or executable.
**Prevention:** Always explicitly pass `env=_sanitize_subprocess_env(os.environ.copy())` to `subprocess.run` or `subprocess.Popen` when `shell=True` is used or when executing external user-defined commands, to prevent secret leakage.
4 changes: 3 additions & 1 deletion tools/process_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
import uuid

_IS_WINDOWS = platform.system() == "Windows"
from tools.environments.local import _find_shell, _resolve_safe_cwd, _sanitize_subprocess_env
from tools.environments.local import _find_shell, _resolve_safe_cwd
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

Expand Down Expand Up @@ -506,6 +506,7 @@ def spawn_local(
else:
from ptyprocess import PtyProcess as _PtyProcessCls
user_shell = _find_shell()
from tools.environments.local import _sanitize_subprocess_env
pty_env = _sanitize_subprocess_env(os.environ, env_vars)
pty_env["PYTHONUNBUFFERED"] = "1"
pty_proc = _PtyProcessCls.spawn(
Expand Down Expand Up @@ -547,6 +548,7 @@ def spawn_local(
# Force unbuffered output for Python scripts so progress is visible
# during background execution (libraries like tqdm/datasets buffer when
# stdout is a pipe, hiding output from process(action="poll")).
from tools.environments.local import _sanitize_subprocess_env
bg_env = _sanitize_subprocess_env(os.environ, env_vars)
bg_env["PYTHONUNBUFFERED"] = "1"
proc = subprocess.Popen(
Expand Down
14 changes: 13 additions & 1 deletion tools/transcription_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,19 @@ def _transcribe_local_command(file_path: str, model_name: str) -> Dict[str, Any]
language=shlex.quote(language),
model=shlex.quote(normalized_model),
)
subprocess.run(command, shell=True, check=True, capture_output=True, text=True)

# Security: Sanitize the environment to prevent leaking secrets to child processes
from tools.environments.local import _sanitize_subprocess_env
sanitized_env = _sanitize_subprocess_env(os.environ.copy())

subprocess.run(
command,
shell=True,
check=True,
capture_output=True,
text=True,
env=sanitized_env
)

txt_files = sorted(Path(output_dir).glob("*.txt"))
if not txt_files:
Expand Down
Loading