Skip to content
Draft
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
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,7 @@
**Vulnerability:** Denial of Service / Availability
**Learning:** Strix security scanners crashed when the backend LLM returned an 'HTTP Error 502: Bad Gateway' response. This was because 'bad gateway' string match and generic 'APIError' were missing from the `is_llm_api_connection_error` function in the Strix retry gate.
**Prevention:** Always include `bad gateway` and `APIError` in string match conditions when handling HTTP API Connection exceptions for LLM backends to ensure proper fail-closed and retry handling.
## 2026-09-12 - Prevent Command Injection via Explicit shell=False in Subprocess
**Vulnerability:** Command Injection hardening (implicit shell default; defense in depth)
**Learning:** Functions executing system commands, like `_probe_isolation_capability` using `subprocess.run`, implicitly default to `shell=False`. However, not explicitly declaring it allows security linters (like Bandit) to report false positives, and obscures the security posture against command injection if untrusted inputs were to reach the execution arguments.
**Prevention:** Always explicitly define `shell=False` in `subprocess.run()` and `subprocess.Popen()` calls, even when it is the default behavior. Ensure corresponding unit tests explicitly verify this configuration by asserting `kwargs.get("shell") is False` in mock implementations.
1 change: 1 addition & 0 deletions scripts/ci/sandboxed_web_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ def _probe_isolation_capability(backend: str) -> None:
text=True,
timeout=10,
check=False,
shell=False,
)
except (OSError, subprocess.TimeoutExpired) as exc:
raise RuntimeError(f"bubblewrap capability probe could not run: {exc}") from exc
Expand Down
6 changes: 6 additions & 0 deletions tests/test_sandboxed_web_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -1396,12 +1396,15 @@ def test_probe_isolation_capability_exercises_the_same_operations_as_real_comman

def _fake_run(command, **kwargs):
captured["command"] = command
captured["kwargs"] = kwargs
return subprocess.CompletedProcess(command, 0, stdout="", stderr="")

monkeypatch.setattr(sandboxed_web_e2e.subprocess, "run", _fake_run)
sandboxed_web_e2e._probe_isolation_capability("/usr/bin/bwrap")

command = captured["command"]
kwargs = captured["kwargs"]
assert kwargs.get("shell") is False
assert "--new-session" in command
assert command.count("--tmpfs") == 2
assert "/tmp" in command
Expand Down Expand Up @@ -1434,12 +1437,15 @@ def test_probe_isolation_capability_ignores_path_shadowed_shell(monkeypatch, tmp

def _fake_run(command, **kwargs):
captured["command"] = command
captured["kwargs"] = kwargs
return subprocess.CompletedProcess(command, 0, stdout="", stderr="")

monkeypatch.setattr(sandboxed_web_e2e.subprocess, "run", _fake_run)
sandboxed_web_e2e._probe_isolation_capability("/usr/bin/bwrap")

command = captured["command"]
kwargs = captured["kwargs"]
assert kwargs.get("shell") is False
probe_executable = command[-3]
assert probe_executable in sandboxed_web_e2e.PROBE_SHELL_PATHS
assert probe_executable != str(shadow_sh)
Expand Down
Loading