审计 profile 内所有插件 HTTP 路由的浏览器信任围栏:插件路由在 Web 服务器的最长前缀匹配中优先,因此缺少围栏的路由会被静默暴露;逐条给出文件与行号级别的证据。
安装
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:Vladimir-Kryshchenko/dsh-route-fence-linter
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。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 只有英文版本。
Every plugin route registered on webServer is dispatched by longest-prefix
match, ahead of the host's /api gateway — so it never passes the gateway's
unified trust check. Each plugin author must ship their own browser-trust
fence, and most do not. This linter finds the ones that did not.
Usage
dsh-route-fence scan # $DSH_HOME/profiles/web
dsh-route-fence scan /path/to/profile
Exit codes: 0 clean · 1 at least one FAIL · 2 usage/IO error.
Verdicts
| Verdict | Meaning |
|---|---|
PASS |
The handler gates on a fence that pins the Host before Origin handling. |
WARN |
Either: inside the fence, Origin / sec-fetch-site is read before the Host is pinned; or the registered routes are defined outside the scanned package, so their fence could not be read. Confirm by hand. |
FAIL |
One of: no Host check anywhere; a fence that compares Origin to Host but never pins the Host to loopback (bypassable by DNS rebinding — the attacker controls both headers and they match); or a fence that exists but this handler never calls. |
Bundles whose sources cannot be read are reported as SKIP and counted in the
summary — a skipped bundle is not a clean bundle.
registerFallback is checked too, and reads worse than any route when
unfenced: the fallback seat answers every request no named route matched,
so one unfenced fallback is a wider surface than a whole prefix tree.
Scan scope
The scan covers what a package publishes (its package.json files field);
a package that declares no files is scanned whole. Anything excluded is named
in the output as a note line, never dropped silently.
Scoping by directory name was tried and reverted: skipping anything called
tests/ or examples/ would have let a plugin hide an unfenced route in a
directory with that name and be reported clean. Publication scope cannot be
gamed that way — the loader can only import what was installed.
The rebinding-bypassable shape is graded FAIL, not WARN: it was found live
in a real, widely-installed plugin and confirmed exploitable against a running
profile — a request with Host: evil.example and Origin: http://evil.example
passed the check and executed a state-changing method.
The fence this checks for
The shape the host itself uses (and dsh-better-sidebar/src/trust-fence.ts
copies): pin the Host header to loopback or a configured trusted authority
first, refuse cross-site fetch markers, and only then compare Origin.
Comparing Origin before pinning the Host is not a fence.
Routes defined in another file
A route registered as routes.map(r => webServer.register(r)), where routes
came from buildWebRoutes() in src/web/routes.ts, has no fence anywhere near
its registration — the fence is one module away. Grading such a route by the
text around the registration produced a false FAIL on real plugins, so the
linter follows the value instead.
When the registered argument is not an inline object — a bare identifier, a
factory call build(...), a spread, or the element parameter of a .map /
.forEach / .flatMap over one — the linter resolves that name to where the
handlers are actually written and grades them there. Each handler in the traced
definition is graded on its own; one unfenced handler fails the whole
registration, so a factory that fences four routes and forgets the fifth is a
FAIL, not a PASS.
The walk is fail-closed at every step. It reports PASS only when it found real
handler bodies that gate on the fence. Routes coming from a bare-specifier
import are WARN ("outside the scanned package — check by hand"), never PASS;
so is a relative import that resolves to nothing scanned. Anything else it
cannot establish — a handler: that is a bare function reference, a factory it
cannot find, a chain longer than three module hops — falls back to the previous
FAIL. Import cycles terminate on a visited-set of (file, name) pairs.
Specifier resolution covers what dsh plugins actually write: relative paths
with .ts / .mts / .tsx / .js / .mjs / .cjs or no extension, the
.js-spelled-for-a-.ts-file convention, and directory imports resolved
through index.<ext>. It is not a Node resolver: package.json exports,
imports (#alias), tsconfig paths, and workspace links are not followed —
they resolve to nothing scanned, which is WARN.
Limits
Heuristic over source text, not dataflow analysis. A fence is identified as the smallest function body that both references the Host and constrains it; a handler counts as gated when it calls that fence, receives it as a value, or performs the check inline — cross-file tracing holds a traced handler to exactly that same standard.
Still out of reach: a fence installed by wrapping (register(withFence(route))
where the wrapper is what checks), routes assembled at runtime from
configuration, handlers reached only through a bare-identifier reference, and
route sources behind a non-relative specifier. These read WARN or FAIL, never
PASS. The linter also checks that a fence is present in a handler, not that
it is textually the first statement.
Report false verdicts; they are bugs — a false PASS most of all.
链接
同类插件
toby-bridges/api-relay-audit★ 824
从 DeepSeek Harness 对 AI API 中转站和 LLM 代理运行本地安全审计,生成 Markdown 报告,覆盖提示词注入、模型替换信号、工具调用改写、错误泄漏、流完整性和按 profile 启用的 Web3 风险。
howmp/dsh-pentest★ 374
面向 DeepSeek Harness 的授权渗透模式:以探索链路记录目标、线索、资产与漏洞,并在 Web 中可视化展示。
SeaOf0/dsh-redteam-model★ 250
面向授权安全研究的 DSH 合集:九个工作模式(redteam 总控、渗透测试、代码审计、二进制分析、攻防评估、免杀对抗、应急溯源、云安全攻防、CTF 解题)与十五个运行时插件,设置页管理台支持一键部署、安装、更新与卸载。
PerryLink/dsh-auto-review★ 134
审批链上的第二模型自动审查:只读审查子代理返回带理由的 allow/deny 结构化裁决,默认 fail-closed。
PerryLink/dsh-permission-rules★ 113
Claude Code 风格的声明式权限规则:按序 allow/deny/ask 的 YAML 规则,在 tools/pre-execute 瀑布上匹配工具名、参数、工作区路径与 agent 身份,带完整会话日志审计、干跑模式与热重载。
PensiveFei/dsh-secure-audit★ 87
DSH 只读安全合规插件:提示注入检测、中文 PII 脱敏、本机配置安全审计,输出脱敏且可复现的报告。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。