semgrep-rule-creator:用 Agent 写出可测试的 Semgrep 安全规则

前言

Semgrep 是目前安全工程里很常见的静态分析工具,用 YAML 规则去匹配代码模式,既能扫注入、危险 API,也能把团队内部的编码规范固化进 CI。问题在于:规则本身并不好写。

只写「能打中漏洞」的那一半,通常会把安全的写法一并报出来;写得太死,换一种调用方式就漏报。污点分析(taint mode)能跟踪「不可信数据有没有流到危险函数」,但 source、sink、sanitizer 怎么写、测试怎么标,文档分散,Agent 如果只凭印象生成一份 YAML,很容易出现假阳性过高、测例不全、一条文件里塞多条规则这类问题。

Trail of Bits 把这件事做成了 Agent Skill:semgrep-rule-creator。它不负责替你跑现成规则集,而是约束 Agent 按「先写测试、再看 AST、再写规则、测通后再优化」的流程,产出可以交给 semgrep --test 验证的规则。下面按官方仓库和 SKILL.md 原文说明它是什么、怎么装、怎么用。

这是什么

semgrep-rule-creator 是 Trail of Bits 安全技能市场(trailofbits/skills)里的一个插件,用来创建可检测安全漏洞、Bug 模式和代码模式的自定义 Semgrep 规则。插件作者是 Maciej Domanski,当前插件元数据里的版本是 1.2.2。Skill 本体在:

https://github.com/trailofbits/skills/tree/main/plugins/semgrep-rule-creator/skills/semgrep-rule-creator

它基于通用的 SKILL.md 格式,Claude Code、Codex CLI、Cursor 这类 AI 编程工具都可以加载。官方仓库把它定位成 Claude Code 插件市场中的一项;Codex 可以通过 Claude marketplace 兼容方式加载同一套市场。仓库采用 Creative Commons Attribution-ShareAlike 4.0 许可。

官方 README 写明的使用时机是:

  • 为特定 Bug 模式写 Semgrep 规则
  • 为代码库里的安全漏洞写检测规则
  • 写污点模式规则,做数据流分析
  • 写模式匹配规则,做代码质量或编码规范检查

明确不要用它的场景是:跑已有 Semgrep 规则集,以及不做自定义规则的通用静态分析。后者官方指向同一市场里的 static-analysis Skill。

前置条件很直接:本机要先装好 Semgrep。

pip install semgrep
# 或
brew install semgrep

核心能力

这个 Skill 真正约束的是「怎么写规则」,而不是再封装一层 Semgrep CLI。对照 SKILL.md 和插件 README,能力可以概括成下面几条。

1、强制测试先行。先写带 # ruleid: / # ok: 注解的测试文件,再写 YAML。只覆盖漏洞用例、不写安全用例,会被明确当成反模式:能打中漏洞只完成了一半,安全写法不能误报,否则规则在生产环境里很难建立信任。

2、先看 AST 再写 pattern。Semgrep 匹配的是抽象语法树,不是源码字符串。foo.bar()foo().bar 看起来接近,解析结果可以完全不同。Skill 要求用 semgrep --dump-ast 看清结构,再写模式。

3、数据流问题优先用 taint mode。纯语法匹配 eval($X) 会同时打中 eval(user_input)eval("safe_literal")。污点模式跟踪「不可信数据是否真的流到 sink」,注入类问题的误报通常会低很多。官方也允许在两种路线之间切换:taint 传播不符合预期时改回 pattern matching;pattern 对安全用例误报太多时再改 taint。目标是规则能用,而不是绑死某一种写法。

4、一份 YAML 只放一条规则。输出固定为「以 rule-id 命名的目录 + 一条 YAML + 一份测试文件」,不把多条规则塞进同一个文件。

5、写规则前要求 Agent 先拉取 Semgrep 官方文档:规则语法、模式语法、规则测试、污点分析概览与进阶、常量传播,以及 Trail of Bits Testing Handbook 里的 Semgrep 章节。本地还带了两份参考:references/quick-reference.md(命令、算子、taint 语法)和 references/workflow.md(完整工作流与示例)。

插件里还有一条 Claude Code 命令 trailofbits:semgrep-rule,会根据当前对话里的漏洞模式、目标语言、是否适合 taint,去调用这个 Skill 的完整流程;上下文不够时会先问清楚要检测什么。

安装与启用

Trail of Bits 官方仓库给出的安装方式以 Claude Code 插件为主。

先把市场加进来,再安装这个插件:

/plugin marketplace add trailofbits/skills
/plugin install trailofbits/skills/plugins/semgrep-rule-creator

也可以用 /plugin menu 在市场里浏览后安装。插件名为 semgrep-rule-creator

Codex 官方说明支持直接加载 Claude 插件市场,不需要额外的 sidecar 元数据:

codex plugin marketplace add trailofbits/skills
codex plugin list
codex plugin add semgrep-rule-creator@trailofbits

如果使用通用的 skills CLI(officialskills.sh 上的安装说明),可以用:

npx skills add https://github.com/trailofbits/skills --skill semgrep-rule-creator

装好之后,在对话里直接描述要检测的模式即可,例如「为 Python 写一条 taint 规则,捕获 request.args 流入 eval()」,或使用上面的 /semgrep-rule 命令。Skill 声明可调用的工具是 Bash、Read、Write、Edit、Glob、Grep、WebFetch,也就是允许 Agent 读文档、改文件、跑 Semgrep 命令。

写规则的工作流

Skill 把流程写成一份必须勾完的清单,官方标注为 strict,步骤不能跳。

Semgrep Rule Progress:
- [ ] Step 1: Analyze the Problem
- [ ] Step 2: Write Tests First
- [ ] Step 3: Analyze AST structure
- [ ] Step 4: Write the rule
- [ ] Step 5: Iterate until all tests pass (semgrep --test)
- [ ] Step 6: Optimize the rule (remove redundancies, re-test)
- [ ] Step 7: Final Run

各步在 workflow.md 里的要求如下。

Step 1 分析问题。 先读文档,再用「能讲给初级开发听懂」的方式说清楚要检测的漏洞或模式,确认目标语言,再决定用 pattern matching 还是 taint mode。taint 适合跨变量、跨函数跟踪不可信数据,尤其是 SQL 注入、命令注入、XSS 这类问题,也更不容易被 if、循环等结构打散。

Step 2 先写测试。 目录结构必须是:

<rule-id>/
├── <rule-id>.yaml     # Semgrep 规则
└── <rule-id>.<ext>    # 带 ruleid/ok 注解的测试文件

测试注解只允许 ruleid:ok:,禁止 todoruleid:todook:,也不要用多行注释来标。注解必须单独一行,并且紧挨在目标代码的上一行——Semgrep 报的是注解后面那一行。测试要同时覆盖:明确的漏洞用例、明确的安全用例、边界与不同写法、经过 sanitize/校验的输入、完全无关的正常代码,以及写在 if、循环、try/catch、回调里的嵌套情况。

Step 3 分析 AST。

semgrep --dump-ast --lang <language> <rule-id>.<ext>

看函数调用怎么表示、变量怎么绑定、控制流怎么展开,避免写出「人眼觉得对、树结构对不上」的 pattern。

Step 4 写规则并校验。 规则必填字段在 quick-reference 里写得很清楚:idlanguagesseverityLOW / MEDIUM / HIGH / CRITICAL)、message,以及 pattern / patterns / pattern-either / mode: taint 之一。写完后:

semgrep --validate --config <rule-id>.yaml
cd <rule-directory>
semgrep --test --config <rule-id>.yaml <rule-id>.<ext>

期望输出是 1/1: ✓ All tests passed。taint 规则调试可以用:

semgrep --dataflow-traces --config <rule-id>.yaml <rule-id>.<ext>

它会打出 source、sink、数据流路径,以及 taint 没有传播下去的原因。

Step 5 迭代到全部通过。 「大多数测试通过」不算完成。漏报通常是 pattern 太死,需要 pattern-either;误报通常是 pattern 太宽,需要 pattern-not 或收紧 sanitizer。

Step 6 最后才优化。 全部测通之后再删冗余:引号变体、被 ... 覆盖的子集、可以用 metavariable-regex 合并的相似调用。每改一次都必须重新跑测试,因为有些看起来重复的 pattern,其实对应不同的 AST。

Step 7 终验。semgrep --config 真正跑一遍规则。message 要短、说明匹配到了什么,且不能留下未插值的元变量(例如 $OP$VAR)——message 里提到的元变量必须能被 pattern 捕获。

官方示例:eval 污点规则

SKILL.md 的 Quick Start 给了一条 Python 规则,检测用户输入进入 eval()。规则文件可以理解为 insecure-eval.yaml

rules:
  - id: insecure-eval
    languages: [python]
    severity: HIGH
    message: User input passed to eval() allows code execution
    mode: taint
    pattern-sources:
      - pattern: request.args.get(...)
    pattern-sinks:
      - pattern: eval(...)

对应测试文件 insecure-eval.py

# ruleid: insecure-eval
eval(request.args.get('code'))

# ok: insecure-eval
eval("print('safe')")

在规则目录里执行 semgrep --test。这条规则用 taint,而不是 pattern: eval(...),因此字面量调用可以标成 ok,不会被当成漏洞。

官方还给出了几类要避免的写法,和上面的流程是配套的。

模式过宽,等于没有检测能力:

# BAD: 匹配任意函数调用
pattern: $FUNC(...)

# GOOD: 对准具体的危险函数
pattern: eval(...)

测试只写漏洞、不写安全用例:

# BAD: 只有漏洞用例
# ruleid: my-rule
dangerous(user_input)

# GOOD: 同时写安全用例,用来卡住误报
# ruleid: my-rule
dangerous(user_input)

# ok: my-rule
dangerous(sanitize(user_input))

# ok: my-rule
dangerous("hardcoded_safe_value")

模式过死,换一种拼接方式就漏报;数据流类问题应改用 taint:

# BAD: 只匹配这一种字符串拼接
pattern: os.system("rm " + $VAR)

# GOOD: 跟踪「输入是否流到 os.system」
mode: taint
pattern-sources:
  - pattern: input(...)
pattern-sinks:
  - pattern: os.system(...)

taint 规则里还可以加 pattern-sanitizers,把 sanitize(...)escape(...) 这类函数标成消毒点。quick-reference 里还列出了 exactby-side-effect 等选项,用来控制「整次调用算 source / 只有某个参数算 source」。这些细节以 Semgrep 文档和 Skill 自带的 quick-reference 为准,写规则时让 Agent 去读,不必凭记忆填。

适用场景与注意

官方和 Skill 索引页列出的典型用法包括:

  • 写 taint 规则,检测用户可控的请求参数流入 SQL 执行点
  • 在 Python 代码里捕获 eval() / exec() 吃不可信输入
  • 在大仓库里标记已废弃 API,做编码规范
  • 安全审计里发现某一类漏洞后,补一条可回归的检测规则
  • 把仓库特有的 Bug 模式做成自定义规则,放进 CI

同一市场里还有两个经常一起出现的 Skill:semgrep-rule-variant-creator 用来把已有规则移植到另一种语言;variant-analysis 用来在代码库里找同类漏洞。需要的是「跑扫描、看 SARIF」,而不是写新规则时,应改用 static-analysis

使用时有几条硬约束,漏掉就会写出不能上生产的规则:

  • 必须先装 Semgrep,Skill 本身不替代扫描器
  • 测试必须 100% 通过,优化只能发生在全部通过之后
  • 目标语言不要用 languages: generic 做泛化匹配
  • 一个 YAML 文件只放一条规则
  • 测试注解不要夹杂其他文字,也不要用 todoruleid / todook 当「以后再修」的占位
  • 元变量必须大写,例如 $X$FUNC,不能写成 $x

另外,这个 Skill 不会帮你选择现成的 p/security-auditp/trailofbits 这类规则集,也不会替你配置 CI 流水线。它的产出是「一条经过测试的自定义规则」;真正扫仓库、进流水线,还是要用 Semgrep 自己的命令和配置。

小结

手写 Semgrep 规则最常见的失败不是 YAML 语法错,而是 pattern 过宽、过死,以及没有用安全用例把误报卡住。semgrep-rule-creator 把 Trail of Bits 的规则写作习惯写成 Agent 必须遵守的清单:先测试、再 AST、数据流优先 taint、测通再优化、一文件一条规则。

官方地址:

  • Skill 目录:https://github.com/trailofbits/skills/tree/main/plugins/semgrep-rule-creator/skills/semgrep-rule-creator
  • 插件说明:https://github.com/trailofbits/skills/tree/main/plugins/semgrep-rule-creator
  • 技能市场:https://github.com/trailofbits/skills
  • 索引页:https://officialskills.sh/trailofbits/skills/semgrep-rule-creator
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜