> For the complete documentation index, see [llms.txt](https://yeasy.gitbook.io/harness_engineering_guide/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yeasy.gitbook.io/harness_engineering_guide/di-si-bu-fen-an-quan-ping-gu-yu-yan-jin/12_security/12.5_miniharness_security.md).

# 12.5 实战：MiniHarness 安全层集成

本节为 MiniHarness 集成可运行的教学安全层。核心设计是“分层防护：权限 → 护栏 → 批准 → 执行”。

> 完整代码见 `lab/mini_harness/security/`，本节聚焦四层防护的设计与集成。

## 12.5.1 第一层：权限决策（权限模型映射表）

MiniHarness 采用 **4 级简化权限模型**，下表展示其与 12.2 的 6 级梯度的映射关系：

| MiniHarness 4 级 | 12.2 六级模型                              | 语义       | 适用场景           |
| --------------- | -------------------------------------- | -------- | -------------- |
| DENY            | DENY                                   | 永不允许     | 禁止操作(rm -rf /) |
| ASK             | APPROVE\_ALWAYS + ASK\_FIRST           | 每次/首次询问  | 敏感操作需人工确认      |
| AUTO            | AUTO\_WITH\_NOTIFICATION + FULL\_TRUST | 已批准后自动执行 | 低风险日常操作        |
| OVERRIDE        | OVERRIDE                               | 管理员强制执行  | 紧急故障恢复(仅管理员)   |

**重要声明**：生产系统应采用 12.2 的 6 级梯度以实现精细化权限控制；此处 4 级为 MiniHarness 教学框架的简化版本，便于初学者理解权限决策的核心逻辑。

权限引擎根据四个权限等级做决策。设计简化为权限策略 + 审计日志：

```python
class PermissionLevel(Enum):
    DENY = 0       # 永不允许
    ASK = 1        # 每次询问用户
    AUTO = 2       # 已批准后自动放行(可缓存之前的批准)
    OVERRIDE = 3   # 管理员覆盖

class PermissionDecisionEngine:
    DEFAULT_APPROVAL_SCOPE = "__tool__"

    def register_policy(self, tool_name: str, level: PermissionLevel):
        """为工具注册权限策略"""
        self.policies[tool_name] = level

    async def decide(
        self,
        tool_name: str,
        user_id: str,
        approval_scope: str | None = None,
    ) -> Decision:
        """返回 ALLOW / DENY / ASK_USER"""
        level = self.policies.get(tool_name, PermissionLevel.ASK)

        if level == PermissionLevel.DENY:
            return Decision.DENY

        elif level == PermissionLevel.AUTO:
            # 检查用户曾否批准过
            if self._was_approved_before(user_id, tool_name, approval_scope):
                return Decision.ALLOW
            else:
                return Decision.ASK_USER

        elif level == PermissionLevel.ASK:
            return Decision.ASK_USER

        else:  # OVERRIDE
            return Decision.ALLOW

    def _approval_key(self, user_id: str, tool_name: str, approval_scope: str | None):
        scope = approval_scope or self.DEFAULT_APPROVAL_SCOPE
        return (user_id, tool_name, scope)

    def _was_approved_before(
        self,
        user_id: str,
        tool_name: str,
        approval_scope: str | None = None,
    ) -> bool:
        return self._approval_key(user_id, tool_name, approval_scope) in self.user_approvals

    def record_approval(
        self,
        user_id: str,
        tool_name: str,
        approval_scope: str | None = None,
    ):
        self.user_approvals.add(self._approval_key(user_id, tool_name, approval_scope))
```

核心设计：策略驱动 + 参数级用户批准缓存，避免对同一低风险调用重复询问，同时避免不同参数复用旧批准。审计日志记录所有决策。

完整实现参见 `lab/mini_harness/security/permissions.py`。

## 12.5.2 第二层：路径校验

Agent 对用户提供的文件路径必须防御路径穿越攻击。五层递进式防护：

```python
class PathValidator:
    def validate(self, user_path: str) -> str:
        """5层递进式校验"""
        # 第1层：长度检查(防止缓冲区溢出)
        if len(user_path) > 4096:
            raise ValueError("路径过长")

        # 第2层：迭代URL解码(防止多重编码)
        path = self._decode_all_encodings(user_path)

        # 第3层：Unicode规范化(防止Unicode绕过)
        path = unicodedata.normalize('NFC', path)

        # 第4层：路径规范化(移除.. / 冗余 /)
        path = posixpath.normpath(path)

        # 第5层：符号链接解析 + 边界检查
        base = os.path.realpath(self.base_path)
        candidate = os.path.realpath(os.path.join(base, path))
        if os.path.commonpath([base, candidate]) != base:
            raise ValueError("路径穿越")

        return candidate
```

关键点：

* **不信任用户输入**：URL编码、Unicode、符号链接都可能被攻击者利用
* **白名单优于黑名单**：只允许特定目录（如 `/tmp/agent/data`），而非禁止特定目录
* **冗余检查**：即使一层被绕过，其他层仍可防护

完整实现参见 `lab/mini_harness/security/path_validator.py`。

## 12.5.3 第三层：命令护栏

对 bash 等命令执行工具，必须检测并阻止危险命令：

```python
class DangerousCommandDetector:
    DANGEROUS_COMMANDS = {
        "rm", "dd", "mkfs", "reboot", "shutdown", "chmod",
        "sudo", "passwd", "useradd", "cryptsetup"
    }
    SHELL_CONTROL_OPERATORS = ("&&", "||", ";", "\n", "\r", "$(", "`")
    INLINE_EXEC_FLAGS = {
        "bash": {"-c"},
        "sh": {"-c"},
        "python": {"-c"},
        "python3": {"-c"},
        "node": {"-e", "--eval"},
    }

    def detect(self, command: str) -> bool:
        """检测是否包含危险命令"""
        tokens = shlex.split(command)
        if any(op in command for op in self.SHELL_CONTROL_OPERATORS):
            return True
        main_cmd = tokens[0].split('/')[-1]  # 移除路径前缀

        # 检查主命令是否在黑名单中
        if main_cmd in self.DANGEROUS_COMMANDS:
            return True

        # 阻止通过 shell / 解释器包装隐藏的第二层命令
        if any(flag in tokens[1:] for flag in self.INLINE_EXEC_FLAGS.get(main_cmd, set())):
            return True

        # 检查管道中的命令(如 ls | rm)
        for piped_cmd in self._extract_piped_commands(command):
            if piped_cmd in self.DANGEROUS_COMMANDS:
                return True

        return False
```

设计策略：

* **静态黑名单**：rm、dd、reboot 等永不允许
* **管道检测**：`cat file | rm -f /` 中的 rm 仍被检测
* **Shell控制符检测**：`&&`、`;`、`$()`、反引号等会被保守阻止，避免危险命令藏在第二段命令中
* **解释器包装检测**：默认阻止 `bash -c`、`sh -c`、`python -c`、`node -e` 等内联执行入口，避免黑名单只检查第一层命令而被绕过
* **子命令白名单**：某些包管理器（apt）仅允许 list/search（不允许 remove）

完整实现参见 `lab/mini_harness/security/guardrails.py`。

## 12.5.4 第四层：安全执行器集成

将权限、护栏、批准三层整合在执行器中：

```python
from mini_harness.security.secure_executor import SecureToolExecutor, ToolCall

secure_executor = SecureToolExecutor(
    permission_engine=permission_engine,
    path_validator=path_validator,
    command_detector=command_detector,
    user_approval_callback=ask_user,
)

async def perform_call(approved_call: ToolCall):
    """只有前三层全部通过后才会进入这个真实执行函数。"""
    tool = registry.get(approved_call.tool_name)
    return await tool.call(approved_call.args)

result = await secure_executor.execute(
    ToolCall(
        tool_name="file_read",
        args={"path": "README.md"},
        user_id="user-1",
    ),
    perform_call,
)
```

`execute()` 的第二个参数是实际副作用函数。权限为 DENY 时，它不会被调用；路径校验会在调用前把参数替换为已验证的绝对路径。`HarnessApplication` 也使用同一个入口，因此本地工具和 MCP 工具不会形成两条安全强度不同的调用路径。检查点回放同样位于该安全入口之后：每次回放都重新授权，保存的结果绑定原 `user_id`，不同 principal 不能复用同一会话调用 ID 读取结果。

四层是逐级递进的防线，任何层失败都会阻止执行。当前批准回调是同步函数；如果产品通过网络或 UI 异步征求批准，应在进入执行器前完成交互，或扩展为明确的异步回调接口。

完整实现参见 `lab/mini_harness/security/secure_executor.py` 与 `lab/mini_harness/application.py`。

## 12.5.5 部署检查清单

* [ ] 权限策略已配置（`file_read=AUTO`，`bash_exec=ASK`，删除类工具为 `DENY`）
* [ ] 路径白名单已设置（仅允许 `/tmp/agent/data` 等安全目录）
* [ ] 危险命令黑名单已更新（rm、dd、reboot 等）
* [ ] 审计日志已启用（记录所有权限决策）
* [ ] 用户批准流程已接入（手机通知、邮件等）
* [ ] 测试路径穿越（../、..%2f、..%252f 等编码攻击）
* [ ] 测试权限缓存（同一用户、同一工具、同一参数指纹，第二次才可自动批准）

## 12.5.6 总结

MiniHarness 安全架构的四大防线：权限决策 → 路径校验 → 命令护栏 → 安全执行，形成深度防御。
