Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

tracking: 外呼与输入校验面(4 项)—— 外部可控输入未被当作不可信数据

Abierto
#191 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
12/100
Tipo de issue
Error
Claridad
Necesita aclaración
Estado de actividad
Activo
Stack tecnológico
rust, shell

Línea de trabajo

This is a tracking issue with no code of its own; the four child issues (#169 to #172) are the real work, and they must land in order. Start by reading src/tools/policy_domain/url_guard.rs around validate_url and is_private_ip, then src/tools/http_call.rs (encode_segment, build_url), src/tools/a2a.rs (the base interpolation and task_id check), and src/tools/install_skill.rs (slug and extract_zip). Done means the four cluster acceptance checks in the body pass, each with negative tests for the bypass cases.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

优先级 P1 · 依赖:无 · 类型:tracking(簇设计入口,不产出代码;子单全部落地后关闭)
记号:正文中的「不变量 #N」指 .dev/AGENTS.md 编号,不是 issue 编号;#150 这类才是 issue。本簇子单头部写 depends-on: #N 表示落地串行(同文件/同契约),不会与语义阻塞混淆。

基线 HEAD = 14bac15a(子单同基线)。本单来自一次全仓审计:先按根因归簇,再拆成可独立验收的子单。

共同根因

四条都是「外部可控输入没有被当成不可信数据」,集中在 url_guard 与两个外呼工具:

  1. SSRF 只查字面主机(#169):src/tools/policy_domain/url_guard.rs:58-145 对 Host::Domain 仅拦 localhost/*.localhost/metadata.google.internal 与 IP 字面写法,全仓无自定义 DNS resolver(连接期不校验),且 is_private_ip 漏 100.64/10、0/8(除 0.0.0.0)、::a.b.c.d、64:ff9b::/96。169.254.169.254.nip.io 这类名字直接放行(重定向绕过已由 Policy::none 关闭,剩下的就是 DNS 面)。
  2. 路径逃逸(#170):src/tools/http_call.rs:437-447 的 encode_segment 保留 .,整段 .. 经 build_url(:577-586)交给 Url::set_path 后被解析消解,且不校验最终 path 是否仍以运维 base path 为前缀 → 请求打到网关根路径并带着运维鉴权头。
  3. shell 注入(#171):validate_url 返回原始输入(url_guard.rs:121),a2a_call 把 base 直接插进单引号命令(src/tools/a2a.rs:272-292),只对 task_id 做了白名单(:389-409)→ 提前闭合引号的 curl|sh 可被注入。
  4. 技能安装缺完整性(#172):install_dir 放行 slug "."(src/tools/install_skill.rs:150-156)→ dest 变成共享根;extract_zip 剥第一层后可在 dest 内覆盖兄弟技能(:228-258);默认跟随重定向、read_to_end 无上限(:290-338、:350-363);slug 未编码即拼 URL(:454-497)。

子单与依赖

子单 依赖 范围
#169 无 自定义 reqwest Resolve:解析结果过 is_private_ip 再连;补齐私网/保留段
#170 depends-on: #169 拒绝 ./.. 参数;断言最终 path 以 base path 为前缀
#171 depends-on: #170 用规范化 Url;拒绝 ' " \ $ \ 空白`;优先 argv/env 传参
#172 depends-on: #171 slug 白名单 [A-Za-z0-9_-]+;redirect(none) + sha256 pin;entry/总量双上限

落地思路

  1. #169 不需要新依赖(不变量 #6 不受影响):本机 reqwest 0.12.28 已提供 ClientBuilder::dns_resolver 与 reqwest::dns::Resolve;把校验放在 resolver 里比放在 validate_url 里更难绕过(连接期生效)。
  2. 顺序按同文件串行:#169 与 #171 都动 url_guard.rs,#170/#171 都动 src/tools/ 与 URL 拼装,#172 依赖同一套出站校验约定。
  3. #170 加的是不变量式断言(final_url.path().starts_with(base_path)),比逐个过滤字符更可靠。
  4. 与 B 簇在 url_guard.rs(validate_url 被权限面复用)相邻;与 G 簇在 src/tools/ 相邻,按全局顺序错开。

簇级验收

  1. WebFetch/a2a 对 nip.io 形态、DNS rebinding 形态与 100.64.x.x 全部被拒;
  2. http_call 的 id=".." 构建失败;正常 id="42" 路径为 /{base}/42;
  3. a2a_call 收到含 '/空白/; 的 URL 时返回错误而非生成可执行命令;
  4. install_skill 拒 slug ".";含 ..//绝对路径/超限的 zip 被拒且无文件落盘。

边界

不含鉴权与租户身份(G 簇 #152、#146);不含 SKILL 内容来源的信任模型(#64/#76/#77 已定的 SkillSource 边界)。

实施顺序、PR 拆分与验收矩阵(补充 · 2026-10-10)

顺序(四单同域,串行):#169 → #170 → #171 → #172。

理由:#169 与 #171 都动 url_guard.rs;#170 与 #171 都动 URL 拼装;#172 依赖同一套"出站 URL 必须经校验"的约定。四单都不大,串行成本可接受。

跨簇重叠:url_guard.rs(validate_url)被权限面复用(B 簇 #153 只改工具名,不改 URL 逻辑,实际不冲突);src/tools/ 目录级重叠与 G 簇相邻。

PR 拆分:一子单一 PR。每个 PR 附"绕过尝试"的负向测试(本簇的价值就在于此)。

簇级验收 → 测试映射:见正文四条,落在 src/tools/policy_domain/url_guard.rs、src/tools/http_call.rs、src/tools/a2a.rs、src/tools/install_skill.rs 的单测中;#169 建议加一个 reqwest resolver 的单测(可用 Resolve trait 的纯函数部分)。

Lenguaje dominante
Rust
Estrellas
4
Forks
0
Merge medio
5 h 32 min
PR fusionados (30 d)
7

Preparar el entorno

  • Incluye un Dockerfile o un archivo de Docker Compose
  • Sin plantilla de pull request
  • Sin guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de jeffkit/recursive

Todos los issues de jeffkit/recursive

Issues similares

Más issues de Rust

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.