本指南详细介绍如何为 astgrep 编写静态分析规则,包括基本模式匹配、高级特性和与 Semgrep 的兼容性说明。
astgrep 规则使用 YAML 格式定义。一个基本的规则文件包含以下结构:
rules:
- id: unique-rule-id
name: "规则名称"
description: "规则描述"
severity: ERROR
confidence: HIGH
languages: [java, python]
patterns:
- pattern: "$FUNC(...)"
message: "发现问题的描述"
metadata:
cwe: "CWE-XXX"
owasp: "A01:2021"| 字段 | 类型 | 说明 |
|---|---|---|
id |
String | 规则的唯一标识符 |
languages |
Array | 适用的编程语言列表 |
message |
String | 发现问题时显示的消息 |
severity |
Enum | 严重程度:INFO, WARNING, ERROR, CRITICAL |
| 字段 | 类型 | 说明 |
|---|---|---|
name |
String | 规则的友好名称 |
description |
String | 详细描述 |
confidence |
Enum | 置信度:LOW, MEDIUM, HIGH |
patterns |
Array | 模式匹配规则列表 |
mode |
String | 分析模式(如 taint) |
fix |
String | 修复建议 |
fix_regex |
Object | 基于正则的自动修复 |
metadata |
Object | 元数据(CWE、OWASP 等) |
enabled |
Boolean | 是否启用此规则(默认 true) |
最简单的模式是直接匹配代码结构:
rules:
- id: hardcoded-password
languages: [java]
message: "发现硬编码密码"
patterns:
- pattern: 'String password = "..."'
severity: WARNING元变量使用 $ 前缀,可以匹配任意表达式:
rules:
- id: sql-injection
languages: [java]
message: "潜在的 SQL 注入"
patterns:
- pattern: '$STMT.execute($QUERY)'
severity: ERROR元变量命名规则:
- 必须以
$开头 - 使用大写字母(如
$VAR,$FUNC,$QUERY) - 可以包含数字(如
$VAR1,$VAR2)
使用 ... 匹配任意数量的参数或语句:
# 匹配任意参数的函数调用
pattern: 'eval(...)'
# 匹配代码块中的任意语句
pattern: |
if ($COND) {
...
dangerous_function()
...
}匹配多个模式中的任意一个:
patterns:
- pattern-either:
- pattern: 'MD5.getInstance()'
- pattern: 'SHA1.getInstance()'
- pattern: 'DES.getInstance()'所有模式都必须匹配:
patterns:
- pattern: '$OBJ.execute($QUERY)'
- pattern-not: '$OBJ.prepareStatement(...)'模式必须在特定上下文中:
patterns:
- pattern: '$VAR = $INPUT'
- pattern-inside: |
function handleRequest($REQ) {
...
}排除特定模式:
patterns:
- pattern: '$STMT.execute($QUERY)'
- pattern-not: '$STMT.execute("SELECT ...")' # 排除字面量查询模式不能在特定上下文中:
patterns:
- pattern: 'eval($INPUT)'
- pattern-not-inside: |
if (isSafe($INPUT)) {
...
}对元变量的值进行进一步匹配:
patterns:
- pattern: '$STMT.execute($QUERY)'
- metavariable-pattern:
metavariable: '$QUERY'
patterns:
- pattern: '$STR + $INPUT' # 查询是字符串拼接使用正则表达式约束元变量:
patterns:
- pattern: 'String $VAR = "..."'
- metavariable-regex:
metavariable: '$VAR'
regex: '^(password|passwd|pwd|secret|token)$'比较元变量的值:
patterns:
- pattern: 'setTimeout($FUNC, $TIME)'
- metavariable-comparison:
metavariable: '$TIME'
comparison: '$TIME > 5000' # 超时时间大于 5 秒支持的比较操作符:
==,!=: 相等/不等>,<,>=,<=: 数值比较in,not in: 包含关系re.match(): 正则匹配
对元变量进行高级分析:
patterns:
- pattern: 'const $VAR = "$VALUE"'
- metavariable-analysis:
metavariable: '$VALUE'
analysis:
entropy:
min: 3.5 # 最小熵值(检测随机字符串)支持的分析类型:
entropy: 熵分析(检测密钥、令牌)type: 类型分析complexity: 复杂度分析
污点分析用于追踪数据从不可信源(source)流向敏感操作(sink)的路径。
rules:
- id: user-input-to-sql
mode: taint
languages: [java]
message: "用户输入流向 SQL 查询"
pattern-sources:
- pattern: 'request.getParameter($PARAM)'
- pattern: 'request.getHeader($HEADER)'
pattern-sinks:
- pattern: 'Statement.execute($QUERY)'
- pattern: 'Statement.executeQuery($QUERY)'
pattern-sanitizers:
- pattern: 'sanitize($INPUT)'
- pattern: 'escape($INPUT)'
severity: ERRORastgrep 支持更简洁的新语法:
rules:
- id: xss-vulnerability
languages: [javascript]
message: "潜在的 XSS 漏洞"
taint:
sources:
- 'req.query.$PARAM'
- 'req.body.$FIELD'
sinks:
- 'res.send(...)'
- 'res.write(...)'
sanitizers:
- 'escape(...)'
- 'sanitizeHtml(...)'
severity: CRITICAL定义数据如何在不同变量间传播:
taint:
sources:
- 'getUserInput()'
sinks:
- 'executeCommand(...)'
propagators:
- pattern: '$A.transform($B)'
from: '$B'
to: '$A'
sanitizers:
- 'validate(...)'使用标签进行更精细的污点追踪:
rules:
- id: labeled-taint
languages: [python]
message: "需要同时满足多个污点条件"
taint:
sources:
- label: TAINTED
pattern: 'user_input()'
- label: SENSITIVE
pattern: 'get_secret()'
sinks:
- requires: TAINTED and SENSITIVE
pattern: 'log(...)'
severity: ERRORdataflow:
sources:
- 'request.getParameter(...)'
sinks:
- 'Statement.execute(...)'
sanitizers:
- 'sanitize(...)'
must_flow: true # 必须存在数据流
max_depth: 10 # 最大分析深度聚焦于特定元变量的位置:
patterns:
- pattern: |
$FUNC($ARG1, $ARG2, $ARG3)
- focus-metavariable: '$ARG2' # 只报告第二个参数的位置约束元变量的名称:
patterns:
- pattern: 'function $FUNC(...) { ... }'
- metavariable-name:
metavariable: '$FUNC'
name_pattern: '^test.*' # 函数名必须以 test 开头rules:
- id: use-const
languages: [javascript]
patterns:
- pattern: 'var $VAR = $VALUE'
message: "使用 const 或 let 代替 var"
fix: 'const $VAR = $VALUE'
severity: INFOfix_regex:
regex: 'var\s+(\w+)'
replacement: 'const \1'
count: 1 # 替换次数paths:
include:
- '*.java'
- 'src/**/*.py'
exclude:
- 'test/**'
- '**/*_test.py'metadata:
cwe: 'CWE-89'
owasp: 'A03:2021 - Injection'
category: 'security'
subcategory: 'sql-injection'
confidence: 'HIGH'
likelihood: 'MEDIUM'
impact: 'HIGH'
references:
- 'https://owasp.org/www-community/attacks/SQL_Injection'当 SQL 语句嵌入在 Java 源码或 MyBatis XML 中时,你可以在规则 YAML 中通过 metadata 启用“预处理器”。预处理器会在分析前从宿主语言中提取 SQL、进行轻量归一化,然后用 SQL 语义匹配器执行规则,并将命中回填到原文件位置。
- 你已有适用于 .sql 文件的 SQL 规则,想让它们同样适用于 Java 注解/字符串中的 SQL 或 MyBatis XML 中的 SQL
- 希望在一个规则里保持“语言为 sql”的语义模式匹配,而不去写语言相关(java/xml)的字符串/标签匹配
在 SQL 规则的 metadata 中声明预处理器:
rules:
- id: sql-performance-issue-where-exists-in-with-orderby
languages: [sql]
patterns:
- pattern-either:
- pattern: |
SELECT $... FROM $T1 WHERE EXISTS ($SUBQUERY) $... ORDER BY $...;
- pattern: |
SELECT $... FROM $T1 WHERE $COL IN ($SUBQUERY) $... ORDER BY $...;
message: "WHERE EXISTS/IN + ORDER BY 可能导致迁移后退化"
severity: WARNING
metadata:
preprocess: embedded-sql # 启用嵌入式 SQL 预处理
preprocess.from: "java,xml" # 指定来源:java、xml(逗号分隔,大小写不敏感)说明:
languages: [sql]仍然表示“使用 SQL 语义匹配器跑本规则”metadata.preprocess=embedded-sql告诉引擎:当输入文件是 Java/XML 时,先做 SQL 抽取与归一化metadata.preprocess.from用于选择来源宿主语言。仅当目标文件语言在此集合内时才执行本规则
避免 SELECT * 的规则,同时在 Java 与 MyBatis XML 上生效:
rules:
- id: sql-avoid-select-star
languages: [sql]
patterns:
- pattern-either:
- pattern: SELECT * FROM $TABLE
- pattern: select * from $TABLE
message: "避免 SELECT *;应明确列名"
severity: WARNING
metadata:
preprocess: embedded-sql
preprocess.from: "java,xml"当前内置的嵌入式 SQL 提取与归一化(后续可扩展):
- Java:
- 注解:
@Select("...")、(可按需扩展:@Query("..."),@SelectProvider(...)等) - JDBC/JPA 常见调用(按需扩展):
prepareStatement("...")、executeQuery("...")、createNativeQuery("...")等 - 处理字符串字面量;复杂拼接/变量替换目前以占位符方式归一化(后续可增强)
- 注解:
- MyBatis XML:
- 标签:
<select>...</select>(可按需扩展<insert>/<update>/<delete>/<sql>) - 占位符归一化:
#{param}→1(值类占位),${param}→T0(标识符类占位)
- 标签:
- 通用归一化:
- 去除多余空白、标准化分号收尾(便于与 SQL 模式匹配)
命中位置映射:
- 每段提取的 SQL 都保留来源文件路径与起始近似行号;命中后会回填到原文件的大致行列范围(后续可提升精度)
假设你已有带 metadata.preprocess 的 SQL 规则配置 rules.yaml:
# 在 XML 文件上执行:
astgrep analyze --language xml --config rules.yaml path/to/mapper.xml
# 在 Java 文件上执行:
astgrep analyze --language java --config rules.yaml path/to/Dao.java只要规则里声明了 metadata.preprocess: embedded-sql 且 preprocess.from 包含对应宿主语言,上述命令就会:
- 先从文件中提取 SQL → 归一化
- 用 SQL 语义匹配器执行规则
- 将命中回填到原 Java/XML 文件
- Java 复杂 SQL 构造(StringBuilder/format/concat/条件拼接/方法返回等)当前以占位处理,后续可增强数据流/拼接还原
- MyBatis 动态 SQL(
<if>/<where>/<trim>/<foreach>/<choose>)目前做弱归一化,适合结构性匹配;可逐步加入“骨架级展开” - 支持来源可扩展:除
java、xml外,未来可加入kotlin、scala、typescript等 - 行列精度:当前定位至片段起始行,后续可结合片段内偏移提高精度
astgrep 支持多方言 SQL 分析(GaussDB、OpenGauss、PolarDB-MySQL、标准 SQL)。你可以通过 dialects: 字段让规则仅在特定方言下触发。
| 方言 | --dialect 值 |
解析器 |
|---|---|---|
| 标准 SQL | standard(默认) |
tree-sitter-sequel |
| GaussDB | gaussdb |
ogsql-parser |
| OpenGauss | opengauss |
ogsql-parser |
| PolarDB-MySQL | polardb-mysql |
sqlparser-rs |
使用 dialects: 字段指定规则适用的方言列表:
rules:
- id: gaussdb-no-on-conflict
name: "GaussDB 不支持 ON CONFLICT"
languages: [sql]
dialects: [gaussdb, opengauss] # 仅在 GaussDB/OpenGauss 方言下触发
patterns:
- pattern: "ON CONFLICT"
message: "GaussDB/OpenGauss 不支持 ON CONFLICT,请使用 MERGE INTO 替代"
severity: ERROR关键点:
- 未声明
dialects:的规则适用于所有方言(向后兼容) - 声明了
dialects:的规则仅在列出的方言下触发 languages: [sql]保持不变,方言过滤通过dialects:字段实现
规则中的 dialects: 字段需要与 CLI 的 --dialect 标志配合使用:
# 用 GaussDB 方言分析,gaussdb-no-on-conflict 规则会触发
astgrep analyze --dialect gaussdb --rules rules.yaml *.sql
# 用标准 SQL 方言分析,gaussdb-no-on-conflict 规则不会触发
astgrep analyze --rules rules.yaml *.sqlSQL 方言规则支持三种模式:
字面量模式(文本匹配,所有解析器通用):
patterns:
- pattern: "VARCHAR2"元变量模式(结构化匹配,经 tree-sitter):
patterns:
- pattern: "SELECT * FROM $TABLE"否定模式(检测子句缺失):
patterns:
- pattern: "UPDATE $T SET $S"
- pattern-not: "UPDATE $T SET $S WHERE $W"GaussDB 方言支持检测以下专有语法:
PREDICT BY(AI 预测)TIMECAPSULE(闪回查询)SHRINK TABLE/INDEX(空间回收)- Plan Hints(
/*+ tablescan(t1) */) - MERGE 语义校验(内置校验器,无需规则文件)
详细的方言支持和规则列表请参考 SQL 方言支持。
astgrep 致力于与 Semgrep 保持高度兼容,但也有一些差异和扩展。
| 特性 | 说明 | 示例 |
|---|---|---|
| 基本模式 | 简单的代码模式匹配 | pattern: 'eval(...)' |
| 元变量 | $VAR 语法 |
pattern: '$FUNC($ARG)' |
| 省略号 | ... 匹配任意内容 |
pattern: 'foo(...)' |
| pattern-either | 或逻辑 | ✅ |
| pattern-not | 否定模式 | ✅ |
| pattern-inside | 上下文匹配 | ✅ |
| pattern-not-inside | 否定上下文 | ✅ |
| metavariable-pattern | 元变量模式 | ✅ |
| metavariable-regex | 元变量正则 | ✅ |
| metavariable-comparison | 元变量比较 | ✅ |
| 污点分析(旧语法) | mode: taint |
✅ |
| 污点分析(新语法) | taint: 块 |
✅ |
| focus-metavariable | 聚焦元变量 | ✅ |
| 特性 | astgrep 支持 | 说明 |
|---|---|---|
| pattern-regex | ✅ | 支持基本正则匹配 |
| metavariable-analysis | ✅ | 支持熵分析,类型分析部分支持 |
| Python 表达式比较 | 🚧 | 部分支持,不支持完整 Python 语法 |
| 跨文件分析 | 🚧 | 计划中 |
| 类型推断 | 🚧 | 部分语言支持 |
| 特性 | 说明 | 替代方案 |
|---|---|---|
pattern-where-python |
不支持完整 Python 表达式 | 使用 metavariable-comparison |
r2c-internal-* |
Semgrep 内部特性 | 无 |
| 某些语言特定特性 | 依赖语言支持程度 | 查看语言支持文档 |
astgrep 提供了一些 Semgrep 没有的特性:
-
增强的污点分析配置
dataflow: max_depth: 20 field_sensitive: true context_sensitive: true
-
更丰富的元数据支持
metadata: confidence: HIGH likelihood: MEDIUM impact: HIGH
-
GUI 和 Web 界面
- 交互式规则测试
- 可视化数据流图
从 Semgrep 迁移到 astgrep:
-
规则文件兼容性
- 大多数 Semgrep 规则可以直接使用
- 建议使用
astgrep validate验证规则
-
语法差异
# Semgrep pattern-where-python: | int($TIME) > 5000 # astgrep(推荐) metavariable-comparison: metavariable: '$TIME' comparison: '$TIME > 5000'
-
测试规则
# 验证规则语法 astgrep validate your-rule.yaml # 测试规则 astgrep analyze --rules your-rule.yaml test-file.java
# ✅ 好的命名
id: java-sql-injection-prepared-statement
id: python-hardcoded-secret-detection
id: javascript-xss-dom-based
# ❌ 不好的命名
id: rule1
id: test
id: my-rule# ✅ 清晰的消息
message: |
发现潜在的 SQL 注入漏洞。用户输入 '$INPUT' 未经验证直接用于 SQL 查询。
建议使用 PreparedStatement 和参数化查询。
# ❌ 模糊的消息
message: "发现问题"# CRITICAL: 严重安全漏洞
severity: CRITICAL # SQL 注入、RCE、认证绕过
# ERROR: 明确的安全问题
severity: ERROR # XSS、路径遍历、敏感信息泄露
# WARNING: 潜在问题
severity: WARNING # 弱加密、不安全配置
# INFO: 代码质量建议
severity: INFO # 代码风格、最佳实践patterns:
- pattern: '$STMT.execute($QUERY)'
# 排除安全的情况
- pattern-not: '$STMT.execute("...")' # 字面量
- pattern-not-inside: |
if (validate($QUERY)) {
...
}
# 确保是字符串拼接
- metavariable-pattern:
metavariable: '$QUERY'
patterns:
- pattern-either:
- pattern: '$A + $B'
- pattern: 'String.format(...)'# ✅ 高效的模式
patterns:
- pattern: 'eval($INPUT)' # 简单直接
# ❌ 低效的模式
patterns:
- pattern-regex: '.*eval.*' # 过于宽泛
- pattern-inside: |
... # 过深的嵌套
...
...rules:
- id: java-xxe-vulnerability
name: "XML 外部实体注入"
description: |
检测 XML 解析器配置不当导致的 XXE 漏洞。
当 XML 解析器允许处理外部实体时,攻击者可以读取服务器文件或进行 SSRF 攻击。
message: |
XML 解析器未禁用外部实体处理,可能导致 XXE 漏洞。
建议设置 setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
metadata:
cwe: "CWE-611"
owasp: "A05:2021 - Security Misconfiguration"
references:
- "https://owasp.org/www-community/vulnerabilities/XML_External_Entity_(XXE)_Processing"
remediation: |
禁用 DTD 和外部实体:
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);rules:
- id: java-sql-injection-comprehensive
languages: [java]
message: "潜在的 SQL 注入漏洞"
patterns:
- pattern-either:
- pattern: '$STMT.execute($QUERY)'
- pattern: '$STMT.executeQuery($QUERY)'
- pattern: '$STMT.executeUpdate($QUERY)'
- pattern-not: '$STMT.execute("...")'
- metavariable-pattern:
metavariable: '$QUERY'
patterns:
- pattern-either:
- pattern: '$A + $B'
- pattern: 'String.format($FMT, ...)'
- pattern: '$STR.concat($OTHER)'
severity: CRITICAL
metadata:
cwe: "CWE-89"
owasp: "A03:2021 - Injection"rules:
- id: javascript-dom-xss
languages: [javascript]
message: "潜在的 DOM XSS 漏洞"
taint:
sources:
- 'location.search'
- 'location.hash'
- 'document.URL'
- 'document.referrer'
sinks:
- '$EL.innerHTML = ...'
- 'document.write(...)'
- 'eval(...)'
sanitizers:
- 'DOMPurify.sanitize(...)'
- 'escapeHtml(...)'
severity: CRITICALastgrep 提供了强大而灵活的规则系统,支持从简单的模式匹配到复杂的污点分析。通过遵循本指南和最佳实践,你可以编写高质量、低误报的静态分析规则。
- 提交 Issue: https://github.com/c2j/astgrep/issues
- 查看示例规则:
tests/rules/目录 - 运行
astgrep validate验证规则语法