关于 semgrep
semgrep 是指导使用 Semgrep 对代码库进行静态安全扫描的 Skill,适合安全审计和代码审查前检查。它编排多语言规则集扫描,合并 SARIF 结果,并要求记录失败或跳过的扫描。
主要功能
- 01自动检测编程语言
- 02并行运行规则集扫描
- 03支持全量或重点结果模式
- 04合并 SARIF 扫描结果
- 05记录失败和跳过的扫描
适用场景
- 01审计代码库中的安全问题
- 02在代码审查前查找漏洞
- 03扫描已知缺陷模式
- 04汇总多个规则集的分析结果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trailofbits/ skills --skill semgrep安装后在 Claude Code 中输入/semgrep确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlassets/trail-of-bits-mark.svgreferences/rulesets.mdscan-modes.mdscripts/merge_sarif.pyrun-scans.shtest_merge_sarif.pyworkflows/scan-workflow.md详细介绍
Semgrep 安全扫描
运行 Semgrep 扫描,自动检测语言、并行执行,并合并输出 SARIF。
核心原则
- 始终使用
--metrics=off— Semgrep 默认会发送遥测数据;--config auto也会向外发送数据。在安全审计期间,每条semgrep命令都必须包含--metrics=off,以防止数据泄露。 - 用户必须批准扫描计划(步骤 3 是硬性门槛) — 用户原本提出的“扫描此代码库”请求不算批准。请列出确切的规则集、目标、引擎和模式;在用户明确回复“同意”或“继续”之前,不得启动扫描器。
- 第三方规则集是必需项,而非可选项 — Trail of Bits、0xdea 和 Decurity 规则可以发现官方注册表中不存在的漏洞。检测到的语言匹配时,必须包含这些规则集。
- 由
scripts/run-scans.sh生成命令;不要自行编写 — 它会根据已批准的列表生成每条semgrep命令。这确保--metrics=off、--include范围限定规则和并行调度特性由代码保证,而非仅靠说明。提供已批准的规则集,然后让脚本运行。 - 扫描前始终检查是否有 Semgrep Pro — Pro 可启用跨文件污点跟踪,并多发现约 250% 的真阳性。跳过检查意味着会在不知情的情况下漏掉关键的文件间漏洞。
- 报告未运行的内容 —
scans.json除了scans,还会记录failed和skipped。无法克隆仓库的规则集,或扫描以非零状态码退出的情况,必须出现在报告中。将部分扫描报告为完整扫描,比完全不扫描更糟糕。
适用场景
- 对代码库进行安全审计
- 在代码审查前查找漏洞
- 扫描已知的错误模式
- 进行初步静态分析
不适用场景
- 二进制分析 → 使用二进制分析工具
- 已配置 Semgrep CI → 使用现有流水线
- 需要跨文件分析,但没有 Pro 许可证 → 可考虑使用 CodeQL 替代
- 创建自定义 Semgrep 规则 → 使用
semgrep-rule-creator技能 - 将现有规则移植到其他语言 → 使用
semgrep-rule-variant-creator技能
输出目录
所有扫描结果、SARIF 文件和临时数据都会存储在同一个输出目录中。
- 如果用户在提示中指定了输出目录,将其用作
OUTPUT_DIR。 - 如果未指定,默认使用
./static_analysis_semgrep_1。如果该目录已存在,则依次递增为_2、_3等。
无论哪种情况,写入任何文件之前,都必须使用 mkdir -p 创建目录。
# Resolve output directory
if [ -n "$USER_SPECIFIED_DIR" ]; then
OUTPUT_DIR="$USER_SPECIFIED_DIR"
else
BASE="static_analysis_semgrep"
N=1
while [ -e "${BASE}_${N}" ]; do
N=$((N + 1))
done
OUTPUT_DIR="${BASE}_${N}"
fi
mkdir -p "$OUTPUT_DIR/raw" "$OUTPUT_DIR/results"
输出目录会在步骤 1 开始时解析一次,并在后续所有步骤中使用。
$OUTPUT_DIR/
├── rulesets.json # The approved plan (Step 3), read by run-scans.sh (Step 4)
├── scans.json # What ran, failed, skipped, and covered nothing (Step 4)
├── raw/ # Per-scan raw output (unfiltered)
│ ├── python-python.json # <language>-<ruleset> for language-scoped rules
│ ├── python-python.sarif
│ ├── python-django.json
│ ├── python-django.sarif
│ ├── all-security-audit.json # all-<ruleset> for cross-language rules, run once
│ ├── all-security-audit.sarif
│ └── ...
└── results/ # Final merged output
└── results.sarif
前提条件
必需: Semgrep CLI(semgrep --version)。如果尚未安装,请参阅 Semgrep 安装文档。
可选: Semgrep Pro — 可启用跨文件污点跟踪、过程间分析,以及更多语言(Apex、C#、Elixir)。使用以下命令检查:
# --metrics=off because Principle 1 has no exceptions, and this is the first semgrep command
# of a run. stderr is kept because "OSS only" has several causes (logged out, no subscription,
# registry blocked) and the run downgrades silently for all of them.
if PRO_ERR=$(semgrep --pro --validate --metrics=off --config p/default 2>&1); then
echo "Pro available"
else
echo "OSS only"
echo " reason: $(printf '%s' "$PRO_ERR" | tail -n 3)"
fi
限制: OSS 模式无法跟踪跨文件的数据流。Pro 模式使用 -j 1 进行跨文件分析(每个规则集的扫描速度较慢,但并行运行规则集可以弥补)。
扫描模式
在步骤 2 中选择模式。模式会影响扫描标志和后处理。
| 模式 | 覆盖范围 | 报告的发现 |
|---|---|---|
| 全部运行 | 所有规则集、所有严重级别 | 全部 |
| 仅重要项 | 所有规则集,扫描前后均进行筛选 | 仅安全漏洞,置信度和影响为中至高 |
仅重要项应用两层筛选:
- 预筛选:
--severity WARNING --severity ERROR(CLI 标志) - 后筛选:JSON 元数据 — 仅保留
category=security、confidence∈{MEDIUM,HIGH}、impact∈{MEDIUM,HIGH}的结果
元数据筛选条件和 jq 筛选命令请参阅 scan-modes.md。
编排架构
┌──────────────────────────────────────────────────────────────────┐
│ MAIN SESSION (this skill) │
│ Step 1: Detect languages + check Pro availability │
│ Step 2: Select scan mode + rulesets (ref: rulesets.md) │
│ Step 3: Present plan + rulesets, get approval [⛔ HARD GATE] │
│ Step 4: Run scripts/run-scans.sh with the approved rulesets │
│ Step 5: Post-filter, merge, report, delete repos/ │
└──────────────────────────────────────────────────────────────────┘
│ Step 4: Bash
▼
┌──────────────────────────────────────────────────────────────────┐
│ scripts/run-scans.sh │
│ clone each third-party repo once, into repos/ │
│ generate one semgrep command per ruleset │
│ ├── python p/python, p/django --include=*.py│
│ ├── javascript p/javascript --include=*.js│
│ ├── docker p/dockerfile │
│ └── cross-language p/security-audit, p/secrets, │
│ the cloned repos (no filter) │
│ run in batches of --jobs, exit code read per process │
│ write scans.json — scans, failed, skipped │
└──────────────────────────────────────────────────────────────────┘
审批门槛保留在会话中;脚本只负责执行,不会询问任何内容。已批准的列表会以 JSON 文件的形式传入,因此扫描不会触及用户拒绝的规则集。
跨语言规则集放在一个共享单元中,而不是按语言重复。p/security-audit、p/secrets 和第三方仓库会对整个目标进行无范围限制的扫描,因此按语言各运行一次会重复执行相同的命令 N 次,并让 SARIF 合并来去重这些副本。
将其作为工作流运行
此插件提供 /static-analysis:semgrep-scan,可端到端运行整个扫描:检测语言和 Pro,从 rulesets.md 选择规则集,运行 scripts/run-scans.sh,合并并生成报告。请传入 JSON 对象,而不是散文:
/static-analysis:semgrep-scan {"target": "/abs/path", "mode": "run-all"}
它不会暂停以等待规则集审批。 使用目标调用它即表示选择加入,这与 /variant-analysis:variants 的工作方式相同。这样做是安全的,因为扫描只会读取目标,不会修改目标——没有 --autofix,所有写入都发生在输出目录中——因此,下方的审批门槛是范围确认,而非安全确认。无论如何,运行的内容都会记录在 rulesets.json 和 scans.json 中。
想要运行扫描时,请使用此工作流;如果规则集选择本身很重要,而且你想先查看并编辑列表,请按以下五个步骤操作。
工作流
请遵循 scan-workflow.md 中的详细工作流。 概要:
| 步骤 | 操作 | 门槛 | 关键参考 |
|---|---|---|---|
| 1 | 确定输出目录,检测语言和 Pro 是否可用 | — | 使用 Glob,而非 Bash |
| 2 | 选择扫描模式和规则集 | — | rulesets.md |
| 3 | 展示计划并获得明确批准 | ⛔ 硬性门槛 | AskUserQuestion |
| 4 | 运行扫描 | — | scripts/run-scans.sh |
| 5 | 后置过滤、合并、生成报告并清理 | — | 合并脚本(见下文) |
任务执行要求: 调用时,创建 5 个带有 blockedBy 依赖关系的任务(每个步骤阻塞前一步)。第 3 步是硬性门槛——只有在用户明确批准后才能标记为完成。
合并命令(第 5 步):
# run-all
uv run --no-project {baseDir}/scripts/merge_sarif.py "$OUTPUT_DIR/raw" "$OUTPUT_DIR/results/results.sarif" \
--scans "$OUTPUT_DIR/scans.json"
# important-only, once the JSON post-filter has run over every file in raw/
uv run --no-project {baseDir}/scripts/merge_sarif.py "$OUTPUT_DIR/raw" "$OUTPUT_DIR/results/results.sarif" \
--important --scans "$OUTPUT_DIR/scans.json"
--scans 会丢弃 .failed 下列出的扫描输出。中途失败的扫描可能仍已写入 .sarif 文件;使用 --important 时,该文件旁没有后置过滤文件,这会导致错误,而不是空过滤结果。不使用此标志时,一次失败的扫描就会使所有正常扫描都无法获得合并结果。被排除的文件会列在标准输出中,因此可以写入报告。
后置过滤器读取的是 SARIF 不包含的元数据,因此无法对合并后的文件重新运行;--important 则会保留 JSON 过滤器保留的发现项,并按 (rule, file, line) 匹配。不使用此标志时,results.sarif 未经过滤,而 JSON 端经过了过滤。
工作流和智能体
| 组件 | 用途 |
|---|---|
scripts/run-scans.sh | 根据已批准的规则集构建每条扫描命令,分批运行,并写入 scans.json |
第 4 步是 Bash 调用。没有任何子智能体会执行扫描的任何部分:退出代码和发现项数量会从进程及其写入的 JSON 中读取。
应拒绝的借口
| 借口 | 错误原因 |
|---|---|
| “用户要求扫描,就等于批准了” | 原始请求 ≠ 计划获批。展示计划,使用 AskUserQuestion,并等待明确的“是” |
| “步骤 3 的任务会阻塞流程,直接标记完成吧” | 谎报任务状态会破坏执行机制。只有获得真实批准后才能标记完成 |
| “我已经知道他们想要什么” | 主观臆断会导致扫描错误的目录或规则集。展示计划以供确认 |
| “直接使用默认规则集吧” | 扫描前,用户必须看到并批准确切的规则集 |
| “不问用户就添加额外规则集” | 未经同意修改已批准的列表会破坏信任 |
| “第三方规则集是可选的” | Trail of Bits、0xdea 和 Decurity 能发现官方注册表中没有的漏洞——必须使用 |
| “使用 --config auto” | 这会发送指标,而且对规则集的控制较少 |
| “我自己运行 semgrep 命令就行” | run-scans.sh 会强制使用 --metrics=off、--include 规则和输出目录的 --exclude。手写命令会悄悄漏掉这些选项 |
| “脚本失败了,我直接运行 semgrep 来获取一些结果” | 返回非零状态意味着扫描未成功。报告这一情况并停止;手动运行的子集会被误认为完整扫描 |
| “有些扫描失败了,但整个流程还是结束了” | failed 和 skipped 是 scans.json 的组成部分。报告它们,否则用户会把不完整的扫描误认为扫描无异常 |
| “Pro 太慢了,跳过 --pro 吧” | 跨文件分析能多发现 250% 的真阳性,值得花时间 |
| “Semgrep 能原生处理 GitHub URL” | 在 YAML 格式不标准的仓库中,URL 处理会失败;务必先克隆 |
| “清理是可选的” | 克隆的仓库会污染用户的工作区,并在多次运行后不断累积 |
“使用 . 或相对路径作为目标” | 子 Agent 需要绝对路径,避免歧义 |
| “让用户之后再选输出目录” | 必须在步骤 1 确定输出目录,之后才能创建任何文件 |
参考索引
| 文件 | 内容 |
|---|---|
| rulesets.md | 完整的规则集目录和选择算法 |
| scan-modes.md | 前置/后置过滤条件和 jq 命令 |
| 工作流 | 用途 |
|---|---|
| scan-workflow.md | 完整的 5 步扫描执行流程 |
scripts/run-scans.sh | 步骤 4 调用的扫描运行器 |
成功标准
- 已确定输出目录(由用户指定或使用自动递增的默认目录)
- 所有生成的文件均存储在
$OUTPUT_DIR中 - 已检测语言并统计文件数量;已检查 Pro 状态
- 用户已选择扫描模式(全部运行/仅运行重要项)
- 规则集包含所有已检测语言对应的第三方规则
- 用户已明确批准扫描计划(步骤 3 的关卡已通过)
-
run-scans.sh以状态码 0 退出,并写入了$OUTPUT_DIR/scans.json -
scans.json中的failed和skipped为空,或已在报告中列出 -
scans.json中标记为partial的扫描为零,或已在报告中列出——这些扫描有部分规则编译失败 - 每条
semgrep命令均使用了--metrics=off - 已在步骤 3 的关卡将获批计划写入
$OUTPUT_DIR/rulesets.json,并原样传递给 扫描器 -
scans.json中的coveredNothing为空,或已在报告中列出 -
scans.json中的oversized为空,或已在报告中列出——这些文件超过--max-target-bytes,没有规则集处理它们 - 每次扫描的原始输出均存储在
$OUTPUT_DIR/raw/中 -
$OUTPUT_DIR/results/中存在results.sarif,且其内容是有效 JSON - 仅运行重要项模式:在合并前已应用后置过滤,合并时使用了
--important,未过滤的结果保留在raw/中 - 已报告结果摘要,并按严重程度和类别分类
- 已从
$OUTPUT_DIR/repos/清理克隆的仓库(如有)