ZUENS2020/Sherpa
GitHub: ZUENS2020/Sherpa
面向C/C++与Java仓库的端到端自动化模糊测试平台,通过LLM驱动实现harness自动合成、构建修复、崩溃分析与补丁生成的一站式安全测试能力。
Stars: 0 | Forks: 0
# SHERPA
## 1. 项目定位
Sherpa 是一个面向 **C/C++ 与 Java 仓库** 的自动化 fuzz 编排系统。输入一个 Git 仓库 URL,系统会自动完成:
`plan -> synthesize -> build -> run -> (optional fix_build/fix_crash) -> summary`
当前主运行路径是 Web 模式(FastAPI + LangGraph + Docker runtime)。
### 1.1 当前实现中的关键事实
1. 仅支持 Docker 执行(Docker-only policy)。
2. `decide` 节点已删除,路由由条件函数直接决定。
3. `plan` 节点负责输出后续策略(是否 crash 后修复、最多修复轮次)。
4. OpenCode 提示词不再硬编码,统一在 `harness_generator/src/langchain_agent/prompts/opencode_prompts.md`。
5. 前端配置聚焦任务参数:仓库 URL、总时长、单次时长、`Max Tokens`、不限时时单轮上限。
6. API Key 与模型不再通过前端编辑,统一由服务端 `.env` 的 MiniMax 配置注入运行时。
7. 断点续跑默认手动触发:启动阶段不自动恢复,需调用 `POST /api/task/{job_id}/resume`。
8. run 阶段并行批次预算已显式记录到 `run_batch_plan`,用于回放预算分配与超时行为。
9. OpenCode 已集成 GitNexus MCP,默认在每次调用前自动分析“仓库快照”增强代码关系理解(避免污染待修复仓库)。
## 2. 总体架构
flowchart LR
U["User / CI"] --> GW["sherpa-gateway (Nginx)"]
GW --> FE["sherpa-frontend (Next.js)"]
GW --> WEB["sherpa-web (FastAPI API)"]
WEB --> JOB["SQLite Job Store (/app/job-store/jobs.sqlite3)"]
WEB --> WF["LangGraph Workflow"]
WF --> OC["OpenCode via CodexHelper"]
WF --> DIND["Docker Daemon (sherpa-docker)"]
DIND --> IMG["Runtime Images (fuzz-cpp / fuzz-java)"]
WF --> OUT["/shared/output/
-"]
WEB --> LOG["/app/job-logs/jobs/*.log"]
WEB --> CFG["/app/config/web_config.json"]
### 2.1 模块职责
| 模块 | 文件 | 作用 |
|---|---|---|
| Web API | `harness_generator/src/langchain_agent/main.py` | 配置管理、任务提交、任务状态聚合、日志落盘与分类 |
| 任务状态存储 | `harness_generator/src/langchain_agent/job_store.py` | SQLite 任务状态持久化与重启恢复 |
| Workflow | `harness_generator/src/langchain_agent/workflow_graph.py` | 节点定义、状态路由、失败策略、summary 输出 |
| Workflow 公共工具 | `harness_generator/src/langchain_agent/workflow_common.py` | 通用校验、预算控制、prompt 模板加载与渲染 |
| Workflow Summary | `harness_generator/src/langchain_agent/workflow_summary.py` | 运行产物盘点、run summary/fuzz effectiveness 输出 |
| Fuzz 执行器 | `harness_generator/src/fuzz_unharnessed_repo.py` | clone/build/run/crash triage/bundle |
| OpenCode 封装 | `harness_generator/src/codex_helper.py` | 调用 CLI、超时/重试、done 语义 |
| 配置层 | `harness_generator/src/langchain_agent/persistent_config.py` | 持久化配置、环境变量同步、key 脱敏 |
| 前端 | `frontend-next/` (Next.js 14 + TS + MUI + TanStack Query + Zustand) | 提交任务、绑定会话、进度/日志/错误可视化 |
| Prompt 模板 | `harness_generator/src/langchain_agent/prompts/opencode_prompts.md` | plan/synthesize/fix_* 统一模板 |
## 3. 容器拓扑与启动过程
### 3.1 docker-compose 拓扑
flowchart TB
subgraph NET["Docker Compose Network"]
INIT["sherpa-oss-fuzz-init"]
GW["sherpa-gateway (nginx)"]
FE["sherpa-frontend (Next.js)"]
WEB["sherpa-web"]
STORE["sherpa-job-store (sqlite sidecar)"]
DIND["sherpa-docker (dind)"]
OC["sherpa-opencode (profile: opencode)"]
end
INIT --> OSS["volume: sherpa-oss-fuzz (/shared/oss-fuzz)"]
WEB --> OSS
DIND --> OSS
WEB --> TMP["volume: sherpa-tmp (/shared/tmp)"]
DIND --> TMP
WEB --> OUT["bind: ./output -> /shared/output"]
DIND --> OUT
WEB --> CFG["volume: sherpa-config (/app/config)"]
WEB --> JLOG["volume: sherpa-job-logs (/app/job-logs)"]
WEB --> JDB["volume: sherpa-job-store-data (/app/job-store)"]
STORE --> JDB
WEB -->|"DOCKER_HOST=tcp://sherpa-docker:2375"| DIND
GW --> FE
GW --> WEB
### 3.2 启动时序
sequenceDiagram
participant Dev as Developer
participant DC as docker compose
participant Init as sherpa-oss-fuzz-init
participant Dind as sherpa-docker
participant Store as sherpa-job-store
participant FE as sherpa-frontend
participant GW as sherpa-gateway
participant Web as sherpa-web
Dev->>DC: docker compose up -d --build
DC->>Init: start init container
Init->>Init: clone oss-fuzz if missing
Init-->>DC: complete successfully
DC->>Dind: start docker daemon container
Dind-->>DC: healthcheck ready
DC->>Store: start sqlite sidecar
DC->>Web: start API service
DC->>FE: start Next.js frontend
DC->>GW: start nginx gateway
Web->>Web: load web_config.json + apply env
GW-->>Dev: :8000 ready
### 3.3 关键 compose 配置点
1. `DOCKER_BUILDKIT=1` 在 `sherpa-web` 中默认开启,使用 BuildKit 构建流程。
2. dind 默认启用 xuanyuan 镜像加速:`SHERPA_DOCKER_REGISTRY_MIRROR=https://7m856d3fdvb9yp.xuanyuan.run`,也可通过本地环境变量覆盖。
3. `/shared/output` 是主产物目录(当前映射到仓库 `./output`)。
4. `sherpa-web` 和 `sherpa-docker` 共享 `sherpa-tmp` 与 `sherpa-oss-fuzz`,保证容器内路径一致。
5. `sherpa-gateway` 统一入口:`/` -> `sherpa-frontend`,`/api/*` -> `sherpa-web`。
6. `sherpa-job-store` 与 `sherpa-web` 共享 `sherpa-job-store-data`,用于持久化任务状态 SQLite。
7. `sherpa-job-store` 启动时会初始化 `/data/jobs.sqlite3` 并收紧权限(目录 `0770`、文件 `0660`)。
### 3.4 容器职责(明确分工)
| 容器 | 职责 | 对外暴露 |
|---|---|---|
| `sherpa-gateway` | 唯一入口网关,转发 UI/API | `:8000` |
| `sherpa-frontend` | Next.js 前端页面与交互 | 内部 `:3000` |
| `sherpa-web` | FastAPI API + workflow 编排 + 任务状态/日志 | 内部 `:8001` |
| `sherpa-job-store` | SQLite sidecar(任务状态库管理与运维入口) | 无 |
| `sherpa-docker` | Docker daemon(dind),负责构建/运行 fuzz 容器 | 内部 `:2375` |
| `sherpa-oss-fuzz-init` | 初始化/校验 oss-fuzz 工作目录 | 无 |
## 4. 快速开始(开发环境)
### 4.1 前置条件
1. Docker Desktop / OrbStack(兼容 Docker API)。
2. Docker Compose plugin。
3. 可用的 MiniMax API Key(`MINIMAX_API_KEY`)。
### 4.2 首次启动建议(镜像预热)
首次部署或网络不稳定环境,建议先预热运行时镜像,再启动 compose,避免任务内现场构建导致长时间停在 `init/plan`。
# 预热 opencode 运行镜像(使用国内镜像源基底)
docker build -t sherpa-opencode:latest \
-f docker/Dockerfile.opencode \
--build-arg OPENCODE_BASE_IMAGE=m.daocloud.io/docker.io/library/node:20-slim \
.
# 预热 C/C++ fuzz 运行镜像
docker pull m.daocloud.io/docker.io/library/ubuntu:24.04
docker tag m.daocloud.io/docker.io/library/ubuntu:24.04 ubuntu:24.04
docker build -t sherpa-fuzz-cpp:latest -f docker/Dockerfile.fuzz-cpp .
快速验收:
docker run --rm sherpa-opencode:latest sh -lc 'opencode --version && gitnexus --version'
docker run --rm sherpa-fuzz-cpp:latest sh -lc 'clang --version | head -n 1 && python3 --version'
### 4.3 启动
docker compose up -d --build
### 4.4 访问与健康检查
- 统一入口(Gateway): `http://localhost:8000`
- 前端(Next.js,经网关访问): `http://localhost:8000/`
- API(FastAPI,经网关访问): `http://localhost:8000/api/*`
curl -s http://localhost:8000/api/system | jq
### 4.5 提交最小任务
curl -s http://localhost:8000/api/task \
-H 'Content-Type: application/json' \
-d '{
"jobs": [
{
"code_url": "https://github.com/madler/zlib.git",
"docker": true,
"docker_image": "auto",
"total_time_budget": 900,
"run_time_budget": 300,
"max_tokens": 1000
}
],
"auto_init": true,
"build_images": true
}' | jq
## 5. 代码目录导览
.
├── docker/
│ ├── Dockerfile.web
│ ├── Dockerfile.fuzz-cpp
│ ├── Dockerfile.fuzz-java
│ ├── Dockerfile.opencode
│ └── requirements.web.txt
├── harness_generator/
│ ├── docs/
│ ├── src/
│ │ ├── langchain_agent/
│ │ │ ├── main.py
│ │ │ ├── workflow_graph.py
│ │ │ ├── persistent_config.py
│ │ │ ├── prompts/opencode_prompts.md
│ │ ├── fuzz_unharnessed_repo.py
│ │ └── codex_helper.py
│ └── requirements.txt
├── tests/
├── docker-compose.yml
└── README.md
## 6. 任务模型与状态聚合
### 6.1 任务层级
Sherpa 采用父子任务模型:
1. `task`:批次级任务(父任务,提交入口)。
2. `fuzz`:单仓库执行任务(子任务,真正执行 workflow)。
### 6.2 状态定义
子任务(`fuzz`)会出现以下状态:
- `queued`
- `running`
- `resuming`
- `recoverable`
- `success`
- `resumed`
- `error`
- `resume_failed`
父任务(`task`)聚合时会做状态归一:
- `queued` -> `queued`
- `running` / `resuming` / `recoverable` -> `running`
- `success` / `resumed` -> `success`
- `error` / `resume_failed` -> `error`
### 6.3 聚合规则(父任务)
1. 任一子任务 `queued/running/resuming/recoverable` -> 父任务 `running`
2. 全部结束且至少一个 `error` -> 父任务 `error`
3. 全部 `success` -> 父任务 `success`
flowchart TD
T["Parent task"] --> C1["child #1"]
T --> C2["child #2"]
T --> CN["child #N"]
C1 --> AGG["aggregate status"]
C2 --> AGG
CN --> AGG
AGG --> R1["if any queued/running => running"]
AGG --> R2["else if any error => error"]
AGG --> R3["else => success"]
## 7. 工作流状态机(核心执行路径)
flowchart TD
I["init"] --> R0{"route_after_init"}
R0 -->|"resume_from_step=plan or empty"| P["plan"]
R0 -->|"resume_from_step=synthesize"| S["synthesize"]
R0 -->|"resume_from_step=build"| B["build"]
R0 -->|"resume_from_step=fix_build"| FB["fix_build"]
R0 -->|"resume_from_step=run"| R["run"]
R0 -->|"resume_from_step=fix_crash"| FC["fix_crash"]
R0 -->|"init failed"| E0["END"]
P -->|"ok"| S["synthesize"]
P -->|"error/failed"| E1["END"]
S -->|"ok"| B["build"]
S -->|"error/failed"| E2["END"]
B -->|"ok"| R["run"]
B -->|"last_error"| FB["fix_build"]
B -->|"failed"| E3["END"]
FB -->|"fixed"| B
FB -->|"still error/failed"| E4["END"]
R -->|"crash && plan allows"| FC["fix_crash"]
R -->|"no crash"| E5["END"]
R -->|"crash && report-only"| E6["END"]
R -->|"failed"| E7["END"]
FC -->|"fixed"| B
FC -->|"error/failed"| E8["END"]
### 7.1 节点与 LLM 职责表
| 节点 | 是否调用 LLM/OpenCode | 主要职责 | 关键输出 |
|---|---|---|---|
| `init` | 否 | 初始化 generator、预算、repo_root | 初始化状态 |
| `plan` | 是 | 生成 fuzz 目标与策略 | `fuzz/PLAN.md`, `fuzz/targets.json`, `plan_fix_on_crash`, `plan_max_fix_rounds` |
| `synthesize` | 是 | 生成 harness/build 脚手架 | `fuzz/build.py` + harness 源 |
| `build` | 否 | 实际构建并提取错误摘要 | `fuzz/build_full.log`, `fuzz/out/*` |
| `fix_build` | 条件调用 | 本地热修 + OpenCode 修 build 错误 | 修改 `fuzz/*`、清空 `last_error` |
| `run` | 否 | 并行运行 fuzz、收集 crash/metrics | `run_details`, crash 文件 |
| `fix_crash` | 条件调用 | 基于 crash 分析修复 harness 或上游代码 | `fix.patch`, `fix_summary.md` |
| workflow end | 否 | 产出最终总结 | `run_summary.md/json`, `fuzz_effectiveness.md/json` |
### 7.2 已移除节点
- `decide` 节点已删除。
- 路由通过 `_route_after_*` 函数直接决策。
## 8. plan / synthesize / fix 的真实行为
### 8.1 `plan` 节点在做什么
`plan` 不只是“挑目标”,还承担执行策略定义:
1. 输出 `fuzz/targets.json`(后续 synthesize/build 输入)。
2. 输出 `fuzz/PLAN.md`(包含建议、约束、策略)。
3. 从 `PLAN.md` 解析策略:
- `Crash policy: report-only|fix`
- `Max fix rounds: N`
4. 生成 `codex_hint` 给后续节点做约束增强。
### 8.2 `synthesize` 节点在做什么
1. 生成或更新 `fuzz/build.py`。
2. 生成至少一个 harness 源文件(`.c/.cc/.cpp/.java`)。
3. 如依赖系统包,写 `fuzz/system_packages.txt`(包名列表,不写 shell)。
### 8.3 `fix_build` 节点在做什么
先本地热修,再必要时调用 OpenCode。
本地热修覆盖典型问题:
1. `-stdlib=libc++` 造成 ABI 不匹配。
2. `main` 符号冲突(注入 `-Dmain=vuln_main` 等)。
3. `cannot find -lz` 链接失败。
若热修不足,再把结构化错误 + 完整构建日志文件路径传给 OpenCode 修复。
### 8.4 `fix_crash` 节点在做什么
1. 读取 `crash_info.md` + `crash_analysis.md`。
2. 若判定 `HARNESS ERROR`,走 harness 修复模板。
3. 否则按上游代码缺陷修复模板执行。
4. 生成 `fix.patch`、`fix_summary.md`,并拷贝进 challenge bundle。
## 9. 构建与运行在不同环境(关键认知)
OpenCode 编辑发生在其自身环境,但 build/run 在 fuzz runtime 容器里执行。Prompt 中已明确告知该事实。
flowchart LR
OC["OpenCode edit env"] -->|"edit files only"| REPO["Shared repo workspace"]
REPO -->|"docker run"| RUNTIME["sherpa-fuzz-cpp/java runtime"]
RUNTIME -->|"build/run logs + artifacts"| REPO
REPO -->|"next fix loop"| OC
这意味着:
1. OpenCode 不能假设当前 shell 环境等同 build/run 环境。
2. build 失败修复应依赖日志与文件,不依赖“本地能运行”。
## 10. 双预算模型与并行执行模型
### 10.1 预算字段
1. `total_time_budget`:整个 workflow 的总预算(硬上限)。
2. `run_time_budget`:单次 fuzz 批次预算(运行阶段上限)。
### 10.2 预算切分逻辑(run 阶段)
flowchart TD
START["run phase start"] --> RT["read total remaining time"]
RT --> CHK{"remaining <= 0 ?"}
CHK -->|"yes"| STOP["mark workflow_time_budget_exceeded"]
CHK -->|"no"| BATCH["take next batch (size=SHERPA_PARALLEL_FUZZERS)"]
BATCH --> CALC["round_budget = min(run_time_budget, remaining/rounds_left)"]
CALC --> EXEC["run batch with hard timeout"]
EXEC --> MORE{"pending fuzzers?"}
MORE -->|"yes"| RT
MORE -->|"no"| END["collect run_details + route"]
### 10.3 并行度控制
- 环境变量:`SHERPA_PARALLEL_FUZZERS`(默认 `2`,上限被代码限制)。
- 若仅一个 fuzzer,则强制串行。
### 10.4 并行运行可观测字段
`run` 节点会输出 `run_batch_plan`(按轮次):
1. `round`: 第几轮批次。
2. `batch_size`: 当前批次 fuzzer 数量。
3. `pending_before`: 本轮执行前待运行数量。
4. `rounds_left`: 预计剩余轮次。
5. `remaining_total_budget_sec`: 本轮开始时总剩余预算。
6. `round_budget_sec`: 分配给本轮的 run 预算。
7. `hard_timeout_sec`: 本轮容器硬超时。
## 11. API 对接说明
### 11.1 接口总览
| 方法 | 路径 | 说明 |
|---|---|---|
| `GET` | `/api/config` | 读取配置(密钥脱敏) |
| `PUT` | `/api/config` | 更新配置并立即应用 |
| `GET` | `/api/system` | 系统状态与运行中任务 |
| `POST` | `/api/task` | 提交父任务(可含多个 repo 子任务) |
| `GET` | `/api/task/{job_id}` | 查询单任务详情(父任务聚合视图) |
| `POST` | `/api/task/{job_id}/resume` | 手动恢复中断任务(支持从记录的 workflow step 续跑) |
| `GET` | `/api/tasks?limit=N` | 最近任务列表(会话选择器) |
### 11.2 `POST /api/task` 请求体(推荐)
{
"jobs": [
{
"code_url": "https://github.com/madler/zlib.git",
"email": null,
"model": "MiniMax-M2.5",
"max_tokens": 1000,
"time_budget": 900,
"total_time_budget": 900,
"run_time_budget": 300,
"docker": true,
"docker_image": "auto"
}
],
"auto_init": true,
"build_images": true,
"images": null,
"force_build": false,
"oss_fuzz_repo_url": null,
"force_clone": false
}
返回:
{
"job_id": "",
"status": "queued"
}
### 11.3 `GET /api/task/{job_id}` 关键字段
1. `status`: 聚合状态。
2. `children_status`: 子任务统计。
3. `children`: 子任务详情(含 `log`, `log_file`, `error`, `result`)。
4. 恢复相关字段(子任务):`resume_from_step`, `resume_attempts`, `last_resume_reason`。
5. run 阶段诊断字段:`run_details` 与 `run_batch_plan`(用于并行轮次预算排查)。
### 11.4 `POST /api/task/{job_id}/resume` 说明
1. 支持 task/fuzz 两类 job_id:
1. task:恢复其可恢复子任务;
2. fuzz:直接恢复该子任务。
2. 恢复优先从保存的 `resume_from_step` 继续;若缺少必要上下文会进入 `resume_failed` 并给出结构化原因。
3. 同一任务重复调用具备幂等保护,不会重复并发调度。
### 11.5 前后端时序
sequenceDiagram
participant UI as Browser UI
participant API as FastAPI
participant WF as Workflow
UI->>API: POST /api/task
API-->>UI: {job_id, status: queued}
UI->>API: GET /api/task/{job_id} (poll)
API->>WF: execute child fuzz jobs
WF-->>API: update _JOBS state/log
API-->>UI: aggregated status + logs
UI->>UI: render status/progress/error/log
### 11.6 断点续跑时序(从当前 step 继续)
sequenceDiagram
participant Store as SQLite Job Store
participant API as FastAPI(main.py)
participant WF as Workflow(workflow_graph.py)
participant UI as Browser UI
Note over API: 服务启动
API->>Store: load_jobs()
API->>API: queued/running/resuming -> recoverable
API->>API: 推断 resume_from_step / resume_repo_root
API-->>UI: 等待用户手动选择 job 并调用 resume
UI->>API: POST /api/task/{job_id}/resume
API->>API: 读取 request + checkpoint 字段
API->>API: 计算 resume_step = resume_from_step|active_step|last_step|build
API->>WF: run_fuzz_workflow(resume_from_step, resume_repo_root)
WF->>WF: init 后按 route_after_init 跳到目标 step
WF-->>API: resumed / resume_failed
API-->>UI: 最新状态与日志
### 11.7 Checkpoint 提取与恢复字段更新
flowchart TD
L["workflow log line"] --> M1{"[wf] -> step ?"}
M1 -->|"yes"| U1["workflow_active_step=step
workflow_last_step=step"]
M1 -->|"no"| M2{"[wf] <- step ?"}
M2 -->|"yes"| U2["workflow_last_completed_step=step
workflow_active_step=''"]
M2 -->|"no"| M3{"contains repo_root=... ?"}
M3 -->|"yes"| U3["workflow_repo_root=repo_root"]
M3 -->|"no"| END0["ignore"]
U1 --> SAVE["persist to sqlite"]
U2 --> SAVE
U3 --> SAVE
SAVE --> RST["service restart"]
RST --> INF["infer checkpoint from fields/log"]
INF --> REC["mark recoverable + set resume_from_step/resume_repo_root"]
### 11.8 失败后恢复 step 选择决策树
flowchart TD
S0["start resume"] --> K1{"job kind?"}
K1 -->|"task"| T1["遍历 children"]
K1 -->|"fuzz"| F1["读取 fuzz checkpoint 字段"]
T1 --> T2{"child status in success/resumed?"}
T2 -->|"yes"| T3["跳过该 child"]
T2 -->|"no"| F1
F1 --> C1["resume_step = resume_from_step"]
C1 --> C2{"为空?"}
C2 -->|"yes"| C3["fallback = workflow_active_step|workflow_last_step|build"]
C2 -->|"no"| C4["使用 resume_from_step"]
C3 --> C4
C4 --> C5{"resume_step != plan 且 resume_repo_root 缺失?"}
C5 -->|"yes"| R1["resume_failed: missing_resume_workspace"]
C5 -->|"no"| R2["提交 _run_fuzz_job(resumed=true)"]
R2 --> W1["workflow init"]
W1 --> W2{"route_after_init"}
W2 --> W3["跳转到目标 step 执行"]
W3 --> OUT["resumed 或 resume_failed"]
## 12. 前端行为说明(当前版本)
前端工程:
- `frontend-next/app/*`
- `frontend-next/components/*`
- `frontend-next/lib/api/*`
- `frontend-next/store/useUiStore.ts`
### 12.1 页面重点
1. 任务触发(仓库 URL、总时长、单次时长)。
2. 会话绑定(任务列表 + 手动 task id)。
3. 任务状态、阶段进度、错误详情、事件日志。
4. Provider/API 配置固定为服务端 `.env` 注入(前端不提供编辑入口)。
### 12.2 日志分类(避免误报)
前端通过显式前缀规则识别日志级别,不会把普通运行日志全部归为错误。
分类入口:`frontend-next/components/logUtils.ts` 中的 `detectLevel(line)`。
规则示例:
1. 明确失败:`Traceback`、`Docker build failed`、`error during connect` 等。
2. 警告:`DEPRECATED`、`warning:`。
3. 普通事件:`[wf]`、`[job]`、`[OpenCodeHelper]`、命令回显行。
### 12.3 日志自动滚动
任务日志更新后,前端会调用自动滚动逻辑将事件窗滚到底部,避免手动追踪。
## 13. 配置系统(MiniMax `.env` 单入口 + 持久化任务配置)
配置模型定义:`WebPersistentConfig`。
### 13.1 配置存储
1. 主配置文件:`config/web_config.json`。
2. OpenCode 环境文件:`config/web_opencode.env`。
### 13.2 配置字段(核心)
| 字段 | 说明 |
|---|---|
| `MINIMAX_API_KEY` | 主 API Key(必填,来自 `.env`) |
| `MINIMAX_BASE_URL` | MiniMax 兼容入口(默认 `https://api.minimaxi.com/anthropic/v1`) |
| `MINIMAX_MODEL` | 默认模型(例如 `MiniMax-M2.5`) |
| `fuzz_time_budget` | 默认总预算 |
| `sherpa_run_unlimited_round_budget_sec` | 总/单次不限时时的单轮上限 |
| `fuzz_use_docker` | Docker 开关(后端强制 true) |
| `fuzz_docker_image` | 默认镜像(`auto`/显式 tag) |
| `oss_fuzz_dir` | 本地 oss-fuzz 路径 |
| `sherpa_git_mirrors` | Git 镜像配置 |
| `sherpa_docker_*_proxy` | Docker 代理相关配置 |
### 13.3 配置优先级(简化理解)
1. 任务请求字段(如 `docker_image`、budget)。
2. MiniMax 环境变量(`MINIMAX_*`)作为运行时 provider 单一来源。
3. 持久化配置(`web_config.json`)仅承载任务/运行参数,不承载 API 密钥。
4. 代码内兜底默认值。
### 13.4 密钥保护
- `GET /api/config` 返回密钥字段始终脱敏(空值)。
- `PUT /api/config` 不再接受前端覆盖 provider/API 字段;密钥仅从服务端 `.env` 注入。
## 14. Prompt 模板系统
模板文件:`harness_generator/src/langchain_agent/prompts/opencode_prompts.md`
### 14.1 模板列表
1. `plan_with_hint`
2. `synthesize_with_hint`
3. `fix_build_execute`
4. `fix_crash_harness_error`
5. `fix_crash_upstream_bug`
6. `plan_fix_targets_schema`
### 14.2 渲染机制
1. 使用 ` ... ` 包裹模板。
2. 运行时通过 `_render_opencode_prompt(name, **kwargs)` 做 `{{var}}` 替换。
3. 模板缺失或文件缺失时直接抛错,避免静默降级。
## 15. 产物目录与文件生命周期
### 15.1 单任务输出根目录
- `/shared/output/-/`
### 15.2 关键产物
| 文件/目录 | 产出节点 | 用途 |
|---|---|---|
| `fuzz/PLAN.md` | `plan` | 目标与策略说明 |
| `fuzz/targets.json` | `plan` | 目标清单(后续输入) |
| `fuzz/build.py` | `synthesize` | 构建入口 |
| `fuzz/build_full.log` | `build` | 完整构建日志,供 fix_build 使用 |
| `fuzz/out/` | `build` | 可执行 fuzzer |
| `fuzz/corpus/*` | `run` | 种子与语料 |
| `fuzz/out/artifacts/*` | `run` | 崩溃样本与工件 |
| `crash_info.md` | `run` | 崩溃信息 |
| `crash_analysis.md` | `run` | 崩溃分类(含 HARNESS ERROR 判定) |
| `reproduce.py` | `run` | 复现脚本 |
| `fix.patch` | `fix_crash` | 修复补丁 |
| `fix_summary.md` | `fix_crash` | 修复摘要 |
| `run_summary.md/json` | workflow end | 最终运行报告 |
| `fuzz/out/fuzz_effectiveness.md/json` | workflow end | fuzz 效果统计 |
### 15.3 文件流转图
flowchart TD
P["plan"] --> F1["fuzz/PLAN.md"]
P --> F2["fuzz/targets.json"]
S["synthesize"] --> F3["fuzz/build.py"]
S --> F4["harness source"]
B["build"] --> F5["fuzz/build_full.log"]
B --> F6["fuzz/out/"]
R["run"] --> F7["fuzz/corpus/*"]
R --> F8["fuzz/out/artifacts/*"]
R --> F9["crash_info.md + crash_analysis.md"]
FC["fix_crash"] --> F10["fix.patch + fix_summary.md"]
ENDN["summary"] --> F11["run_summary.md/json"]
ENDN --> F12["fuzz_effectiveness.md/json"]
## 16. 构建失败:原因与可自动修复范围
### 16.1 常见失败因素
1. 构建脚本路径或目标名不一致。
2. 链接参数错误(例如 `-lz` 不可解析)。
3. `main` 符号冲突导致 libFuzzer 链接失败。
4. `-stdlib=libc++` 与当前 runtime ABI 不匹配。
5. 环境类问题(Docker daemon 不可达、DNS/网络超时、磁盘满)。
### 16.2 fix_build 决策树
flowchart TD
IN["build failed"] --> INFRA{"infra/non-source blocker?"}
INFRA -->|"yes"| STOP["stop: environment/infrastructure issue"]
INFRA -->|"no"| HOT{"known hotfix pattern?"}
HOT -->|"stdlib/main/-lz"| LOCAL["apply local hotfix"]
LOCAL --> RET["return to build"]
HOT -->|"no"| OC["call OpenCode with full build log"]
OC --> CHG{"relevant files changed?"}
CHG -->|"yes"| RET
CHG -->|"no"| FAIL["fix_build no-op => stop"]
### 16.3 可由 OpenCode 解决的问题(当前系统)
1. harness 源代码错误(include、函数签名、调用方式)。
2. `fuzz/build.py` 编译参数、源路径、输出路径错误。
3. 缺失最小 build glue。
4. 目标选择与实现偏差导致的编译失败。
### 16.4 通常不能由 OpenCode 直接解决的问题
1. Docker daemon 完全不可用。
2. 网络层拉镜像失败(TLS 握手超时、DNS 故障)且无备用源。
3. 宿主机资源耗尽(磁盘、内存)导致构建中止。
4. 仓库本身非 C/C++/Java 且仍强行使用不匹配 runtime。
## 17. 失败追踪与排障手册
### 17.1 先看哪里
1. API 返回中的 `error` 与 `children[].error`。
2. `log_file` 指向的任务日志。
3. 输出目录下 `fuzz/build_full.log`。
4. `run_summary.md/json` 与 `fuzz_effectiveness.md/json`。
### 17.2 常用排障命令
# 1) 看容器状态
docker compose ps
# 2) 看 web 与 dind 日志
docker compose logs -f sherpa-web
docker compose logs -f sherpa-docker
docker compose logs -f sherpa-job-store
# 3) 看最近任务列表
curl -s 'http://localhost:8000/api/tasks?limit=20' | jq
# 4) 看指定任务
curl -s 'http://localhost:8000/api/task/' | jq
# 5) 检查 dind 健康
DOCKER_HOST=tcp://127.0.0.1:2375 docker info
# 6) 查看 SQLite 任务状态库
docker compose exec sherpa-job-store sqlite3 /data/jobs.sqlite3 '.tables'
docker compose exec sherpa-job-store sqlite3 /data/jobs.sqlite3 'select job_id,status,updated_at from jobs order by updated_at desc limit 20;'
### 17.3 典型错误与处理建议
| 现象 | 根因方向 | 建议 |
|---|---|---|
| `BuildKit is enabled but buildx missing` | 构建器能力不匹配 | 保持 `DOCKER_BUILDKIT=0` 或安装 buildx |
| `lookup sherpa-docker ... no such host` | 容器网络/DNS 问题 | 检查 compose 网络与服务名、重建容器 |
| `TLS handshake timeout` 拉基础镜像失败 | 到 Docker Hub 网络不稳 | 配置公共镜像加速、重试、检查代理 |
| 卡在 `plan` 且日志停在 `building opencode image` | 首次动态构建 `sherpa-opencode`(npm 全局安装慢) | 先手动预构建 `sherpa-opencode:latest`,再提交任务 |
| 卡在 `init` 且日志停在 `building sherpa-fuzz-cpp` | 首次动态构建 `sherpa-fuzz-cpp`(apt 下载慢) | 先手动预构建 `sherpa-fuzz-cpp:latest`,再提交任务 |
| `workflow stopped (time budget exceeded)` 且 `last=synthesize/plan` | 总流程预算已被前置节点耗尽(非 run 阶段) | 提高 `total_time_budget`(或设 `0` 不限时),并降低 `SHERPA_OPENCODE_IDLE_TIMEOUT_SEC` 快速回收无进展尝试 |
| `remote rejected ... workflow ... without workflow scope` | HTTPS/OAuth 凭证缺少 GitHub `workflow` 权限 | 刷新或重建凭证并加入 `workflow` scope,或改用 SSH 推送 |
| 长时间 running 无进展 | 预算过大或卡在外部依赖 | 查看 workflow 日志 step,缩短预算定位 |
| 多轮 fix 无效果 | 同签名反复失败 | 检查 `same_*_repeats` 保护是否触发 |
补充说明(当前实现):
1. Docker 网络预检查是 best-effort;若本地无 `busybox:latest`,预检查会自动跳过,不会误报为 DNS 故障。
2. `no such host` 诊断已收窄:仅在 registry 相关上下文下归类为 registry DNS 问题,避免误判 `sherpa-docker` 服务名异常。
### 17.4 镜像预热(推荐)
在网络不稳定或首次部署时,建议先预热运行时镜像,避免任务内现场构建导致“看起来卡住”。
# 预热 opencode 运行镜像(使用国内镜像源基底)
docker build -t sherpa-opencode:latest \
-f docker/Dockerfile.opencode \
--build-arg OPENCODE_BASE_IMAGE=m.daocloud.io/docker.io/library/node:20-slim \
.
# 预热 C/C++ fuzz 运行镜像
docker pull m.daocloud.io/docker.io/library/ubuntu:24.04
docker tag m.daocloud.io/docker.io/library/ubuntu:24.04 ubuntu:24.04
docker build -t sherpa-fuzz-cpp:latest -f docker/Dockerfile.fuzz-cpp .
快速验收:
docker run --rm sherpa-opencode:latest sh -lc 'opencode --version && gitnexus --version'
docker run --rm sherpa-fuzz-cpp:latest sh -lc 'clang --version | head -n 1 && python3 --version'
## 18. 前端对接重点
### 18.1 会话绑定逻辑
1. `GET /api/tasks` 加载会话列表。
2. 用户选择 task 或手输 ID。
3. 前端周期轮询 `GET /api/task/{id}`。
4. 若 task 结束(success/error),停止轮询并刷新列表。
### 18.2 进度展示逻辑
1. 从日志解析 `"[wf] -> step"` 与 `"[wf] <- step"`。
2. 推导每个 step 的 `pending/running/done/error`。
3. 错误面板只在明确失败条件时显示。
### 18.3 自动滚动
日志渲染后自动滚动到底,避免手动拉到底部追踪最新事件。
## 19. 测试与质量保障
测试目录:`tests/`
当前覆盖重点:
1. API 配置脱敏、Docker-only 约束。
2. build 阶段重试与热修行为。
3. run 阶段 crash 检测、预算终止、metrics 结构。
4. summary 与 fuzz_effectiveness 产物写入。
### 19.1 运行测试
pytest -q tests
### 19.2 建议的最小回归清单
1. 提交 zlib 仓库任务能跑通主流程。
2. 故意制造 build 失败,验证 `build_full.log` 和 `fix_build` 行为。
3. 前端绑定历史会话,验证进度和错误分区显示。
4. 验证 `run_summary.md/json` 与 `fuzz_effectiveness.md/json` 结构稳定。
当前基线验证(2026-02-25):
1. `tests/test_workflow_run_detection.py`
2. `tests/test_opencode_stability_guards.py`
3. `tests/test_workflow_build_resilience.py`
4. `tests/test_api_stability.py`
5. `tests/test_job_store_persistence.py`
6. 结果:`44 passed`
## 20. 运维参数速查
### 20.1 常用环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
| `DOCKER_HOST` | `tcp://sherpa-docker:2375` | web 调 dind |
| `DOCKER_BUILDKIT` | `0` | 避免 buildx 依赖问题 |
| `SHERPA_PARALLEL_FUZZERS` | `2` | run 阶段并行数 |
| `SHERPA_RUN_UNLIMITED_ROUND_BUDGET_SEC` | `7200` | 当总时长/单次时长设为不限时时,run 阶段每轮默认上限秒数;设 `0` 恢复完全不限时 |
| `SHERPA_WEB_JOB_LOG_DIR` | `/app/job-logs/jobs` | 任务日志目录 |
| `SHERPA_WEB_JOB_STORE_MODE` | `sqlite` | 任务状态存储模式,`sqlite` 或 `memory` |
| `SHERPA_WEB_JOB_DB_PATH` | `/app/job-store/jobs.sqlite3` | SQLite 任务状态库路径 |
| `SHERPA_WEB_RESTORE_LOG_MAX_CHARS` | `200000` | 重启恢复时从日志文件回填到 API 的最大字符数 |
| `SHERPA_WEB_AUTO_RESUME_ON_START` | `0` | 保留兼容字段,当前默认不做启动自动恢复(仅手动调用 `/api/task/{job_id}/resume`) |
| `SHERPA_OUTPUT_DIR` | `/shared/output` | 输出根目录 |
| `SHERPA_DEFAULT_OSS_FUZZ_DIR` | `/shared/oss-fuzz` | oss-fuzz 本地根目录 |
| `SHERPA_GITNEXUS_AUTO_ANALYZE` | `1` | OpenCode 调用前自动执行 GitNexus 分析快照 |
| `SHERPA_GITNEXUS_SKIP_EMBEDDINGS` | `1` | GitNexus 自动分析时默认跳过 embeddings(更快) |
| `SHERPA_OPENCODE_CONFIG_PATH` | `/app/config/opencode.generated.json` | OpenCode 运行时配置文件路径(由后端自动生成) |
| `SHERPA_DOCKER_REGISTRY_MIRROR` | `https://7m856d3fdvb9yp.xuanyuan.run` | Docker 镜像源(可覆盖) |
| `SHERPA_DOCKER_NETWORK_PRECHECK` | `1` | Docker 网络预检查开关(可设 `0` 跳过) |
| `SHERPA_DOCKER_PROXY_HOST` | `host.docker.internal` | 本机代理主机映射 |
### 20.2 日志分流
`main.py` 会按 level/category 写分流日志,例如:
- `.level.error.log`
- `.cat.workflow.log`
- `.cat.docker.log`
用于前端或外部系统按维度检索。
### 20.3 Job Store 权限说明
`sherpa-job-store` 启动时会初始化:
1. `/data` 目录权限:`0770`
2. `/data/jobs.sqlite3` 文件权限:`0660`
该策略用于避免 `permission denied` 的同时减少 world-writable 风险。
## 21. 安全与协作约定
1. 不把个人镜像源、个人代理、个人 key 写入仓库文件。
2. 使用 `config/web_config.json` 与容器环境做本地化注入。
3. `output/`、运行日志、配置目录默认应被 `.gitignore` 忽略。
4. 对外共享日志时注意脱敏(key、token、私有仓库地址)。
## 22. 二次开发指南
### 22.1 新增 workflow 节点(推荐步骤)
1. 在 `workflow_graph.py` 增加 `def _node_xxx(...)`。
2. 在 `build_fuzz_workflow()` 中 `add_node` + `add_conditional_edges`。
3. 在 `FuzzWorkflowState` 中补充状态字段。
4. 在 `run_summary` 输出中补充可观测字段。
5. 在 `tests/` 增加节点行为测试。
### 22.2 新增 Prompt 模板
1. 在 `opencode_prompts.md` 新增模板块。
2. 通过 `_render_opencode_prompt("template_name", ...)` 调用。
3. 保持“仅编辑文件,不执行命令”的统一约束语义。
### 22.3 新增前端面板
1. 在 `frontend-next/components/*` 新增面板组件。
2. 在 `frontend-next/lib/api/hooks.ts` 接入数据轮询。
3. 在 `frontend-next/store/useUiStore.ts` 管理会话绑定与日志 UI 状态。
## 23. 多图速览(便于对接汇报)
### 图 A:系统架构
见 [第 2 节](#2-总体架构) 架构图。
### 图 B:容器拓扑
见 [第 3 节](#3-容器拓扑与启动过程) 拓扑图。
### 图 C:启动时序
见 [第 3 节](#3-容器拓扑与启动过程) 时序图。
### 图 D:工作流状态机
见 [第 7 节](#7-工作流状态机核心执行路径)。
### 图 E:环境隔离关系
见 [第 9 节](#9-构建与运行在不同环境关键认知)。
### 图 F:预算切分流程
见 [第 10 节](#10-双预算模型与并行执行模型)。
### 图 G:API 调用时序
见 [第 11 节](#11-api-对接说明)。
### 图 H:build 失败修复决策树
见 [第 16 节](#16-构建失败原因与可自动修复范围)。
### 图 I:产物流转图
见 [第 15 节](#15-产物目录与文件生命周期)。
### 图 J:断点续跑时序
见 [第 11 节](#11-api-对接说明) 中 `11.6`。
### 图 K:Checkpoint 提取与恢复
见 [第 11 节](#11-api-对接说明) 中 `11.7`。
### 图 L:失败后恢复 step 选择决策树
见 [第 11 节](#11-api-对接说明) 中 `11.8`。
## 24. FAQ(按当前代码行为)
### Q1:`plan` 节点和 `decide` 节点的关系?
A:当前无 `decide` 节点。`plan` 输出策略后,路由函数直接决策。
### Q2:哪个节点写后续 build/fuzz 必需文件?
A:`synthesize` 写 `fuzz/build.py` 和 harness;`build` 产出 `fuzz/out/`。
### Q3:构建失败时会不会把 traceback/完整日志给 fix?
A:会。`build_full.log` 会落盘,并在 `fix_build` 阶段作为关键上下文传入。
### Q4:build/run 和 OpenCode 在同一容器吗?
A:不是。OpenCode 编辑环境与 fuzz runtime 是分离的。
### Q5:为什么前端有时会显示 warning 但任务未失败?
A:warning 与 error 分级独立;只有显式失败条件才进入错误面板。
### Q6:能否并行跑多个 fuzzer?
A:可以,`SHERPA_PARALLEL_FUZZERS` 控制并行度,run 阶段按批次执行。
### Q7:怎么从日志快速判断当前卡在哪个节点?
A:看每条工作流日志前缀:`[wf step= last= next=-]`。
`last` 是当前(或刚完成)节点名,例如 `last=synthesize` 表示耗时主要在合成阶段,`last=run` 才是 fuzz 运行阶段。
## 25. 推荐对接流程(团队协作)
1. 研发先阅读第 7/8/16 节,理解状态机与失败路径。
2. 平台确认第 3/20 节,完成容器与网络基建。
3. 测试依据第 19 节建立回归脚本。
4. 前端依据第 12/18 节对齐状态字段和日志分级。
5. 联调时统一围绕 `run_summary.json` 与 `fuzz_effectiveness.json` 对账。
## 26. 许可证
本项目许可证见 `LICENSE`。标签:AV绕过, C/C++, DevSecOps, Docker 容器, FastAPI, Fuzzing, Git 集成, Harness Synthesis, LangGraph, LLM 应用, OpenCode, REST API, SQLite, 上游代理, 事务性I/O, 云安全监控, 代码安全, 大模型编排, 崩溃分析, 开源框架, 持续集成, 测试用例生成, 漏洞枚举, 自动修复, 补丁生成, 请求拦截, 软件供应链安全, 远程方法调用, 逆向工具, 静态分析