quasilyte/go-ruleguard

GitHub: quasilyte/go-ruleguard

一款基于模式匹配的 Go 语言自定义 Linter 框架,支持动态加载规则、自动修复建议和 golangci-lint 集成。

Stars: 862 | Forks: 41

# go-ruleguard ![构建状态](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/47df216df4004008.svg) ![构建状态](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/76f95533fa004010.svg) [![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/quasilyte/go-ruleguard)](https://pkg.go.dev/mod/github.com/quasilyte/go-ruleguard) ![Logo](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/4c12a6e6b4004012.png) ## 概述 基于 [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, 上游代理, 云安全监控, 云计算, 威胁情报, 安全评估工具, 开发者工具, 开源项目, 抽象语法树, 日志审计, 模式匹配, 盲注攻击, 程序破解, 自动化资产收集, 自定义规则, 规则引擎, 静态分析