pentest-dsh
Verifiedpentest-dsh · v1.1.0 · MIT
p2p 三环并行渗透测试系统 - DeepSeek Harness (dsh) 移植版
Install
dsh plugin add pentest-dsh Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
pentest-dsh 插件
dsh 版本双轨说明(已知结构性差异,2026-09-20 登记)
pinned 六包(@deepseek-ai/dsh-*,package.json:19-24 精确 pin 0.1.1-rc.2)与全局 dsh CLI(0.1.5-rc.1,~/.npm-global/bin/dsh)版本不同,是有意保持的双轨而非遗漏:
- pinned 六包:供插件进程内后端使用(plugin/pentest-dsh/adapter-inprocess.mjs(进程内,adapter-inprocess.mjs:21-24 顶部仅 import node:crypto/fs/os/path; :207-210 动态 import 四个 pinned 包; :217 worker 经 sharedAgents.create() 进程内创建, 全文件无 spawn 调用; :18 注释'复用自 @deepseek-ai/dsh-subagent-in-process-driver 的公开配方'. dsh-tools/dsh-mcp-client 两个 pinned 包不在本文件 import. 解析自 plugin/pentest-dsh/node_modules(磁盘实证 dsh-agent/package.json:4 version=0.1.1-rc.2), 即 pinned 安装);plugin/pentest-dsh/adapter-dsh.mjs(spawn 全局,spawn=全局 PATH,adapter-dsh.mjs:4-7 仅 import node:child_process/fs/os/path, 零 @deepseek-ai import; :27-37 DSH_BIN IIFE(注释 :25 'systemd user 服务 PATH 常缺 ~/.npm-global/bin → spawn(dsh) 127 全灭'); :182 唯一 spawn 点. 全局实证: ~/.npm-global/bin/dsh -> ../lib/node_modules/@deepseek-ai/dsh/lib/bin.js, 该包 package.json:4 version=0.1.5-rc.1; pinned 的 plugin/pentest-dsh/node_modules/@deepseek-ai/ 下只有六个库及传递依赖, 无 dsh CLI 包, 不可能提供二进制. 例外: scripts/ops/env.sh:6 会 export P2P_DSH_BIN="$D2D/dsh/node_modules/.bin/dsh"(仓库本地第三安装位, 本机 /dsh 不存在, ls 实证; 显式分支 :28-29 不做存在性检查, source 该脚本且路径缺失时 spawn 将打到死路径));plugin/pentest-dsh/test/adapter-inprocess.test.mjs(其他(单测, 测 adapter-inprocess 失败路径与事件折叠契约),test/adapter-inprocess.test.mjs:2-3 仅 import node:assert 与 ../adapter-inprocess.mjs; 用假 ctx.agents 驱动 spawnWorker(:18-59), 不 import 任何 @deepseek-ai 包, 不 spawn 子进程; :5-6 注释'可离线单测…真实回合路径由 localhost 冒烟脚本覆盖'));CI 测试轨(.github/workflows/ci.yml:15 npm ci 按 pin 安装 + npx mocha)与 dsh-compat.yml 的 pinned 列(失败=阻塞,退出 2)对其提供兼容护栏(蓝图 #20)。
- 全局 dsh CLI:供 headless worker 运行时使用(spawn 点:plugin/pentest-dsh/adapter-dsh.mjs:182、plugin/pentest-dsh/adapter-dsh.mjs:27-37、scripts/ops/env.sh:6、scripts/systemd/d2d-dsh-web.service:8、install.sh:35、scripts/ops/patch-dsh-tool-fs.mjs:28-30);随上游独立演进。
- 为什么不互锁:dsh 为 developer preview(每周可能 breaking),pin 锁 API 保证测试可重复;CLI 侧由 dsh-compat 的 latest 列(失败=仅通知,退出 3)监控漂移,升级为显式决策。
adapter 契约(dsh 双轨)
本节消除两处代码悬空引用:adapter-dsh.mjs:2「契约见 README.md」、adapter-inprocess.mjs:16「见 README」。两轨对调度器(scheduler.js)暴露同一组原语 spawnWorker / killAllWorkers / registerGate(adapter-inprocess.mjs:2-3 头注、index.js:22-24 注释同口径),spawnWorker 均返回 Promise<{code, text}>,调度器侧消费代码完全共用(scheduler.js:607-608)。killAllWorkers 进程外轨为 SIGTERM 快照批杀 + 3s 后 SIGKILL 兜底并清注册(adapter-dsh.mjs:58-69),进程内轨为逐槽 abort+dispose+强制 settle(adapter-inprocess.mjs:284-297)。
a. 轨道选路(index.js)
P2P_INPROCESS=1→adapter-inprocess.mjs的createInProcessAdapter(进程内子 agent;该文件 :21-24 仅 import node:crypto/fs/os/path,全文件无 spawn),并在 apply 时调用adapter.attachHost(ctx)(index.js:25-30、:35)。- 缺省(非字面
'1',判定式String(process.env.P2P_INPROCESS ?? '') === '1',index.js:25)→adapter-dsh.mjs的createDshAdapter(进程外 spawn 全局 dsh CLI--profile headless;分支 index.js:31-34,spawn 点 adapter-dsh.mjs:182)。
b. adapter-dsh 契约(进程外 headless CLI spawn)
输入
- brief 文本作 CLI 末位位置参数:
spawn('timeout', […, DSH_BIN, '--profile', 'headless', dshTask])(adapter-dsh.mjs:182)。CLI 侧语义实证(全局 dsh 0.1.5-rc.1,~/.npm-global/lib/node_modules/@deepseek-ai/dsh/package.jsonversion 字段):dsh-headless/lib/startup.js:35把位置参数 join 成 task,dsh-headless/lib/index.js:152将其作为首条 user messagefollowup(createUserMessage({ content:[{type:'text', text: task}], source:{kind:'user'} }))投入新建会话(:138sessionId: session-<randomUUID>)。 - MAX_STEPS 预算行追加:
P2P_MAX_STEPS(默认 60,adapter-dsh.mjs:174)具体化为硬数字,以「## 预算硬线(调度器注入, 单位=工具轮…)」追加到 brief 末尾,含MAX_STEPS-6步停止新探索的软着陆线与超限强杀警告(:176-177);同时把 brief 中$P2P_WORKER_TOKEN单点替换为$DSH_ENV_P2P_WORKER_TOKEN(:175)。
执行边界
timeout --signal=KILL --kill-after=5 <秒>s包裹 dsh 进程(:182);超时秒数按 ring 取:deep/creative 用DEEP_TIMEOUT_MS(=min(P2P_DEEP_TIMEOUT_MS 默认 10m, WORKER_TIMEOUT_MS)),其余用WORKER_TIMEOUT_MS(默认 20m)(:46-51、:179)。stdio: ['ignore','pipe','pipe']——无 stdin(:223);detached: true独立进程组(:224),killGroup对-pid与pid双杀(:54-57)。- 三重计时器:
hardTimer到时 killGroup(:264);settleTimer硬超时 +60s 兜底 killGroup 并finish(null)(:267);stepTimer每 20s 解析 dsh 会话文件计数(sessionCounts:73-101,按 cwd basename 定位 sessions 目录、unzstd 解压后计 step/tool/compaction :92-96),steps >= stepCeil杀进程组,活跃产出(会话 90s 内有新写入)一次性 +50% 延长,延长后再到线照杀(:269-285,延长分支 :274-278)。 - per-worker 选模:临时 DSH_HOME overlay(settings.yaml 改写 + 其余 symlink 回真身,:129-145),经
env.DSH_HOME = modelHome ?? DSH_HOME_DIR注入(:187),终态dropModelHome清理(:146-149、:237)。
输出
- stdout+stderr 合并累积(:287-288),进程内存上限 8MB 保头尾各 4MB(:229-232);全程证据落盘
<ring>.log(按 engagement 分目录,固定名追加写、>50MB 轮转.old,:240-254)。 - 终态
resolve({ code, text }):text为头尾各 120k 截断(中段...[truncated middle]...标记;:259-262)。调度器据此写 checkpoint 与账本:输出治理后文本尾 800 字写 AgentIdentity.checkpoint、status 置 done/error/failed(scheduler.js:650-652;消费入参 :607-608);终态 runLog 事件(:661);用量账本model-usage.jsonl追加 code/存活毫秒/steps/tools/compactions/input_tokens/output_tokens(:666-668,计数来自adapter.sessionCounts,adapter-dsh.mjs:307-308 导出)。
错误语义
- 计时器超时一律杀进程组(hardTimer :264 / settleTimer :267 / stepTimer :280-281);
close事件回传真实 exit code(:298-304);spawn 失败(ENOENT 等)经error事件finish(null),Promise 必然 settle(:289-297)。code为 null 的路径=无真实退出码。 - bash 门控 deny reason 是唯一运行中文本回注:
tools/pre-execute钩子内_gateHandler(cmd)返回真值即return { kind:'deny', reason }(:12-23,deny 在 :19)。除此外 spawn 为一次性投递、无 stdin(:223),adapter 内不存在其他向运行中 worker 注入文本的路径。 - egress 代理与 token/lease 经 env 注入:
P2P_PROXY_URL(默认http://127.0.0.1:8888,off/none显式关闭)→http(s)_proxy/HTTP(S)_PROXY+no_proxy=127.0.0.1,localhost(:196-203);D2D_CA_BUNDLE→CURL_CA_BUNDLE(:206-209);P2P_WORKER_TOKEN缺失时从P2P_WORKER_TOKEN_FILE/~/.config/d2d/worker-token读入(:211-216);leaseId→P2P_WORKER_LEASE_ID(仅可观测用途,:217-220);宿主P2P_HOST_TOKEN被剥离(:188)。
c. adapter-inprocess 契约(进程内子 agent)
输入
sharedAgents.create({ sessionId: SessionId(randomUUID()), meta:{ cwd:产物目录, origin:'subagent', delegationDepth:1 }, [agentOptions], signal, setup }):sessionId 每次 spawn 新生成(:217-224,randomUUID 在 :218);sharedAgents为 attachHost 时模块级捕获的宿主 agents 服务(:119-124、:32)。按任务选模走agentOptions.{provider,model}('provider/model' 解析 :79-86,使用 :215、:224),仅宿主 home 已注册 provider 可用(:78 注释)。- brief 以单条 user message 投递:
child.followup(createUserMessage({ content:[{type:'text', text: dshTask}], source:{kind:'user'} })),随后await whenIdle()等回合结束(:256-260);同样做$P2P_WORKER_TOKEN → $DSH_ENV_P2P_WORKER_TOKEN替换(:213)。
输出
- 进程内 session 产出折叠:
foldConsumedWork(child.session.events).end取终态 reason(:263-270,import 自 @deepseek-ai/dsh-agent :210),finalAssistantOutput(...)+textOf提取交付物纯文本(:271,import :209,textOf :90-95);回合结束即handle.dispose()静止化销毁(:273)。 - 终态
resolve({ code, text })与 adapter-dsh 同形:头尾各 120k 截断(:185-190);text 全文另写证据EVIDENCE_DIR/<ring>.log(50MB 轮转,:97-111、:184)。checkpoint/账本写入由共用调度器完成(scheduler.js:607-668,同 b)。
错误语义(按实际代码)
- code 语义:仅
reason === 'completed'→ 0,否则 1;text 为提取产物或(无文本交付物; stopReason=<reason>)(:274)。reason:abort →'aborted',否则 turn/end reason 的字符串或.kind,兜底'error'(:267-270)。 - 超时:
WORKER_TIMEOUT_MS(默认 20m,:64)到点ac.abort(timeoutMsg)(:193-198),ABORT_GRACE_MS(默认 10s,:68)宽限内等自然路径保留真实输出,仍不静止则finish(1, '(无文本交付物; stopReason=timeout: …)')强制 settle(:199-200)——spawnWorker 的 Promise 必然 settle(头注 :14-16,即本节消解的悬空引用)。 - 异常:IIFE catch →
finish(1, 'worker 失败: …')(:275-280);宿主 agents 服务不可用直接抛「P2P_INPROCESS=1 仅支持在 dsh 宿主(web/headless)内使用」(:206)。 killAllWorkers():逐槽abort('stop-all')+dispose+finish(1, '(无文本交付物; stopReason=stop-all)'),清空槽位与 token 放行名单(:284-297);强制收敛期间已创建的 handle 不得复活(:247-251)。- 降级行为:worker 侧 bash 门控挂在子 agent scope 的 setup 内(与主会话 registerGate 同一 handler),setup 失败降级为无门控(:226-243,降级 :239-242);attachHost 时 worker-token 文件缺失仅告警不阻断(:126-129);token 桥以 shellEnv 贡献者注册、按
sessionId ∈ sharedWorkerSessions过滤放行(:135-153)。
d. 双轨行为边界
- headless 每 spawn 新建会话绝不复用(one task per invocation):adapter 每次
spawnWorker起一个全新 dsh CLI 进程(adapter-dsh.mjs:161-182),不传任何 session/resume 参数;CLI 侧每次 run 新建 agent +session-<randomUUID>会话,单任务答完即 flush/exit(dsh-headless lib/index.js:129-139、:161-165;launcher 帮助同口径「answer one task, print the result, and exit」,dsh lib/bin.js:37)。 - 无运行中注入通道:headless 无 stdin(adapter-dsh.mjs:223)、CLI 单 followup 后不再消费任何输入(dsh-headless lib/index.js:152-165)——调度器无法向运行中 worker 追加文本;运行中唯一文本回注是宿主 bash 门控的 deny reason(adapter-dsh.mjs:19,见 b)。
- in-process 当前也只投一条 followup:单次
child.followup(...)后await whenIdle()即进入终态收集(adapter-inprocess.mjs:256-260),无多轮追加投递代码。 - 两轨均不做 resume:adapter-dsh 全文件无 resume/会话复用逻辑(唯一 spawn 点 :182 无 session 参数);adapter-inprocess 每次 spawn 新 randomUUID sessionId(:218)且回合毕 dispose(:273),全文件无 resume 调用。调度器的「断点恢复」是继任者机制——把前任 AgentIdentity.checkpoint 文本注入新 worker 的 brief(scheduler.js:294-302),并非 worker 会话级 resume。