security: Sandbox verbirgt API-Schlüssel und Home-Verzeichnis
Erzeugter Code las über pandas.io.common.os – ohne import os, an validate_imports vorbei – den OPENROUTER_API_KEY und das gesamte Home-Verzeichnis (nachgewiesen). Kein Netz, aber alles Gedruckte landet in Ergebnis-JSON und Report. In gespeicherten Ergebnissen fand sich kein Schlüssel. - Umgebung des Programms ist eine Positivliste (solver_env), auch bei SOLVER_SANDBOX=aus - bubblewrap verdeckt das Home per --tmpfs; nur sys.prefix/sys.base_prefix wieder lesbar Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
3c4d2fb92c
commit
8dd5073cd1
1 changed files with 59 additions and 2 deletions
|
|
@ -482,9 +482,18 @@ def sandbox_command(script: Path, tmp: str) -> List[str]:
|
|||
if SANDBOX == "bwrap":
|
||||
raise CodeRejected("SOLVER_SANDBOX=bwrap verlangt bubblewrap, es ist aber nicht installiert.")
|
||||
return [sys.executable, str(script)]
|
||||
# Das Home-Verzeichnis verdecken (Schlüsseldateien, .env, andere Projekte) – `validate_imports` hält den
|
||||
# Zugriff nicht auf: beobachtet, dass `pandas.io.common.os` ohne `import os` das ganze Home auflistete.
|
||||
# Wieder lesbar werden nur die Python-Installationen, die der Solver-Lauf braucht (.venv, Basis-Python).
|
||||
home = Path.home().resolve()
|
||||
python = []
|
||||
for pfad in dict.fromkeys(Path(p).resolve() for p in (sys.prefix, sys.base_prefix)):
|
||||
if pfad.is_relative_to(home):
|
||||
python += ["--ro-bind", str(pfad), str(pfad)]
|
||||
return [
|
||||
"bwrap", "--unshare-all", "--die-with-parent",
|
||||
"--ro-bind", "/", "/", # alles lesbar (Python, Solver-Bibliotheken), nichts beschreibbar
|
||||
"--tmpfs", str(home), *python, # außer dem Home-Verzeichnis
|
||||
"--bind", tmp, tmp, # einzige Ausnahme: das Arbeitsverzeichnis dieses Laufs
|
||||
"--dev", "/dev", "--proc", "/proc",
|
||||
"--setenv", "HOME", tmp, "--setenv", "TMPDIR", tmp,
|
||||
|
|
@ -493,6 +502,16 @@ def sandbox_command(script: Path, tmp: str) -> List[str]:
|
|||
]
|
||||
|
||||
|
||||
# Umgebung des Solver-Programms: nur diese Variablen, nie die ganze Umgebung – sonst liest der erzeugte Code
|
||||
# OPENROUTER_API_KEY/GEMINI_API_KEY (nachgewiesen) und kann sie in Ergebnis-JSON oder Report schreiben.
|
||||
SOLVER_ENV_VARS = ("PATH", "LANG", "LC_ALL", "LC_CTYPE", "OMP_NUM_THREADS", "OPENBLAS_NUM_THREADS", "MKL_NUM_THREADS")
|
||||
|
||||
|
||||
def solver_env(tmp: str) -> dict:
|
||||
env = {name: os.environ[name] for name in SOLVER_ENV_VARS if name in os.environ}
|
||||
return {**env, "HOME": tmp, "TMPDIR": tmp, "PYTHONDONTWRITEBYTECODE": "1"}
|
||||
|
||||
|
||||
def run_solver_code(code: str, timeout: int = 60, data: dict | None = None,
|
||||
data_files=()) -> SolverRun:
|
||||
try:
|
||||
|
|
@ -506,7 +525,7 @@ def run_solver_code(code: str, timeout: int = 60, data: dict | None = None,
|
|||
# Beigelegte Daten ins Laufverzeichnis: es ist das einzige, das die Sandbox beschreibbar bindet
|
||||
for quelle in data_files or ():
|
||||
shutil.copy2(quelle, Path(tmp) / Path(quelle).name)
|
||||
env = dict(os.environ)
|
||||
env = solver_env(tmp)
|
||||
if data is not None: # geänderte Eingabedaten ohne neuen LLM-Aufruf
|
||||
daten_datei = Path(tmp) / "daten.json"
|
||||
daten_datei.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
|
||||
|
|
|
|||
Loading…
Reference in a new issue