From 28ae90a6cc4d9654bcc9a0ce020faa2a62d10ada Mon Sep 17 00:00:00 2001 From: XiuChengWu <732857315@qq.com> Date: Wed, 3 Jun 2026 17:56:45 +0800 Subject: [PATCH] W23: close mailroom metadata and network validation tests - Refresh agents STATUS to W22.6 and exclude mailroom from metadata scans - Add mailroom dispatch checklist and defensive rules - Register F-23.D-1 and tag network input validation defense-in-depth - Update network plugin tests for header length limits - Fix LSP test metadata and remove orphan anthropic_internal.hpp Verification: - cmake --build build --config Release: 0 error, 0 warning - ctest --test-dir build --output-on-failure: 10/10 passed - ctest --test-dir build -R dstalk_smoke_test --output-on-failure: passed - python scripts/check_agents_metadata.py --strict: passed Co-Authored-By: Claude Opus 4.8 --- agents/POSTMORTEM.md | 2 + agents/PROMPT_TEMPLATE.md | 3 +- agents/STATUS.md | 28 +++++------ agents/audits/findings-registry.md | 5 +- plugins_middle/network/src/network_plugin.cpp | 24 ++++++++-- .../anthropic/src/anthropic_internal.hpp | 46 ------------------- scripts/check_agents_metadata.py | 12 ++--- scripts/refresh_status.py | 2 +- tests/CMakeLists.txt | 2 +- tests/lsp_plugin_test.cpp | 2 + tests/network_plugin_test.cpp | 8 ++-- 11 files changed, 55 insertions(+), 79 deletions(-) delete mode 100644 plugins_upper/anthropic/src/anthropic_internal.hpp diff --git a/agents/POSTMORTEM.md b/agents/POSTMORTEM.md index 9c8e7bf..d12f8a5 100644 --- a/agents/POSTMORTEM.md +++ b/agents/POSTMORTEM.md @@ -162,6 +162,8 @@ | R-LOADER-CONTINUE | PM-004 | 插件加载/初始化永不 fail-fast,单点故障不级联阻断 | | R-LOADER-RETVAL | PM-004 | `initialize_all` 返回值 >0 = 部分成功,调用方必须区分 | | R-NO-FORCE-PUSH | PM-005 | 子代理 prompt 必须禁止 `git push --force` / `--force-with-lease` | +| R-MAIL-SCOPE | Mailroom v1 | 子代理不得读写超出自身 inbox 之外的他人邮箱内容,仅可投递新邮件 | +| R-MAIL-NO-DELETE | Mailroom v1 | 接收者不可物理删除邮件,只能移到 `agents/mailroom/archive/W/`;删除仅限 CEO | --- diff --git a/agents/PROMPT_TEMPLATE.md b/agents/PROMPT_TEMPLATE.md index 457c1df..76462f2 100644 --- a/agents/PROMPT_TEMPLATE.md +++ b/agents/PROMPT_TEMPLATE.md @@ -186,7 +186,7 @@ cmake --build build --config Release && ctest --test-dir build -C Release --- -## CEO 派活前 4 步检查 +## CEO 派活前 5 步检查 派活前逐项确认,缺一不可: @@ -196,6 +196,7 @@ cmake --build build --config Release && ctest --test-dir build -C Release | 2 | **找前序 Wave 产出** | 从 `agents/*/profile.md` 的 performance_log 追溯与本任务关联的之前 Wave 的产出文件 | `前序成果` | | 3 | **设定任务范围三档** | 把需求拆成"必做/可做/不做",不做 = 硬边界 | `任务范围` | | 4 | **统一字数上限** | 固定 250 字,不再按任务调整 | `字数上限` | +| 5 | **扫候选执行者 inbox** | 查看 `agents/mailroom/inbox//` 是否有 pending handoff / blocker;有阻塞先处理阻塞 | `前序成果` / `禁忌` | --- diff --git a/agents/STATUS.md b/agents/STATUS.md index e3b96c3..a95363d 100644 --- a/agents/STATUS.md +++ b/agents/STATUS.md @@ -1,6 +1,6 @@ # dstalk 实时编制状态 -> **最后更新**: 2026-05-31 +> **最后更新**: 2026-06-03 > **数据来源**: 由 `scripts/refresh_status.py` 自动扫描全部 16 个 `agents/*/profile.md` + 5 个 `agents/groups/*.md` 生成。 ## 表 1:员工状态(16 人) @@ -8,20 +8,20 @@ | Agent ID | 姓名 | 角色 | 最近一次贡献 | perf_log | 当前小组 | 状态 | |---|---|---|---|---|---|---| | architect-huang | 黄岭 | 架构师 | W13.4 深度审计 lsp_plugin.cpp (749行) | 3 | -- | idle | -| architect-lin | 林深 | 架构师 | W14.4 诊断 W12.2 双 store 整合未生效根因——测试加载了 build/tests/plugins/ 下 pre-W12.2 的旧 DLL | 8 | grp-ai-plugins, grp-quality-core | idle | +| architect-lin | 林深 | 架构师 | W22.6 plugin_loader 新增 validate_dependencies() —— 遍历所有已加载插件 deps[] 做缺失依赖检测 ... | 11 | grp-ai-plugins, grp-quality-core | idle | | architect-yang | 杨帆 | 架构师 | W15.7 根据 W15.4 审查发现修复 WORKFLOW.md 3 处交叉引用 | 6 | -- | idle | -| designer-zhu | 朱晴 | UX/CLI 设计师 | W10.3 创建 agents/PROMPT_TEMPLATE.md 子代理 prompt 模板(约 170 行) | 2 | grp-cli-ux | idle | -| devops-hu | 胡桐 | DevOps 工程师 | W15.3 设计 agents/ 目录元数据自检机制 (scripts/check_agents_metadata.py) | 6 | grp-build-matrix | idle | -| devops-ma | 马奔 | DevOps 工程师 | 落地 CI pipeline (GitHub Actions) | 2 | grp-build-matrix | idle | -| engineer-chen | 陈风 | 工程师 | W11.2 审计 config_plugin / ConfigStore 职责划分与跨 DLL 堆合规 | 4 | -- | idle | +| designer-zhu | 朱晴 | UX/CLI 设计师 | W19.4 定义 CLI 退出码语义 — 0=正常退出, 1=用户中断(SIGINT/Ctrl+C), 2=致命错误 | 4 | grp-cli-ux | idle | +| devops-hu | 胡桐 | DevOps 工程师 | W22.1 测试覆盖率度量 + CI 门禁 | 12 | grp-build-matrix | idle | +| devops-ma | 马奔 | DevOps 工程师 | W19.5 CI 跨平台构建矩阵 Phase 1 -- YAML 语法验证 + VS 路径修复 | 4 | grp-build-matrix | idle | +| engineer-chen | 陈风 | 工程师 | W19.2 修复 plugin_loader 4 条 MEDIUM 发现 (F-18.3-2/3/4/5) | 6 | -- | idle | | engineer-li | 李明 | 工程师 | W12.5 使用 scripts/refresh_status.py 重新生成 agents/STATUS.md (46行) | 4 | -- | idle | -| engineer-sun | 孙宇 | 工程师 | W14.2 修复 lsp_plugin.cpp 致命死锁 (W13.4 审计发现) + vtable 异常包装 | 4 | -- | idle | +| engineer-sun | 孙宇 | 工程师 | W22.4 prompt stdin pipe 打通 -- --prompt 无参数或参数为 - 时从 stdin 读取 | 8 | -- | idle | | engineer-zhao | 赵码 | 工程师 | W9.6 CLI新增/history[N]命令,含三种边界处理;/status增加history count | 6 | grp-ai-plugins, grp-cli-ux | idle | -| engineer-zhou | 周岩 | 工程师 | W16.5 W13.3 网络审计报告补充 Findings Summary | 5 | -- | idle | -| qa-liu | 刘静 | 质量工程师 | W11.3 event_bus 单元测试 (6 cases, tests/event_bus_test.cpp) + service_registry... | 3 | grp-security-audit | idle | -| qa-wang | 王测 | 质量工程师 | W15.8 根据 W15.5 审查发现修复 §14 内部问题 + PROMPT_TEMPLATE 缺失标注 | 9 | grp-cli-ux, grp-quality-core | idle | -| qa-xu | 徐磊 | 质量工程师 | W13.6 扩展 tests/smoke_test.cpp (430→623 行, +193): 新增 4 个回归保护 case — R1 conte... | 5 | grp-security-audit | idle | -| security-cao | 曹武 | 安全工程师 | W14.3 修复 W13.5 审计发现 — 路径遍历 + 全局状态加锁 + 9 vtable try/catch | 5 | grp-security-audit | idle | +| engineer-zhou | 周岩 | 工程师 | W22.5 get_default_session_path() 加 mkdir 保障 + 静态缓存 | 8 | -- | idle | +| qa-liu | 刘静 | 质量工程师 | W19.2 验证 plugin_loader MEDIUM 发现修复 — F-18.3-2 诊断日志/F-18.3-3 路径验证/F-18.3-4 日... | 4 | grp-security-audit | idle | +| qa-wang | 王测 | 质量工程师 | W21.6 anthropic/deepseek plugin 单元测试框架搭建 | 13 | grp-cli-ux, grp-quality-core | idle | +| qa-xu | 徐磊 | 质量工程师 | W13.6 扩展 tests/smoke_test.cpp (430→623 行, +193): 新增 4 个回归保护 case — R1 conte... | 10 | grp-security-audit | idle | +| security-cao | 曹武 | 安全工程师 | W14.3 修复 W13.5 审计发现 — 路径遍历 + 全局状态加锁 + 9 vtable try/catch | 10 | grp-security-audit | idle | | writer-deng | 邓书 | 技术作家 | W12.6 ABI 文档缺口填补: plugin-abi.md 追加 §8 异常安全(涵盖 service vtable 函数 | 3 | -- | idle | > **状态判定规则**: 基于 `performance_log` 最后一条的 `rating`——`ongoing` 视为 `working`,其余 (`A/A+/B/completed/done/success/good`) 视为 `idle`。 @@ -40,7 +40,7 @@ ## Wave 进度 -**已完成高水位**: W17(由 CEO 直接执行,ai_common 模块提取) +**已完成高水位**: W22.6(基于 16 份 profile.md 的 performance_log 聚合) -**已发现 Wave 编号**: W1.1, W2.1, W2.2, W5.1, W6.1, W7, W9.3, W9.4, W9.6, W9.10, W10.1, W10.2, W10.3, W10.4, W11, W11.1, W11.2, W11.3, W11.6, W11.7, W12, W12.1, W12.2, W12.4, W12.5, W12.6, W13.1, W13.2, W13.3, W13.4, W13.5, W13.6, W14.1, W14.2, W14.3, W14.4, W14.5, W15.1, W15.2, W15.3, W15.4, W15.5, W15.6, W15.7, W15.8, W15.9, W16.5, W17 +**已发现 Wave 编号**: W1.1, W2.1, W2.2, W5.1, W6.1, W7, W9.3, W9.4, W9.6, W9.10, W10.1, W10.2, W10.3, W10.4, W11, W11.1, W11.2, W11.3, W11.6, W11.7, W12, W12.1, W12.2, W12.4, W12.5, W12.6, W13.1, W13.2, W13.3, W13.4, W13.5, W13.6, W14, W14.1, W14.2, W14.3, W14.4, W14.5, W15.1, W15.2, W15.3, W15.4, W15.5, W15.6, W15.7, W15.8, W15.9, W16.1, W16.2, W16.3, W16.4, W16.5, W17.1, W17.3, W17.4, W18.1, W18.2, W18.3, W18.4, W19, W19.1, W19.2, W19.3, W19.4, W19.5, W20.2, W20.3, W20.4, W20.5, W20.6, W21.1, W21.2, W21.4, W21.5, W21.6, W22.1, W22.2, W22.4, W22.5, W22.6 diff --git a/agents/audits/findings-registry.md b/agents/audits/findings-registry.md index cf519c4..9de8207 100644 --- a/agents/audits/findings-registry.md +++ b/agents/audits/findings-registry.md @@ -2,7 +2,7 @@ > **维护人**: grp-quality-core (王测) > **格式定义**: 见 `agents/WORKFLOW.md` §14.2 -> **最后更新**: 2026-05-27 (W19 CEO 验收,关闭 plugin_loader 全部 5 条发现,findings 归零) +> **最后更新**: 2026-06-03 (W23.D 登记 network 输入验证 defense-in-depth 发现) --- @@ -10,7 +10,7 @@ | ID | Severity | Source | Title | Status | Assigned To | Fix Wave | Verified By | |----|----------|--------|-------|--------|-------------|----------|-------------| -| — | — | — | 暂无 OPEN 发现 | — | — | — | — | +| F-23.D-1 | LOW | W23.D security-cao review | network_plugin request input validation defense-in-depth: headers_json length limits and host/target/port validation were absent | FIXED | security-cao | W23.D | CEO | --- @@ -54,6 +54,7 @@ | Date | Change | Author | |------|--------|--------| | 2026-05-27 | W15.2 初始化,从 W11.1/W11.7 提取 10 条发现 | 王测 (qa-wang) | +| 2026-06-03 | W23.D: 登记 F-23.D-1 LOW,network_plugin 输入验证 defense-in-depth;W23.D 代码已补 headers_json 长度限制与 host/target/port 校验,进入 FIXED 等待 CEO 验收 | CEO | | 2026-05-27 | W16.1: F-11.7-1 状态 CLOSED,W12.4 已彻底修复 build 产物路径不一致,验证通过 | 曹武 (security-cao) | | 2026-05-27 | W16.2: F-11.1-1 状态 FIXED,context_set_max_tokens / on_shutdown 添加 try/catch 包装 | 孙宇 (engineer-sun) | | 2026-05-27 | W16.3: F-11.1-2 状态 FIXED,strdup OOM 检查在 W12.1 strdup_message_fields() 已实现,g_host->strdup 四调用含 nullptr 检查+oom 回滚,编译 0 error + ctest 4/4 pass 验证通过 | 陈风 (engineer-chen) | diff --git a/plugins_middle/network/src/network_plugin.cpp b/plugins_middle/network/src/network_plugin.cpp index 9a989fe..4b435b7 100644 --- a/plugins_middle/network/src/network_plugin.cpp +++ b/plugins_middle/network/src/network_plugin.cpp @@ -37,6 +37,7 @@ using tcp = asio::ip::tcp; // ============================================================ // 安全常量和输入验证辅助函数 / Security constants and input-validation helpers +// Fixes: F-23.D-1 (network request input validation defense-in-depth) // ============================================================ static constexpr size_t MAX_HEADER_KEY_LENGTH = 256; static constexpr size_t MAX_HEADER_VALUE_LENGTH = 8192; @@ -83,6 +84,21 @@ static bool is_valid_port(const char* port) { return std::strlen(port) <= 15; } +/// 读取环境变量,避免 MSVC 对 std::getenv 的弃用警告 / Read an environment variable without MSVC std::getenv deprecation warnings. +static std::string get_env_var(const char* name) { +#ifdef _MSC_VER + char* value = nullptr; + size_t len = 0; + if (_dupenv_s(&value, &len, name) != 0 || !value) return {}; + std::string result(value, len > 0 ? len - 1 : 0); + std::free(value); + return result; +#else + const char* value = std::getenv(name); + return value ? std::string(value) : std::string(); +#endif +} + // ============================================================ // 全局状态 / Global state // ============================================================ @@ -188,14 +204,14 @@ struct HttpClientCtx { // 但显式 load_verify_file 提供明确的错误码用于报告)/ Fallback 1: SSL_CERT_FILE / SSL_CERT_DIR (already consulted by // OpenSSL internally, but an explicit load_verify_file gives us // a clear error code to report). - const char* cert_file = std::getenv("SSL_CERT_FILE"); - if (cert_file && *cert_file) { + std::string cert_file = get_env_var("SSL_CERT_FILE"); + if (!cert_file.empty()) { ssl_ctx.load_verify_file(cert_file, ec); if (!ec) loaded = true; } if (!loaded) { - const char* cert_dir = std::getenv("SSL_CERT_DIR"); - if (cert_dir && *cert_dir) { + std::string cert_dir = get_env_var("SSL_CERT_DIR"); + if (!cert_dir.empty()) { ssl_ctx.add_verify_path(cert_dir, ec); if (!ec) loaded = true; } diff --git a/plugins_upper/anthropic/src/anthropic_internal.hpp b/plugins_upper/anthropic/src/anthropic_internal.hpp deleted file mode 100644 index 4bdae03..0000000 --- a/plugins_upper/anthropic/src/anthropic_internal.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// ============================================================================ -// anthropic_internal.hpp — 内部声明:供单元测试访问的函数与数据结构 -// ============================================================================ -// 仅在 tests 中使用;非 plugin 公共 API -// ============================================================================ - -#ifndef ANTHROPIC_INTERNAL_HPP -#define ANTHROPIC_INTERNAL_HPP - -#include "dstalk/dstalk_host.h" -#include "dstalk/dstalk_services.h" - -#include "ai_common.hpp" - -#include -#include - -// ---- 从 dstalk_ai 命名空间导入共享类型与函数 ---- -using dstalk_ai::PluginConfig; -using dstalk_ai::ToolCallAccum; -using dstalk_ai::StreamContext; -using dstalk_ai::secure_zero; -using dstalk_ai::extract_host_port; - -// ---- 全局变量 ---- -extern PluginConfig g_cfg; -extern std::string g_tools_json; - -// ---- 测试用函数声明 ---- -bool parse_sse_data(const std::string& data, std::string& token_out, - StreamContext* ctx); - -std::string build_request_json(const dstalk_message_t* history, int history_len, - const std::string& user_input, - const std::string& tools_json, - bool stream); - -std::string build_headers_json(); - -void my_free_result(dstalk_chat_result_t* result); - -int my_configure(const char* provider, const char* base_url, - const char* api_key, const char* model, - int max_tokens, double temperature); - -#endif // ANTHROPIC_INTERNAL_HPP diff --git a/scripts/check_agents_metadata.py b/scripts/check_agents_metadata.py index 5f68651..a691625 100644 --- a/scripts/check_agents_metadata.py +++ b/scripts/check_agents_metadata.py @@ -116,7 +116,7 @@ def check_yaml_parse(agents_dir): # Profile files for child in sorted(agents_dir.iterdir()): - if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits'): + if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits', 'mailroom'): continue pf = child / 'profile.md' if not pf.is_file(): @@ -158,7 +158,7 @@ def check_rating_range(agents_dir): findings = [] for child in sorted(agents_dir.iterdir()): - if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits'): + if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits', 'mailroom'): continue pf = child / 'profile.md' if not pf.is_file(): @@ -206,7 +206,7 @@ def check_group_refs(agents_dir): valid_groups.add(str(gid).strip()) for child in sorted(agents_dir.iterdir()): - if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits'): + if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits', 'mailroom'): continue pf = child / 'profile.md' if not pf.is_file(): @@ -243,7 +243,7 @@ def check_member_refs(agents_dir): # Collect valid agent_ids valid_agents = set() for child in sorted(agents_dir.iterdir()): - if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits'): + if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits', 'mailroom'): continue if (child / 'profile.md').is_file(): valid_agents.add(child.name) @@ -286,7 +286,7 @@ def check_duplicate_ids(agents_dir): agent_ids = {} for child in sorted(agents_dir.iterdir()): - if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits'): + if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits', 'mailroom'): continue pf = child / 'profile.md' if not pf.is_file(): @@ -306,7 +306,7 @@ def check_duplicate_ids(agents_dir): # Also verify dir name matches agent_id for child in sorted(agents_dir.iterdir()): - if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits'): + if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits', 'mailroom'): continue pf = child / 'profile.md' if not pf.is_file(): diff --git a/scripts/refresh_status.py b/scripts/refresh_status.py index b041991..41c0bfe 100644 --- a/scripts/refresh_status.py +++ b/scripts/refresh_status.py @@ -434,7 +434,7 @@ def main(): # ---- Scan profiles ---- profiles = [] for child in sorted(agents_dir.iterdir()): - if not child.is_dir() or child.name.startswith('.') or child.name == 'groups': + if not child.is_dir() or child.name.startswith('.') or child.name in ('groups', 'audits', 'mailroom'): continue pf = child / 'profile.md' if pf.is_file(): diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fbe0fa3..1a2388c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -213,7 +213,7 @@ target_link_libraries(dstalk_network_plugin_test add_test(NAME dstalk_network_plugin_test COMMAND dstalk_network_plugin_test) # ============================================================ -# dstalk_lsp_plugin_test — LSP 插件单元测试 (GoogleTest, 新增) +# dstalk_lsp_plugin_test — LSP 插件单元测试 (hand-rolled CHECK, 新增) # 覆盖: lsp_trim / lsp_frame_message / lsp_parse_content_length # ============================================================ diff --git a/tests/lsp_plugin_test.cpp b/tests/lsp_plugin_test.cpp index b7f4572..88e789f 100644 --- a/tests/lsp_plugin_test.cpp +++ b/tests/lsp_plugin_test.cpp @@ -5,6 +5,8 @@ // - lsp_trim: 字符串 trim 逻辑 // - lsp_frame_message: Content-Length header 构建 // - lsp_parse_content_length: Content-Length header 解析 +// 说明: 本测试仅覆盖纯函数路径;不覆盖 reader_loop 并发、进程生命周期或 +// Server->Client Request 集成路径,这些需由 smoke/集成测试覆盖。 // ============================================================================ #include "lsp_internal.hpp" diff --git a/tests/network_plugin_test.cpp b/tests/network_plugin_test.cpp index 5cc107f..345f4a2 100644 --- a/tests/network_plugin_test.cpp +++ b/tests/network_plugin_test.cpp @@ -205,15 +205,15 @@ int main() std::string long_key(1000, 'K'); std::string json = "{\"" + long_key + "\":\"v\"}"; auto h = parse_headers_json(json.c_str()); - CHECK(h.size() == 1, "T4.3: 1000-char key, size=1"); - CHECK(h[long_key] == "v", "T4.4: long key lookup works"); + CHECK(h.empty(), "T4.3: 1000-char key rejected by MAX_HEADER_KEY_LENGTH"); + CHECK(h.find(long_key) == h.end(), "T4.4: overlong key not inserted"); } { std::string huge(10000, 'Z'); std::string json = "{\"huge\":\"" + huge + "\"}"; auto h = parse_headers_json(json.c_str()); - CHECK(h.size() == 1, "T4.5: 10000-char value parsed"); - CHECK(h["huge"].size() == 10000, "T4.6: size preserved"); + CHECK(h.size() == 1, "T4.5: overlong value key parsed"); + CHECK(h["huge"].size() == 8192, "T4.6: value truncated at MAX_HEADER_VALUE_LENGTH"); } { auto h = parse_headers_json("{\"\":\"value\"}");