ctx.web 接口的本地受控抓取提供方——dsh 自带完整的 web_fetch 工具,却没有随附后端。每一跳重定向都会重新解析并对照非公网地址段校验,另有流式大小上限与内容类型白名单。
安装
# npm 包(预构建)
dsh plugin --profile web add @creait/dsh-web-fetch
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:CREAIT-nl/dsh-plugins#path:/web-fetch
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
该插件的 README 只有英文版本。
The backend DeepSeek Harness's web_fetch tool is missing.
Why this exists
@deepseek-ai/dsh-tool-web already implements the entire model-facing web_fetch
tool: argument schema, validation, timeout budget, HTML→markdown rendering,
output cap with a truncation footer, and the fetch card the web UI draws. What
dsh does not ship is a provider for the fetch half of the ctx.web seam, so
dsh-base mounts the tool with fetch: false and the capability is inert.
The bundle says why:
Fetch stays disabled and no fetch provider is mounted: that provider defers SSRF protection and the model would choose the request target.
That is a statement about ownership, not a verdict that fetching is unsafe. The provider is where the guards belong, and dsh's own error taxonomy names the set it expects one to carry — "invalid or blocked URLs, redirects, size and timeout limits, and unsupported content types." This package is that provider.
Install
dsh plugin --profile web add @creait/dsh-web-fetch
That mounts the provider and registers it with ctx.web. It does not turn on
web_fetch: the tool belongs to tool-web, which dsh-base mounts with
fetch: false because no provider shipped. Turn it on in your profile patch:
- id: tool-web
config:
fetch: true
Restart dsh — the boot manifest is assembled at startup.
Nothing has to name the provider. dsh ships none of its own, so local is the
only fetch provider registered and the seam selects it on that basis. Name it
explicitly only if something else registers a second one, which is when the
seam refuses to guess:
- id: web
config:
fetchProvider: local
Under dsh web, fetch: true on that row is not enough on its own:
dsh-web-app disables the host-plane tool-web, and each agent preset mounts
its own copy with fetch: false. Revive the host row instead of forking every
preset, and give it only the half the presets do not own — the tool registry
throws on a duplicate tool name:
- id: tool-web
disabled: false
config:
search: false
fetch: true
If you installed this before it shipped a bundle patch, your profile patch
inserts the row by hand. Drop that - insert: block: insert appends
unconditionally, and the second row would register the same provider id twice,
which ctx.web rejects with WEB_DUPLICATE_PROVIDER.
Guards
| Guard | Behaviour |
|---|---|
| Scheme | http: and https: only |
| Credentials | URLs carrying user:pass@ are refused outright |
| Address | Every resolved address is checked against loopback, RFC 1918, CGNAT, link-local (incl. 169.254.169.254), multicast, reserved and IPv6 ULA/link-local ranges |
| IPv4-in-IPv6 | ::ffff: and 64:ff9b:: addresses are unwrapped and judged as the IPv4 they carry |
| Multi-homed names | A hostname resolving to any blocked address is refused — which address fetch would pick is not ours to decide |
| Redirects | Followed by hand, capped at maxRedirects, and every hop is re-resolved and re-checked |
| Size | Body is streamed and cut at maxBytes; the result reports truncated |
| Content type | HTML and text-ish types only; anything else is an error rather than mojibake |
| Cancellation | The seam's AbortSignal is honoured before and during the request |
A non-2xx response is a result, not an error — the status code is part of the resource's state, and the seam's contract says so.
Config
| Key | Default | Meaning |
|---|---|---|
maxBytes |
5242880 |
Ceiling on the buffered body |
maxRedirects |
5 |
Hops followed before refusing |
allowHosts |
[] |
Exact hostnames allowed to resolve into blocked ranges |
blockHosts |
[] |
Exact hostnames always refused |
userAgent |
deepseek-harness/0.0.1 (web fetch) |
Sent on every request |
allowHosts is the deliberate escape hatch for reaching something on your own
network — a LAN wiki, an internal dashboard:
- id: web-fetch
config:
allowHosts: ['wiki.lan', 'dashboard.lan']
Known limitation: DNS rebinding
The window between resolving a hostname and the connection being made is not
closed. A name whose DNS flips to a private address inside that window would be
checked as public and connected as private, because global fetch
re-resolves on its own and offers no pinned-address dispatcher.
Closing it needs a custom undici dispatcher whose connect re-validates the
resolved peer, which is a real dependency this package does not currently take.
On a trusted workstation network the residual risk is small — but it is not
zero, and allowHosts/blockHosts are the levers if your threat model needs
them tighter.
Tests
node --test test/*.test.js
Covers the address classifier range by range (including the IPv4-in-IPv6 unwrap
paths, where a subtle parser bug would have let 64:ff9b::169.254.169.254
through) and drives the provider against a stubbed fetch for redirect
re-validation, hop caps, allow/block lists, content-type handling and
cancellation.
Licence
MIT
链接
同类插件
Tencent/BrowserSkill#dsh-plugin-browserskill★ 8347
BrowserSkill 的 DeepSeek Harness 浏览器自动化桥接插件,通过原生浏览器工具控制可见的 Chrome 和 Edge Agent Window,支持可访问性与 VOM 页面观察、截图、隔离的多会话控制和 Web UI 实时观察浮层。
omdsh-dev/dsh-browser#packages/browser/bridge-browser★ 781
Chrome 侧边栏扩展,让 DSH 直接操控你的浏览器,无需视觉能力。
liustack/modsearch★ 603
纯文本 agent 的联网搜索桥:搜索网页与 X,返回结构化 JSON 证据(search/fetch/引用)。
DDDMUC/dsh-free-search★ 338
DSH 免费搜索插件:7 个引擎(DuckDuckGo/Bing/SearXNG 免费 + Exa/Perplexity/DeepSeek 付费)、自动回退、设置页 UI(API key 输入 + 官网链接)、web_fetch、引擎测试工具。
wqty123/dsh-browser★ 104
共享真实浏览器:用户可观看并随时接管的原生 Electron 窗口,agent 通过 CDP 驱动,内置 20 个 browser_* 工具(打开/快照/执行/填表/截图/下载/登录态);任务级会话隔离、登录态持久化、人机验证识别,纯 `dsh web` 无需桌面外壳即可自托管。
Tabbit-Browser/dsh-tabbit★ 103
让 DeepSeek Harness 能够控制 Tabbit 浏览器:安装即自动加载 tabbit-browser skill,检测国际版 Tabbit 与国内版 Tabbit Browser 正式版(>= 1.9.0),检查 tabbit-cli 常驻运行时,按平台诊断调用 CLI 所需的 DSH sandbox 模式,并在没有合格版本时通过后台任务下载与系统地区匹配的正式版安装包。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。