# OpenClaw Evaluation 文档第四轮审计结果

**审计日期**: 2026-04-25  
**审计对象**:

- `docs/adr/0003-openclaw-comprehensive-evaluation.md`
- `docs/specs/openclaw-evaluation.md`

**总体状态**: 可进入实现，建议先做 1 个安全小修。  
**第四轮重点**: 复核第三轮 OE-AUD3-001 至 OE-AUD3-008 是否闭合，并检查 v1.3 新增的 SSH 渲染、只读 fixture 拓扑、结构化 native audit 配置、路径匹配和 coverage 指标。

## 总体结论

v1.3 已基本闭合第三轮问题：

- SSH 执行契约已修正为“内部 argv 建模与校验，执行层经 `shlex.quote()` 渲染为字符串后交给 Paramiko”。
- `target-local-http` 已从 v1 中移除，server-audit active test 不再静默回退到目标侧写入/启动服务。
- `native_audit_command` 已改为 `native_audit_argv`。
- `allowed_paths` 已拆成 `prefix/exact/glob`。
- SFTP 写操作已默认禁用，并预留 per-run 目录模板。
- coverage 指标已改为 planned/executed/scored 口径。
- 文档版本与审计引用已同步到 v1.3。

剩余唯一需要在实现前修正的是路径 prefix 匹配示例：当前文档仍写 `realpath(p).startswith(realpath(allowed.path))`，这在路径边界上不安全。除此之外，其他问题都属于实现测试用例细化，不再阻塞阶段 A/B。

## Findings 摘要

| ID | 严重度 | 位置 | 结论 |
|---|---|---|---|
| OE-AUD4-001 | High | Spec:399-403 | `allowed_paths` 的 prefix 匹配示例使用 `startswith`，存在路径边界绕过风险 |
| OE-AUD4-002 | Low | Spec:367-374, 423-432 | 全局拒绝 shell 元字符会限制 `grep -E` 等合法模式，建议标为有意取舍 |
| OE-AUD4-003 | Low | Spec:163-169 | Pydantic 示例继续使用 `[]` 默认值，建议实现时改 `Field(default_factory=list)` |

## 详细发现

### OE-AUD4-001: Prefix 路径匹配需要边界感知

**严重度**: High  
**证据**: Spec 第 399-403 行说明：

```text
prefix: realpath(p).startswith(realpath(allowed.path))
```

普通字符串前缀匹配会误接受相邻路径。例如 `realpath("/tmp/openclaw2/secret")` 会以 `realpath("/tmp/openclaw")` 开头。如果实现照此写，路径 allowlist 可被边界相似目录绕过。

**建议**: 改为 `os.path.commonpath` 或等价边界感知校验：

```python
allowed_root = os.path.realpath(os.path.expanduser(rule.path))
candidate = os.path.realpath(os.path.expanduser(path))
if os.path.commonpath([allowed_root, candidate]) != allowed_root:
    reject()
```

同时要求 prefix rule 的 `allowed_root` 必须是目录；若目录不存在，按配置错误处理或显式允许“未来会创建”的只读路径。对 glob rule，也建议先校验 pattern 父目录，再用 `commonpath` 确认 realpath 未逃逸。

### OE-AUD4-002: Shell 元字符全局拒绝是合理但应显式标注取舍

**严重度**: Low  
**证据**: Spec 第 380-383 行要求最终 argv token 中出现 `|`、`&`、`;`、反引号、`$(`、`<`、`>`、换行就拒绝；第 423-432 行允许 `grep -E`，但正则模式里常见的 `|` 会被拒绝。

**影响**: 这是安全上可接受的保守策略，但实现者可能误以为 `grep -E "foo|bar"` 被支持。

**建议**: 在 SSH policy 注释中增加一句：“v1 故意不支持包含 shell 元字符的 grep pattern；需要复杂匹配时先拉取允许路径内的受限文件列表，再在 evaluator 本地分析。”

### OE-AUD4-003: 示例模型建议避免 mutable defaults

**严重度**: Low  
**证据**: Spec 第 163-169 行 `observations: list[Observation] = []`，前文 `outbound_requests: list[dict] = []`、`tags: list[str] = []` 也有类似写法。

Pydantic v2 通常会处理可变默认值，但这仍容易被实现者复制到 dataclass 或普通 Python 类里形成共享状态。

**建议**: 规格示例统一改为：

```python
observations: list[Observation] = Field(default_factory=list)
```

其他 list/dict 字段同理。这是代码质量建议，不阻塞架构实现。

## 第三轮 findings 关闭状态

| 第三轮 ID | 状态 | 说明 |
|---|---|---|
| OE-AUD3-001 | 关闭 | 已承认 Paramiko 只接收字符串，并定义 argv 校验 + `shlex.quote` 渲染 |
| OE-AUD3-002 | 关闭 | `target-local-http` 已从 v1 移除，fixture-server capability 留到未来审计 |
| OE-AUD3-003 | 关闭 | `native_audit_command` 已改为 `native_audit_argv` |
| OE-AUD3-004 | 部分关闭 | 路径规则已结构化，但 prefix 示例需改为 `commonpath` |
| OE-AUD3-005 | 关闭 | SFTP 写操作默认禁用，并引入 per-run path template |
| OE-AUD3-006 | 关闭 | coverage 已改为 planned/executed/scored |
| OE-AUD3-007 | 关闭 | ADR 与规格版本引用已同步到 v1.3，并引用三轮审计 |
| OE-AUD3-008 | 关闭 | 占位符替换与元字符检测顺序已明确 |

## 外部核验来源

- GitHub Advisory GHSA-g8p2-7wf7-98mq / CVE-2026-25253: <https://github.com/advisories/GHSA-g8p2-7wf7-98mq>
- OpenClaw CLI security audit docs: <https://docs.openclaw.ai/cli/security>
- OpenClaw audit check catalog: <https://docs.openclaw.ai/gateway/security/audit-checks>
- Paramiko `SSHClient.exec_command` API docs: <https://docs.paramiko.org/en/3.1/api/client.html>

## 建议下一步

做一个很小的 v1.3 patch：

1. 将 prefix 路径校验从 `startswith` 改为 `os.path.commonpath`。
2. 在 SSH policy 注释中说明 `grep` pattern 的安全限制。
3. 把 Pydantic list/dict 示例改为 `Field(default_factory=...)`。

完成后，文档可以作为 v0.2.0 阶段 A/B 的实现依据。
