首页
/ Trivy 内置 Rego 检查规则贡献指南:从元数据、Schema 到 result.new 的完整实践

Trivy 内置 Rego 检查规则贡献指南:从元数据、Schema 到 result.new 的完整实践

2026-09-05 20:33:52作者:薛曦旖Francesca

本篇围绕 Trivy 官方文档《Contribute Rego Checks》展开,系统讲解如何为 Trivy 内置检查(builtin checks)贡献一条新的 Rego 规则:从撰写前的查重清单、.rego 文件的目录与包命名规范、# METADATA 元数据块各字段含义,到 deny 规则与 result.new 内置函数的用法,最后覆盖文档生成与测试要求。读完本文,你可以独立完成一条可被合并进 Trivy 内置检查库的合规检查规则,并理解 Trivy 扫描器在运行时如何解析这些规则。

撰写前的准备工作清单

所有 Trivy 内置检查统一维护在独立的 trivy-checks 仓库中(而非 Trivy 主仓库)。在动手写规则之前,官方文档要求先确认三件事:

  1. 检查是否已存在:确认你想贡献的检查不已经在 trivy-checks 仓库的默认检查中;
  2. 是否有他人正在贡献:查看 trivy-checks 仓库的 Pull Request 列表,避免重复开发;
  3. 是否有相关 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)
}

从结构上看,一个检查文件由三部分组成:

  1. # METADATA 注释块:位于文件顶部,实际上是"包级注解(package annotation)"格式的 YAML;
  2. package 声明:如 builtin.aws.rds.aws0176,遵循 builtin.PROVIDER.SERVICE.ID 的命名格式;
  3. 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 即同时出现在 idavd_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 严重级别,解析时会统一转为大写(如 MEDIUMHIGH
provider / service 归属的 provider 与 service;缺省时分别回退为 genericgeneral(见 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_examplesbad_exampleslinksremediation_markdown(见 NewEngineMetadatametadata.go
input.selector 输入选择器,声明规则适用的输入类型与子类型

其中 input.selector 声明了规则的适用范围,解析结构见 metadata.goInputOptions/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 类型在解析时会自动改写为 cloudmetadata.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(&rego.Function{
		Name: "result.new",
		Decl: types.NewFunction(types.Args(types.S, types.A), types.A),
	},
		createResult,
	)
	...
}

result.new 的第一个参数是展示给用户的消息,第二个参数是检测到问题的资源对象(任意引用 input 文档字段的 Rego 变量都可以)。从源码实现(custom.gocreateResult)可以进一步理解其工作原理:

  1. 它先构造一份默认元数据对象(startline/endline/filepath/sourceprefix/explicit/managed 等字段初始为默认值);
  2. 随后从第二个参数(cause)中提取 __defsec_metadata 字段(若不存在则直接把 cause 本身当作 Dockerfile 类型的输入),并从中读取 startlineendlinefilepathsourceprefixresourcemanaged 等元信息回填到结果中(custom.goupdateMetadata)。

这意味着:当 cause 传入的是带 iacTypes.Metadata 序列化痕迹的 provider 结构体字段时,结果会自动携带资源定义所在的文件路径与行号,这正是 Trivy 扫描报告中能精确定位到 Terraform/Kubernetes 资源位置的原因。

结果最终由 pkg/iac/rego/result.goparseResult 解析,它兼容多种返回形态:字符串(直接作为消息)、数组(其中字符串为消息、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

规则写完后,官方流程要求:

  1. 生成文档:在 trivy-checks 仓库根目录运行 make docs 为新策略生成文档,随 PR 一并提交;
  2. 添加测试:所有 Rego 检查都必须有测试,checks 目录中每个检查都附带测试文件可作范本;测试写法详见 自定义误配置检查的测试说明

主仓库中 Rego 检查的加载与执行逻辑可参考 pkg/iac/rego/scanner.gopkg/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/cloudchecks/dockerchecks/kubernetes 下创建 .rego 包名 builtin.PROVIDER.SERVICE.ID
5 生成 ID make id(trivy-checks 根目录)
6 编写 # METADATAdeny 规则,使用 result.new 返回结果 参考 pkg/iac/rego/custom.go
7 生成文档并添加测试 make docs;测试写法见 testing 文档

掌握本文后,你既能按规范写出可合并的内置检查规则,也能基于 pkg/iac/rego 下的实现源码,向他人解释 Trivy 是如何把一份 .rego 文件变成报告中带文件行号的合规发现的。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384