|
14 | 14 | from pathlib import Path |
15 | 15 | from typing import Any, List, Optional, Tuple |
16 | 16 |
|
17 | | -from .sandbox_types import FsChange, SandboxResult |
| 17 | +from .sandbox_types import FsChange, SandboxResult, SandboxSecurityResult |
18 | 18 |
|
19 | 19 | _MOUNTINFO_ESC_RE = re.compile(r"\\([0-7]{3})") |
20 | 20 |
|
@@ -945,95 +945,11 @@ def simulate( |
945 | 945 | ) |
946 | 946 |
|
947 | 947 |
|
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 | | - |
1031 | 948 | __all__ = [ |
1032 | 949 | "FsChange", |
1033 | 950 | "SandboxResult", |
1034 | 951 | "SandboxConfig", |
1035 | 952 | "SandboxExecutor", |
1036 | | - "SandboxSecurity", |
1037 | 953 | "SandboxSecurityResult", |
1038 | 954 | "SandboxUnavailableError", |
1039 | 955 | ] |
0 commit comments