Trivy 内置 Rego 检查规则贡献指南:从元数据、Schema 到 result.new 的完整实践
本篇围绕 Trivy 官方文档《Contribute Rego Checks》展开,系统讲解如何为 Trivy 内置检查(builtin checks)贡献一条新的 Rego 规则:从撰写前的查重清单、.rego 文件的目录与包命名规范、# METADATA 元数据块各字段含义,到 deny 规则与 result.new 内置函数的用法,最后覆盖文档生成与测试要求。读完本文,你可以独立完成一条可被合并进 Trivy 内置检查库的合规检查规则,并理解 Trivy 扫描器在运行时如何解析这些规则。
撰写前的准备工作清单
所有 Trivy 内置检查统一维护在独立的 trivy-checks 仓库中(而非 Trivy 主仓库)。在动手写规则之前,官方文档要求先确认三件事:
- 检查是否已存在:确认你想贡献的检查不已经在
trivy-checks仓库的默认检查中; - 是否有他人正在贡献:查看
trivy-checks仓库的 Pull Request 列表,避免重复开发; - 是否有相关 issue:查看 Trivy 仓库的 issue 列表,了解社区是否标记了某个缺失的检查,可以优先认领。
文档同时建议:如果上述任何环节不清楚,先发起社区讨论再动手。这一点从仓库结构也能得到印证——Trivy 主仓库负责检查的"运行时与数据模型",trivy-checks 仓库负责"规则本体",两者通过元数据约定解耦。
检查规则的整体结构
Trivy 中的检查用 OPA Rego 编写,并遵循固定的结构。官方文档给出的 AWS 示例检查如下(完整保留原文,可直接作为模板复制):
# METADATA
# title: "RDS IAM Database Authentication Disabled"
# description: "Ensure IAM Database Authentication is enabled for RDS database instances to manage database access"
# scope: package
# schemas:
# - input: schema["aws"]
# related_resources:
# - https://docs.aws.amazon.com/neptune/latest/userguide/iam-auth.html
# custom:
# id: AVD-AWS-0176
# avd_id: AVD-AWS-0176
# provider: aws
# service: rds
# severity: MEDIUM
# short_code: enable-iam-auth
# recommended_action: "Modify the PostgreSQL and MySQL type RDS instances to enable IAM database authentication."
# input:
# selector:
# - type: cloud
# subtypes:
# - service: rds
# provider: aws
package builtin.aws.rds.aws0176
deny[res] {
instance := input.aws.rds.instances[_]
instance.engine.value == ["postgres", "mysql"][_]
not instance.iamauthenabled.value
res := result.new("Instance does not have IAM Authentication enabled", instance.iamauthenabled)
}
从结构上看,一个检查文件由三部分组成:
# METADATA注释块:位于文件顶部,实际上是"包级注解(package annotation)"格式的 YAML;package声明:如builtin.aws.rds.aws0176,遵循builtin.PROVIDER.SERVICE.ID的命名格式;deny规则:以部分集合(partial set)形式定义违规条件,命中即产出一条结果。
确认 Provider 与 Service 是否已受支持
每一条云服务检查都引用一个云 provider。provider 列表定义在 Trivy 主仓库的 pkg/iac/providers 目录中(可参见 pkg/iac/providers)。
在为一个云 provider 写新检查前,需要验证:
- 目标 provider 或资源类型是否已被 Trivy 支持,不支持则需先添加支持;
- 即使 provider 已存在,你检查所针对的 service 也必须已支持。AWS provider 的聚合结构可以 pkg/iac/providers/aws/aws.go 为参考。
重要例外:新的 Kubernetes 和 Dockerfile 检查不需要任何额外的 provider 定义,可以直接在 trivy-checks 仓库中编写对应规则,这是贡献门槛最低的入门方向。
为已有 Provider 添加新 Service
如果你的目标 service 尚不存在,需要回到 Trivy 主仓库做工程改造,具体流程见姊妹文档 添加 Service 支持,其要点为:
- 在 provider 目录下创建新 service 的 Go 结构体(如
pkg/iac/providers/aws/codebuild/codebuild.go),所有资源结构体内嵌iacTypes.Metadata,字段值使用iacTypes.BoolValue等包装类型以携带"是否用户显式设置、文件行号"等元信息; - 在 provider 聚合结构体中登记新 service;
- 更新 pkg/iac/adapters 下各 adapter(如 Terraform AWS adapter),把扫描目标转换成上述结构体;
- 完成后在 Trivy 根目录运行
mage schema:generate重新生成 provider 的 JSON Schema,并用mage schema:verify校验。
创建 .rego 文件与包命名
trivy-checks 仓库的 checks 目录包含全部内置检查,按类型嵌套在三个子目录中:
| 子目录 | 用途 |
|---|---|
cloud |
所有云 provider 及其 service 的检查 |
docker |
Docker 构建检查 |
kubernetes |
Kubernetes 配置检查 |
包名必须遵循 builtin.PROVIDER.SERVICE.ID 的格式,例如 builtin.aws.rds.aws0176。命名时建议对照 checks 目录下已有包名保持风格一致。
生成检查 ID
每条检查有一个唯一的自定义 ID,贯穿整个元数据(示例中的 AVD-AWS-0176 即同时出现在 id、avd_id 和包名中)。在 trivy-checks 仓库根目录运行 make id,可以得到当前可用的下一个 ID。注意:ID 仅在你计划把检查贡献回 trivy-checks 内置库时才必须有效;纯自用规则可以省略。
Check Metadata 深度解析
# METADATA 块必须位于检查文件顶部,内容是从其他检查复制粘贴即可起步的 YAML。官方文档说明:该格式本质上是"Rego 注释中的 YAML",并遵循 OPA Rego 自身的包级注解(package-level annotation)机制定义。一个关键差异是:对你自己的自定义检查,Metadata 是可选的;但贡献到 Trivy 内置检查时,Metadata 段是必需的。
Trivy 主仓库中,元数据的解析实现位于 pkg/iac/rego/metadata.go。该文件中的 StaticMetadata 结构体定义了 Trivy 实际消费的全部元数据字段(第 25–49 行):
type StaticMetadata struct {
Deprecated bool
ID string
AVDID string
Title string
LongID string
ShortCode string
Aliases []string
Description string
Severity string
RecommendedActions string
PrimaryURL string
References []string
InputOptions InputOptions
Package string
Frameworks map[framework.Framework][]string
Provider string
Service string
Library bool
CloudFormation *scan.EngineMetadata
Terraform *scan.EngineMetadata
Examples string
MinimumTrivyVersion string
}
结合 populate 方法的字段映射逻辑(metadata.go),可以整理出对贡献者最实用的字段说明表:
| 元数据字段 | 说明 |
|---|---|
id / avd_id |
检查唯一 ID(如 AVD-AWS-0176);avd_id 已标记为 ID 的兼容别名 |
title |
检查标题,映射为规则的 Summary |
description |
检查说明,映射为规则的 Explanation |
severity |
严重级别,解析时会统一转为大写(如 MEDIUM、HIGH) |
provider / service |
归属的 provider 与 service;缺省时分别回退为 generic 和 general(见 ToRule(),metadata.go) |
short_code |
短代码,用于生成规则名 |
recommended_action |
修复建议(源码同时兼容 recommended_actions 复数写法) |
related_resources / url |
参考链接,会汇总进规则的 Links,第一条同时作为 PrimaryURL |
aliases |
历史兼容的别名列表 |
deprecated |
布尔值,标记弃用检查;扫描器默认会过滤弃用规则 |
minimum_trivy_version |
规则生效所需的最低 Trivy 版本 |
frameworks |
按框架划分检查 ID,未声明时默认为 default 框架 |
cloud_formation / terraform |
引擎级元数据,支持 good_examples、bad_examples、links、remediation_markdown(见 NewEngineMetadata,metadata.go) |
input.selector |
输入选择器,声明规则适用的输入类型与子类型 |
其中 input.selector 声明了规则的适用范围,解析结构见 metadata.go 的 InputOptions/Selector/SubType 定义:
type Selector struct {
Type string
Subtypes []SubType
}
type SubType struct {
Group string
Version string
Kind string
Namespace string
Service string // only for cloud
Provider string // only for cloud
}
这正对应示例检查中的 type: cloud + service: rds / provider: aws 子类型声明——它告诉扫描器"只把 AWS RDS 的输入交给本规则求值"。此外,源码保留了向后兼容处理:defsec 类型在解析时会自动改写为 cloud(metadata.go),历史规则无需迁移。
Check Schema:把 input 绑定到具体对象
Trivy 的 Rego 检查可以利用 Schema 将 input 映射到特定对象。可用 Schema 列于 pkg/iac/rego/schemas 目录,实际包含以下 JSON 文件:
Schema 与输入源类型的绑定关系由 pkg/iac/rego/schemas/schemas.go 中的 SchemaMap 决定:
var SchemaMap = map[types.Source]Schema{
types.SourceDefsec: Cloud,
types.SourceCloud: Cloud,
types.SourceKubernetes: Kubernetes,
types.SourceRbac: Kubernetes,
types.SourceDockerfile: Dockerfile,
types.SourceTOML: Anything,
types.SourceYAML: Anything,
types.SourceJSON: Anything,
types.SourceTerraformRaw: TerraformRaw,
}
即:云配置(含遗留的 defsec 源)走 cloud.json,Kubernetes 与 RBAC 走 kubernetes.json,Dockerfile 走 dockerfile.json,Terraform raw 走 terraform-raw.json。更细的 Schema 使用细节可查阅 自定义误配置检查的 Schema 说明。
编写 Rego 规则:deny 与 result.new
规则本体使用 OPA Rego 语法编写。以下仍是官方示例中的规则部分:
deny[res] {
instance := input.aws.rds.instances[_]
instance.engine.value == ["postgres", "mysql"][_]
not instance.iamauthenabled.value
res := result.new("Instance does not have IAM Authentication enabled", instance.iamauthenabled)
}
逐行解读示例规则:
instance := input.aws.rds.instances[_]:遍历输入中所有 RDS 实例([_]是 Rego 的迭代器写法);instance.engine.value == ["postgres", "mysql"][_]:engine 是带.value包装的值类型,判断引擎是否为 PostgreSQL 或 MySQL;not instance.iamauthenabled.value:当 IAM 认证未启用时条件成立;- 四个条件全部满足时,向部分集合
res添加一条结果。
规则必须通过 result.new 返回结果。该函数无需 import——它由 Trivy 在运行时注册为 Rego 内置函数,注册代码见 pkg/iac/rego/custom.go:
func init() {
checksrego.RegisterBuiltins()
rego.RegisterBuiltin2(®o.Function{
Name: "result.new",
Decl: types.NewFunction(types.Args(types.S, types.A), types.A),
},
createResult,
)
...
}
result.new 的第一个参数是展示给用户的消息,第二个参数是检测到问题的资源对象(任意引用 input 文档字段的 Rego 变量都可以)。从源码实现(custom.go 的 createResult)可以进一步理解其工作原理:
- 它先构造一份默认元数据对象(
startline/endline/filepath/sourceprefix/explicit/managed等字段初始为默认值); - 随后从第二个参数(cause)中提取
__defsec_metadata字段(若不存在则直接把 cause 本身当作 Dockerfile 类型的输入),并从中读取startline、endline、filepath、sourceprefix、resource、managed等元信息回填到结果中(custom.go 的updateMetadata)。
这意味着:当 cause 传入的是带 iacTypes.Metadata 序列化痕迹的 provider 结构体字段时,结果会自动携带资源定义所在的文件路径与行号,这正是 Trivy 扫描报告中能精确定位到 Terraform/Kubernetes 资源位置的原因。
结果最终由 pkg/iac/rego/result.go 的 parseResult 解析,它兼容多种返回形态:字符串(直接作为消息)、数组(其中字符串为消息、map 为 cause)、map(解析为 cause),其他情况兜底为 "Rego check resulted in DENY"。因此虽然推荐用 result.new,直接返回消息字符串在解析层面也是合法的。
另外,扫描器运行时还会注入环境信息:pkg/iac/rego/runtime.go 将进程环境变量、OPA 版本与 commit 以 runtime 值形式提供给规则,规则中还可使用 isManaged 内置函数判断资源是否为托管资源(同样注册于 custom.go)。需要留意的是,pkg/iac/rego/scanner.go 定义了 DefaultAllowedRegoErrors = 10,即扫描器默认容忍至多 10 个 Rego 编译错误——单条规则的编译失败不会让整个扫描崩溃,而是被记为警告,方便你定位到具体是哪一个文件写错了。
生成文档、添加测试与提交 PR
规则写完后,官方流程要求:
- 生成文档:在
trivy-checks仓库根目录运行make docs为新策略生成文档,随 PR 一并提交; - 添加测试:所有 Rego 检查都必须有测试,
checks目录中每个检查都附带测试文件可作范本;测试写法详见 自定义误配置检查的测试说明。
主仓库中 Rego 检查的加载与执行逻辑可参考 pkg/iac/rego/scanner.go 与 pkg/iac/rego/load.go,元数据解析行为在 pkg/iac/rego/metadata_test.go 中有完整用例覆盖,可作为字段解析口径的"权威参照"。
文档最后给出了一个新增规则的完整示例 PR(aquasecurity/defsec 仓库的第 1000 号 PR)作为参考模板——它展示了新规则文件、测试、元数据与生成文档如何组成一个可合并的贡献。
小结:贡献一条内置检查的完整路径
| 步骤 | 动作 | 关键命令/位置 |
|---|---|---|
| 1 | 查重:已有检查、在途 PR、社区 issue | trivy-checks 仓库与 Trivy issue 列表 |
| 2 | 确认 provider/service 已支持(K8s、Dockerfile 免此步) | pkg/iac/providers |
| 3 | 按需添加新 service 与 schema | service 支持文档、mage schema:generate |
| 4 | 在 checks/cloud、checks/docker 或 checks/kubernetes 下创建 .rego |
包名 builtin.PROVIDER.SERVICE.ID |
| 5 | 生成 ID | make id(trivy-checks 根目录) |
| 6 | 编写 # METADATA 与 deny 规则,使用 result.new 返回结果 |
参考 pkg/iac/rego/custom.go |
| 7 | 生成文档并添加测试 | make docs;测试写法见 testing 文档 |
掌握本文后,你既能按规范写出可合并的内置检查规则,也能基于 pkg/iac/rego 下的实现源码,向他人解释 Trivy 是如何把一份 .rego 文件变成报告中带文件行号的合规发现的。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00