Files
dstalk/agents/audits/W13.3-network-audit.md
XiuChengWu 6f492489c6
Some checks failed
CI / Determine matrix (push) Has been cancelled
CI / ${{ matrix.os }} / ${{ matrix.build_type }} (push) Has been cancelled
W16: close CRITICAL/HIGH findings, integrate metadata gate, complete audit summaries (W16.1-W16.6)
- W16.1 (曹武): F-11.7-1 CLOSED — confirmed W12.4 fix, corrupt binary eliminated
- W16.2 (孙宇): F-11.1-1 FIXED — context_plugin.cpp try/catch on set_max_tokens + on_shutdown
- W16.3 (陈风): F-11.1-2 CLOSED — confirmed W12.1 fix, strdup OOM protection already in place
- W16.4 (胡桐): Integrate check_agents_metadata into refresh_status.py as pre-gate (error→exit 1)
- W16.5 (周岩): Add Findings Summary to W13.3 network audit, register 3 findings
- W16.6 (赵码): Add Findings Summary to W13.1+W13.2 AI audits, register 8 findings (4 already W14-fixed)

Build 0 error, ctest 4/4 pass, metadata check 0 error 0 warning.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 18:45:03 +08:00

9.0 KiB
Raw Blame History

W13.3 Network Plugin Audit

Auditor: 王测 (qa-wang) Date: 2026-05-27 File: plugins/network/src/network_plugin.cpp (322 行) Scope: plugin-abi.md §2 §3 §5 §8 §9, TLS, timeout, concurrency, resources


1. 跨 DLL 堆纪律 (§2 §3)

评级: A (完全合规)

逐行检查:

  • L259: g_host->strdup(result_body.c_str()) — 正确使用 host 堆分配,符 §3.2 模式 A。调用方 dstalk_free 释放。
  • L40-80 parse_headers_json: 内部 std::string / unordered_map,不跨 DLL 边界。
  • L118-123 Lambda capture: 内部 std::string,不跨边界。
  • 全文零处裸 malloc/free/strdup/new/delete

唯一跨边界分配 L259 使用 g_host->strdup,正确。

2. 异常安全 ABI (§5 §8)

评级: B (基础合规,缺 catch(...))

  • on_init (L296-303): 无线程局部 STL 操作,不会抛异常。安全。
  • do_post_stream (L140-254): 整个 I/O 逻辑包裹在 try { ... } catch (std::exception& e) (L251)。所有 std::string/unordered_map/std::function 使用均在保护范围内。
  • 缺失: L251 仅捕获 std::exception&,无 catch(...) 兜底。符 §8.2 推荐但不符最佳实践§8.2 正例含 catch(...))。
  • vtable 函数 http_post_json / http_post_stream 直接调用 do_post_stream,间接享受到保护。不直接暴露异常。
  • 若出现非 std::exception 派生的异常SEH / 自定义异常),将穿越 ABI → std::terminate()

3. 字符串返回值生命周期 (§9)

评级: A

  • L248: result_body = parser.get().body() — 复制到本地 std::string
  • L252-253: result_body = e.what()e.what() 的 C 字符串在 e 析构前已复制到 std::string,安全。
  • L259: *response_body = g_host->strdup(result_body.c_str()) — 在函数返回前用 host 堆复制,符 §9.2 模式 A拥有权转移。调用方负责释放。
  • 无悬垂指针 / 锁外返回 c_str() / 临时对象 c_str() 等 §9.3 反模式。

4. TLS 证书验证

评级: F (完全未启用)

// L87-93 — HttpClientCtx 构造函数
ssl::context ssl_ctx{ssl::context::tlsv12_client};
HttpClientCtx() {
    ssl_ctx.set_default_verify_paths();  // 设置了 CA 路径...
    // ★ 但从未调用 set_verify_mode()
}
  • set_verify_mode(ssl::verify_peer) 未被调用: 默认模式为 ssl::verify_none,接受任何证书(自签名、过期、域名不匹配)。
  • 无 hostname 验证: 即使启用 verify_peerOpenSSL 本身不做 hostname 匹配。需额外调用 SSL_set1_host() 或使用 boost::asio::ssl::host_name_verification。代码未做。
  • SNI 已设置 (L148): SSL_set_tlsext_host_name 调用存在,但仅影响 SNI 扩展,不含验证语义。
  • set_default_verify_paths 形同虚设: CA 路径已配置,但因验证模式未开启,永不使用。

影响: 所有 AI API 调用 (含 api_key 的 Authorization header) 可被中间人任意拦截。CVSS 7.4 (High)。

5. 超时 (connect / read / write)

评级: C (DNS 无超时可永久 hang)

阶段 位置 超时 状态
DNS 解析 L142 resolver.resolve() 缺失 — DNS 无响应则永久阻塞
TCP 连接 L154-156 connect_timeout (默认 30s) OK
SSL 握手 L160-163 connect_timeout (默认 30s) OK
HTTP 写入 L180-183 request_timeout (默认 120s) OK
HTTP 读头 L188-191 request_timeout (默认 120s) OK
流式读体 L218-219 request_timeout (默认 120s) OK (每轮循环重设)
非流式读体 L241-242 request_timeout (默认 120s) OK (每轮循环重设)
SSL 关闭 L250 stream.shutdown() 低风险ec 参数,不抛异常)

关键缺失: L142 的同步 resolver.resolve() 无法设置 socket 级超时stream 尚未创建)。需改用 async_resolve 或在独立线程中执行 resolver。

可配置性: L129-135 从 config service 读取 http.connect_timeout / http.request_timeout,有兜底默认值 (30s/120s)。

6. 错误码语义与重试

评级: B

  • L261: (result_code >= 200 && result_code < 300) ? 0 : -1 — 2xx 成功,其余返回 -1。
  • 调用方可通过 *status_code 区分: -1 = 连接/超时/DNS 错误4xx/5xx = HTTP 错误码。语义正确。
  • 无重试逻辑: 无任何 retry。对低层 HTTP 客户端这是合理设计,重试应由上层 (AI 插件) 实现。
  • 异常路径 (L251-254): result_code = -1, result_body = e.what() — 异常消息通过 response_body 传递给调用方,设计合理。
  • L221 / L244: if (ec) break; — 流/非流 read_some 错误静默退出循环,错误原因不记录日志(全文零 g_host->log 调用)。

7. 并发安全

评级: A

  • 每次 do_post_stream 调用创建新 HttpClientCtx (L125),内含独立 io_context。零共享状态。
  • g_host (L33): on_init 写入一次,后续只读。on_shutdown 不置空L305-307 为空函数体)。
  • g_config_svc (L34): 同上。
  • 无全局 socket / stream / resolver 共享。每个请求自包含。
  • 多线程并发调用 http_post_json 安全。

8. 资源清理

评级: A

  • HttpClientCtx (L125): 栈上对象,内含 RAII io_context + ssl::context。析构自动清理。
  • ssl_stream (L144), resolver (L141), flat_buffer (L145): 全部栈上 RAII。
  • 早期退出路径: goto done (L150, L214) — 栈展开正确析构所有对象。
  • 异常路径: catch(std::exception&) (L251) — 栈展开触发析构。
  • L249 cancel() + L250 stream.shutdown(ec) — 正常路径优雅关闭。

零泄漏路径,包括异常和提前退出。无动态分配(除 g_host->strdup 的返回值由调用方管理)。

9. 缓冲区限制 / DoS

评级: A

  • L187: parser.body_limit(16 * 1024 * 1024) — 16MB 响应体硬上限。防止恶意服务器返回无限大数据导致 OOM。
  • parse_headers_json 无长度限制 (L40-80): 理论上调用方可传入超大 JSON但 headers_json 来源为同进程内其他插件(可信),非网络输入。低风险。

TOP 3 严重问题

发现 1 — [严重] TLS 证书验证完全禁用 (L87-93)

问题: 从未调用 ssl_ctx.set_verify_mode(ssl::verify_peer),默认接受任何证书。无 hostname 验证。所有 API key 和通信内容可被 MITM 拦截。

修复方向: 添加 ssl_ctx.set_verify_mode(ssl::verify_peer) + ssl_ctx.set_verify_callback(ssl::host_name_verification(host)) 或在 handshake 后调用 SSL_get_peer_certificate + X509_check_host

发现 2 — [高] DNS 解析无超时 (L142)

问题: resolver.resolve(host, port) 同步调用,无法通过 expires_after 设置超时socket 尚不存在)。若 DNS 服务器不响应 → 线程永久阻塞 → 调用方 hang。

修复方向: 使用 async_resolve 并将 io_context 的 run()run_for() 与超时结合,或在独立 future/promise 线程中执行 resolution 并使用 wait_for。

发现 3 — [中] 异常处理缺 catch(...) 兜底 (L251)

问题: 仅捕获 std::exception&,无 catch(...)。非标准异常 → 穿越 C ABI → std::terminate()。虽然 Boost.Beast/Asio 的已知异常均派生自 std::exception,但 §8.2 要求双级 catch 全覆盖。

修复方向: 在 catch (std::exception&) 之后追加 catch (...) { result_code = -1; result_body = "unknown exception"; }


TLS 验证状态

未启用ssl_ctx.set_verify_mode 从未调用,默认 verify_none 接受一切证书。hostname 验证(SSL_set1_host / boost::asio::ssl::host_name_verification)也未实现。set_default_verify_paths 的调用形同虚设。

影响评估: 生产不可用。任何可访问网络路径的中间人可解密全部通信,包括 API 密钥、对话内容。在公共 WiFi 或不可信网络环境下,这是可直接利用的凭证泄露漏洞。


整体评级: C

维度 评级
跨 DLL 堆纪律 A
异常安全 ABI B
字符串返回生命周期 A
TLS 证书验证 F
超时 C
错误码语义 B
并发安全 A
资源清理 A
缓冲区限制 A
综合 C

总评: RAII、堆纪律、字符串生命周期、并发安全均高质量。但 TLS 证书验证完全禁用 (F) 是致命安全缺陷DNS 无超时可无限 hang。两个问题 (TLS + DNS) 使该插件在任何生产环境中不可用。修复后预期可达 A 级。


Findings Summary

ID Severity Title
F-13.3-1 CRITICAL TLS 证书验证完全禁用:set_verify_mode(ssl::verify_peer) 未调用,默认 verify_none 接受任何证书,无 hostname 验证 (L87-93)
F-13.3-2 HIGH DNS 解析无超时:resolver.resolve(host, port) 同步调用socket 未创建无法设超时DNS 无响应则线程永久阻塞 (L142)
F-13.3-3 MEDIUM 异常处理缺 catch(...) 兜底:仅捕获 std::exception&,非标准异常 (SEH/自定义) 穿越 C ABI → std::terminate() (L251)