Skip to content

Commit e798b08

Browse files
committed
Refine IPC sandbox fallback handling
1 parent 26048ca commit e798b08

7 files changed

Lines changed: 368 additions & 303 deletions

File tree

src/aish/security/sandbox.py

Lines changed: 1 addition & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from pathlib import Path
1515
from typing import Any, List, Optional, Tuple
1616

17-
from .sandbox_types import FsChange, SandboxResult
17+
from .sandbox_types import FsChange, SandboxResult, SandboxSecurityResult
1818

1919
_MOUNTINFO_ESC_RE = re.compile(r"\\([0-7]{3})")
2020

@@ -945,95 +945,11 @@ def simulate(
945945
)
946946

947947

948-
# ---------------------------------------------------------------------------
949-
# 高层封装:SandboxSecurity
950-
# ---------------------------------------------------------------------------
951-
952-
953-
@dataclass
954-
class SandboxSecurityResult:
955-
"""综合沙箱执行 + (未来的)风险评估后的结果。
956-
957-
当前阶段仅包含 SandboxResult,本身不再直接耦合旧的 RiskEngine。
958-
"""
959-
960-
command: str
961-
cwd: Path
962-
sandbox: SandboxResult
963-
964-
965-
class SandboxSecurity:
966-
"""高层封装:给定命令和 cwd,执行沙箱并返回 SandboxResult。
967-
968-
注意:
969-
- 默认情况下仅使用 repo_root 构造一个最小 SandboxConfig;
970-
- 上层安全管理模块可以根据 SecurityPolicy 计算出更精细的
971-
SandboxConfig 并通过 ``config`` 参数传入,以实现基于规则的
972-
只读/读写挂载白名单控制。
973-
"""
974-
975-
def __init__(
976-
self,
977-
repo_root: Path,
978-
enabled: bool = True,
979-
config: Optional[SandboxConfig] = None,
980-
) -> None:
981-
self._repo_root = repo_root.resolve()
982-
self._enabled = enabled
983-
self._warned_unavailable = False
984-
985-
effective_config = config or SandboxConfig(repo_root=self._repo_root)
986-
self._executor = SandboxExecutor(effective_config)
987-
988-
@property
989-
def enabled(self) -> bool:
990-
return self._enabled
991-
992-
def set_enabled(self, enabled: bool) -> None:
993-
self._enabled = enabled
994-
995-
def run(
996-
self, command: str, cwd: Optional[Path] = None
997-
) -> Optional[SandboxSecurityResult]:
998-
"""在沙箱中执行命令并返回结果。
999-
1000-
如果未启用(enabled=False),则返回 None。
1001-
"""
1002-
1003-
if not self._enabled:
1004-
return None
1005-
1006-
cwd = (cwd or self._repo_root).resolve()
1007-
stripped_command, sudo_detected, ok = strip_sudo_prefix(command)
1008-
if sudo_detected:
1009-
if not ok:
1010-
raise SandboxUnavailableError(
1011-
"sandbox_execute_failed", details="missing_command"
1012-
)
1013-
command = stripped_command
1014-
try:
1015-
sandbox_result = self._executor.simulate(command, cwd=cwd)
1016-
except SandboxUnavailableError:
1017-
# Disable sandbox for the rest of this process/session to avoid
1018-
# repeated mount/bwrap failures flooding stderr.
1019-
self._enabled = False
1020-
# UI 提示由上层安全管理器统一处理(Rich Panel)。
1021-
self._warned_unavailable = True
1022-
raise
1023-
1024-
return SandboxSecurityResult(
1025-
command=command,
1026-
cwd=cwd,
1027-
sandbox=sandbox_result,
1028-
)
1029-
1030-
1031948
__all__ = [
1032949
"FsChange",
1033950
"SandboxResult",
1034951
"SandboxConfig",
1035952
"SandboxExecutor",
1036-
"SandboxSecurity",
1037953
"SandboxSecurityResult",
1038954
"SandboxUnavailableError",
1039955
]

src/aish/security/sandbox_ipc.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Any, Optional
99

1010
from .sandbox import DEFAULT_SANDBOX_SOCKET_PATH, SandboxUnavailableError
11-
from .sandbox_types import FsChange, SandboxResult
11+
from .sandbox_types import FsChange, SandboxResult, SandboxSecurityResult
1212

1313

1414
class SandboxIpcClient:
@@ -126,7 +126,7 @@ def simulate(self, *, command: str, cwd: Path, repo_root: Path) -> SandboxResult
126126

127127

128128
class SandboxSecurityIpc:
129-
"""A SandboxSecurity-compatible wrapper that delegates simulate() to a privileged daemon via IPC."""
129+
"""Main-process sandbox runner that delegates execution to the privileged daemon via IPC."""
130130

131131
def __init__(
132132
self,
@@ -148,9 +148,7 @@ def set_enabled(self, enabled: bool) -> None:
148148
self._enabled = enabled
149149

150150
def run(self, command: str, cwd: Optional[Path] = None):
151-
# Keep return type aligned with SandboxSecurity.run() to minimize caller changes.
152-
from .sandbox import SandboxSecurityResult
153-
151+
# Keep the shared SandboxSecurityResult contract for callers.
154152
if not self._enabled:
155153
return None
156154

src/aish/security/sandbox_types.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from __future__ import annotations
33

44
from dataclasses import dataclass
5+
from pathlib import Path
56
from typing import Dict, List, Optional
67

78

@@ -37,3 +38,12 @@ class SandboxResult:
3738
stdout_truncated: bool = False
3839
stderr_truncated: bool = False
3940
changes_truncated: bool = False
41+
42+
43+
@dataclass
44+
class SandboxSecurityResult:
45+
"""Shared sandbox execution result for main-process callers."""
46+
47+
command: str
48+
cwd: Path
49+
sandbox: SandboxResult

0 commit comments

Comments
 (0)