mcpField
I
irify-sast

irify-sast

github.com/yaklang安全443 次安装35 周前更新
代码仓库

关于 irify-sast

irify-sast 是面向编码 Agent 的静态分析技能,使用 IRify 的 SSA 编译器和 SyntaxFlow 查询跨文件数据流。它通过 yaklang MCP 服务支持查询驱动的安全分析与增量重新编译。

主要功能

  1. 01基于 SSA 的跨文件分析
  2. 02SyntaxFlow 查询指导
  3. 03增量编译流程
  4. 04按用户意图构造安全查询
  5. 05查询错误重试指导

适用场景

  1. 01追踪代码中的用户输入
  2. 02查找指定的漏洞路径
  3. 03定位调用方和数据去向
  4. 04代码变更后更新分析

MCP 相关

需要配置 yaklang MCP 服务,并使用 `yak mcp -t ssa` 命令。该技能通过其 SSA 编译与查询能力进行分析。

安装

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

$npx skills add https://github.com/yaklang/irify-sast-skill --skill irify-sast

安装后在 Claude Code 中输入/irify-sast确认已加载。

附带文件nativecall-reference.mdsyntaxflow-examples.md

详细介绍

IRify SAST

由 IRify 的 SSA 编译器和 SyntaxFlow 查询引擎驱动的深度静态分析技能。

前置条件

此技能需要 yaklang MCP 服务。请在 Agent 的 MCP 设置中进行配置:

# Codex: ~/.codex/config.toml
[mcp_servers.yaklang-ssa]
command = "yak"
args = ["mcp", "-t", "ssa"]
// Claude Code / Cursor / others
{ "command": "yak", "args": ["mcp", "-t", "ssa"] }

工作流:引擎优先(sf → read → rg)

重要:始终遵循引擎优先漏斗模型。SSA 引擎可以同时查看所有文件中的跨过程数据流,而 grep 做不到。查询前不要使用 grep/rg 构建“候选文件池”。应先让引擎充当雷达。

步骤 1:编译(每个项目一次,自动缓存)
ssa_compile(target="/path/to/project", language="java", program_name="MyProject")
→ full compilation, returns program_name

自动缓存:如果程序已编译且源文件未更改,引擎会立即返回 [Cache Hit],无需重新编译。始终提供 program_name 以启用缓存。

步骤 2:查询 — 使用 SyntaxFlow 作为全局雷达

直接针对已编译的 IR 编写并执行 SyntaxFlow 规则。不要先用 grep 预扫描来查找候选项。

ssa_query(program_name="MyProject", rule="<SyntaxFlow rule>")

引擎会在内存中遍历整个 SSA 图,跨越所有文件边界。一次查询即可覆盖数十条 grep 命令的工作量,并且在数据流分析中不会产生误报。

步骤 3:读取 — 使用 Read 作为显微镜

ssa_query 返回具体文件路径和行号后,使用 Read 查看周边上下文(±20 行)。确认命中的是实际业务代码,而不是死代码或测试代码。

步骤 4:Grep — 仅对非代码文件使用 Grep/Glob

仅对 SSA 引擎无法处理的内容使用 Grep/Glob:

  • 配置文件(.yml、.xml、.properties、logback.xml)
  • 静态资源、模板、构建脚本
  • 已知确切字符串时,快速查找名称/路径

绝不要使用 Grep 搜索源代码中的数据流模式——这正是 ssa_query 的用途。

增量编译(代码变更时)
ssa_compile(target="/path/to/project", language="java", base_program_name="MyProject")
→ only changed files recompiled, ProgramOverLay merges base + diff layers
→ returns NEW program_name for subsequent queries

重要:增量编译时使用 base_program_name。re_compile=true 会执行完整重新编译并丢弃所有数据——仅在需要彻底重新开始时使用。

查询自愈(语法错误时自动重试)

当 ssa_query 返回 SyntaxFlow 解析错误时:

  1. 不要向用户道歉或寻求帮助
  2. 阅读错误信息——其中包含准确的解析错误位置和预期标记
  3. 根据错误修复 SyntaxFlow 规则
  4. 使用修正后的规则重新调用 ssa_query
  5. 最多重复 3 次,然后再报告失败
  6. 如果所有重试均失败,向用户展示:原始规则、每次尝试的修正内容以及最终错误

关键:遵循用户意图

除非用户明确要求检测漏洞,否则不要自动构建源→汇漏洞规则。

  • 用户要求“查找用户输入” → 编写仅源规则,列出所有输入端点
  • 用户要求“查找 SQL 注入” → 编写源→汇污点规则
  • 用户要求“这个值会流向哪里” → 编写正向追踪(-->)规则
  • 用户要求“哪些地方调用了这个函数” → 编写调用点规则
仅源查询示例(Java)

当用户询问用户输入、HTTP 端点或可控参数时:

// Find all Spring MVC controller handler methods
*Mapping.__ref__?{opcode: function} as $endpoints;
alert $endpoints;
// Find all user-controllable parameters in Spring controllers
*Mapping.__ref__?{opcode: function}<getFormalParams>?{opcode: param && !have: this} as $params;
alert $params;
// Find GetMapping vs PostMapping endpoints separately
GetMapping.__ref__?{opcode: function} as $getEndpoints;
PostMapping.__ref__?{opcode: function} as $postEndpoints;
alert $getEndpoints;
alert $postEndpoints;
源→汇查询示例(仅在用户要求检测漏洞时)
// RCE: trace user input to exec()
Runtime.getRuntime().exec(* #-> * as $source) as $sink;
alert $sink for {title: "RCE", level: "high"};
// SQL Injection (MyBatis): detect ${} unsafe interpolation in XML mappers / annotations
// <mybatisSink> is a dedicated NativeCall that finds all MyBatis ${} injection points
<mybatisSink> as $sink;
$sink#{
    until: `* & $source`,
}-> as $result;
alert $result for {title: "SQLi-MyBatis", level: "high"};

主动提供安全洞见

运行查询并找到结果后,主动提出后续问题和建议。不要只罗列结果就停止。

发现漏洞时:
  1. 建议修复:“此 exec() 调用接收了未经清理的用户输入。建议使用白名单,或使用参数明确分离的 ProcessBuilder。”
  2. 询问相关问题:
    • “要我检查是否还有其他端点也调用了 Runtime.exec() 吗?”
    • “要我追踪源和汇之间是否存在输入验证/清理吗?”
    • “要我在其他控制器中查找类似模式吗?”
  3. 交叉检查:如果发现一种漏洞类型,主动扫描相关类型:
    • 发现 RCE → “我还检查了 SSRF,并发现 2 个潜在问题。需要查看详情吗?”
未发现结果时:
  1. 不要只说“没有结果”——解释原因:
    • “没有发现直接的 exec() 调用,但我看到了 ProcessBuilder 的用法。要我改为检查这些用法吗?”
    • “查询匹配到 0 个汇点。这可能意味着代码使用了框架抽象——要我搜索特定于框架的模式吗?”
  2. 建议替代查询
结果模糊时:
  1. 请求澄清:“我发现了 8 条指向 executeQuery() 的数据流路径,但其中 5 条使用了参数化查询(安全)。要我筛选出仅使用字符串拼接的 3 条吗?”

配套参考文件

编写 SyntaxFlow 规则时,使用 Read 工具阅读以下文件,以了解语法并查看实际示例:

文件阅读时机路径(相对于此文件)
NativeCall 参考编写需要 <nativeCallName()> 函数的规则时——包含全部 40 多个 NativeCall 函数的语法和示例nativecall-reference.md
SyntaxFlow 示例编写新规则时——包含 20 多条适用于 Java/Go/PHP/C 的生产规则,按漏洞类型分类syntaxflow-examples.md

工作流:

  1. 阅读 syntaxflow-examples.md,查找类似的规则模式
  2. 需要 NativeCall?阅读 nativecall-reference.md
  3. 通过 ssa_query 编写并执行

SyntaxFlow 快速参考

搜索与匹配
documentBuilder          // variable name
.parse                   // method name (dot prefix)
documentBuilder.parse    // chain
*config*                 // glob pattern
/(get[A-Z].*)/           // regex pattern
函数调用与参数
.exec()                           // match any call
.exec(* as $params)               // capture all params
.parse(*<slice(index=1)> as $a1)  // capture by index
数据流运算符
运算符方向用途
#>向上 1 层直接定义
#->向上递归追踪到来源——“它来自哪里?”
->向下 1 层直接使用
-->向下递归追踪到最终使用位置——“它会去哪里?”
.exec(* #-> * as $source)            // trace param origin
$userInput --> as $sinks              // trace where value goes
$sink #{depth: 5}-> as $source       // depth-limited trace
$val #{
  include: `*?{opcode: const}`
}-> as $constSources                  // filter during trace
$sink #{
  until: `* & $source`,              // stop when reaching source
}-> as $reachable
筛选器 ?{...}
$vals?{opcode: call}                // by opcode: call/const/param/phi/function/return
$vals?{have: 'password'}            // by string content
$vals?{!opcode: const}              // negation
$vals?{opcode: call && have: 'sql'} // combined
$factory?{!(.setFeature)}           // method NOT called on value
变量、检查与告警
.exec() as $sink;                                      // assign
check $sink then "found" else "not found";             // assert
alert $sink for { title: "RCE", level: "high" };       // mark finding
$a + $b as $merged;                                    // union
$all - $safe as $vuln;                                 // difference
NativeCall(40 多个内置函数)

最常用的函数——完整列表请参阅 nativecall-reference.md:

<include('rule-name')>         // import lib rule
<typeName()>                   // get short type name
<fullTypeName()>               // get full qualified type name
<getReturns>                   // function return values
<getFormalParams>              // function parameters
<getFunc>                      // enclosing function
<getCall>                      // find call sites
<getCallee>                    // get called function
<getObject>                    // parent object
<getMembers>                   // object members
<name>                         // get name
<slice(index=N)>               // extract by index
<mybatisSink>                  // MyBatis SQL injection sinks
<dataflow(include=`...`)>      // filter data flow paths

提示

  1. #-> =“它来自哪里?”,--> =“它会去哪里?”
  2. 参数使用 *,不要硬编码名称
  3. SSA 会解析赋值:a = getRuntime(); a.exec(cmd) = getRuntime().exec(cmd)
  4. 使用 opcode 筛选器区分常量/参数/调用
  5. 结合使用 check + alert,以获得可执行的结果
  6. 代码变更后,使用 base_program_name(而非 re_compile)进行快速增量更新
  7. 编写新规则前,阅读 syntaxflow-examples.md,查找类似模式
  8. 不确定 NativeCall 用法时,阅读 nativecall-reference.md,查看用法和示例

评论

支持 Markdown