其他
处理 SARIF 静态分析结果:读取、按严重级别过滤…
当你拿到 CodeQL、Semgrep 等扫描器产出的 SARIF 文件,需要读取、过滤、按严重级别门禁、跨运行去重、聚合多工具结果或转成其他格式时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 48/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 55/100 ×0.15
适用场景
处理 SARIF 静态分析结果:读取、按通用
解决思路
- 按 SARIF 2.1.0 层级读取文件:顶层为 sarifLog(version、可选 $schema)→ runs[];每个 run 含 tool(driver.name/version/rules[]、extensions[])、results[](ruleId、ruleIndex、level、message.text、locations[].physicalLocation.artifactLocation.uri 与 region.startLine/startColumn、fingerprints{}、partialFingerprints{})与 artifacts[](被扫描文件元数据)。
- 解析严重级别时不要直接读 result.level,按 SARIF 2.1.0 §3.27.10 顺序解析:① kind 不是 "fail"(pass/notApplicable 记录)返回 "none";② result.level 存在则用它;③ 用 ruleIndex 关联 runs[].tool.driver.rules[],工具省略 ruleIndex 时用 ruleId 匹配 rules[].id,取该规则的 defaultConfiguration.level;④ 兜底 "warning"。所有按级别过滤的查询都必须从该解析结果出发(否则 CodeQL 输出会被误判为干净)。
- 用稳定指纹跨运行跟踪发现:对内容(代码片段、规则 ID、相对位置)做哈希,得到与环境路径无关的标识;用于基线对比(新发现还是已见过)、回归检测(该 PR 是否引入新漏洞)、抑制(未来运行忽略已知误报)。
- 按场景选择处理工具:jq 做快速 CLI 查询(brew install jq / apt install jq);pysarif 做简单 Python 脚本(uv run --with pysarif python script.py);sarif-tools 做高级 Python 脚本(uv run --with sarif-tools python script.py);SARIF SDK(NuGet 包)用于 .NET;sarif-js(npm 包)用于 JS/Node;garif(go get github.com/chavacava/garif)用于 Go;SARIF Validator(sarifweb.azurewebsites.net)做校验。
- 用 jq 漂亮打印:`jq '.' results.sarif`。
- 用 jq 统计发现总数:`jq '[.runs[].results[]] | length' results.sarif`。
- 用 jq 列出所有触发的规则 ID:`jq '[.runs[].results[].ruleId] | unique' results.sarif`。
- 定义 jq 的 LEVEL_FN 严重级别解析函数:`rule($run)` 按 ruleIndex(校验为数字且 >= 0)或 ruleId 关联 run.tool.driver.rules[],`level($run)` 依次判断 kind → result.level → 规则 defaultConfiguration.level → 兜底 "warning";之后所有按级别过滤的 jq 查询都复用它(带注释的完整版本见 resources/jq-queries.md)。
- 用 jq 只提取 error 级别发现:`jq "$LEVEL_FN"'.runs[] as $run | $run.results[] | select(level($run) == "error")' results.sarif`。
- 用 jq 提取带文件位置的发现:`jq '.runs[].results[] | {rule: .ruleId, message: .message.text, file: .locations[0].physicalLocation.artifactLocation.uri, line: .locations[0].physicalLocation.region.startLine}' results.sarif`。
- 用 jq 按严重级别过滤并按规则统计数量:`jq "$LEVEL_FN"'[.runs[] as $run | $run.results[] | select(level($run) == "error")] | group_by(.ruleId) | map({rule: .[0].ruleId, count: length})' results.sarif`。
- 用 jq 提取指定文件的发现:`jq --arg file "src/auth.py" '.runs[].results[] | select(.locations[].physicalLocation.artifactLocation.uri | contains($file))' results.sarif`。
- 用 pysarif 做编程访问:`load_from_file("results.sarif")` 加载,遍历 `sarif.runs` 取 `run.tool.driver.name`,遍历 `run.results` 取 rule_id、message.text 与 locations[0].physical_location.artifact_location.uri / region.start_line;用 `save_to_file(sarif, "modified.sarif")` 保存。注意 pysarif 会把缺失的 result.level 填成 "warning",所以 .level 不是规则继承后的严重级别(CodeQL 的 error 会读成 warning),门禁必须改用 jq 的 level() 或 resources/sarif_helpers.py 的 resolve_level()。
- 用 sarif-tools 做聚合与报告:`loader.load_sarif_file("results.sarif")` 加载单个,`loader.load_sarif_files(["tool1.sarif", "tool2.sarif"])` 加载多个;用 `get_report()` 拿汇总,用 `get_issue_type_histogram_for_severity("error")` / ("warning") 拿按严重级别的直方图。
- 用 sarif-tools 过滤级别时不要读 r["level"](它返回原始 result dict,级别可能落在规则上),改为对 run 解析:`filter_by_level(extract_findings(load_sarif("results.sarif")), "error")`。
- sarif-tools CLI 输出发现摘要:`sarif summary results.sarif`。
- sarif-tools CLI 列出所有结果详情:`sarif ls results.sarif`。
- sarif-tools CLI 按严重级别列出结果:`sarif ls --level error results.sarif`。
- sarif-tools CLI 对比两个 SARIF 文件找出新增/已修复问题:`sarif diff baseline.sarif current.sarif`。
- sarif-tools CLI 转换格式:`sarif csv results.sarif > results.csv`、`sarif html results.sarif > report.html`。
- 聚合多个 SARIF 文件:以 `{"version": "2.1.0", "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "runs": []}` 为骨架,逐个读取文件并把 `sarif.get("runs", [])` extend 进 aggregated["runs"],再用 `deduplicate(extract_findings(...))` 去重。
- 去重时优先使用工具自带的 fingerprints / partialFingerprints,缺失时回退到对 rule ID、完整规范化路径、行号与消息做哈希;键里必须保留目录,因为 auth/login.py 与 admin/login.py 同规则同行是两个不同发现,只取 basename 会丢掉一个。
- 用 resources/sarif_helpers.py 提取可操作数据(仅依赖标准库):`extract_findings()` 返回已解析严重级别的 Finding 对象,`filter_by_level()`、`sort_by_severity()`、`deduplicate()`、`diff_findings()` 消费这些对象;例如 `findings = sort_by_severity(extract_findings(load_sarif("results.sarif")))`,再遍历 `filter_by_level(findings, "error")` 打印 `f.file_path:f.start_line [f.level] f.rule_id: f.message`。
- 自己写提取器时实现 resolve_level(result, run):kind 非 "fail" 返回 "none" → result.level → 把 ruleIndex 关联到 run.tool.driver.rules[index](越界则回退按 ruleId 匹配)→ 取 rule.defaultConfiguration.level → 兜底 "warning"。不要只按 ruleIndex 或只按 ruleId 一种方式关联,否则对另一类工具的输出会静默返回默认级别。
- 规范化路径:不同工具报路径格式不同(绝对、相对、URI 编码),`file:///src/a%20b.py` 与 `src/a b.py` 可能是同一文件;比较或哈希前先去掉 file:// scheme、做百分号解码、相对基准路径解析、统一分隔符(resources/sarif_helpers.py 的 normalize_path() 一次做完这四步)。
- 处理跨运行指纹不匹配:路径环境差异、工具版本变更指纹算法、代码重排改变行号都会导致失配;用 compute_stable_fingerprint 组合 ruleId、message.text 前 100 字符,若提供文件内容则追加该行 strip 后的代码片段,取 sha256 的 16 位十六进制作为环境无关指纹。
- 防御性访问可选字段:写 safe_get_location(result) 用 try/except 捕获 IndexError/KeyError/TypeError,依次取 locations[0].physicalLocation.artifactLocation.uri 与 region.startLine,异常时返回 ("unknown", 0)。
- 大文件(100MB+)流式处理:用 ijson(`uv run --with ijson`)写 stream_results,通过 `ijson.items(f, "runs.item.results.item")` 逐个产出 result,避免把整个文件载入内存。
- 处理前先做 schema 校验:ajv-cli 用 `ajv validate -s sarif-schema-2.1.0.json -d results.sarif`;或用 Python jsonschema 的 validate/ValidationError,读取 sarif 与 schema 后用 `validate(sarif, schema)` 捕获 ValidationError 并打印 e.message。
- GitHub Actions 集成:用 `github/codeql-action/upload-sarif@v3` 并指定 `sarif_file: results.sarif` 上传;再用解析级别后的 jq 统计 error 数(直接 select(.level == "error") 在 CodeQL 输出上恒为 0),大于 0 时 `echo "Found $HIGH_COUNT high severity issues"` 并 exit 1。
- 只对新增问题失败:分别用 loader.load_sarif_file 加载 baseline 与 current,取各自结果的指纹集合求差,返回不在基线指纹集合中的 current 结果数量作为 check_for_regressions 的返回值。
- 遵循关键原则:先校验 SARIF 结构;解析严重级别而非直接读 result.level;防御性访问可选字段;尽早规范化路径;多策略指纹去重;100MB+ 文件流式处理;聚合时保留各工具元数据。
- 复用技能自带资源:resources/jq-queries.md 提供 40+ 条常用 SARIF jq 查询与 LEVEL_FN;resources/sarif_helpers.py 提供 resolve_level()、normalize_path()、compute_fingerprint()、deduplicate();resources/fixtures 下两个 fixture(一个严重级别只在规则上、一个在结果上)各含恰好一个 error,可用于在信任门禁前先对已知答案验证。
适用边界 · 注意事项
- 需要实际运行静态分析扫描时不要用(应改用 CodeQL 或 Semgrep 技能)。
- 需要编写 CodeQL 或 Semgrep 规则时不要用(用各自对应的技能)。
- 没有 SARIF 输入、要直接分析源码或对发现做分诊时不要用(改用 variant-analysis 或 audit 技能)。