行式输入安全(INV-LINE-01)
wesgine 统一处理行式输入的截断风险,避免 bufio.Scanner 默认上限导致的静默数据丢失。
核心问题
bufio.Scanner 超限时 Scan() 返回 false——与 EOF 同一个返回值:
for scanner.Scan() {
// "这一行太长"被读成"输入结束了"
// 循环正常退出,函数返回 nil
// 调用方得到更短的答案和成功的状态
}
三次犯同样的错
本仓曾在三处犯同样的错误:
| 位置 | 后果 |
|---|---|
| Cell 导入 | 丢弃 64KB 以上的行(数据丢失) |
| 邮件头解析 | 停在 Auto-Submitted: 之前,防环被长 References: 绕过(安全绕过) |
pip list | 停在首个长行,低报环境能力(能力误判) |
共同点不是"用了 Scanner",而是默认上限是别人给的数:64KB 从来不是谁决定的。
解决方案
内存输入
// 零拷贝、无上限
lines := bytes.Lines(data)
流式输入
// 超限报告而非结束流,下一个换行处重新同步
reader := lineio.NewReader(r, maxLineBytes)
internal/infra/lineio 的行为:
- 超限时报告,不是终止
- 下一个换行符处重新同步
- 调用方知道发生了截断
网络输入
读网络 body 必须显式 .Buffer(...):
scanner := bufio.NewScanner(conn)
scanner.Buffer(make([]byte, maxBytes), maxBytes)
上限一律命名常量并取自生产者上界:
client.MaxEventBytes= 32MBread工具单次可返回 10MB
同一条流只有一个名字。
截断处理策略
截断的处置必须显式选择:
| 场景 | 策略 |
|---|---|
| LLM delta(tool_call JSON 片段流) | 致命——丢一片拼不回来 |
| wesgine 事件(自包含) | 跳过该条继续——丢一条只赔一条 |
lineio 自身的约束
禁止在 internal/infra/lineio 内部用 Scanner:
- 会在每个迁移过来的调用方脚下重新装回上限
- 闸门单独设规则堵这条出口
回归保护
测试 fixture 必须含 vacuity guard:
- 断言 fixture 仍然超限
- 合法的 fixture 让测试恒绿而不再测任何东西
闸门
scripts/audit-lock/40-bounded-line-reads.sh
相关文档
- 子进程组管理 →
exec-process-group.md - Cell 导出/导入 →
cell-export-import.md - Exec 安全模型 →
exec-security-model.md