← 返回列表
未验证
一个 DeepSeek Harness 函数插件,将出站 URL…
尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/8/31 · 已提供中文文档
DeepSeek Harness 插件:在请求打开之前运行的故障关闭式 URL 主机/协议允许列表
综合分
27.8
GitHub 分
27.8
用户评分
—
★ Stars
0
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add jwilson411/dsh-ssrf-guard该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
信任档位:仅索引本站尚未对其实装验证,仅收录元数据
- 是什么
- dsh 原生插件 · tool
- 装得上吗
- 本站尚未做安装检查
- 安全吗
- 本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
- 还在维护吗
- 活跃:最近一次提交在 26 天前
档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →
数据截至 2026/9/21(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-tools@deepseek-ai/cordis用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
由 DeepSeek 最新模型翻译生成dsh-ssrf-guard
一个 DeepSeek Harness 函数插件,将出站 URL 限制在故障关闭(fail-closed)的主机和协议允许列表内,并在请求打开之前进行检查。不在列表上的主机永远不会离开本机,因为这里不会打开任何东西:检查只是一次 URL 解析和一次字符串匹配。
范围只有一个问题:这个 URL 可以被抓取吗? 答案要么是一个小的结构化记录,要么是一个抛出的错误,携带 code: 'SSRF_DENIED' 和一个原因令牌。默认配置什么都不允许——你需要自己指定实际需要的主机。
它不是什么
这是一个 URL 主机允许列表。它不是 DNS 重绑定防护,也不是 WAF。
- 不是 DNS 重绑定防护。 主机名是按 URL 中书写的形式进行匹配的,从不经过解析。一个被允许的名称,如果其 DNS 应答指向 127.0.0.1——或者第二次查询的应答与第一次不同——本插件仍会允许它,因为本插件从不进行查询。如果你需要那种防护,你需要解析器级别或连接级别的控制,那是位于不同层的另一回事。
- 不是 WAF。 不会检查请求体、响应、头部或载荷。没有规则集,没有签名列表,也没有要过滤的流量。
- 不是反向代理。 它作为你调用的函数存在于你的进程中,而不是作为你服务前的一跳。
- 不是拦截器。 固定的候选发布版本 0.1.1-rc.2 没有暴露任何 HTTP 或 fetch 接缝,因此本插件不会自行发明一个,也不会静默包装你的客户端。它给你一个可在自己的出口点调用的断言,以及一个模型在请求抓取之前可以调用的工具。
允许的判定是尝试的许可,而不是对主机安全的承诺。
安装
dsh plugin --profile web add github:jwilson411/dsh-ssrf-guard
dsh plugin 会在 $DSH_HOME/profiles/web 内转发给 pnpm,然后协调配置文件:由于此包的清单声明了 dsh.bundle.patch,它会被追加到配置文件清单中有序的 dsh.profile.bundles 列表,并且其 cordis.patch.yml 成为一个层。移除方式相同,将 add 换成 remove 即可。
固定的 DSH 候选发布版本
此包是针对固定的候选发布版本 0.1.1-rc.2 编写和测试的——@deepseek-ai/dsh-tools@0.1.1-rc.2 被精确固定在 devDependencies 中,以便测试针对一个已知 API 运行,而对等依赖范围是 ^0.1.1-rc.2,与 harness 自身工具包声明它的方式一致。
请注意,@deepseek-ai/dsh-tools 的 npm latest 标签仍指向较旧的 0.0.1-rc.1;0.1.1-rc.2 系列发布在 next 下。请显式固定,而不是依赖该标签。
它注册了什么
| | |
|---|---|
| Cordis 插件 id | ssrf-guard(cordis.patch.yml 中的行 id) |
| 注入 | tools——一个硬依赖;插件会等待而不是降级 |
| 工具 | ssrf_check |
| 参数 | url(字符串,必填) |
在允许时,该工具返回 { ok: true, url, host, scheme, plugin },其结构
由其声明的输出模式决定。在拒绝时,它会抛出异常,而不是返回
ok: false —— 一个调用者只要忘记读取布尔值就能绕过的守卫,算不上守卫。
库 API
该工具只是一个薄封装。函数才是产品:
import { assertUrlAllowed, SsrfDeniedError } from 'dsh-ssrf-guard'
const config = { allowHosts: ['.example.com'], allowSchemes: ['https'] }
try {
const { host, scheme } = assertUrlAllowed(candidate, config)
await fetch(candidate) // your call, at your own egress point
} catch (error) {
if (error instanceof SsrfDeniedError) {
log.warn({ code: error.code, reason: error.reason, host: error.host })
return
}
throw error
}
在构建请求之前调用它。对于更愿意基于值进行分支的调用者,有一个不抛
异常的形式 checkUrl(url, config),它返回
{ ok: false, reason, url, host, scheme, message }。
配置
| 键 | 类型 | 默认值 | |
|---|---|---|---|
| allowHosts | string[] | [] | 精确主机名和以点开头的后缀规则。为空时什么都不允许。 |
| allowSchemes | string[] | ["https"] | 允许的 URL 协议,带或不带冒号均可 |
从 profile 自身的 cordis.patch.yml 中设置它们 —— 注意,以 id 为目标的
补丁会替换该行的整个 config,因此要重新声明你想保留的每个字段:
- id: ssrf-guard
config:
allowHosts:
- .example.com
- api.vendor.test
allowSchemes: [https]
解析顺序是补丁配置,然后是环境变量,最后是默认值:补丁行是部署所声明
的意图,因此不会被环境变量静默覆盖。环境变量回退项是
DSH_SSRF_ALLOW_HOSTS 和 DSH_SSRF_ALLOW_SCHEMES,各自是以逗号分隔的
列表,且仅在补丁行完全省略该键时使用。显式为空的列表是一种声明的意图,
并且优先。
主机条目的匹配方式
| 条目 | 匹配 | 不匹配 |
|---|---|---|
| example.com(精确) | example.com | evil.example.com、notexample.com |
| .example.com(后缀) | foo.example.com、a.b.example.com、example.com | notexample.com、example.com.evil.test |
- 比较是不区分大小写的,因为 DNS 就是如此:EXAMPLE.com 匹配
example.com。
- 末尾的根点会被规范化去除,因此 https://example.com./ 无法作为一个不同
的字符串溜过 example.com 条目。
- IPv6 方括号会被规范化去除,因此配置可以写 ::1 或 [::1]。
- 端口不属于匹配的一部分。 https://example.com:8443/ 会被
example.com 允许。如果你需要端口控制,那是另一个层面的事。
- 非字符串或空白条目会被丢弃,而不是被强制转换:格式错误的允许列表会向拒绝
收缩,绝不会向允许扩张。
默认拒绝的内容
完全没有配置时,一切都拒绝。配置了主机后,以下内容仍会被拒绝,除非你明确
指定它们:
| 拒绝 | reason |
|---|---|
| 任何未被 allowHosts 匹配的主机 | HOST_DENIED |
| 任何不在 allowSchemes 中的协议——在仅限 https 的默认设置下包括 http:,以及 file:、data:、gopher:、ftp:(除非明确列出,否则始终拒绝) | SCHEME_DENIED |
| 环回地址和未指定地址:127.0.0.0/8、localhost 和 *.localhost、::1、0.0.0.0、:: | LOOPBACK_DENIED |
| 链路本地地址,包括位于 169.254.169.254 的云元数据服务:169.254.0.0/16、fe80::/10 | LINK_LOCAL_DENIED |
| 携带凭据的 URL,例如 https://example.com@evil.test/ | CREDENTIALS_DENIED |
| 相对路径或无法解析的字符串 | INVALID_URL |
| 能解析但不含主机的 URL,例如 mailto: | HOST_MISSING |
环回地址和链路本地地址只能通过精确条目访问。后缀规则永远无法解锁它们:.localhost 不允许 localhost,.254 不允许元数据地址。在 allowHosts 中写明 127.0.0.1 则可以——这种情况是本地开发服务器,理应被明确写下来。
错误
error instanceof SsrfDeniedError
error.code // 'SSRF_DENIED' — 始终如此,对每一次拒绝
error.reason // 'HOST_DENIED' | 'SCHEME_DENIED' | 'LOOPBACK_DENIED' |
// 'LINK_LOCAL_DENIED' | 'CREDENTIALS_DENIED' |
// 'HOST_MISSING' | 'INVALID_URL'
error.url // 输入字符串,与传入时完全一致
error.host // 规范化后的主机,若解析未进行到该步骤则为 null
error.scheme // 不含冒号的协议,同样可能为 null
error.message // 人类可读,说明允许了什么
根据 code 和 reason 进行分支判断;message 是供人阅读日志用的。完整的 reason 集合以 REASONS 导出。
目录结构
package.json manifest + dsh.bundle.patch — 使其成为 bundle 的关键
cordis.patch.yml bundle 的补丁层:一次插入,一行插件
src/ssrf.js 纯逻辑部分:规范化、匹配、允许或抛出
src/index.js 插件:name、inject、apply(ctx, config)、工具
test/ 离线测试:URL 解析、桩上下文、卫生扫描
package-lock.json 锁定的依赖树,CI 中由 npm ci 安装
测试
npm install
npm test
从构造上就是离线的,而不仅仅是意图上:每个用例都是一次 URL 解析,apply 接收一个记录注册信息的桩上下文,工具通过与注册表调用的同一个 execute 驱动,结果针对锁定为 0.1.1-rc.2 的真实 @deepseek-ai/dsh-tools 进行验证。不启动任何 profile,不解析任何名称,不打开任何套接字,不读取任何密钥。
test/hygiene.test.js 断言这一主张而非复述它:它扫描 src/ 中是否存在任何解析器、套接字、HTTP 或动态导入构造,一旦出现即失败;检查唯一的非相对导入是锁定的工具包;并扫描整棵树以查找机器名、挂载路径和凭据形态。
CI(.github/workflows/ci.yml)在 Node 22 和 24 上,基于已提交的 lockfile,仅针对公共 registry 运行 npm ci 和 npm test。它不需要任何凭据,测试套件也不访问网络。
范围之外
- DNS 重绑定防御。 上文已涵盖:名称按书写形式检查,而非按解析结果检查。
- 反向代理或云 WAF。 两者都不在此列,这也不是它们的简化版本。
- 打开套接字。 该包不建立任何类型的连接,包括不验证允许的主机是否存在。
- 扫描网络。 没有探测、没有扫描、没有发现。
- 回环和链路本地之外的 IP 范围策略。 诸如 10.0.0.0/8 之类的私有范围被拒绝,是因为允许列表本身就是允许列表,而不是因为某个特例。如果你希望它们可达,就把它们列出来。
- 替你做决定。 该工具只回答关于一个 URL 的问题。你的代码如何处理“允许”的结果,是你代码自己的事。
许可证
MIT — 参见 LICENSE。