feat: 基于 Skills + 沙箱 + 数据库存储构建自动代码评审 Agent #212
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #212 +/- ##
==========================================
Coverage ? 87.90237%
==========================================
Files ? 479
Lines ? 44984
Branches ? 0
==========================================
Hits ? 39542
Misses ? 5442
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
AI Code Review
发现的问题🚨 Critical
|
| result = asyncio.run(executor.execute_code(ctx, input_data)) | ||
| run.duration_ms = (time.time() - start) * 1000 | ||
|
|
||
| output = (result.stdout or "") + (result.stderr or "") |
There was a problem hiding this comment.
SDK 沙箱执行器结果字段访问错误
execute_code() 返回的 CodeExecutionResult 只有 outcome 与 output 字段,代码访问了不存在的 result.stdout/result.stderr/result.exit_code,必然抛 AttributeError,异常被 except Exception: return None 吞掉后静默回退到 subprocess 路径,导致 container/cube/local SDK 执行器形同虚设、生产沙箱隔离从不生效。应改用 result.output/result.outcome(Outcome.OUTCOME_OK)判定成功失败。
|
|
||
| return ReviewResult( | ||
| task=ReviewTask( | ||
| id=task_id if "task_id" in dir() else str(uuid.uuid4()), |
There was a problem hiding this comment.
异常分支 task_id 判断使用 dir() 不可靠
"task_id" in dir() 几乎恒为真或受命名污染影响,无法真正判断局部变量是否已绑定;若 SqliteCrRepository 构造前抛异常,task_id 未定义却会生成一个与数据库记录无关的随机 UUID,掩盖真实失败任务。应改用 locals().get("task_id") 或在 try 顶部预初始化 task_id = None 后判断。
AI Code Review我已经掌握足够的信息了。让我更精确地确认一下 让我再确认一下 让我再检查一下 让我检查一下 现在我已经有足够的内容来撰写审查报告了。让我再确认一下 再确认一点 —— 第 1050-1057 行: 让我完成最终审查。我已经有了足够的验证发现,现在开始汇总审查结果。让我对报告中的
现在开始汇总审查结果。 发现的问题🚨 Critical
|
| findings=[], warnings=[], needs_human_review=[], | ||
| sandbox_runs=[], filter_intercepts=[], | ||
| ) | ||
| except Exception as e: |
There was a problem hiding this comment.
重复且不可达的 except Exception 块
run_review_via_graph 中存在两个连续、完全相同的 except Exception 块,Python 只会进入第一个,第二段(470-477 行)是复制粘贴残留的死代码,且两处 import traceback 均未使用。建议删除重复块。
| else: | ||
| diff_content = config.input_value | ||
|
|
||
| if not diff_content: |
There was a problem hiding this comment.
空 diff 不中断流水线导致失败任务被标记为 COMPLETED
_read_input 发现无 diff 时仅设置 error_message 并返回,未抛异常;后续 _generate_reports 无条件将 task.status 改回 COMPLETED 并落库,使无输入的审查报告成功。应在空 diff 时抛异常,由外层 except 返回失败结果。
| if ":" in full: | ||
| key, _ = full.split(":", 1) | ||
| return f"{key}: *** # {label}" | ||
| if "://" in full: |
There was a problem hiding this comment.
URL 型密钥脱敏后仍泄露明文密码
对 postgres://admin:secret123@ 这类匹配,_mask_matched 中 split("@")[0] 仍包含密码明文,脱敏后字符串里密码依旧存在。应整体替换用户名/密码段,例如 re.sub(r"://[^@]+@", "://***@", full)。
| } | ||
|
|
||
|
|
||
| async def _handle_error(state: ReviewState) -> dict[str, Any]: |
There was a problem hiding this comment.
_handle_error 读取 state["repo"] 但无节点写入该键
每个节点自建本地 SqliteCrRepository 并 close,从不把 repo 放入 state;_handle_error 中 repo 恒为 None,if task and repo 分支永不执行,失败任务状态无法持久化。需在 state 中共享 repo 或让错误处理器自建连接。
AI Code ReviewThe 发现的问题🚨 Critical
|
|
|
||
| def _mask_matched(self, match: re.Match, label: str) -> str: | ||
| """Replace matched secret with a masked version. | ||
|
|
There was a problem hiding this comment.
URL 连接串脱敏不彻底,密码明文残留
_mask_matched 对含 :// 的匹配用 full.split("@")[0] 拼接,但该段实际为 postgres://admin:secret123,密码原样保留;review_agent.py 的 mask_secrets 同样泄漏。应改为仅保留 scheme/host,对用户名与密码整体替换为 ***。该脱敏作用于报告与数据库落库路径,属敏感信息泄露。
AI Code Review已确认 — 现在我来写最终的审查报告。## 发现的问题 🚨 Critical
|
| findings=[], warnings=[], needs_human_review=[], | ||
| sandbox_runs=[], filter_intercepts=[], | ||
| ) | ||
| except Exception as e: |
There was a problem hiding this comment.
重复且不可达的 except Exception 死代码
run_review_via_graph 中出现两个完全相同的 except Exception as e: 块,第二个永远不可达,且两处 import traceback 都未使用,属复制粘贴遗留。异常兜底路径从未被测试触发,可靠性存疑。建议删除第二个 except 块及未用的 import。
AI Code Review确认:调查结果在脱敏前已存储(第 7 步存储原始数据,第 9 步后才进行脱敏)。因此,包含明文密钥的证据(例如 AWS keys、JWT、数据库 URL)会被持久化到 SQLite 我现在已经核实了关键问题。让我来写审查报告。 发现的问题🚨 Critical
|
| repo.close() | ||
|
|
||
|
|
||
| def _build_static_check_script(diff_content: str) -> str: |
There was a problem hiding this comment.
静态检查脚本通过字符串拼接注入 diff,存在代码注入
_build_static_check_script/_build_secret_detection_script 将 diff_content 转义后嵌入三引号字符串,但转义未处理 ''' 子序列,攻击者可用 ''';import os;...;x=''' 闭合字符串并注入任意代码,脚本随后在本机执行。改用写临时文件或 base64/json 解码传递 diff,彻底消除注入面。
|
|
||
| # ── 7. Store Findings ── | ||
| for finding in classified["findings"] + classified["warnings"] + classified["needs_human_review"]: | ||
| repo.create_finding(finding) |
There was a problem hiding this comment.
SECRET 类 finding 在脱敏前落库,明文证据被持久化
第 7 步 repo.create_finding 存储的是未脱敏的 classified 结果,而 _mask_finding_secrets 在第 9 步后才执行,导致 evidence 列含明文 AWS Key/JWT/数据库连接串,违背 DESIGN.md 安全边界。应先脱敏再入库。
| return _result | ||
|
|
||
| # Last resort: native subprocess | ||
| return _run_with_subprocess(script_content, timeout, max_output, start, run) |
There was a problem hiding this comment.
沙箱 Filter 未覆盖本机 subprocess 回退路径
run_sandbox_script 在 SDK 执行器不可用时回退到 _run_with_subprocess 直接以 sys.executable -c 在宿主机执行脚本,而 sandbox_type 默认 local,run_filter_governance 仅做正则黑名单可被变体绕过。应默认强制走容器执行器,仅在显式开关下允许 local 回退并改用白名单 Filter。
核心改进: - 新增 review_agent.py 核心审查管道(993行) - 新增 storage/ 数据持久化层(6 表 + 抽象接口 + SQLite) - 新增 skills/code-review/ CR Skill(5 类规则 + 4 个脚本) - 新增 filters/ Filter 治理层(沙箱安全 + 敏感信息脱敏) - 新增 monitoring/ 监控审计层 - 新增 agent/review_graph.py GraphAgent 7 节点编排 - 新增 dry_run.py / run_agent.py / query_task.py CLI 入口 - 新增 evals/ 评测体系(8 条公开 fixture + 8 条隐藏样本) - 修复 run_sandbox_script() 支持容器回退链 - 修复 Filter 治理接入 run_review() 管道 - 修复 --repo-path git 工作区输入支持 - 修复 OpenTelemetry 上下文错误 - 修复 agent/tools.py 相对导入问题
d808359 to
9ddf482
Compare
AI Code Review
我已经有足够的已验证发现。让我撰写最终审查。 发现的问题🚨 Critical
|
| findings=[], warnings=[], needs_human_review=[], | ||
| sandbox_runs=[], filter_intercepts=[], | ||
| ) | ||
| except Exception as e: |
There was a problem hiding this comment.
重复且不可达的 except Exception 块
run_review_via_graph 在 :462 已有 except Exception 捕获所有异常,紧随其后的第二个相同 except 永远不可达,是复制粘贴残留。若原意是区分异常类型,该逻辑从未生效,建议删除第二个块。
| total_fp = results["total_false_positives"] | ||
|
|
||
| results["detection_rate"] = (total_detected / total_expected * 100) if total_expected else 100.0 | ||
| results["false_positive_rate"] = (total_fp / total_expected * 100) if total_expected else 0.0 |
There was a problem hiding this comment.
误报率 FPR 计算分母错误,使 AC-03 门槛失效
false_positive_rate = total_fp / total_expected * 100 用预期缺陷数作分母,语义错误且可超过 100%,导致 pass_fp = FPR <= 15% 判定不可信、CI 退出码可能误判通过。应改为 FP / (TP + FP) 或 FP / total_findings。
ReviewMind — 基于 tRPC-Agent 框架的自动代码审查 Agent
基于 tRPC-Agent 框架构建的自动代码评审 Agent,集成 Skills、沙箱执行、Filter 治理、数据库存储和监控审计能力。输入 git diff / PR patch / 本地变更,通过 code-review Skill 加载规则,经 Filter 前置拦截后进入沙箱执行检查,发现问题结构化输出并落库持久化。
新增目录
examples/skills_code_review_agent/核心模块
review_agent.pyrun_review()完整审查流程:diff 解析 → 模式检测 → Filter 治理 → 沙箱执行 → 去重降噪 → 报告生成 → 数据库存储config.pyReviewAgentConfig配置类,支持环境变量storage/models.pystorage/schema.sql+sqlite_repository.pyCrRepository抽象接口(保留切换后端的空间)skills/code-review/SKILL.md+ 5 类规则文档(安全/异步/资源泄漏/数据库/敏感信息)+ 4 个沙箱脚本filters/SandboxSecurityFilter(8 种高风险模式拦截)+SecretRedactionFilter(12 种敏感模式脱敏)monitoring/audit.pyreports/generator.pyagent/review_graph.pyrun_agent.py/dry_run.py/query_task.py--diff-file/--fixture/--repo-path三种输入,--dry-run模式,按 task_id 查询evals/server/a2a_server.py/server/agui_server.pyknowledge/coding_standards.md+LangchainKnowledge+knowledge_search_toolagent/agent.pyload_memory_tool+SqlMemoryService(30 天 TTL)检测能力
40+ 种模式匹配检测,覆盖:
沙箱执行
支持三级回退链:
ContainerCodeExecutor→UnsafeLocalCodeExecutor→ 原生 subprocess。超时 30s、输出 1MB 上限、环境变量白名单、失败不崩溃。验收标准达成
独立部署
pip install trpc-agent-py pip install "trpc-agent-py[knowledge,a2a,ag-ui]" python dry_run.py --all python run_agent.py --fixture 01_clean无需克隆仓库,仅依赖 PyPI 包。
关联 Issue: #92