mcpField
A
analyze-external-methods

analyze-external-methods

github.com/seqra安全17059 次安装1 天前更新
代码仓库

关于 analyze-external-methods

analyze-external-methods 是用于分析 OpenTaint 未能建模方法的技能,可判断方法是否传递数据,并可选判断其是否为安全 sink。它将分类结果写入跟踪文件,并检查批次中的方法是否全部覆盖。

主要功能

  1. 01分类方法的数据传递行为
  2. 02可选识别安全 sink
  3. 03将方法分类记录到 YAML
  4. 04检查批次分类覆盖情况
  5. 05复核跳过项以排查遗漏传递

适用场景

  1. 01为近似建模工作分类未跟踪方法
  2. 02深度分析时识别 sink 方法
  3. 03确认计划中的方法均已分类

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/seqra/opentaint --skill analyze-external-methods

安装后在 Claude Code 中输入/analyze-external-methods确认已加载。

附带文件references/java.mdscripts/check-coverage.py

详细介绍

技能:分析外部方法

OpenTaint 是一种数据流污点分析器:它从来源引入的数据开始,逐次跟踪每次调用,直到数据流停止。数据流停止有两种原因——数据到达了一个不会保留任何数据的可调用对象(对受污染的集合调用 size(),其全部内容会折叠成一个数字,因此污点消失);或者到达了一个分析器无法查看其函数体的可调用对象,通常位于外部依赖中。这个不透明的可调用对象可能会消除污点,也可能实际上会继续传递数据——这种情况下,就需要一个近似规则,准确告知引擎数据如何通过该调用传递,否则每条经过它的跟踪路径都会被悄然截断。

你将获得这些被丢弃方法的列表。请判断哪些方法确实会传递数据,哪些不会;对于每个会传递数据的方法,确定它需要哪种近似规则,以便构建阶段恢复数据流。

在深度运行中,这些相同的方法还要回答另一个独立问题——该调用本身是否属于危险操作(即汇点);此检查是步骤 2。

输入

由调用方提供,省略时使用默认值。仅当缺少必需输入且没有合理默认值时才询问

  • project-root(可选)——目标项目的根目录。Opentaint 会将所有分析产物保存在固定的 <project-root>/.opentaint/ 目录下,因此下文所有 .opentaint/... 路径都会在此目录下解析。默认值:当前目录
  • language(必需)——此项目的目标语言及特定语言的说明
  • plan(必需)——此 Agent 的批次计划文件路径 .opentaint/tracking/approximations/plans/<batch>.yaml:要分类的被丢弃方法
  • sinks(可选)——是否按照步骤 2 对汇点进行分类的标志

工作流

1. 分类传播方式

从计划的 scopes 中获取成员,逐一判断,并将判定结果写入批次文件 .opentaint/tracking/approximations/<batch>.yaml——<batch> 是计划文件名(不含扩展名),也就是批次 ID;下文的覆盖率检查会再次使用它。

始终根据方法的实际代码进行分类,绝不能根据方法名判断。先阅读方法的源代码——如何获取源代码,请参阅 <skill-dir>/references/<language>.md。然后针对每个方法回答:它的输入数据去了哪里?接收方或参数中收到的数据,会通过返回值、方法写入数据的参数、接收方,还是存储数据的对象或字段传递出去?答案决定了它所属的类别:

  • passthrough——方法通过简单复制,将数据从一个位置传到另一个位置:getter、简单的参数到结果的复制、构建器、将参数存入接收方或其他对象的写入器、先放入再取出的集合操作等
  • dataflow——方法通过函数、lambda 或回调参数,或异步链传递数据。当受污染的数据确实经过这类函数式参数时,优先选择此项而非 passthrough。如果数据确实在传播,但不是通过可以明确指出、连接各位置的简单复制来传播——也就是说,传播方式不透明,或过于复杂,难以描述为 passThrough 边——也应优先选择此项,因为 dataflow 模型基于真实代码,可以复现任何行为
  • skipped——方法没有传递数据,并填写简短的 reason。这正是数据流结束的地方。以下是几个例子:只测试、比较或测量输入的谓词或检查器(返回布尔值或数字);将数据折叠为不再包含原数据的标量的转换(例如大小、解析为数字、单向哈希——前言中的 size());不保留任何数据的副作用。这些只是说明理念的示例,并非封闭列表——许多方法和情况不在其中,因此要根据每个方法的实际代码进行判断

常见陷阱是忽略隐式载体——也就是把数据传到普通返回值以外位置的方法。会将参数写入接收方或其他对象的 void 方法仍然会传递数据:数据会保留在该对象中,数据流也会继续。清理器或编码器也是载体——它会返回经过转换的输入副本,因此数据会经过该方法继续流动;应对其建模,绝不能跳过(转换是否真的能消除污点,将由后续规则处理,不在此处判断)。拿不准时就建模:高估一个不传递数据的方法,代价很低;漏掉真正的载体则会造成运行中无法补救的假阴性。

不透明的外部键值存储——缓存、Redis/DB 模板、会话映射,以及任何将数据存放在进程之外的存储——也是载体,而不是死路:通过 put/set 写入的值会在整个往返过程中保留,因此后续的 get 可以返回攻击者控制的数据。两端都应建模为 passthrough。只有不传递值的键值或布尔操作——delete、exists、hasKey、size——才应标记为 skipped。

每个条目都要记录计划中的方法 signature,以区分重载——传播方式不同的重载应各自作为独立条目。此处只需将每个方法归入相应类别;构建阶段会读取方法自身的代码,精确建模数据的传递方式。

被跳过的方法也可能是应用内部方法,而不只是库代码——如果分析器无法读取其方法体(例如方法是 native、abstract 或 generated),就会跳过它。仍应根据代码的行为按同样方式进行分类。

2. 分类汇(仅深度运行)

设置 sinks 输入时,需要对同一批成员进行第二轮检查,以判断另一项属性:该方法是否为汇?汇是指安全敏感操作:一旦攻击者可控的数据到达该操作,就会形成漏洞。调用会执行或解释其输入,或针对敏感资源处理输入,且存在可被利用的方式,例如将其作为查询或 OS 命令运行、用作文件路径或 URL、对其反序列化、将其渲染到输出中,或通过反射或命名/目录查找解析它,以及其他许多情况。

根据方法自身的代码和行为判断它是否为汇,不要考虑项目如何使用它——不要追踪污点是否实际能到达该调用,这是分析器的工作。同时,要将汇判断与传播判断分开:传播结论不能决定它是否为汇,发现汇也不会改变传播结论。汇可能出现在你刚建模的传递者中,skipped 方法也可能是汇——不向后传递任何内容,并不能说明调用本身没有危险。

根据 Tracking 的要求,将每个汇记录在其所属包的汇单元 .opentaint/tracking/rules/sinks/<package-kebab>.yaml 中,并按描述其不安全用法的漏洞标签分组。先阅读 .opentaint/tracking/rules/tags.yaml,只要现有标签的语义合适,就重复使用。如果没有合适的标签,选择一个精确、规范的 kebab-case *-sink 标签,先将其添加到注册表,再创建该分组。标签应命名可复用的汇类别,而不是单个方法。

tags.yaml 是此次扇出中唯一共享的文件。只有确实需要新标签时才编辑它:在进行追加编辑前立即重新读取,并保留其他叶节点可能已添加的所有标签。使用已注册标签进行常规分类时,绝不写入注册表。

3. 验证覆盖率

完成分类后,从项目根目录对计划运行附带的检查:

uv run <skill-dir>/scripts/check-coverage.py --batch <batch>

传入你的 <batch>。它会列出尚未归入任何分类桶的所有批次方法。对它列出的每个方法进行分类,并重新运行检查,直到报告 0 UNCOVERED。仍有未覆盖项时,不要返回。

4. 再次验证跳过项

返回前,检查每个跳过的方法,并确认它确实不传递任何数据——仅凭名称不足以作为证据。对于代码证明属于传递者的方法,返回步骤 1 重新分类,并将其从 skipped 中移除。只保留已通过代码证明非传递者的方法

输出

简短扼要地报告完成的工作

产物:
  • .opentaint/tracking/approximations/<batch>.yaml — 批次分类(根据 Tracking)
  • .opentaint/tracking/rules/sinks/<package-kebab>.yaml — 每个包的汇单元(设置了 sinks 时)
摘要:
  • 按类型统计的方法数量(passthrough / dataflow / skipped),以及设置了 sinks 时的汇数量
  • check-coverage.py --batch <batch> 报告 0 UNCOVERED

Tracking

批次分类

.opentaint/tracking/approximations/<batch>.yaml — 一个批次的可调用项分类,<batch> 是计划的文件名主体。每个可调用项恰好属于一个判定类别,以计划中的语言特定 method 和 signature 精确标识,确保不同变体彼此区分:

  • passthrough、dataflow — 已建模的载体;每个条目为 { method, signature }
  • skipped — 终止型非载体;每个条目为 { method, signature, reason }
  • engine_issues — 单独的类别,用于引擎可证实无法传播的载体(已构建但仍被丢弃);每个条目为 { method, signature, reason }。这是终止类别,处理方式与 skipped 相同——唯一的区别是原因。merge-skipped 会将其带入 skipped.yaml,作为与常规跳过的 methods 并列的独立 engine_issues 组。

dependencies 列出数据流测试项目所需的依赖标识符。build 块跟踪构建情况——test_project 记录每个数据流方法的测试项目状态(如果样例已写入该批次的测试项目,则为 done;如果无法写入样例,因此该方法未纳入测试项目,则为 failed),done 则存放已完成的 { method, signature }。请通过注释明确说明

passthrough:
  - { method: "<qualified-member-a>", signature: "<language-signature-a>" }
dataflow:
  - { method: "<qualified-member-b>", signature: "<language-signature-b>" }
skipped:
  - { method: "<qualified-member-c>", signature: "<language-signature-c>", reason: "retains none of its input data" }
engine_issues: []
dependencies: []
build:
  test_project:
    - { method: "<qualified-member-b>", signature: "<language-signature-b>", status: done }
  done: []

此技能会写入 passthrough、dataflow、skipped 和 dependencies;请不要改动 build 块和 engine_issues——它们将在后续的近似处理阶段填写。

Sink 单元(仅当设置了 sinks 时)

.opentaint/tracking/rules/sinks/<package-kebab>.yaml — 每个包一个 sink 单元(一个依赖项可以包含多个包,每个包各有一个单元),文件以包名命名,并将 . 替换为 -。dependencies 指明该包所属的依赖项。groups 按可复用的 sink 标签划分包中的危险操作;每个组为 { tag, sinks },每个 sink 为 { method, signature, note, rule_id }。标签属于组,而非单个方法:一条规则可以覆盖多个方法,多个规则也可以扩展同一个漏洞类别。method 和 signature 使用计划中的精确语言特定标识符。stages 跟踪该单元的规则编写进度。请通过注释明确说明

dependencies:
  - <dependency-id>
groups:
  - tag: path-traversal-sink
    sinks:
      - { method: "<qualified-member>", signature: "<language-signature>", note: writes data to an untrusted path, rule_id: null }
stages:
  test_project: pending
  tests_passing: pending

这种划分会将整个包保留在同一个批次中,因此只填充计划中由你负责的单元。请将 rule_id: null 和 stages 留待规则编写阶段处理。如果某个包在之前的轮次中已有单元,请将新方法合并到匹配的标签组中,而不要重写该单元。

约束

OpenTaint 是一种整程序、过程间、字段敏感的别名分析引擎。它已经能够传播经过可见应用代码、调用、别名和单个字段的污点;自定义规则和近似处理仅用于对指定的 source、sink 或不透明方法边界进行建模。编译时常量和字面量不携带污点,因此输出仅为常量的 source 或载体不会引入任何内容。

  • 仅判断内在传播,绝不要按项目中的流向判断。不要因为某个载体的数据似乎无法到达此处的 sink 就跳过它,也不要根据数据的去向限制 sink——流是否到达 sink 是分析器的职责,不是你的
  • 分类计划分配的所有方法,且只分类这些方法——每个方法都是真正发生数据丢失的位置,不要在计划之外编造方法。返回前,check-coverage.py --batch 必须报告 0 UNCOVERED

评论

支持 Markdown