quasilyte/go-ruleguard
GitHub: quasilyte/go-ruleguard
一款基于模式匹配的 Go 语言自定义 Linter 框架,支持动态加载规则、自动修复建议和 golangci-lint 集成。
Stars: 862 | Forks: 41
# go-ruleguard


[](https://pkg.go.dev/mod/github.com/quasilyte/go-ruleguard)

## 概述
基于 [analysis](https://pkg.go.dev/golang.org/x/tools/go/analysis) 的 Go linter,运行动态加载的规则。
您编写规则,`ruleguard` 检查它们是否被满足。
`ruleguard` 与 [GitHub CodeQL](https://securitylab.github.com/tools/codeql) 有一些相似之处,但它专用于 Go。
**功能特性:**
* 无需重新编译和 Go plugins 即可使用自定义 linting 规则
* 以声明式方式编写诊断信息
* 支持 [Quickfix](_docs/dsl.md#suggestions-quickfix-support) 操作
* 强大的匹配过滤功能,例如表达式 [类型模式匹配](_docs/dsl.md#type-pattern-matching)
* 不局限于 AST 规则;例如,可以编写与注释相关的规则
* 规则可以作为 [Go modules](https://quasilyte.dev/blog/post/ruleguard-modules/) 安装和分发
* 可以使用常规 Go 工具开发、调试和分析规则
* 集成到 [golangci-lint](https://github.com/golangci/golangci-lint) 中
它还可以轻松嵌入到其他静态分析器中。可以以 [go-critic](https://github.com/go-critic/go-critic) 为例。
## 快速开始
建议您从 [最新发布版本](https://github.com/quasilyte/go-ruleguard/releases/tag/v0.3.18) 获取二进制文件 {[linux/amd64](https://github.com/quasilyte/go-ruleguard/releases/download/v0.3.18/ruleguard-linux-amd64.zip), [linux/arm64](https://github.com/quasilyte/go-ruleguard/releases/download/v0.3.18/ruleguard-linux-arm64.zip), [darwin/amd64](https://github.com/quasilyte/go-ruleguard/releases/download/v0.3.18/ruleguard-darwin-amd64.zip), [darwin/arm64](https://github.com/quasilyte/go-ruleguard/releases/download/v0.3.18/ruleguard-darwin-arm64.zip), [windows/amd64](https://github.com/quasilyte/go-ruleguard/releases/download/v0.3.18/ruleguard-windows-amd64.zip), [windows/arm64](https://github.com/quasilyte/go-ruleguard/releases/download/v0.3.18/ruleguard-windows-arm64.zip)}。
如果您想从源代码安装 ruleguard,非常简单:
```
# 在您的 `$(go env GOPATH)/bin` 下安装 `ruleguard` 二进制文件
$ go install -v github.com/quasilyte/go-ruleguard/cmd/ruleguard@latest
# 获取 DSL 包(执行 ruleguard 文件所需)
$ go get -v -u github.com/quasilyte/go-ruleguard/dsl@latest
```
如果您的系统 `$PATH` 包含 `$GOPATH/bin`,之后 `ruleguard` 命令应该可用:
```
$ ruleguard -help
ruleguard: execute dynamic gogrep-based rules
Usage: ruleguard [-flag] [package]
Flags:
-rules string
comma-separated list of ruleguard file paths
-e string
execute a single rule from a given string
-fix
apply all suggested fixes
-c int
display offending line with this many lines of context (default -1)
-json
emit JSON output
```
创建一个测试用的 `rules.go` 文件:
```
//go:build ruleguard
// +build ruleguard
package gorules
import "github.com/quasilyte/go-ruleguard/dsl"
func dupSubExpr(m dsl.Matcher) {
m.Match(`$x || $x`,
`$x && $x`,
`$x | $x`,
`$x & $x`).
Where(m["x"].Pure).
Report(`suspicious identical LHS and RHS`)
}
func boolExprSimplify(m dsl.Matcher) {
m.Match(`!($x != $y)`).Suggest(`$x == $y`)
m.Match(`!($x == $y)`).Suggest(`$x != $y`)
}
func exposedMutex(m dsl.Matcher) {
isExported := func(v dsl.Var) bool {
return v.Text.Matches(`^\p{Lu}`)
}
m.Match(`type $name struct { $*_; sync.Mutex; $*_ }`).
Where(isExported(m["name"])).
Report("do not embed sync.Mutex")
m.Match(`type $name struct { $*_; sync.RWMutex; $*_ }`).
Where(isExported(m["name"])).
Report("do not embed sync.RWMutex")
}
```
创建一个测试用的 `example.go` 目标文件:
```
package main
import "sync"
type EmbedsMutex struct {
key int
sync.Mutex
}
func main() {
var v1, v2 int
println(!(v1 != v2))
println(!(v1 == v2))
if v1 == 0 && v1 == 0 {
println("hello, world!")
}
}
```
对该目标文件运行 `ruleguard`:
```
$ ruleguard -rules rules.go -fix example.go
example.go:5:1: exposedMutex: do not embed sync.Mutex (rules.go:24)
example.go:12:10: boolExprSimplify: suggestion: v1 == v2 (rules.go:15)
example.go:13:10: boolExprSimplify: suggestion: v1 != v2 (rules.go:16)
example.go:14:5: dupSubExpr: suspicious identical LHS and RHS (rules.go:7)
```
由于我们使用了 `-fix` 参数运行 `ruleguard`,两项**建议的**修改都已应用到 `example.go`。
还有一个在模式调试期间非常有用的 `-e` 模式:
```
$ ruleguard -e 'm.Match(`!($x != $y)`)' example.go
example.go:12:10: !(v1 != v2)
```
它会自动将 `Report("$$")` 插入到指定的模式中。
您可以使用 `-debug-group ` 标志查看说明,
了解为什么某些规则拒绝了匹配(例如哪个 `Where()` 条件失败了)。
`-e` 生成的规则将具有 `e` 名称,因此也可以对其进行调试。
## 它是如何工作的?
首先,它在启动期间解析 [ruleguard](_docs/dsl.md) 文件(例如 `rules.go`)以加载规则集。
然后使用加载的规则检查指定的目标(Go 文件、packages)。
`rules.go` 文件是使用 [`dsl`](https://pkg.go.dev/github.com/quasilyte/go-ruleguard/dsl) API 编写的。Ruleguard 文件包含一组充当规则组的函数。每个这样的函数接受一个 [`dsl.Matcher`](https://pkg.go.dev/github.com/quasilyte/go-ruleguard/dsl#Matcher) 参数,然后用于定义和配置组内的规则。
规则定义始终以 [`Match(patterns...)`](https://pkg.go.dev/github.com/quasilyte/go-ruleguard/dsl#Matcher.Match) 方法调用开始,并以 [`Report(message)`](https://pkg.go.dev/github.com/quasilyte/go-ruleguard/dsl#Matcher.Report) 方法调用结束。
在这两者之间可以有额外的调用。例如,[`Where(cond)`](https://pkg.go.dev/github.com/quasilyte/go-ruleguard/dsl#Matcher.Where) 调用对匹配应用约束,以决定是接受还是拒绝它。因此,即使有与模式匹配的内容,除非它满足 `Where()` 条件,否则不会产生报告消息。
## 故障排除
为了使 ruleguard 正常工作,`dsl` 包必须在_运行时_可用。如果不可用,您将看到如下错误:
```
$ ruleguard -rules rules.go .
ruleguard: load rules: parse rules file: typechecker error: rules.go:6:8: could not import github.com/quasilyte/go-ruleguard/dsl (can't find import: "github.com/quasilyte/go-ruleguard/dsl")
```
通过将 dsl 包添加到模块中可以解决此问题:
```
$ ruleguard-test go get github.com/quasilyte/go-ruleguard/dsl
go: downloading github.com/quasilyte/go-ruleguard v0.3.18
go: downloading github.com/quasilyte/go-ruleguard/dsl v0.3.21
go: added github.com/quasilyte/go-ruleguard/dsl v0.3.21
$ ruleguard-test ruleguard -rules rules.go .
.../test.go:6:5: boolExprSimplify: suggestion: 1 == 0 (rules.go:9)
```
如果您遵循了过去的建议,在 rules.go 文件中使用构建约束,如下所示:
```
$ ruleguard-test head -4 rules.go
//go:build ignore
// +build ignore
package gorules
```
您会注意到 `go.mod` 文件将 dsl 列为间接依赖:
```
$ grep dsl go.mod
require github.com/quasilyte/go-ruleguard/dsl v0.3.21 // indirect
```
如果您现在运行 `go mod tidy`,您会注意到 dsl 包从 `go.mod` 文件中消失了:
```
$ go mod tidy
$ grep dsl go.mod
$
```
这是因为 `go mod tidy` 的行为就像所有构建约束都生效一样,_除了 `ignore` 之外_。[这在 Go 网站上有文档说明](https://go.dev/ref/mod#go-mod-tidy)。
通过使用不同的构建约束(例如 `ruleguard` 或 `rules`)可以解决此问题。
## 文档
* [Ruleguard by example](https://go-ruleguard.github.io/by-example/) 教程
* [Ruleguard files](_docs/dsl.md) 格式文档
* [dsl package](https://pkg.go.dev/github.com/quasilyte/go-ruleguard/dsl) 参考
* [ruleguard package](https://pkg.go.dev/github.com/quasilyte/go-ruleguard/ruleguard) 参考
* 介绍文章:[EN](https://quasilyte.dev/blog/post/ruleguard/),[RU](https://habr.com/ru/post/481696/)
* [在 golangci-lint 中使用 ruleguard](https://quasilyte.dev/blog/post/ruleguard/#using-from-the-golangci-lint)
## 规则集示例
* 来自 ruleguard 的基本规则集:[go-ruleguard/rules](rules)
* [Damian Gryski](github.com/dgryski/) 规则集:[github.com/dgryski/semgrep-go/ruleguard.rules.go](https://github.com/dgryski/semgrep-go)
* [go-critic](https://github.com/go-critic/go-critic) 规则集:[github.com/go-critic/go-critic/checkers/rules/rules.go](https://github.com/go-critic/go-critic/blob/master/checkers/rules/rules.go)
* 部分 [Uber-Go](https://github.com/uber-go/guide) 风格规则集:[github.com/quasilyte/uber-rules](https://github.com/quasilyte/uber-rules)
* [go-perfguard](https://github.com/quasilyte/go-perfguard) 规则集:[github.com/quasilyte/go-perfguard/perfguard/_rules/universal_rules.go](https://github.com/quasilyte/go-perfguard/blob/master/perfguard/_rules/universal_rules.go)
注意:`go-critic` 和 `go-perfguard` 使用 IR 预编译功能嵌入规则。
## 更多参考
* 在线 ruleguard playground:[go-ruleguard.github.io/play](https://go-ruleguard.github.io/play)
* [gogrep](https://github.com/quasilyte/gogrep) - 底层 AST 匹配引擎
* [NoVerify: Dynamic Rules for Static Analysis](https://medium.com/@vktech/noverify-dynamic-rules-for-static-analysis-8f42859e9253)
* [Ruleguard comparison with Semgrep and CodeQL](https://speakerdeck.com/quasilyte/ruleguard-vs-semgrep-vs-codeql)
标签:CodeQL, DevSecOps, DNS解析, EVTX分析, Go, golangci-lint, Go语言, Linter, odt, pptx, Ruby工具, SAST, 上游代理, 云安全监控, 云计算, 威胁情报, 安全评估工具, 开发者工具, 开源项目, 抽象语法树, 日志审计, 模式匹配, 盲注攻击, 程序破解, 自动化资产收集, 自定义规则, 规则引擎, 静态分析