DeepSHPlugins
dsh-plugins#web-fetch 專案預覽
GITHUB / COMMUNITY PROJECT

dsh-plugins#web-fetch

工具與整合JavaScriptMIT

ctx.web 介面的本地受控抓取提供方——dsh 自帶完整的 web_fetch 工具,卻沒有隨附後端。每一跳重定向都會重新解析並對照非公網地址段校驗,另有流式大小上限與內容型別白名單。

5 StarsCREAIT-nl 更新時間 2026-08-26
訪問 GitHub 倉庫
dsh-plugins#web-fetch 專案預覽

專案介紹

中文

ctx.web 介面的本地受控抓取提供方——dsh 自帶完整的 web_fetch 工具,卻沒有隨附後端。每一跳重定向都會重新解析並對照非公網地址段校驗,另有流式大小上限與內容型別白名單。

English

A guarded local fetch provider for the ctx.web seam — the backend dsh does not ship for its own web_fetch tool. Every redirect hop is re-resolved and checked against the non-routable ranges, with a streamed size cap and a content-type allowlist.

專案資訊與收錄訊號

快照 2026-09-11
作者 / 組織
CREAIT-nl
倉庫
CREAIT-nl/dsh-plugins#web-fetch
GitHub Stars
5
Forks
2
開發語言
JavaScript
許可證
MIT
最近推送
2026-08-26
Manifest
資料中檢測到宣告

公開倉庫不自動等於可商用;許可證與當前相容性以原專案文件為準。Manifest 和資料評分不構成安裝安全認證。

資料評分

72/ 100
維護活躍
90
文件與實用資訊
85
生態關注度
35
安裝便捷訊號
75
專案宣告訊號
100

近 16 天仍在更新;README 含完整安裝與使用說明。

評分來自已有資料快照的五維模型,僅作瀏覽參考,不是實際使用評測或安全檢測結論。

安裝與使用

所有專案資料與許可均以對應 GitHub 倉庫為準。

dsh plugin --profile web add @creait/dsh-web-fetch

複製命令不會自動執行。請先閱讀倉庫文件,確認環境要求與許可證。

專案截圖

倉庫 README

GitHub 原文

已儲存的倉庫文件快照;文件抓取時間未記錄,可能與上方目錄介紹處於不同版本。安裝前請核對當前倉庫說明。

儲存庫檔案保留原文,指令與授權文字不作自動改寫。

@creait/dsh-web-fetch

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

GuardBehaviour
Schemehttp: and https: only
CredentialsURLs carrying user:pass@ are refused outright
AddressEvery 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 namesA hostname resolving to *any* blocked address is refused — which address fetch would pick is not ours to decide
RedirectsFollowed by hand, capped at maxRedirects, and every hop is re-resolved and re-checked
SizeBody is streamed and cut at maxBytes; the result reports truncated
Content typeHTML and text-ish types only; anything else is an error rather than mojibake
CancellationThe 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

KeyDefaultMeaning
maxBytes5242880Ceiling on the buffered body
maxRedirects5Hops followed before refusing
allowHosts[]Exact hostnames allowed to resolve into blocked ranges
blockHosts[]Exact hostnames always refused
userAgentdeepseek-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

使用前核對

安裝步驟、相容版本與授權範圍以原專案當前文件為準。Stars 和評分是快照中的導航訊號。

安裝與排錯 · 專案問題反饋

同類專案