保留工具桥和验证证据错误

保留图片证据读取和哈希底层原因

补充 Direct 工具错误展示约定
This commit is contained in:
kdletters
2026-10-05 19:08:01 +08:00
parent f4414a5ac5
commit f2946aaeca
4 changed files with 18 additions and 15 deletions
@@ -1053,15 +1053,15 @@ fn bridge_resource_request_fingerprint(input: &DirectResourceGenerationInput) ->
fn bridge_png_content(root: &Path, path: &Path) -> Result<String, String> {
let root = root
.canonicalize()
.map_err(|_| "工具桥项目根无法安全解析".to_string())?;
.map_err(|error| format!("工具桥项目根无法安全解析:{error}"))?;
let path = path
.canonicalize()
.map_err(|_| "工具桥图片不存在".to_string())?;
.map_err(|error| format!("工具桥图片不存在或无法解析:{error}"))?;
if !path.starts_with(&root) {
return Err("工具桥图片越出当前项目边界".to_string());
}
let metadata =
std::fs::symlink_metadata(&path).map_err(|_| "读取工具桥图片失败".to_string())?;
std::fs::symlink_metadata(&path).map_err(|error| format!("读取工具桥图片失败:{error}"))?;
if metadata.file_type().is_symlink()
|| !metadata.is_file()
|| metadata.len() > DIRECT_TOOL_BRIDGE_MAX_IMAGE_BYTES
@@ -1071,7 +1071,7 @@ fn bridge_png_content(root: &Path, path: &Path) -> Result<String, String> {
let (mut file, _) = open_project_snapshot_regular_file(&path, "工具桥图片")?;
let mut bytes = Vec::new();
file.read_to_end(&mut bytes)
.map_err(|_| "读取工具桥图片失败".to_string())?;
.map_err(|error| format!("读取工具桥图片失败:{error}"))?;
if !bytes.starts_with(b"\x89PNG\r\n\x1a\n") {
return Err("工具桥图片不是有效 PNG".to_string());
}
@@ -104,17 +104,17 @@ fn evidence_hashes(root: &Path, result: &Value) -> Result<Value, String> {
return Err("validation-evidence: 证据不属于受控验证目录".into());
}
let path = resolve_local_project_path(root, relative)?;
let meta =
std::fs::symlink_metadata(&path).map_err(|_| "validation-evidence: 证据文件丢失")?;
let meta = std::fs::symlink_metadata(&path)
.map_err(|error| format!("validation-evidence: 证据文件丢失:{error}"))?;
if !meta.is_file() || meta.file_type().is_symlink() || meta.len() > 16 * 1024 * 1024 {
return Err("validation-evidence: 证据类型或大小无效".into());
}
let mut bytes = Vec::new();
std::fs::File::open(path)
.map_err(|_| "validation-evidence: 无法读取证据")?
.map_err(|error| format!("validation-evidence: 无法读取证据:{error}"))?
.take(16 * 1024 * 1024 + 1)
.read_to_end(&mut bytes)
.map_err(|_| "validation-evidence: 无法读取证据")?;
.map_err(|error| format!("validation-evidence: 无法读取证据:{error}"))?;
if bytes.len() > 16 * 1024 * 1024 {
return Err("validation-evidence: 证据文件在读取时超限".into());
}
@@ -234,14 +234,15 @@ fn fingerprint(root: &Path, include_outputs: bool) -> Result<String, String> {
let mut bytes = 0u64;
let mut visited = 0usize;
while let Some(directory) = directories.pop() {
for entry in
std::fs::read_dir(&directory).map_err(|_| "validation-fingerprint: 读取项目失败")?
for entry in std::fs::read_dir(&directory)
.map_err(|error| format!("validation-fingerprint: 读取项目失败:{error}"))?
{
visited += 1;
if visited > 20_000 {
return Err("validation-fingerprint: 项目文件数超限".into());
}
let entry = entry.map_err(|_| "validation-fingerprint: 读取目录项失败")?;
let entry = entry
.map_err(|error| format!("validation-fingerprint: 读取目录项失败:{error}"))?;
let name = entry.file_name().to_string_lossy().to_ascii_lowercase();
if matches!(
name.as_str(),
@@ -264,7 +265,7 @@ fn fingerprint(root: &Path, include_outputs: bool) -> Result<String, String> {
}
let ty = entry
.file_type()
.map_err(|_| "validation-fingerprint: 读取类型失败")?;
.map_err(|error| format!("validation-fingerprint: 读取类型失败:{error}"))?;
let path = entry.path();
if !include_outputs
&& name == "dist"
@@ -300,14 +301,14 @@ fn fingerprint(root: &Path, include_outputs: bool) -> Result<String, String> {
if name.starts_with("game-creator.config") {
continue;
}
let mut file =
std::fs::File::open(&path).map_err(|_| "validation-fingerprint: 打开输入失败")?;
let mut file = std::fs::File::open(&path)
.map_err(|error| format!("validation-fingerprint: 打开输入失败:{error}"))?;
let mut digest = Sha256::new();
let mut buffer = [0u8; 64 * 1024];
loop {
let read = file
.read(&mut buffer)
.map_err(|_| "validation-fingerprint: 读取输入失败")?;
.map_err(|error| format!("validation-fingerprint: 读取输入失败:{error}"))?;
if read == 0 {
break;
}
@@ -117,6 +117,7 @@ DirectProject 已经解决过同一类问题([`【ADR】DirectProject命令接
- Codex Provider proxy 的请求体读取/JSON 改写、上游请求、响应构造和 SSE 传输错误也必须把 serde、join、reqwest 或流错误写入返回正文,不能只回 `provider upstream unavailable`。
- 浏览器健康检查的发现、临时目录、配置、DevTools 版本请求和清理失败保留底层错误/超时阶段;稳定浏览器码用于分类,但不能覆盖诊断正文。
- WebView 普通错误出口遇到 Tauri 的结构化对象时,优先展示 `message/detail/reason/code`,再有界序列化对象;不得把对象转成 `[object Object]` 后回落“未提供具体错误正文”。
- Direct 工具桥图片读取和真实试玩验证证据的 OS 读取/哈希错误保留底层原因;路径越界、私有路径和安全拒绝仍使用稳定安全码。
- 浏览器启动/DevTools 握手与 Codex model-catalog 子进程失败保留 stderr、退出状态、解析错误和阶段;稳定机器码仍用于分类,但不能单独成为用户可见正文。
- Node/npm 环境探测和 Web scaffold 构建持续排空并保存有界 stdout/stderr 尾部,失败返回退出状态、超时阶段和脱敏正文;环境预检回执同时下发 `code` 与 `diagnostic`。
- HTTP 409 只有明确包含泥点不足事实时才映射为 `paidCreditsInsufficient`;Claude Code 的普通 409 冲突保留为 `upstreamFailed`。
@@ -20,6 +20,7 @@ DirectProject 回合失败在确认不是客户端内部不可归类故障时,
- Codex Provider proxy 的请求体改写、上游发送、响应构造和 SSE 错误保留底层正文,避免代理层再次把具体网络/解析故障压成单一 unavailable。
- 浏览器健康检查不再把发现、配置、DevTools 版本和清理失败只返回机器码;阶段码保留,同时带底层错误或明确的超时预算。
- WebView 结构化对象错误保留 `message/detail/reason/code` 或有界 JSON 正文,避免 `[object Object]` 触发普通错误兜底。
- Direct 工具桥图片与真实试玩证据读取/哈希错误保留 OS 正文,安全边界拒绝继续使用稳定码。
- Transport / Stream / IPC / host process / memory exhaustion 的回归测试与错误事件证据。
## 不做