mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 23:12:22 +02:00
feat(security): sanitize subprocess environment inheritance and scrub credentials
Replace unscrubbed os.environ inheritance with an explicit allowlist (_SAFE_SUBPROCESS_VARS) containing only variables necessary for bash/python execution (PATH, locale, terminal, Python virtualenv/site-packages, Windows essentials) and regex-based credential scrubbing (_SENSITIVE_PATTERN) to prevent provider tokens, database URLs, and API keys from leaking into child processes.
This commit is contained in:
@@ -500,8 +500,10 @@ def _owned_spec(cwd: str, env: Optional[dict], timeout: int, readonly_extra: tup
|
||||
visible = any(prefix == root or prefix.startswith(root + os.sep) for root in ("/usr", "/etc"))
|
||||
if not visible and prefix not in _NAMESPACE_RESERVED_DESTS:
|
||||
readonly.append(prefix)
|
||||
from src.tool_execution import _agent_subprocess_env
|
||||
clean_env = _agent_subprocess_env() if env is None else dict(env)
|
||||
return containment.agent_spec(
|
||||
cwd, dict(os.environ if env is None else env), timeout,
|
||||
cwd, clean_env, timeout,
|
||||
readonly_extra=tuple(dict.fromkeys([*readonly, *readonly_extra])),
|
||||
)
|
||||
|
||||
|
||||
+38
-1
@@ -1246,8 +1246,45 @@ def _split_bg_marker(content: str):
|
||||
return False, content
|
||||
|
||||
|
||||
import re as _re
|
||||
|
||||
# Variables a legitimate agent bash/python subprocess needs from the host.
|
||||
# Anything not listed here is never inherited.
|
||||
_SAFE_SUBPROCESS_VARS = frozenset({
|
||||
# POSIX execution
|
||||
"PATH", "LANG", "LC_ALL", "LC_CTYPE", "LC_MESSAGES", "TZ",
|
||||
"USER", "LOGNAME", "SHELL", "TMPDIR", "TEMP", "TMP",
|
||||
# Python isolation / virtualenvs
|
||||
"PYTHONPATH", "PYTHONHOME", "VIRTUAL_ENV",
|
||||
"ODYSSEUS_PYTHON_TOOL_SITE_PACKAGES",
|
||||
# Windows system essentials
|
||||
"SYSTEMROOT", "WINDIR", "COMSPEC", "PATHEXT",
|
||||
"ALLUSERSPROFILE", "PROGRAMDATA", "COMMONPROGRAMFILES",
|
||||
"PROGRAMFILES", "PROGRAMFILES(X86)",
|
||||
# XDG / runtime
|
||||
"XDG_RUNTIME_DIR", "XDG_DATA_HOME", "XDG_CONFIG_HOME", "XDG_CACHE_HOME",
|
||||
# Bubblewrap / container paths
|
||||
"LD_LIBRARY_PATH",
|
||||
})
|
||||
|
||||
# Defence-in-depth: reject any allowlisted variable whose *name* matches
|
||||
# a credential-bearing pattern (e.g. a user who sets PATH_TOKEN=...).
|
||||
_SENSITIVE_PATTERN = _re.compile(
|
||||
r"(?:KEY|TOKEN|SECRET|PASSW|AUTH|CREDENTIAL|PRIVATE|DATABASE_URL)",
|
||||
_re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def _agent_subprocess_env() -> dict:
|
||||
return {**os.environ, "TERM": "xterm-256color", "COLUMNS": "120", "LINES": "40", "HOME": _AGENT_WORKDIR}
|
||||
base = {
|
||||
key: os.environ[key]
|
||||
for key in _SAFE_SUBPROCESS_VARS
|
||||
if key in os.environ and not _SENSITIVE_PATTERN.search(key)
|
||||
}
|
||||
base.setdefault("PATH", os.environ.get("PATH") or os.defpath or "/usr/local/bin:/usr/bin:/bin")
|
||||
base.setdefault("LANG", "C.UTF-8")
|
||||
base.update({"TERM": "xterm-256color", "COLUMNS": "120", "LINES": "40", "HOME": _AGENT_WORKDIR})
|
||||
return base
|
||||
|
||||
|
||||
async def _direct_fallback(
|
||||
|
||||
Reference in New Issue
Block a user