From fe35e832a42f21c388b1d000ab6649aa894f69ff Mon Sep 17 00:00:00 2001 From: Yufeng He <40085740+he-yufeng@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:50:00 +0800 Subject: [PATCH] fix(sandbox): require local runtime opt-in (#3092) --- .../src/dbgpt_sandbox/sandbox/config.py | 14 +++++-- .../execution_layer/runtime_factory.py | 22 +++++++++-- .../tests/test_runtime_factory.py | 39 +++++++++++++++++++ 3 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 packages/dbgpt-sandbox/tests/test_runtime_factory.py diff --git a/packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/config.py b/packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/config.py index 68c2bc009..1c3dfceb1 100644 --- a/packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/config.py +++ b/packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/config.py @@ -35,6 +35,14 @@ MAX_DEPENDENCY_INSTALL_SIZE = 200 * 1024 * 1024 # 200MB MAX_PROCESSES = 10 -SANDBOX_RUNTIME = os.getenv( - "SANDBOX_RUNTIME", "local" -) # Optional values: docker, podman, nerdctl, local +def _env_flag(name: str, default: bool = False) -> bool: + value = os.getenv(name) + if value is None: + return default + return value.lower() in {"1", "true", "yes", "on"} + + +# Optional values: docker, podman, nerdctl, local. When unset, RuntimeFactory +# auto-detects container runtimes and fails closed if none are available. +SANDBOX_RUNTIME = os.getenv("SANDBOX_RUNTIME") +SANDBOX_ALLOW_LOCAL_RUNTIME = _env_flag("SANDBOX_ALLOW_LOCAL_RUNTIME") diff --git a/packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/execution_layer/runtime_factory.py b/packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/execution_layer/runtime_factory.py index 8e3540a99..5e0b3b1a5 100644 --- a/packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/execution_layer/runtime_factory.py +++ b/packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/execution_layer/runtime_factory.py @@ -4,7 +4,7 @@ 根据本机环境优先级自动选择 Docker/Podman/Nerdctl/Local 运行时。 """ -from ..config import SANDBOX_RUNTIME +from ..config import SANDBOX_ALLOW_LOCAL_RUNTIME, SANDBOX_RUNTIME from .docker_runtime import DockerRuntime from .local_runtime import LocalRuntime from .nerdctl_runtime import NerdctlRuntime @@ -15,6 +15,16 @@ from .utils import EnvironmentDetector class RuntimeFactory: """自动选择最佳沙箱运行时""" + @staticmethod + def _local_runtime() -> LocalRuntime: + if not SANDBOX_ALLOW_LOCAL_RUNTIME: + raise RuntimeError( + "LocalRuntime executes code on the host. Set " + "SANDBOX_RUNTIME=local and SANDBOX_ALLOW_LOCAL_RUNTIME=true " + "to opt in explicitly." + ) + return LocalRuntime() + @staticmethod def create(runtime_preference: str = None): """ @@ -42,7 +52,7 @@ class RuntimeFactory: ): return NerdctlRuntime() if runtime_preference == "local": - return LocalRuntime() + return RuntimeFactory._local_runtime() raise RuntimeError(f"指定的运行时不可用: {runtime_preference}") if EnvironmentDetector.is_docker_sdk_available(): @@ -59,4 +69,10 @@ class RuntimeFactory: return PodmanRuntime() if EnvironmentDetector.is_nerdctl_available(): return NerdctlRuntime() - return LocalRuntime() + if SANDBOX_ALLOW_LOCAL_RUNTIME: + return LocalRuntime() + raise RuntimeError( + "No container sandbox runtime is available. Install Docker, Podman, " + "or Nerdctl, or explicitly opt into host-local execution with " + "SANDBOX_RUNTIME=local and SANDBOX_ALLOW_LOCAL_RUNTIME=true." + ) diff --git a/packages/dbgpt-sandbox/tests/test_runtime_factory.py b/packages/dbgpt-sandbox/tests/test_runtime_factory.py new file mode 100644 index 000000000..0f3c04055 --- /dev/null +++ b/packages/dbgpt-sandbox/tests/test_runtime_factory.py @@ -0,0 +1,39 @@ +import pytest + +from dbgpt_sandbox.sandbox.execution_layer import runtime_factory +from dbgpt_sandbox.sandbox.execution_layer.local_runtime import LocalRuntime + + +def _disable_container_runtimes(monkeypatch): + detector = runtime_factory.EnvironmentDetector + monkeypatch.setattr( + detector, "is_docker_sdk_available", staticmethod(lambda: False) + ) + monkeypatch.setattr(detector, "is_podman_available", staticmethod(lambda: False)) + monkeypatch.setattr(detector, "is_nerdctl_available", staticmethod(lambda: False)) + + +def test_auto_runtime_fails_closed_without_container(monkeypatch): + _disable_container_runtimes(monkeypatch) + monkeypatch.setattr(runtime_factory, "SANDBOX_RUNTIME", None) + monkeypatch.setattr(runtime_factory, "SANDBOX_ALLOW_LOCAL_RUNTIME", False) + + with pytest.raises(RuntimeError, match="No container sandbox runtime"): + runtime_factory.RuntimeFactory.create() + + +def test_local_runtime_requires_explicit_opt_in(monkeypatch): + _disable_container_runtimes(monkeypatch) + monkeypatch.setattr(runtime_factory, "SANDBOX_ALLOW_LOCAL_RUNTIME", False) + + with pytest.raises(RuntimeError, match="LocalRuntime executes code on the host"): + runtime_factory.RuntimeFactory.create("local") + + +def test_local_runtime_can_be_enabled_explicitly(monkeypatch): + _disable_container_runtimes(monkeypatch) + monkeypatch.setattr(runtime_factory, "SANDBOX_ALLOW_LOCAL_RUNTIME", True) + + runtime = runtime_factory.RuntimeFactory.create("local") + + assert isinstance(runtime, LocalRuntime)