name: dataflow-analysis description: >- 跨函数白盒数据流追踪——基于 SyntaxFlow / yak MCP 的 SSA 引擎,对已知 source / sink 证明跨函数 source→sink 静态可达性,产出链路证据。source-sink 集合按调用者意图动态决定,不绑定具体漏洞 类型。 when-to-use: 当需要对 SAST 候选集做数据流确认、验证污点传播可达性,或需要分析 request→session、cookie→auth、owner→mapper 等跨函数调用链路时 allowed-tools: bash,read_file,list_files,rg mcp: [syntaxflow, yak] user-invocable: true argument-hint: "[target_path] [--lang java|go|python|js|php|c]" arguments: - target_path - lang
跨漏洞数据流追踪(SyntaxFlow MCP)
1. 触发线索 / 适用信号
本能力是 Cat-B-Cross 跨漏洞数据流工具,按"已定位 source/sink + 链路不可见性 + 跨端点共源"识别命中场景。起点既可来自直接阅读 / 框架侦察自定位的 sink,也可来自上游 sast-scan 候选——后者只是可选加速输入,其缺位 / 空命中不阻塞本能力(参 §5 起点来源)。
直接定位 sink 维度(主路径):
- 直接阅读源码 / 框架侦察 / grep 自盘点定位到的危险 sink(含项目自有 wrapper),需证跨函数 source 可达性
- 语义链路追踪:request→session、cookie→auth、owner→mapper 等跨函数调用链路需证可达
- 同一 source pool 被多个不同类型 sink 消费(用户输入既流入 SQL 又流入命令执行,需共用追踪起点)
承接 sast-scan 候选维度(可选加速):
- sast-scan 若已产出
needs_dataflow_confirmation桶条目——sink pattern 已命中但跨函数 source 可达性未证,可直接消费其file_location省去再定位 - sast-scan 命中
Runtime.exec(String[])/JdbcTemplate.queryForObject(sql, args)等"同名 API 既能拼也能参数化"的弱 sink,需追上游确认实际形态
已定位 source/sink 但中间链路问题维度:
- 调用栈多层(Controller → Service → DAO / Mapper)+ 中间过滤层 / wrapper / AOP 拦截不可见
- ORM 自定义封装层(项目自有
BaseRepository/CommonMapper/CmdUtil.run等)把标准 sink 藏在 wrapper 内 - 二阶链路:source 先 INSERT 后另一段 SELECT 回读拼接,需跨文件追写-读关系
- 反编译来的依赖(参 dependency-decompile 产物)需在恢复源码上重新追内部数据流
交叉端点维度:
- 多个端点(admin / user / api)handler 复用同一 service 方法——单点确认后需横扫所有入口
- 同
pattern_id的端点集合需统一证明可达性(避免逐 handler 重复手工追)
反向信号(不命中本能力):
- source 和 sink 都在同一函数内、且无 wrapper——直接看代码即可
- 单类漏洞已有动态可观测效果证据——本能力只做"静态链路证明",不再补做
- 项目无 SSA 编译产物且语言不被 SyntaxFlow 支持——走对应单漏洞 skill 的 grep + 人读 fallback
2. 造成原因
本能力 n/a(原因:跨多漏洞类型,单类漏洞成因归对应单漏洞 skill 的 §2。SQLi 成因看 code-audit/sast-scan 协作的 SQL 注入审计能力;命令注入成因看对应能力;反序列化成因看对应能力。本能力只提供「跨函数链路证明」工具,不承担成因解释)。
3. 领域 source-sink 数据流模型
本能力作为跨漏洞数据流工具,source-sink 集合按调用者意图动态决定(由直接阅读 / 框架侦察自定位、用户给定、或 sast-scan 候选等 source / sink 模式驱动);本节只描述通用框架与 SyntaxFlow 表达方式,不固化某类漏洞的 source-sink 列表——单漏洞专属集合归对应单漏洞 skill 的 §3。
通用 source 类别(按白盒视角)
- HTTP 请求字段:query / body / header / cookie / path 参数(具体 API 因框架而异,见 §6)
- 外部输入:文件读取 / 环境变量 / 命令行参数 / 配置文件 / 消息队列消费
- 数据库回读:先入库后再读出来的字段(二阶 source,写入点参数化 ≠ 回读点安全)
- 反序列化产物:JSON / XML / pickle / Java 序列化对象的字段
- 跨服务边界返回值:RPC / HTTP 调用返回的字段(信任级别取决于下游可信度)
通用 sink 类别(按白盒视角)
- 代码 / 命令执行:
Runtime.exec/eval/exec/os.system/ template engineevaluate - SQL 上下文:直接拼接 / ORM Raw / MyBatis
${}/ 字段名 / 排序方向位置 - 文件 / 路径:
new File/Path.resolve/ 上传写盘 / 模板加载 - 反序列化触发:
ObjectInputStream.readObject/pickle.loads/ Jackson polymorphic - 网络发送:
HttpClient.execute/requests.get/ SSRF 出站调用 - 响应输出:模板渲染 /
innerHTML/ 直接写 response body(XSS sink)
跨函数 / 跨文件追踪规则
- 跨函数:source 流向其他函数参数、流向类成员、流向闭包捕获变量;从 sink 反向追到 source 入口
- 跨文件:DAO / Repository / Service / Controller 调用链;按项目命名约定(
*Mapper/*DAO/*Service)建立调用图 - 跨框架边界:Spring
@Service/ Django middleware / Express middleware / AOP 拦截层是否引入过滤 - 跨依赖边界:闭源依赖内部数据流不可见,走 dependency-decompile 反编译后再续追(参 §11)
- 跨服务边界:本服务范围内追到出站 RPC / 消息发送即停,下游服务由对应 skill 独立审计
SyntaxFlow 表达方式(基础语法)
SyntaxFlow DSL 是 SSA 上的图查询语言。基本运算符:
- TopDef(向上追溯定义):
#>(一跳直接定义) /#->(递归到定义链起点) - BottomUse(向下追踪使用):
->(一跳直接使用) /-->(递归到使用链终点) - 限深控爆炸:
$x #{depth: 5}-> $y;/$x -{depth: 3}-> $y;(首次调试建议加,避免大项目 SSA 图爆炸) - 锚定 + 捕获:
Method.parameter() as $sink;(先选具体调用 / 方法链作 anchor,再用* as $arg捕获关心的参数) - Alert 输出:
alert $var for { message: "...", level: info };(每条规则至少一个 alert,否则视为无可见输出)
完整 DSL 语法 / Cookbook / 各类 sink 查询模板见 references/syntaxflow-cookbook.md。
4. 常见类型
本能力 n/a(原因:跨多漏洞类型,攻击变体属漏洞自身属性,归对应单漏洞 skill 的 §4。本能力只对"任意 source → 任意 sink 链路"做静态可达性证明,不分变体)。
5. 入口点定位
本能力的"入口点"是「已知 source / sink 端点 + 中间链路问题」——不像单漏洞 skill 从项目结构盘点 sink,本能力直接从上游候选或用户给定端点起步。
起点来源
起点来源并列,按上下文选,无固定优先级;sast-scan 候选只是可选加速输入,缺位时直接走自定位(grep / read / 框架侦察),不阻塞:
- 直接 grep / read 自定位 sink:直接阅读源码 / 框架侦察 /
rg盘点危险 sink(含项目自有 wrapper),按代码 pattern 锚定,再跨函数追 source。这是不依赖任何上游产物的主路径 - 用户给定端点:用户直接传入"某 Controller 方法 + 某 sink 形态",按代码 pattern 定位
- 单漏洞 skill 委托:单漏洞 skill 已自定位 sink 但跨函数链路追不动,转交本能力补链路证明
- sast-scan 候选承接(可选加速):若已有
shared/coverage-ledger/findings/sast-scan.jsonl里confidence=needs_dataflow_confirmation的条目,可直接读其file_location当 sink 位置省去再定位;source 由本能力跨函数追溯。无此产物或其空命中时不影响上述自定位路径
项目 SSA IR 准备
下列项目类型仅作示例,以目标实际栈为准。
- 用
yakMCP 加载目标项目,ssa_compile(target, language, program_name)编译到 SSA IR program_name用项目稳定名(如petclinic-main),便于复用查询- 编译失败 / 语言不支持时走 references/syntaxflow-cookbook.md 的 fallback 流程
查询起点选择
追踪方向有两个维度,按上下文选:
- 从 sink 反向追(推荐默认):sink 已知具体形态时——锚定 sink 调用,用 TopDef(
#->)追上游定义链。why:sink pattern 更具体、anchor 收敛性强,比从 source 正向追的 SSA 图爆炸面小 - 从 source 正向追:source 已知具体形态(如某 filter 输出值)、需要看流向哪些 sink 时——锚定 source 表达式,用 BottomUse(
-->)追下游使用链。why:交叉端点场景下用一次正向追覆盖多个 sink 比逐 sink 反向追高效 - 双向同时追(同时回答"从哪来 / 到哪去"):在已定位 sink 上同时跑 TopDef + BottomUse,参 §8 / cookbook 片段 E
前置探索 SSA IR 结构
直接写 SyntaxFlow 容易漏对项目实际 IR 形态,why:项目自定义 wrapper / 注解处理器 / Lombok 生成代码会让 IR 与源码长得不一样。用 ssa_query 先探索:
- 锚定一个已知的 sink 调用,跑
<ANCHOR>(* as $arg) as $call; alert $call,看实际匹配到的调用点是否符合预期 - 若匹配为 0 / 数量异常 → 检查 sink 表达式是否覆盖了项目自有 wrapper、注解形态、Lombok 生成方法名
6. 跨框架代码变体
本能力跨语言可用,不同语言项目的 SyntaxFlow 查询差异主要在 source / sink anchor 表达式以及 IR 形态——业务调用语义相同,但 anchor 写法不同。
下列框架 / 语言示例仅作类似项目示例,不限于此;以目标实际栈为准。
| 语言 / 框架 | source anchor 形态 | sink anchor 形态 | 项目 IR 差异要点 |
|---|---|---|---|
| Java / Spring | @RequestParam / request.getParameter / @RequestBody 注解参数 / request.getAttribute |
Runtime.getRuntime().exec / JdbcTemplate.queryForObject / EntityManager.createNativeQuery |
Bean 注入 / AOP 拦截 / Lombok 生成 getter — anchor 要覆盖代理类与原始类两套方法名 |
| Java / MyBatis | 同上 + service 方法参数 | Mapper 接口方法 + @Select / XML 模板 ${} |
XML 不在 SSA IR 内 — 需 grep XML + SSA 联合,参 cookbook 模板 F |
| Go | gin.Context.Query / Context.PostForm / Context.GetHeader / struct binding |
db.Raw / db.Exec(fmt.Sprintf(...)) / os/exec.Command / Runtime |
接口实现 / 方法集 — anchor 写接口方法时要兼容所有实现;channel 传值跨 goroutine 在 SSA 上是边断点 |
| Python / Django | request.GET.get / request.POST / request.META / form cleaned_data |
cursor.execute / Model.objects.raw / os.system / subprocess.Popen |
动态属性访问(getattr / **kwargs)— SyntaxFlow 静态追踪到此为边断点(参 §11) |
| JS / Node.js / Express | req.query.x / req.body.x / req.cookies / req.params |
sequelize.query / child_process.exec / template literal 拼接 |
CommonJS vs ESM 导入;解构赋值(const {x} = req.body)— anchor 要兼顾解构 alias |
| PHP / Laravel | $request->input / $_GET / Request::input |
DB::raw / whereRaw / eval / system |
Facade 静态代理(DB:: → \Illuminate\Support\Facades\DB)— anchor 写实际实现类 |
跨语言通用要点:
- 项目自有 wrapper / 工具类 anchor 必须显式纳入(如
CmdUtil.run/BaseRepository.rawQuery)—— 漏掉就误以为"项目无 sink" - 框架自动转义层(Spring HTML 转义 / Django 模板自动 escape)在 SSA 上看不到的位置——必须按框架文档判断是否引入过滤,参 §10 反例 3
- Lombok / 注解处理器 / 代码生成器(mybatis-generator)产出的代码——SSA IR 里方法名可能是生成后名字,要核对编译产物
7. 思考检查点
加载本 skill 时按这些问题思考(按 sink 语义、不按业务命名):
- source 表达式是否覆盖了所有可能的入口位置(路径参数 / body / header / cookie / 已入库回读字段)?漏掉任一类源就可能把"真实可达"误判为"不可达"
- sink 表达式是否覆盖了项目自有 wrapper / 封装层?规则只查标准库 API 时,项目把
Runtime.exec封装成CmdUtil.run会让命中数异常少 - 跨函数追踪到的"终点 sink"是真实危险操作还是中间 helper?helper 内部如果再调用真正的危险 API,需继续追下一跳
- 中间过滤层 / 白名单 / 类型转换是否被 SyntaxFlow 识别到?SSA 图能看到调用边但判断不了语义("过滤是否完整"是业务判断,本能力只给"路径上有无过滤调用"事实)
- 同一 sink 是否被多个入口点到达?每个入口点单独成行(参 §9 产物契约),禁止合并折叠
- 闭源依赖 / 反射调用 / 动态字符串构造是否标了
static-unknown?默认 not_vulnerable 是白盒最常见的诚信底线失守(参 §11)
8. 检测方法论 / 数据流追踪
本能力只到链路证明——动态利用证据由黑盒 / graybox 流程取证;单漏洞 confirmed 结论由对应单漏洞 skill 给出,本能力只提供"静态可达"素材。
总览
跨函数数据流追踪的核心动作:锚定(Anchor) → 捕获(Capture) → 追踪(Trace) → 输出(Alert)——这不是固定步骤编号,而是写每条 SyntaxFlow 规则的四个递进维度。完整 DSL 语法、各类 sink 查询模板、Alert 规范、限深策略见 references/syntaxflow-cookbook.md。
查询构造的递进维度(非编号步骤)
前置:SSA 编译 + IR 探索
ssa_compile(target, language, program_name)——若失败 / 语言不支持 → 走 cookbook 的 fallback 协议- 前置探索 SSA IR 结构(参 §5 末段)——直接写 SyntaxFlow 容易漏对项目实际 IR 形态。why:项目 wrapper / Lombok 生成代码 / 注解处理器会让 IR 与源码长得不一样,先用一两条粗查询核验 anchor 真能命中
锚定(Anchor):选具体调用点 / 方法链作起点(如 Runtime.getRuntime().exec / Files.write / .parse / .evaluate)
- 查询泛化的取舍:anchor 太宽(如
*.* #-> ...全局 wildcard)召回大量噪声 SSA 图爆炸;太窄会漏掉项目自有 wrapper。why:先按已知标准 API 锚定,命中后看是否需要把 wrapper API 加入 anchor 集合 - 项目自有 wrapper anchor 是否纳入——这是本能力区分于"只查标准库"的关键
捕获(Capture):用 * as $arg 把关心的参数 / 值抓出来
- 多参数 sink 要分清哪个参数是数据流终点(如
JdbcTemplate.queryForObject(sql, args)——sql是 sink 上下文、args是参数化绑定通道,追错参数等于追错语义)
追踪(Trace):默认 #->(必要时先 #> 粗定位);首次调试加 depth: 3~8 限深
- 反向(sink → source)vs 正向(source → sink)的取舍参 §5
- depth 限制太小会漏深链路,太大会图爆炸;从小开始按需放宽
输出(Alert):每条规则至少一个 alert $var for { message: "...", level: info }
- 无 alert 视为无可见输出(即使内部追到结果也读不到)
跨函数追踪示例
示例 1:从 SQL sink 反向追到 HTTP source
锚定 JdbcTemplate.queryForObject 第一个参数为 sink,TopDef 递归到 source;若 source 命中 @RequestParam 注解或 request.getParameter 调用 → 静态可达;若链路中出现 WhitelistUtil.check / Integer.parseInt 等过滤调用 → 标 static-unknown,需人工判定过滤是否完整。
示例 2:交叉端点 source 一次正向追
source 是 filter 派生的 request.setAttribute("userId", decoded);锚定 setAttribute("userId", ...),BottomUse 一次追到所有读取 getAttribute("userId") 的 handler,再对每个 handler 看其下游 sink。
示例 3:二阶链路追
source 是 INSERT 写入字段(INSERT INTO user(email) VALUES(?));BottomUse 追该字段被哪些 SELECT 引用,每个回读点单独追是否参数化。
与 sast-scan 协作模式
- sast-scan
needs_dataflow_confirmation桶 → 本能力按file_location锚定 sink,反向追 source 可达性 - 同
(source, sink)对在多端点出现 → 每个entry_point单独成行(参 §9) - 链路证明完成 → 把
confidence从needs_dataflow_confirmation升为dataflow-confirmed(即本能力的static-confirmed),写入shared/coverage-ledger/findings/dataflow-analysis.jsonl - 单漏洞 skill 拿本能力的
dataflow-confirmed条目 + 黑盒可观测效果证据 → 最终判confirmed
基线检查项
以下是已知的检查角度,作为基线起点而非必检硬清单。结合目标代码动态调整,按三态标注(
[x]/[-]/[+])处置。
- SSA 编译成功;失败或语言不支持时已走 cookbook fallback
- anchor 表达式已纳入项目自有 wrapper / 封装层
- 每条规则至少含一个 alert
- 跨函数追踪到真实终点 sink 而非中间 helper
- source 覆盖所有入口位置(query / body / header / cookie / path / attribute / 二阶回读)
- 中间过滤层 / 白名单调用点已列出(不替业务做"过滤是否完整"判断)
- 反射 / 闭源依赖 / 动态构造已标
static-unknown,未默认 not_vulnerable - 每个
(source, sink, entry_point)三元组独立成行落 jsonl
9. 闭环要求(必须遵守)
闭环判定 / 取证完整性 / 破坏性动作以 common/closure-verification.md 为准,下面只列本能力特有的判定上限与产物契约。
为什么这里是「必须」:本节属交付契约——本能力的产物结构是下游单漏洞 skill /
result-with-file机器消费的接口;产物聚合或省略会让单漏洞 skill 无法回溯具体 file:line 与跨函数链路,整条链路失效,因此是刚性要求。
白盒判定上限
本能力作为跨漏洞数据流工具,判定上限为 static-confirmed(source-sink 链路静态可达,跨函数链路完整无 unknown 断点),不等于动态 confirmed,且不独立给出单漏洞 confirmed 结论——本能力提供"链路证明"作为单漏洞 skill 升级 confirmed 的素材。
| 链路状态 | 上限状态 | 升级路径 |
|---|---|---|
| 跨函数链路完整可达、无过滤、终点 sink 危险形态 | static-confirmed (dataflow) |
交单漏洞 skill 收黑盒可观测效果证据 → 最终 confirmed |
| 链路可达但中间有过滤 / 白名单调用、过滤完整性需业务判定 | needs_review |
业务复核过滤逻辑;过滤不完整则降回 static-confirmed |
| 链路追到反射 / 闭源依赖 / 动态构造边界 | static-unknown |
推 dependency-decompile 反编译续追,或推动态分析 |
| 链路证明经参数化绑定 / 白名单 enum / 无 raw 通道 | not_vulnerable |
— |
禁止仅凭本能力链路证明直接判 confirmed——无可观测效果证据,仅静态可达不构成动态利用。
产物契约(必须遵守)
为什么这里是「必须」:产物结构是下游机器消费的接口;聚合 / 区间 / 抽样会让 result-with-file 计数闸门失效,并让单漏洞 skill 无法回溯到具体 file:line 与跨函数链路上的中间过滤点。
每确认一条数据流发现 / 需复核项立即 append 一行规范化 jsonl 到 shared/coverage-ledger/findings/dataflow-analysis.jsonl,不等汇总阶段回头整理(why:"事后总结"是聚合 / 区间 / "等"省略的根源):
{
"id": "df-001",
"title": "request.getParameter -> JdbcTemplate.queryForObject 跨函数可达",
"severity": "high",
"cwe": "CWE-89",
"source": "request.getParameter(\"name\")",
"sink": "JdbcTemplate.queryForObject(sql, ...)",
"entry_point": "GET /user/search",
"status": "needs_review",
"confidence": "static-confirmed",
"file_location": "UserController.java:42 -> UserService.java:88 -> UserDao.java:120",
"source_report": "dataflow-analysis",
"description": "..."
}
字段约束:
id带df-前缀全局唯一status ∈ confirmed | needs_review | not_vulnerable | false_positive | superseded(本能力默认needs_review,不独立判confirmed)confidence ∈ static-confirmed | needs_review | static-unknown | not_vulnerable(source, sink, entry_point)三元组任一不同即各自独立成行——同一 sink 被多个入口点到达时每个入口点各一行,禁止合并折叠file_location用->连接跨函数链路(source 位置 → 中间函数 → sink 位置);单点 sink 也填具体file:line,不留空entry_point填该流可达的 HTTP 入口点(method+URL);无明确入口点的系统性命中填systemic
禁止:
- 聚合计数("50 处可达 SQL sink + 13 处可达命令注入")—— 丢失了具体链路,下游无法消费
- "等" / "..." / "(略)" / "(其余 N 条同理)" 省略 finding —— 看似覆盖完整实则漏检
- 同一 sink 多入口点合并成一行 —— 入口点对应不同的鉴权 / 业务上下文,合并掉等于丢失语义
反例义务(必须遵守)
why:本能力"链路不可达 / 已防护"结论是覆盖完整性产物声明,缺失反向验证会让下游单漏洞 skill 误信"该子系统该 sink 安全"。
写"未发现可达链路"或"已防护"前,产物必须包含:
- 所有 source 候选位置完整清单(每个 handler / Controller / filter 派生值都有追踪结论)
- 所有 sink 候选位置完整清单(含项目自有 wrapper anchor 覆盖证据)
- 每个 (source, sink, entry_point) 三元组的判定结果(链路状态 + 中间过滤点)
static-unknown单元格的具体原因(反射 / 闭源 / 动态构造 / 跨服务边界)
清单不完整 → 结论降级 partial-coverage。
10. 具象化反例库
FP(看似命中实际不构成)
反例 1:SyntaxFlow 查询过宽召回大量 helper / wrapper 误报
- 抽象规则:anchor 写得太泛(如直接锚
*.exec而不限定具体类)会把 helper、test、不可达分支都召回 - 具体场景:
Runtime.*.exec锚定后召回数百条,其中大量是测试代码 / 反射工具类调用 - 关键识别特征:召回数远超项目规模合理上限;多数命中在
test//*Test.java/MockExec类 - 排除方法:anchor 收敛到具体调用链(
Runtime.getRuntime().exec),或加 depth 限制,或在追踪后用文件路径过滤排除 test
反例 2:source 表达匹配到了非用户可控的内部入口
- 抽象规则:
request.getAttribute("x")命中但实际值由 filter 内部设定而非用户可控 - 具体场景:admin-only handler 接收
request.getAttribute("internalUserId"),值由可信 filter 写入,攻击者无法直接控制 - 关键识别特征:source 追到上游是某个 filter 内部
setAttribute而非request.getParameter - 排除方法:本能力客观记录链路(filter → handler → sink),把"filter 是否可绕"判断留给单漏洞 skill 业务复核;不在数据流阶段判 FP
反例 3:框架自动转义层在 SSA 图上看不到
- 抽象规则:Spring HTML 转义 / Django template autoescape / Express EJS 默认转义在 SSA 上没有显式调用边
- 具体场景:链路从
request.getParameter直达model.addAttribute再到 Thymeleaf 模板渲染,SSA 看似无过滤;但 Thymeleaf 默认 HTML escape 让 XSS 不成立 - 关键识别特征:sink 是模板渲染 / response 输出,且项目用了带默认转义的模板引擎
- 排除方法:本能力链路证明仍记为
static-confirmed,但补一行description注明"sink 是 Thymeleaf 模板渲染、默认 HTML escape";最终判定由单漏洞 skill(如 stored-xss-detection)结合框架默认行为给出
FN(看似不命中实际是真洞)
反例 4:查询过窄漏掉等价 sink
- 抽象规则:
Runtime.exec写了但ProcessBuilder.start没写,漏掉等价 sink - 具体场景:项目主要用
ProcessBuilder().command(...).start()启动子进程,规则只锚Runtime.exec,命中为零 - 关键识别特征:命中数异常少 / 项目代码里能 grep 到
ProcessBuilder但 SyntaxFlow 报告无命中 - 确认方法:sink 类别按"语义等价集合"扩展(执行:
Runtime.exec+ProcessBuilder.start+Kernel32#CreateProcess;SQL:Statement.execute*+PreparedStatement.execute*+ ORM raw)
反例 5:项目自定义包装层未识别
- 抽象规则:项目封装
CmdUtil.run(String)内部调Runtime.exec,规则只查 JDK API 漏报 - 具体场景:业务调用点全是
CmdUtil.run(userInput),SyntaxFlow 报告"无命令执行 sink" - 关键识别特征:project-framework-analysis 输出的"自有类与方法"清单提示项目有自定义封装但 SyntaxFlow 命中数异常少
- 确认方法:把
CmdUtil.run加入 anchor 集合重跑;或对CmdUtil类先做一遍"内部是否调危险 API"扫描确认其为 sink wrapper
易混淆案例
反例 6:闭源依赖里的 sink 默默被忽略
- 抽象规则:依赖库内部有 sink 但 SyntaxFlow 没有源码可追
- 具体场景:项目调用
external-lib.queryUser(name)数据流到此为止,但 external-lib 内部用拼接 - 关键识别特征:链路追到依赖包边界即停;依赖图谱里该包是
unknown - 排除方法:标
static-unknown推 dependency-decompile 反编译后重新追内部数据流;禁止默认为 not_vulnerable
11. 静态分析边界
白盒底线:不假装看到看不到的代码。本能力的可观测能力到 SSA 图上的可见调用边为止;下列情形必须标
static-unknown,不允许默认为 not_vulnerable。
SyntaxFlow 追踪到边界的情形
反射 / 动态分派:
- Java:
Method.invoke()/ 通过字符串决定调用哪个 DAO 方法 - Python:
getattr(obj, method_name)(args)/eval/exec - JS:
obj[methodName]()/new Function(...) - 处置:SyntaxFlow 标
static-unknown,记录反射点file:line;不替业务做"反射目标是否危险"判断
闭源 / 无源码依赖:
- 三方依赖(jar / dll / so / 闭源 SaaS SDK)
- 链路追到依赖边界即停
- 处置:依赖图谱标
unknown→ 走 dependency-decompile 反编译恢复源码 → 在反编译产物上重新跑本能力续追内部数据流;反编译恢复后续追时,若污点跨入产物内部新引用的无源码依赖,同样回 dependency-decompile triage——别停在第一层
动态字符串构造:
- 配置文件驱动的 SQL(
config.get("query.user")然后执行) - 代码生成器(mybatis-generator / sequelize-cli)生成的 SQL
- 模板引擎动态加载(
Template.evaluate(string)) - 处置:标
static-unknown,记录配置 / 模板位置;如有必要读实际配置文件验证;推 dynamic analysis
跨服务 / 跨进程边界:
- RPC 调用(gRPC / Thrift / REST)跨微服务
- 消息队列(Kafka / RabbitMQ)异步消费
- 跨进程 IPC
- 处置:本服务范围内的 source 追踪到出站调用即停;下游服务由对应 skill 独立审计
AOP / 注解处理器 / 框架自动注入:
- Spring
@Aspect拦截器在 sink 调用前后插入逻辑 - Django middleware 修改 request 上下文
- 注解处理器(Lombok / mapstruct)生成的方法体
- 处置:列出所有拦截器 / middleware / 注解处理器的实现位置;本能力客观记录链路,业务复核判过滤完整性
运行时配置切换 / feature flag:
- dev / prod 不同分支用不同 SQL 模板
- feature flag 控制查询路径
- 处置:每个分支独立审计;不能只看 dev 分支下结论
SSA 编译失败 / 语言不支持:
- SyntaxFlow MCP 未连接 / yak 未安装 /
ssa_compile失败 / 语言暂不支持 - 处置:走 references/syntaxflow-cookbook.md 的 fallback 协议(
rg入口盘点 + 参数角色盘点 + 固定 checklist),禁止直接判"无可达链路"
底线:本能力写"该 (source, sink) 对无可达链路"前,所有 static-unknown 单元格必须显式列出原因(反射点 file:line / 闭源依赖名 / 动态构造位置 / 跨服务出站调用点)。否则结论降级 partial-coverage。
12. 修复建议
本能力 n/a(原因:本能力是跨漏洞数据流工具,不对单类漏洞修复负责。修复路径见对应单漏洞 skill 的 §12:SQL 注入修复 → 参 sast-scan 协作的 SQL 注入审计能力下游 skill;命令注入修复 → 对应能力;XSS 修复 → stored-xss-detection §12;反序列化修复 → 对应能力。本能力只提供"链路证明"素材,让单漏洞 skill 在制定修复时知道该修哪一段链路——上游 source 加白名单还是 sink 上加参数化绑定)。