58 lines
2.9 KiB
Python
58 lines
2.9 KiB
Python
"""Sandbox 临时 Python 包扫描与 best-effort 持久化。"""
|
|
from __future__ import annotations
|
|
import json
|
|
import subprocess
|
|
from datetime import datetime, timezone
|
|
from typing import Optional
|
|
from uuid import UUID
|
|
|
|
|
|
def scan_container(container: str) -> Optional[dict]:
|
|
try:
|
|
scan = subprocess.run(["docker", "exec", "--user", "zcbot", "--workdir", "/sandbox", container,
|
|
"python", "-I", "-S", "/sandbox/package_scan.py"], capture_output=True,
|
|
text=True, timeout=30)
|
|
if scan.returncode != 0:
|
|
raise RuntimeError(scan.stderr.strip())
|
|
packages = json.loads(scan.stdout)
|
|
if not isinstance(packages, list):
|
|
raise ValueError("scanner output is not a package list")
|
|
inspect = subprocess.run(["docker", "inspect", "--format={{.Image}}", container],
|
|
capture_output=True, text=True, timeout=15)
|
|
py = subprocess.run(["docker", "exec", "--workdir", "/sandbox", container, "python", "-I", "-S", "-c",
|
|
"import sys;print('.'.join(map(str,sys.version_info[:3])))"],
|
|
capture_output=True, text=True, timeout=15)
|
|
if inspect.returncode != 0 or py.returncode != 0:
|
|
raise RuntimeError("failed to inspect image or Python version")
|
|
return {"packages": packages, "image_digest": inspect.stdout.strip(),
|
|
"python_version": py.stdout.strip(),
|
|
"total_installed_bytes": sum(int(x.get("installed_bytes") or 0) for x in packages)}
|
|
except Exception as exc:
|
|
print(f"[sandbox-package-scan] failed container={container}: {type(exc).__name__}: {exc}")
|
|
return None
|
|
|
|
|
|
def persist_scan(*, container_session_id: str, user_id: UUID, execution_kind: str,
|
|
result: Optional[dict]) -> None:
|
|
if not result or not result.get("packages"):
|
|
return
|
|
try:
|
|
from core.storage import session_scope
|
|
from core.storage.models import SandboxPackageScan
|
|
with session_scope() as s:
|
|
s.merge(SandboxPackageScan(
|
|
container_session_id=container_session_id, user_id=user_id,
|
|
execution_kind=execution_kind, image_digest=result["image_digest"],
|
|
python_version=result["python_version"], packages=result["packages"],
|
|
package_count=len(result["packages"]),
|
|
total_installed_bytes=result["total_installed_bytes"],
|
|
finished_at=datetime.now(timezone.utc),
|
|
))
|
|
except Exception as exc:
|
|
print(f"[sandbox-package-scan] persist failed session={container_session_id}: {type(exc).__name__}: {exc}")
|
|
|
|
|
|
def scan_and_persist(container: str, user_id: UUID, kind: str, session_id: str) -> None:
|
|
persist_scan(container_session_id=session_id, user_id=user_id,
|
|
execution_kind=kind, result=scan_container(container))
|