☰
Trivy 许可证扫描(License Scanning)完整指南:分类体系、全量扫描与自定义配置
2026/10/6 21:31:07 网站建设 项目流程

Trivy 许可证扫描(License Scanning)完整指南:分类体系、全量扫描与自定义配置

【免费下载链接】trivyFind vulnerabilities, misconfigurations, secrets, SBOM in containers, Kubernetes, code repositories, clouds and more项目地址: https://gitcode.com/GitHub_Trending/tr/trivy

Trivy 的许可证扫描(License Scanning)能力会检查容器镜像中的许可证文件与包许可证,并基于 Google License Classification 提供一套有观点(opinionated)的风险分类。本文以 docs/guide/scanner/license.md 为骨架,结合仓库源码(标志定义、分类器实现、文件分析器)深入讲解:许可证如何被分类为 Forbidden / Restricted 等 7 类并映射为严重级别、标准扫描与--license-full全量扫描的区别、以及--ignored-licenses、--license-confidence-level、自定义分类、文本许可证正则等全套配置方法。读完本文,你将能够独立完成镜像与文件系统的许可证合规扫描,并按团队合规策略定制分类规则。

许可证扫描是什么

Trivy 会扫描任意容器镜像中的许可证文件,并对许可证关联的风险给出意见化视图。许可证由Google License Classification(Google 开源项目维护的第三方许可证分类标准)分为以下 7 类:

分类(Classification)含义
Forbidden禁止使用
Restricted限制使用(如强制开源分发)
Reciprocal互惠(修改后需开源)
Notice需保留版权声明
Permissive宽松许可
Unencumbered无负担(可任意使用)
Unknown无法识别

提示:Trivy 无法识别的许可证会被归类为UNKNOWN。由于这些许可证可能违反合规要求,官方建议连同未知许可证一并检查。

从源码看,这 7 个分类在 pkg/fanal/types/license.go 中被定义为LicenseCategory常量(forbidden、restricted、reciprocal、notice、permissive、unencumbered、unknown),贯穿扫描、分类与报告的整个链路。

默认扫描与全量扫描

  • 默认(标准)扫描:只扫描由apk、apt-get、dnf、npm、pip、gem等包管理器安装的包的许可证,覆盖范围见 docs/guide/coverage/index.md。
  • 全量扫描(--license-full):在包许可证之外,还会扫描源码文件、Markdown 文档、文本文件以及LICENSE文档,以识别镜像或文件系统内的许可证使用情况。

扫描模式支持矩阵

License scanningImageRootfsFilesystemRepositorySBOM
Standard✅✅✅12✅12✅
Full (--license-full)✅✅✅✅-

注意:全量许可证扫描开销较大,耗时较长,官方在文档中明确提示这一点。

分类到严重级别的映射

许可证检查会将识别出的许可证分类,并映射到对应的严重级别(Severity):

分类(Classification)严重级别(Severity)
ForbiddenCRITICAL
RestrictedHIGH
ReciprocalMEDIUM
NoticeLOW
PermissiveLOW
UnencumberedLOW
UnknownUNKNOWN

该映射的源码实现在 pkg/licensing/scanner.go 的categoryToSeverity函数中:

func categoryToSeverity(category types.LicenseCategory) dbTypes.Severity { switch category { case types.CategoryForbidden: return dbTypes.SeverityCritical case types.CategoryRestricted: return dbTypes.SeverityHigh case types.CategoryReciprocal: return dbTypes.SeverityMedium case types.CategoryNotice, types.CategoryPermissive, types.CategoryUnencumbered: return dbTypes.SeverityLow } return dbTypes.SeverityUnknown }

需要说明的是,源码实现将 Forbidden 映射为 CRITICAL、Restricted 映射为 HIGH、Reciprocal 映射为 MEDIUM、Notice/Permissive/Unencumbered 统一映射为 LOW,与文档表格完全一致。

快速开始

本节演示如何扫描容器镜像与文件系统中的许可证。

标准扫描

使用--scanners license指定镜像名称即可:

$ trivy image --scanners license --severity UNKNOWN,HIGH,CRITICAL alpine:3.15 2022-07-13T17:28:39.526+0300 INFO License scanning is enabled OS Packages (license) ===================== Total: 6 (UNKNOWN: 0, HIGH: 6, CRITICAL: 0) ┌───────────────────┬─────────┬────────────────┬──────────┐ │ Package │ License │ Classification │ Severity │ ├───────────────────┼─────────┼────────────────┼──────────┤ │ alpine-baselayout │ GPL-2.0 │ Restricted │ HIGH │ ├───────────────────┤ │ │ │ │ apk-tools │ │ │ │ ├───────────────────┤ │ │ │ │ busybox │ │ │ │ ├───────────────────┤ │ │ │ │ musl-utils │ │ │ │ ├───────────────────┤ │ │ │ │ scanelf │ │ │ │ ├───────────────────┤ │ │ │ │ ssl_client │ │ │ │ └───────────────────┴─────────┴────────────────┴──────────┘

如示例所示,Alpine 3.15 的 6 个 OS 包全部使用 GPL-2.0,被分类为 Restricted(HIGH),这是 GPL 系许可证在默认分类下的典型结果。默认分类列表中 GPL-2.0 / GPL-3.0 等属于 Restricted 类别(见 pkg/licensing/expression/category.go)。

全量扫描

指定--license-full开启全量扫描:

$ trivy image --scanners license --severity UNKNOWN,HIGH,CRITICAL --license-full grafana/grafana 2022-07-13T17:48:40.905+0300 INFO Full license scanning is enabled OS Packages (license) ===================== Total: 20 (UNKNOWN: 9, HIGH: 11, CRITICAL: 0) ┌───────────────────┬───────────────────┬────────────────┬──────────┐ │ Package │ License │ Classification │ Severity │ ├───────────────────┼───────────────────┼────────────────┼──────────┤ │ alpine-baselayout │ GPL-2.0 │ Restricted │ HIGH │ ├───────────────────┤ │ │ │ │ apk-tools │ │ │ │ ├───────────────────┼───────────────────┼────────────────┼──────────┤ │ bash │ GPL-3.0 │ │ │ ├───────────────────┼───────────────────┼────────────────┼──────────┤ │ keyutils-libs │ GPL-2.0 │ Restricted │ HIGH │ │ ├───────────────────┼────────────────┼──────────┤ │ │ LGPL-2.0-or-later │ Non Standard │ UNKNOWN │ ├───────────────────┼───────────────────┤ │ │ │ libaio │ LGPL-2.1-or-later │ │ │ ├───────────────────┼───────────────────┼────────────────┼──────────┤ │ libcom_err │ GPL-2.0 │ Restricted │ HIGH │ │ ├───────────────────┼────────────────┼──────────┤ │ │ LGPL-2.0-or-later │ Non Standard │ UNKNOWN │ ├───────────────────┼───────────────────┼────────────────┼──────────┤ │ tzdata │ Public-Domain │ Non Standard │ UNKNOWN │ └───────────────────┴───────────────────┴────────────────┴──────────┘ Loose File License(s) (license) =============================== Total: 6 (UNKNOWN: 4, HIGH: 0, CRITICAL: 2) ┌────────────────┬──────────┬──────────────┬──────────────────────────────────────────────────────────────┐ │ Classification │ Severity │ License │ File Location │ ├────────────────┼──────────┼──────────────┼──────────────────────────────────────────────────────────────┤ │ Forbidden │ CRITICAL │ AGPL-3.0 │ /usr/share/grafana/LICENSE │ │ │ │ │ │ │ │ │ │ │ ├────────────────┼──────────┼──────────────┼──────────────────────────────────────────────────────────────┤ │ Non Standard │ UNKNOWN │ BSD-0-Clause │ /usr/share/grafana/public/build/5069.d6aae9dd11d49c741a80.j- │ │ │ │ │ s.LICENSE.txt │ │ │ │ ├──────────────────────────────────────────────────────────────┤ │ │ │ │ /usr/share/grafana/public/build/6444.d6aae9dd11d49c741a80.j- │ │ │ │ │ s.LICENSE.txt │ │ │ │ ├──────────────────────────────────────────────────────────────┤ │ │ │ │ /usr/share/grafana/public/build/7889.d6aae9dd11d49c741a80.j- │ │ │ │ │ s.LICENSE.txt │ │ │ │ ├──────────────────────────────────────────────────────────────┤ │ │ │ │ /usr/share/grafana/public/build/canvasPanel.d6aae9dd11d49c7- │ │ │ │ │ 41a80.js.LICENSE.txt │ └────────────────┴──────────┴──────────────┴──────────────────────────────────────────────────────────────┘

全量扫描的报告中新增了Loose File License(s)区块,展示散落在镜像文件系统各处的许可证文件(例如前端的*.js.LICENSE.txt)及其分类与严重级别。上例中AGPL-3.0被归类为 Forbidden(CRITICAL),这与源码中 pkg/licensing/expression/category.go 的ForbiddenLicenses默认列表一致——AGPL-1.0、AGPL-3.0 及全部 CC-BY-NC 系列都被列为禁止。

从实现上看,--license-full对应标志LicenseFull,其定义为 "eagerly look for licenses in source code headers and license files"(见 pkg/flag/license_flags.go),即启用后 Trivy 会额外启动文件级许可证分析器。

配置项详解

Trivy 为许可证扫描提供了一系列配置标志,以下逐一说明。

忽略指定许可证:--ignored-licenses

使用--ignored-licenses可以从结果中显式移除已识别的许可证:

$ trivy image --scanners license --ignored-licenses MPL-2.0,MIT --severity HIGH grafana/grafana:latest 2022-07-13T18:15:28.605Z INFO License scanning is enabled OS Packages (license) ===================== Total: 2 (HIGH: 2, CRITICAL: 0) ┌───────────────────┬─────────┬────────────────┬──────────┐ │ Package │ License │ Classification │ Severity │ ├───────────────────┼─────────┼────────────────┼──────────┤ │ alpine-baselayout │ GPL-2.0 │ Restricted │ HIGH │ ├───────────────────┤ │ │ │ │ ssl_client │ │ │ │ └───────────────────┴─────────┴────────────────┴──────────┘

在 pkg/flag/license_flags.go 中,IgnoredLicenses对应配置名license.ignored,即同样可以在trivy.yaml中以license.ignored配置。

配置分类器置信度:--license-confidence-level

--license-confidence-level用于调整分类器的置信度阈值,取值范围0.0 ~ 1.0,默认 0.9。也就是说,默认情况下 Trivy 只保留置信度 ≥ 0.9 的分类结果;降低阈值可以识别出置信度更低、但可能正确的许可证。

例如,以默认置信度对 SPDX 许可证列表文本(text目录)做全量扫描,只能检测出 258 个许可证:

$ trivy fs --scanners license --license-full <path/to/spdx/list/data> 2023-04-18T10:05:13.601-0700 INFO Full license scanning is enabled Loose File License(s) (license) =============================== Total: 258 (UNKNOWN: 70, LOW: 90, MEDIUM: 18, HIGH: 58, CRITICAL: 22)

将置信度调整为 0.8 后,可以检测出 282 个许可证:

$ trivy fs --scanners license --license-full --license-confidence-level 0.8 <path/to/spdx/list/data> 2023-04-18T10:21:39.637-0700 INFO Full license scanning is enabled Loose File License(s) (license) =============================== Total: 282 (UNKNOWN: 81, LOW: 97, MEDIUM: 24, HIGH: 58, CRITICAL: 22)

实现层面,置信度阈值定义在 pkg/flag/license_flags.go(配置名license.confidenceLevel),并最终传入 pkg/licensing/classifier.go 的Classify函数——该函数基于 Google 的github.com/google/licenseclassifier/v2对文件内容做匹配,只保留match.Confidence > confidenceLevel的匹配结果,并将匹配类型区分为Header(文件头)与License(许可证文件)。

自定义分类:--generate-default-config

使用--generate-default-config可以生成默认配置文件,然后在其中定制许可证分类。例如,若只想禁止 AGPL-3.0,可以把它保留在forbidden下,而把其他许可证移到别的分类。

$ trivy image --generate-default-config $ vim trivy.yaml

生成的默认配置结构如下(完整内容):

license: forbidden: - AGPL-3.0 restricted: - AGPL-1.0 - CC-BY-NC-1.0 - CC-BY-NC-2.0 - CC-BY-NC-2.5 - CC-BY-NC-3.0 - CC-BY-NC-4.0 - CC-BY-NC-ND-1.0 - CC-BY-NC-ND-2.0 - CC-BY-NC-ND-2.5 - CC-BY-NC-ND-3.0 - CC-BY-NC-ND-4.0 - CC-BY-NC-SA-1.0 - CC-BY-NC-SA-2.0 - CC-BY-NC-SA-2.5 - CC-BY-NC-SA-3.0 - CC-BY-NC-SA-4.0 - Commons-Clause - Facebook-2-Clause - Facebook-3-Clause - Facebook-Examples - WTFPL - BCL - CC-BY-ND-1.0 - CC-BY-ND-2.0 - CC-BY-ND-2.5 - CC-BY-ND-3.0 - CC-BY-ND-4.0 - CC-BY-SA-1.0 - CC-BY-SA-2.0 - CC-BY-SA-2.5 - CC-BY-SA-3.0 - CC-BY-SA-4.0 - GPL-1.0 - GPL-2.0 - GPL-2.0-with-autoconf-exception - GPL-2.0-with-bison-exception - GPL-2.0-with-classpath-exception - GPL-2.0-with-font-exception - GPL-2.0-with-GCC-exception - GPL-3.0 - GPL-3.0-with-autoconf-exception - GPL-3.0-with-GCC-exception - LGPL-2.0 - LGPL-2.1 - LGPL-3.0 - NPL-1.0 - NPL-1.1 - OSL-1.0 - OSL-1.1 - OSL-2.0 - OSL-2.1 - OSL-3.0 - QPL-1.0 - Sleepycat reciprocal: - APSL-1.0 - APSL-1.1 - APSL-1.2 - APSL-2.0 - CDDL-1.0 - CDDL-1.1 - CPL-1.0 - EPL-1.0 - EPL-2.0 - FreeImage - IPL-1.0 - MPL-1.0 - MPL-1.1 - MPL-2.0 - Ruby notice: - AFL-1.1 - AFL-1.2 - AFL-2.0 - AFL-2.1 - AFL-3.0 - Apache-1.0 - Apache-1.1 - Apache-2.0 - Artistic-1.0-cl8 - Artistic-1.0-Perl - Artistic-1.0 - Artistic-2.0 - BSL-1.0 - BSD-2-Clause-FreeBSD - BSD-2-Clause-NetBSD - BSD-2-Clause - BSD-3-Clause-Attribution - BSD-3-Clause-Clear - BSD-3-Clause-LBNL - BSD-3-Clause - BSD-4-Clause - BSD-4-Clause-UC - BSD-Protection - CC-BY-1.0 - CC-BY-2.0 - CC-BY-2.5 - CC-BY-3.0 - CC-BY-4.0 - FTL - ISC - ImageMagick - Libpng - Lil-1.0 - Linux-OpenIB - LPL-1.02 - LPL-1.0 - MS-PL - MIT - NCSA - OpenSSL - PHP-3.01 - PHP-3.0 - PIL - Python-2.0 - Python-2.0-complete - PostgreSQL - SGI-B-1.0 - SGI-B-1.1 - SGI-B-2.0 - Unicode-DFS-2015 - Unicode-DFS-2016 - Unicode-TOU - UPL-1.0 - W3C-19980720 - W3C-20150513 - W3C - X11 - Xnet - Zend-2.0 - zlib-acknowledgement - Zlib - ZPL-1.1 - ZPL-2.0 - ZPL-2.1 unencumbered: - CC0-1.0 - Unlicense - 0BSD permissive: []

这套默认分类清单正是由源码中的各分类默认列表生成的:LicenseForbidden、LicenseRestricted、LicenseReciprocal、LicenseNotice、LicensePermissive、LicenseUnencumbered分别对应配置项license.forbidden、license.restricted、license.reciprocal、license.notice、license.permissive、license.unencumbered,其默认值直接取自 pkg/licensing/expression/category.go 中移植自 Google licenseclassifier 的ForbiddenLicenses、RestrictedLicenses、ReciprocalLicenses、NoticeLicenses、PermissiveLicenses、UnencumberedLicenses列表(见 pkg/flag/license_flags.go)。

文本许可证(Text licenses)

默认情况下,若 Trivy 无法从许可证文本中确定许可证名称,会将其归类为 UNKNOWN。要为文本许可证定义分类,需要在许可证分类中加入带text://前缀的条目。例如:

license: forbidden: - "text://Text of Apache Software Foundation License"

由于文本许可证可能很长,Trivy 支持在分类中使用正则表达式:

license: forbidden: - "text://.* Apache Software .*"

注意:regex仅用于文本许可证,不能用来配置许可证 ID。

从源码看,text://前缀即LicenseTextPrefix,其匹配逻辑实现在 pkg/licensing/scanner.go 的ScanTextLicense函数中:遍历各分类下带text://前缀的条目,将其余部分作为正则表达式与许可证文本匹配,命中即返回对应分类与严重级别;同时源码注释指出,匹配失败会记录 Debug 日志并继续,最终未命中任何模式时返回 UNKNOWN。

只启用部分包类型:--pkg-types

如果你只想扫描某些包类型,可以通过--pkg-types传入逗号分隔的包类型列表。可选值:

  • os:扫描由 OS 包管理器(如dpkg、yum、apk)管理的 OS 包;
  • library:扫描语言特定包(如由pip、npm、gem安装的包)。
$ trivy image --pkg-types os ruby:2.4.0

该标志同时作用于其他扫描器,用于从源头控制纳入分析的包类型范围,从而缩小扫描面、提升扫描速度。

源码级的实现原理

文件级许可证分析器

全量扫描依赖文件级许可证分析器 pkg/fanal/analyzer/licensing/license.go,其关键行为包括:

  • 跳过的目录:node_modules/(由 node 扫描负责)、usr/share/doc/(由 dpkg 负责)等目录会被启发式跳过,避免重复扫描(见 license.go)。
  • 接受的扩展名:.c、.h、.java、.js、.md、.py、.php、.rb、.sh、.txt、.ts等源码/文本扩展名会被纳入扫描(见 license.go)。
  • 许可证文件名:文件名为LICENSE、LICENCE、COPYRIGHT(大小写不敏感)的文件始终会被检查(classifier.go 中定义的LicenseFileNames)。
  • 可读性预检:isHumanReadable会读取文件头部 300 字节,通过二进制控制字符检测排除非文本文件(见 license.go)。
  • 置信度注入:分析器通过AnalyzerOptions.LicenseScannerOption.ClassifierConfidenceLevel拿到--license-confidence-level的值并用于分类(见 license.go)。

复合许可证表达式的分类判定

当包声明的是复合许可证表达式(例如(MIT OR GPL-2.0))时,pkg/licensing/scanner.go 的detectCategory会递归解析表达式:

  • 简单表达式:直接取该许可证所属分类;
  • AND运算符:取两侧分类中严重级别更高的一类;
  • OR运算符:取两侧分类中严重级别更低的一类;
  • 任一侧为 UNKNOWN:整体判定为 UNKNOWN;
  • WITH运算符(如GPL-2.0 WITH Classpath-exception):按简单表达式处理。

该判定逻辑会在报告生成前,将LicenseFinding的分类与严重级别写入结果(见 pkg/fanal/types/license.go)。

使用建议与注意事项

  • 全量扫描(--license-full)耗时较长,建议在 CI 的定时任务或发布前检查中开启,日常快速扫描可仅使用标准模式。
  • 许可证的合规判定具有强业务属性:默认分类来自 Google License Classification,团队应结合自身法务口径,通过trivy.yaml的license.*配置或--ignored-licenses定制规则。
  • 对 UNKNOWN 分类的结果不要直接放过——它们可能是新的、非常见许可证,也可能包含潜在违规风险;降低--license-confidence-level能帮助识别更多边缘许可证,但同时可能引入误报,需要结合置信度列综合判断。
  • 许可证扫描结果与 OS 包、语言包检测深度绑定:若镜像中缺少包管理器的元数据(如精简过的 distroless 镜像),标准扫描可能无法识别包许可证,此时可结合全量文件扫描兜底。

总结

Trivy 的许可证扫描覆盖从"包许可证快速检查"到"全量文件级许可证识别"的完整链路,并通过 Google License Classification 提供统一的风险分类与严重级别映射。结合--ignored-licenses、--license-confidence-level、--pkg-types以及基于--generate-default-config的自定义分类(含文本许可证正则),你完全可以将许可证合规检查标准化为可复用的 CI 流程,持续管控镜像与代码仓库中的许可证风险。


  1. 支持的语言文件清单见 docs/guide/coverage/language/index.md。

    ↩ ↩
  2. 部分锁文件需要额外文件(例如缓存目录中的文件)才能检测许可证,详见 docs/guide/coverage/index.md。

    ↩ ↩

【免费下载链接】trivyFind vulnerabilities, misconfigurations, secrets, SBOM in containers, Kubernetes, code repositories, clouds and more项目地址: https://gitcode.com/GitHub_Trending/tr/trivy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询