npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@logictan/dsh-fakeip-fetch

v0.1.2

Published

DSH web_fetch provider that works behind a fake-ip DNS proxy (Clash / sing-box). Narrows the upstream non-public-address guard to the configured reserved range instead of removing it.

Readme

@logictan/dsh-fakeip-fetch

让 DSH 的 web_fetch 在 fake-ip DNS 环境下可用。

它解决什么

本地 DNS 代理(Clash / sing-box 的 fake-ip 模式)会把每个外部域名都解析到同一个保留网段 (默认 198.18.0.0/15)。上游 @deepseek-ai/dsh-web-fetch-http 的安全守卫按地址形状判定, 把 198.18.0.0/15 归为 reserved(与 198.51.100.0/24 同类)并抛出:

Error: URL hostname "sing-box.sagernet.org" resolves to a non-public IP address

结果是所有正常站点都无法抓取。但该 fake-ip 实际可达——curl --resolve <host>:443:<fake-ip> 返回 200,直连该地址同样 200。这是守卫的误判,不是真实的拦截。

它怎么修

收窄守卫,而不是移除它。 注册一个替换 provider:

  1. 先用上游原封不动的守卫尝试(公共单播地址);
  2. 仅当它抛出 WEB_BLOCKED_URL 时,才用放宽后的 resolver 重试一次;
  3. 放宽后的 resolver 只放行落在配置白名单内的地址,其余一律拒绝;
  4. 兜底也失败时,上报兜底自己的错误;只有兜底也以 WEB_BLOCKED_URL 拒绝时, 才保留守卫的诊断。

因为严格路径优先,豁免只作用于真实守卫确实拒绝过的请求。回环、内网、链路本地与云元数据 地址在两条路径上都仍然被拒。放宽路径没有「公共单播」旁路:公共地址在严格路径上本就已被 接受,在兜底里再放行一次只会让守卫更宽。

安装

dsh plugin --profile <profile> add @logictan/dsh-fakeip-fetch

本包自带 dsh.bundle.patch,安装即挂载。它的 patch 做两件事:

  • 把上游 web-fetch-http 行 disabled: true;
  • 插入本插件的行 fakeip-fetch。

两者都必需:本插件故意注册上游自己的 provider id http,这样 dsh-base 里 web 行的 fetchProvider: http 一个字都不用改(本包因此永远不需要复述、也就不会随上游漂移 searchProvider 等配置)。同一个 id 注册两次会抛 WEB_DUPLICATE_PROVIDER,所以原行必须让位。

配置

| 字段 | 默认 | 说明 | |---|---|---| | allowedCidrs | ['198.18.0.0/15'] | 要豁免的 fake-ip 网段 | | maxResponseBytes | 上游默认 | 透传 | | maxBodyChars | 上游默认 | 透传 | | timeoutMs | 上游默认 | 透传 | | maxRedirects | 上游默认 | 透传 | | userAgent | 上游默认 | 透传 |

传输限制直接展开上游 schema,因此默认值跟随上游,不抄进本包。

限制的校验同样归上游:本包的 patch 禁用了上游 web-fetch-http 行,那行的 apply 本来 负责校验这四个限制,所以 apply 会先把 config 交给上游的 apply 跑一遍,再丢弃它建出的 provider、只取校验结果。这不是洁癖——Node 会把超过 2^31-1 毫秒的定时器压成 1 毫秒, 未校验的 timeoutMs 会让每次抓取立即超时。委托上游可让规则与报错文本都留在上游手里。

改白名单:

- id: fakeip-fetch
  name: '@logictan/dsh-fakeip-fetch'
  config:
    allowedCidrs:
      - 198.18.0.0/15

白名单条目必须是保留网段

allowedCidrs 的每个条目掩码后都必须完整落在一个 ipaddr.js 的 reserved 段内,否则 加载即失败:

| 条目 | 结果 | |---|---| | 198.18.0.0/15、192.0.2.0/24、240.0.0.0/4 | 接受 | | 2001:db8::/32、2001::/23、3fff::/20 | 接受,但会削弱 NAT64 防护(见下)——实际部署不要配 IPv6 条目 | | 198.18.0.0/5、192.0.2.7/1、192.0.0.0/1、240.0.0.0/1、2001:db8::/1 | 拒绝(掩码后越出 reserved,会覆盖真实内网/回环) | | 0.0.0.0/0、::/0、0.0.0.0/8 | 拒绝(掩码后是 unspecified,不是 reserved) | | 192.0.0.0/24、192.0.0.128/25 | 拒绝(reserved 但含 192.0.0.192 Oracle 云元数据、192.0.0.170/171 NAT64 探测) | | 8.8.8.0/24、2606:4700::/32 | 拒绝(公共单播) | | 10.0.0.0/8、192.168.0.0/16、127.0.0.0/8、169.254.0.0/16、fc00::/7 | 拒绝(真实内网) |

判据是掩码后的有效块,不是书写出来的 base:ipaddr.parseCIDR 不做掩码,所以 198.18.0.0/5 的 base 看着无害(落在默认 fake-ip 池里),实际覆盖 192.0.0.0/5,包含 192.168.0.0/16 整个私网段。少写一位(.0/5 而非 .0/15)就足以打开局域网,因此必须 先掩码再判定。

reserved 是必要条件,不是充分条件。 192.0.0.0/24 是 reserved,却装着 Oracle 云 实例元数据端点 192.0.0.192 与 NAT64/DNS64 探测地址 192.0.0.170/.171。因此含这些 地址的条目一律拒绝(192.0.0.0/25 只覆盖 .0–.127,不含 .192,仍然接受)。其余常见 元数据端点(169.254.169.254、100.100.100.200、169.254.0.23)本就落在所有 reserved 块之外,天然无法被列入。

拒绝真实内网是刻意的:豁免 10.0.0.0/8 等于让一个域名把抓取器指向局域网或云元数据端点。

安全边界(如实披露)

  • 豁免的语义是「信任本机 TUN」。 resolver 只能看到 DNS 应答,无法证明该地址的可达性。 若本机 DNS 被恶意劫持,理论上可以把一个域名指向白名单网段再由 TUN 转进内网。用户自行运行 fake-ip TUN 已隐含接受这一类风险。
  • 解析是全有或全无。 一个域名若同时返回 fake-ip 地址与真实内网地址,整体拒绝——攻击者 控制的记录集无法把内网目标夹带过豁免。
  • 云元数据端点不可豁免。 reserved 不等于安全:192.0.0.0/24 是 reserved 却含 Oracle 云 元数据 192.0.0.192。含已知元数据/NAT64 探测地址的 allowedCidrs 条目在加载时即被拒绝。
  • 放宽路径只认白名单。 它不放行任何白名单之外的地址,公共单播也不例外。这是刻意的: 放宽路径只在严格路径已经拒绝之后才被走到,公共地址在那里本就被接受,再放行一次只会更宽。
  • IPv6 白名单条目会削弱 NAT64 防护。 上游对 IPv6 应答有 RFC 6052 二次校验(把内嵌的 IPv4 取出再判一次),本插件注入的 resolver 会替换该步骤。放宽路径只认白名单,所以默认 白名单下 NAT64 地址(如 2001:4860:64::a9fe:a9fe,内嵌 169.254.169.254)与上游一样被拒; 但若把 IPv6 保留段(如 2001:db8::/32、2001::/23)写进 allowedCidrs,其内嵌地址就会被 放行。实际部署不需要 IPv6 条目:fake-ip 段是 IPv4(198.18.0.0/15),而 Clash/sing-box 的 IPv6 fake-ip 段 fc00::/18 属 ULA,本就不在 reserved 内、无法被列入。只配 IPv4 条目即可。
  • 解析结果为空或行形状不合法时报 WEB_PROVIDER_ERROR 而非 WEB_BLOCKED_URL,与上游 resolvePublicAddresses 的分界一致。这个区分是必需的:本插件只在 WEB_BLOCKED_URL 上重试, 把「什么都没解析到」当成策略拒绝只会把同一次空查询再跑一遍。
  • 兜底失败时上报的是兜底自己的错误。 请求已经通过豁免解析、却在解析之后失败(跨域重定向、 超出体积上限、连接被拒)时,守卫的 non-public IP address 与真实原因无关;照搬它会让 「插件没生效」和「站点抓不动」看起来一模一样,把排查引向一个本来正常工作的 resolver。 只有两条路径都以 WEB_BLOCKED_URL 拒绝同一目标时,豁免才确实不适用,此时保留守卫措辞。
  • 上游的守卫仍是第一道防线;只有它拒绝后才走放宽路径。

开发

pnpm install            # 在仓库根执行一次;ESM 的裸导入按文件位置解析,与 cwd 无关
node build.mjs          # src/ → lib/(prepare / prepack 自动跑)
node --test tests/      # 在包目录内执行

lib/ 不入版本控制。

测试与运行依赖 @deepseek-ai/dsh-web(取 WebError,错误码才不会被上游重写)、 @deepseek-ai/dsh-web-fetch-http、@deepseek-ai/schemastery 与 ipaddr.js, 由仓库根的 pnpm install 装到本包的 node_modules。ESM 的裸导入按文件位置解析, 所以换到任何目录、用绝对路径跑 node --test <包>/tests/ 都能找到依赖。