实战指南:`omx url read` 的安全边界与结构化输出)
OmX 被动 URL 读取器Passive URL Reader实战指南omx url read的安全边界与结构化输出【免费下载链接】oh-my-codexOmX - Oh My codeX: Your codex is not alone. Add hooks, agent teams, HUDs, and so much more.项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-codexomx url read url --json是 OmXOh My codeX提供的一项被动式、有界的 URL 读取能力它只负责把用户提供的 HTTP(S) 地址抓取回来并以结构化 JSON 交付给自动化流程全程不依赖浏览器、不注入 Cookie、不解挑战码也不修改任何全局二进制或PATH。本文以 docs/url-reader.md 为骨架结合 src/url-reader/index.ts 的真实实现与测试用例完整讲解命令用法、JSON 输出结构、网络地址封锁策略、DNS 防重绑定机制与重定向校验并给出可在仓库内直接复现的验证方式。读完你将能安全地把抓取一个 URL封装进自己的脚本、Agent 或自动化管线。一、命令总览一个命令四类结果omx url read的 v0 读取器在设计上刻意保守它的定位不是万能抓取器而是可审计、可自动化的安全读取器。CLI 帮助文本见 src/cli/url.ts明确列出了它的约束不启动浏览器自动化也不引入浏览器依赖不注入 Cookie、不解挑战、不做任何反机器人检测绕过不改变全局二进制或PATH的所有权仅支持http:与https:协议在抓取之前就封锁本地、回环、私网、链路本地、唯一本地、组播、保留及内部网络地址IPv6 特殊用途/保留地址段全部封锁细节见下文第三节主机名在抓取前先解析任何解析出不安全地址的主机名都会使用与字面 IP 相同的分类器被拦截重定向被手动跟踪且每一个重定向目标在被请求前都会重新校验在文本解码之前对响应体做有界读取输出结构化verdictok、redirect、blocked或error。命令格式如下omx url read https://example.com --jsonCLI 层面的参数解析位于 src/cli/url.ts 的parseUrlReadArgs--json输出结构化 JSONv0 阶段必填缺省会直接报Missing required --json flag-h, --help/help打印帮助其余以-开头的 token 一律报Unknown option恰好接受一个位置参数URL多余或缺失都会报错。CLI 命令在 src/cli/index.ts 中被分发到urlCommand最终调用核心函数readUrlsrc/url-reader/index.ts。二、JSON 输出结构每一个字段的含义读取结果由 src/url-reader/types.ts 的UrlReadResult定义字段如下字段类型说明input_urlstring用户最初输入的 URLfinal_urlstring | null重定向后的最终 URL被封锁或出错时为nullverdictok|redirect|blocked|error整体判定结论statusnumber | nullHTTP 状态码如 200、302、403status_textstring | null状态文本如OK、Forbiddencontent_typestring | null响应头的Content-Typeredirectedboolean是否发生过重定向titlestring | null文本类响应的 best-effort 标题HTMLtitle最长 200 字符snippetstring | null文本类响应的摘要剔除 script/style 与标签后截取 500 字符signalsstring[]封锁/挑战信号列表见下truncatedboolean响应体是否因超过maxBytes被截断bytes_readnumber实际读取的字节数errorUrlReadError | null失败时的安全错误细节{ name, message, code? }一个成功抓取 HTML 的典型输出大致形如{ input_url: https://example.com/, final_url: https://example.com/, verdict: ok, status: 200, status_text: OK, content_type: text/html; charsetutf-8, redirected: false, title: Example Domain, snippet: Example Domain This domain is for use in illustrative examples ..., signals: [], truncated: false, bytes_read: 1256, error: null }verdict 的四态语义ok成功取得响应且没有检测到封锁/挑战信号redirect请求最终停在一个重定向后的 URLfinal_url与input_url不同响应本身仍可读blocked目标被安全策略拦截——可能是不支持的协议、不安全地址、本地主机名、HTTPS 主机名无法钉扎pinning、非法/过多重定向或响应体命中封锁信号见signalserror抓取过程中发生运行时错误如ECONNREFUSED、DNS 解析失败、fetch 实现不可用。signals封锁与挑战信号src/url-reader/index.ts 的classifySignals会把两类信息写进signals状态码信号401、403、407、423、429、451、503会生成status-code如status-403挑战内容信号对响应文本做正则匹配src/url-reader/index.ts 的CHALLENGE_MARKERS包括captcha-marker匹配captchacloudflare-marker匹配cloudflare|cf-chl|cf_clearanceaccess-denied-marker、just-a-moment-marker、human-verification-marker、bot-detection-marker、blocked-marker、challenge-marker只要signals非空verdict就会被置为blocked。测试 src/url-reader/tests/url-reader.test.ts 验证了403 Just a moment Verify you are human这类反爬页面会被同时标记status-403、just-a-moment-marker与human-verification-marker。三、网络地址封锁策略字面 IP 与主机名共用同一分类器这是 v0 读取器最核心的安全设计。无论目标是 IP 字面量还是主机名最终都汇入同一套地址分类器isSafeIpAddresssrc/url-reader/index.ts。IPv4 封锁规则isSafeIpv4src/url-reader/index.ts以下地址段在抓取前一律拦截0.0.0.0/8a 010.0.0.0/8私网127.0.0.0/8回环224.0.0.0/4及以上组播/保留100.64.0.0/10运营商级 NATa 100 64 b 127169.254.0.0/16链路本地含云元数据地址169.254.169.254172.16.0.0/12私网192.168.0.0/16私网192.0.0.0/24IANA 保留192.88.99.0/246to4 relay anycast198.18.0.0/15、198.51.100.0/24基准测试/文档地址203.0.113.0/24文档地址IPv6 封锁规则isSafeIpv6src/url-reader/index.tsIPv6 判定把地址转成大整数后逐段比较封锁范围与原文档完全一致全零::与::1回环/未指定64:ff9b:1::/48本地使用的 IPv4/IPv6 翻译NAT64100::/64仅丢弃discard-only、100:0:0:1::/64伪前缀dummy2001::/23IETF 协议分配2001:db8::/32与3fff::/20文档/保留2002::/166to45f00::/16Segment Routing SIDfc00::/7唯一本地地址ULAfe80::/10链路本地ff00::/8组播IPv4 映射地址::ffff:a.b.c.d会先解包成 IPv4 再走isSafeIpv4判定因此::ffff:10.0.0.5、::ffff:127.0.0.1、::ffff:169.254.169.254全部被拦公网 IPv6如2606:4700:4700::1111允许访问测试 src/url-reader/tests/url-reader.test.ts 用一组不安全 IPv6 字面量逐一断言先封锁、绝不发起抓取同时验证公网 IPv6 字面量返回ok。主机名解析与 DNS 防重绑定对主机名目标validateAndResolveSafeUrl 会先调用dnsLookup(hostname, { all: true, verbatim: true })解析出全部地址只要任意一个解析结果不安全整个读取即被封锁signal 为unsafe-address。localhost、localhost.localdomain以及*.localhost后缀的主机名直接以localhost-name拦截不会进入解析流程。更关键的是防 DNS 重绑定/TOCTOU 设计src/url-reader/index.ts 的prepareSafeFetchTargetHTTP 主机名抓取请求不会发给原主机名而是发给已校验并钉扎pin的 IPfetchUrl.hostname validation.address同时用Host头携带原始主机名含端口见originalHttpHostHeadersrc/url-reader/index.ts。测试 src/url-reader/tests/url-reader.test.ts 证明http://example.test:8080/page实际请求的是http://93.184.216.34:8080/page且Host: example.test:8080防重绑定测试src/url-reader/tests/url-reader.test.ts则证明同一主机名只解析一次绝不给第一次返回公网 IP、第二次返回 127.0.0.1的翻转攻击留机会HTTPS 主机名由于当前 v0 运行时而无法在钉扎 TCP 连接的同时安全保留 TLS SNI/证书校验因此直接封锁signalhttps-hostname-not-pinned见测试 src/url-reader/tests/url-reader.test.ts公网 IP 字面量的 HTTPS不涉及主机名解析直接放行测试 src/url-reader/tests/url-reader.test.ts。四、重定向手动跟随逐个校验v0 读取器不依赖 fetch 内置的自动重定向而是设置redirect: manual手动处理src/url-reader/index.ts只认301、302、303、307、308为可跟随重定向读取Location头以当前 URL 为基准解析出下一个目标解析失败直接按unsafe-redirect-url封锁跟随前对下一个目标再次执行完整的prepareSafeFetchTarget校验——包括协议、本地主机名、DNS 解析与地址分类每跳都重复上述过程超过maxRedirects默认 10即按too-many-redirects封锁。因此任何把用户重定向到http://localhost/secret、10.0.0.2、[::1]或解析到私网地址的攻击在下一跳发生前就会被拦截测试 src/url-reader/tests/url-reader.test.ts 覆盖了重定向到 localhost、私网 IPv4、不安全 IPv6 字面量以及解析出不安全 IPv6 的主机名四类场景均断言只发出了第一跳请求。五、有界读取在解码之前截断响应体通过readBoundedBodysrc/url-reader/index.ts按字节上限流式读取默认maxBytes 256 * 1024256 KiB。读取逻辑逐块累积达到上限后立即停止并取消底层流reader.cancel()恰好等于上限的完整响应不算截断测试 src/url-reader/tests/url-reader.test.ts超过上限的响应标记truncated: truebytes_read为实际读取字节数测试 src/url-reader/tests/url-reader.test.ts 验证 128 字节上限下bytes_read 128且流被取消。文本解码发生在有界读取之后优先使用Content-Type中的charset缺省按 UTF-8TextDecoder容错模式src/url-reader/index.ts。只有文本类响应text/*、application/json、application/xml、application/xhtmlxml、application/rssxml、application/atomxml、application/ldjson见 src/url-reader/index.ts才会提取title与snippetsnippet生成前会先剥离script/style与所有标签并解码常见 HTML 实体src/url-reader/index.ts。六、可调参数与可注入依赖虽然 CLI 的 v0 形态只暴露--json但核心函数readUrl(url, options)的UrlReaderOptionssrc/url-reader/types.ts为嵌入方提供了四个可调项与两个注入点选项默认值说明timeoutMs15_000单次请求超时毫秒通过AbortSignal.timeout生效maxBytes256 * 1024响应体读取上限字节maxRedirects10最大重定向跳数fetchglobalThis.fetch可注入自定义 fetch 实现如测试中的桩实现resolveHostnamenode:dns/promises的lookup可注入自定义 DNS 解析器测试用它模拟公网/私网解析结果其中timeoutMs、maxBytes、maxRedirects均需为正整数非法值自动回退到默认值normalizePositiveIntegersrc/url-reader/index.ts。这正是 src/url-reader/tests/url-reader.test.ts 与 src/cli/tests/url.test.ts 能够在不联网的前提下完整验证全部安全行为的原因。七、错误处理只暴露安全细节任何异常都会被normalizeErrorsrc/url-reader/index.ts收敛为{ name, message, code? }绝不携带堆栈。测试 src/url-reader/tests/url-reader.test.ts 明确断言一个带code: ECONNREFUSED的异常返回verdict: error、error.name Error、error.code ECONNREFUSED且 JSON 化后的结果不包含 stack。同时读取器会校验运行时是否存在 fetch 实现缺失时返回FetchUnavailableErrorsrc/url-reader/index.ts。八、在仓库中验证与扩展如果你想在本地复现或深入调试运行单元测试覆盖全部封锁规则、重定向校验、截断与标题/摘要提取node --test src/url-reader/__tests__/url-reader.test.ts node --test src/cli/__tests__/url.test.ts其中 src/cli/tests/url.test.ts 还会通过dist/cli/omx.js真实执行omx url --help断言帮助文本与被动读取不绕过挑战、不使用浏览器、不注入 Cookie的承诺出现在顶层及子命令帮助中。CLI 实跑需要先完成构建得到dist/cli/omx.jsnode dist/cli/omx.js url read https://example.com --json阅读实现入口 src/url-reader/index.ts、类型 src/url-reader/types.ts、CLI 封装 src/cli/url.ts、命令分发 src/cli/index.ts。总而言之omx url read --json把读取一个 URL封装成了一个默认安全的原语地址封锁发生在任何网络请求之前主机名解析被钉扎到已校验 IP重定向每跳重新校验响应体有界读取输出全程结构化 JSON。对于需要把抓取外部网页接入 Agent、HUD 或自动化脚本的开发者而言这是一份开箱即用且可审计的参考实现。【免费下载链接】oh-my-codexOmX - Oh My codeX: Your codex is not alone. Add hooks, agent teams, HUDs, and so much more.项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-codex创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考