前言¶
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 里写得很清楚:id、languages、severity(LOW / 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 里还列出了 exact、by-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-audit、p/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