Taketo-Yoda/uv-sbom
GitHub: Taketo-Yoda/uv-sbom
为 uv 管理的 Python 项目生成 SBOM 并检查漏洞与许可证合规性的命令行工具
Stars: 1 | Forks: 0
# uv-sbom
[](https://github.com/Taketo-Yoda/uv-sbom/releases) [](https://pypi.org/project/uv-sbom-bin/) [](https://crates.io/crates/uv-sbom)
[![shield_license]][license_file] [](https://github.com/Taketo-Yoda/uv-sbom/actions/workflows/ci.yml)
[](https://github.com/Taketo-Yoda/uv-sbom/actions/workflows/dependabot/dependabot-updates) [](https://github.com/Taketo-Yoda/uv-sbom/actions/workflows/github-code-scanning/codeql)
[English](README.md) | [日本語](README-JP.md)
为通过 [uv](https://github.com/astral-sh/uv) 管理的 Python 项目生成 SBOM (软件物料清单)。
## 功能特性
- 📦 解析 `uv.lock` 文件以提取依赖信息
- 🔍 通过重试逻辑自动从 PyPI 获取许可证信息
- 🛡️ 使用 OSV API 检查已知漏洞(仅限 Markdown 格式)
- 📋 许可证合规性策略检查,支持可配置的允许/拒绝列表和通配符
- 🔎 **漏洞解决指南** - 识别哪个直接依赖引入了每个有漏洞的传递依赖
- 📊 多种格式输出:
- **CycloneDX 1.6** JSON 格式(标准 SBOM 格式)
- **Markdown** 格式,清晰区分直接依赖和传递依赖
- 🚀 快速且独立 - 使用 Rust 编写
- 💾 输出到标准输出或文件
- 🛡️ 健壮的错误处理,提供有用的错误消息和建议
- 📈 许可证信息检索期间的进度跟踪
- 🏗️ 采用 **六边形架构** (端口与适配器) + **领域驱动设计**,确保可维护性和可测试性
- ✅ 全面的测试覆盖(单元测试、集成测试、E2E 测试)
## 范围与 CycloneDX 的主要区别
### SBOM 范围
本工具基于 **uv.lock** 文件内容生成 SBOM,包括:
- 直接运行时依赖
- 传递运行时依赖
- 开发依赖(如果锁定在 uv.lock 中)
**不包含的内容:**
- 构建系统依赖(例如 hatchling, setuptools)
- 发布工具(例如 twine, build)
- 仅存在于虚拟环境中但未锁定在 uv.lock 中的依赖
### 与 CycloneDX 官方工具的对比
截至 v7.2.1,官方 cyclonedx-python 库尚未直接支持 uv。在为 Python 项目生成 SBOM 时:
| 方面 | uv-sbom (本工具) | CycloneDX 官方工具 |
|--------|---------------------|--------------------------|
| **数据来源** | `uv.lock` 文件 | `.venv` 虚拟环境 |
| **范围** | 仅生产运行时依赖 | 包括构建/开发工具在内的完整供应链 |
| **包数量** | 通常较少(例如 16 个包) | 通常较多(例如 38+ 个包) |
| **使用场景** | 生产环境安全扫描 | 全面供应链审计 |
| **准确性** | 反映锁定的依赖 | 反映已安装的包 |
### 应该使用哪个工具?
- **用于生产环境安全扫描**:使用 `uv-sbom` 专注于将要部署到生产环境的依赖
- **用于全面供应链审计**:使用 CycloneDX 官方工具以包含所有开发和构建时依赖
- **用于法规合规**:检查您的具体要求 - 某些法规可能要求采用全面的方法
`uv-sbom` 的聚焦方法通过排除不随最终应用程序发布的构建时依赖,减少了安全漏洞扫描中的噪音。
## 安装
### Cargo (推荐 Rust 用户使用)

从 [crates.io](https://crates.io/crates/uv-sbom) 安装:
```
cargo install uv-sbom
```
### uv tool (Python 用户)

安装 Python 封装包:
```
uv tool install uv-sbom-bin
```
或通过 pip 安装:
```
pip install uv-sbom-bin
```
安装后,使用 `uv-sbom` 命令:
```
uv-sbom --version
```
**注意**:包名是 `uv-sbom-bin`,但安装的命令是 `uv-sbom`。
### 预编译二进制文件

从 [GitHub Releases](https://github.com/Taketo-Yoda/uv-sbom/releases) 下载预编译二进制文件:
**macOS (Apple Silicon)**:
```
curl -LO https://github.com/Taketo-Yoda/uv-sbom/releases/latest/download/uv-sbom-aarch64-apple-darwin.tar.gz
tar xzf uv-sbom-aarch64-apple-darwin.tar.gz
sudo mv uv-sbom /usr/local/bin/
```
**macOS (Intel)**:
```
curl -LO https://github.com/Taketo-Yoda/uv-sbom/releases/latest/download/uv-sbom-x86_64-apple-darwin.tar.gz
tar xzf uv-sbom-x86_64-apple-darwin.tar.gz
sudo mv uv-sbom /usr/local/bin/
```
**Linux (x86_64)**:
```
curl -LO https://github.com/Taketo-Yoda/uv-sbom/releases/latest/download/uv-sbom-x86_64-unknown-linux-gnu.tar.gz
tar xzf uv-sbom-x86_64-unknown-linux-gnu.tar.gz
sudo mv uv-sbom /usr/local/bin/
```
**Windows**:
从 [releases 页面](https://github.com/Taketo-Yoda/uv-sbom/releases) 下载 `uv-sbom-x86_64-pc-windows-msvc.zip` 并解压到您想要的位置。
### 从源码构建
```
# 克隆仓库
git clone https://github.com/Taketo-Yoda/uv-sbom.git
cd uv-sbom
# 构建并安装
cargo build --release
cargo install --path .
```
### 验证安装
```
uv-sbom --version
```
## 使用方法
### 基本用法
为当前目录生成 CycloneDX JSON SBOM:
```
uv-sbom
```
### 输出格式
生成包含直接和传递依赖的 Markdown 表格:
```
uv-sbom --format markdown
```
生成 CycloneDX JSON(默认):
```
uv-sbom --format json
```
### 指定项目路径
分析不同目录中的项目:
```
uv-sbom --path /path/to/project
```
### 保存到文件
输出到文件而不是标准输出:
```
uv-sbom --format json --output sbom.json
uv-sbom --format markdown --output SBOM.md
```
### 组合选项
```
uv-sbom --path /path/to/project --format markdown --output SBOM.md
```
### 排除包
您可以使用 `--exclude` 或 `-e` 选项从 SBOM 中排除特定包:
```
# 排除单个包
uv-sbom -e "pytest"
# 排除多个包
uv-sbom -e "pytest" -e "mypy" -e "black"
# 使用通配符排除模式
uv-sbom -e "debug-*" # Exclude all packages starting with "debug-"
uv-sbom -e "*-dev" # Exclude all packages ending with "-dev"
uv-sbom -e "*-test-*" # Exclude all packages containing "-test-"
# 与其他选项结合
uv-sbom --format json --output sbom.json -e "pytest" -e "*-dev"
```
**模式语法:**
- 使用 `*` 作为通配符匹配零个或多个字符
- 模式区分大小写
- 每次调用最多 64 个模式
**防止信息泄露:**
使用 `--exclude` 选项跳过特定的内部或专有库。这可以防止它们的名称在元数据检索期间被发送到外部注册表(如 PyPI),从而确保您的内部项目结构保持私密。
### 配置文件
您可以使用配置文件 (`uv-sbom.config.yml`) 来设置默认选项,而不必每次都在命令行上传递它们。
#### 生成配置模板
使用 `--init` 选项生成模板配置文件,其中包含所有可用字段作为注释示例:
```
# 在当前目录生成模板
uv-sbom --init
# 在特定目录生成模板
uv-sbom --init --path ./my-project
```
这将创建一个 `uv-sbom.config.yml` 文件,其中包含每个选项的内联文档。如果文件已存在,该命令将以错误退出,以防止意外覆盖。
**自动发现**:在您的项目目录(`uv.lock` 所在位置)中放置一个 `uv-sbom.config.yml` 文件。工具会自动检测并加载它。
**显式路径**:使用 `--config` / `-c` 在自定义位置指定配置文件。
```
# 自动发现配置文件(放置在项目目录中)
uv-sbom --check-cve
# 显式配置文件路径
uv-sbom --config ./custom-config.yml --check-cve
```
**示例配置文件** (`uv-sbom.config.yml`):
```
# 输出格式:json 或 markdown
format: markdown
# 从 SBOM 排除的包(支持通配符)
exclude_packages:
- "pytest"
- "mypy"
- "*-dev"
# 启用 CVE 漏洞检查
check_cve: true
# 漏洞检查的严重性阈值 (low/medium/high/critical)
severity_threshold: high
# 漏洞检查的 CVSS 阈值 (0.0-10.0)
# cvss_threshold: 7.0
# 忽略的 CVE(可附带原因)
ignore_cves:
- id: CVE-2024-1234
reason: "False positive for our use case"
- id: CVE-2024-5678
reason: "Mitigated by network configuration"
# 许可证合规策略
license_policy:
allow: ["MIT", "Apache-2.0", "BSD-*", "ISC", "PSF-2.0"]
deny: ["GPL-3.0-only", "GPL-3.0-or-later", "AGPL-*"]
unknown: "warn" # "warn" | "deny" | "allow"
```
#### 配置文件结构参考
| 字段 | 类型 | 必填 | 描述 |
|-------|------|----------|-------------|
| `format` | string | No | 输出格式 (`json` / `markdown`) |
| `exclude_packages` | string[] | No | 包排除模式(支持通配符) |
| `check_cve` | bool | No | 启用 CVE 检查 |
| `severity_threshold` | string | No | 严重性阈值 (`low` / `medium` / `high` / `critical`) |
| `cvss_threshold` | number | No | CVSS 阈值 (0.0 - 10.0) |
| `ignore_cves` | object[] | No | 要忽略的 CVE 列表 |
| `ignore_cves[].id` | string | Yes | CVE ID (例如, `CVE-2024-1234`) |
| `ignore_cves[].reason` | string | No | 忽略的原因 |
| `license_policy` | object | No | 许可证合规策略配置 |
| `license_policy.allow` | string[] | No | 允许的许可证模式(支持通配符) |
| `license_policy.deny` | string[] | No | 拒绝的许可证模式(支持通配符) |
| `license_policy.unknown` | string | No | 未知许可证处理方式 (`warn` / `deny` / `allow`) |
#### 优先级与合并规则
- **CLI 参数覆盖配置文件值**(针对标量字段:`format`、`severity_threshold`、`cvss_threshold`)
- **`check_cve`** 如果通过 CLI 标志或配置文件设置,则启用(逻辑或)
- **`exclude_packages`** 从 CLI 和配置文件**合并**,然后去重
- **`ignore_cves`** 从 CLI (`--ignore-cve`) 和配置文件**合并**,按 ID 去重(CLI 条目在重复时优先)
- **`check_license`** 如果通过 CLI 标志或配置文件设置,则启用(逻辑或,与 `check_cve` 相同)
- **`--license-allow`** 和 **`--license-deny`** CLI 选项**覆盖**配置文件中的 `license_policy.allow` / `license_policy.deny`(不合并)
### 忽略特定 CVE
您可以使用 `--ignore-cve` / `-i` 从命令行忽略特定的 CVE:
```
# 从 CLI 忽略特定 CVE
uv-sbom --check-cve --ignore-cve CVE-2024-1234 --ignore-cve CVE-2024-5678
# 简写形式
uv-sbom --check-cve -i CVE-2024-1234 -i CVE-2024-5678
# 结合配置文件和 CLI 忽略项(合并两个来源)
uv-sbom --config ./config.yml --check-cve -i CVE-2024-9999
```
### 检查漏洞
使用 `--check-cve` 选项,通过 [OSV (开源漏洞) 数据库](https://osv.dev) 检查包的已知安全漏洞:
```
# 检查 Markdown 输出中的漏洞
uv-sbom --format markdown --check-cve
# 保存漏洞报告到文件
uv-sbom --format markdown --check-cve --output SBOM.md
# 与排除模式结合
uv-sbom --format markdown --check-cve -e "pytest" -e "*-dev"
```
### 许可证合规性检查
使用 `--check-license` 选项根据可配置的许可证策略检查包:
```
# 启用许可证合规检查(使用配置文件策略)
uv-sbom --check-license --format markdown
# 使用内联策略(覆盖配置文件)
uv-sbom --check-license --license-allow "MIT,Apache-2.0,BSD-*" --license-deny "GPL-3.0,AGPL-*"
# 结合漏洞检查
uv-sbom --check-license --check-cve --severity-threshold high
```
**工作原理:**
- **拒绝优先于允许**:如果许可证同时匹配两个列表,则被拒绝
- **通配符模式**:使用 `BSD-*`、`AGPL-*` 等进行模式匹配(不区分大小写)
- **未知许可证处理**:配置如何处理不在任一列表中的许可证:
- `warn`(默认):报告为警告但不失败
- `deny`:将未知许可证视为违规
- `allow`:静默允许未知许可证
- **退出代码**:检测到策略违规时返回退出代码 1
### 漏洞阈值选项
您可以使用阈值选项控制哪些漏洞触发非零退出代码:
```
# 检查是否存在任何漏洞(如果发现则退出码为 1)
uv-sbom --format markdown --check-cve
# 仅检查高或严重严重性
uv-sbom --format markdown --check-cve --severity-threshold high
# 仅检查严重严重性
uv-sbom --format markdown --check-cve --severity-threshold critical
# 仅检查 CVSS >= 7.0
uv-sbom --format markdown --check-cve --cvss-threshold 7.0
# 仅检查 CVSS >= 9.0(严重)
uv-sbom --format markdown --check-cve --cvss-threshold 9.0
```
**阈值选项:**
- `--severity-threshold `:按严重性级别过滤
- `--cvss-threshold `:按 CVSS 分数过滤 (0.0-10.0)
**注意:**
- 一次只能使用一个阈值选项
- 需要启用 `--check-cve`
- 低于阈值的漏洞仍会显示在报告中,但不会触发退出代码 1
- 使用 `--cvss-threshold` 时,没有 CVSS 分数 (N/A) 的漏洞被排除在阈值评估之外
### PyPI 链接验证
使用 `--verify-links` 选项在生成超链接之前验证包是否存在于 PyPI 上。不存在于 PyPI 的包将被渲染为纯文本:
```
# 生成带有已验证 PyPI 链接的 Markdown
uv-sbom --format markdown --verify-links
# 与其他选项结合
uv-sbom --format markdown --verify-links --check-cve --output SBOM.md
```
**行为:**
- 不使用 `--verify-links`:所有包名都获得 PyPI 超链接(默认,快速)
- 使用 `--verify-links`:仅经过验证的包获得超链接;未验证的包渲染为纯文本
- 网络错误会优雅地回退到纯文本(不会崩溃)
- 请求并行执行(最多 10 个并发)以提高性能
### CI 集成
使用漏洞阈值进行 CI/CD 流水线集成:
```
# GitHub Actions 示例
- name: Generate SBOM
run: uv-sbom --format markdown --output sbom.md
- name: Security Check (High and Critical only)
run: uv-sbom --format markdown --check-cve --severity-threshold high
- name: Security Check (CVSS >= 7.0)
run: uv-sbom --format markdown --check-cve --cvss-threshold 7.0
```
```
# GitHub Actions - 许可证合规检查
- name: License Compliance Check
run: uv-sbom --check-license --format markdown
- name: Combined Security and License Check
run: uv-sbom --check-license --check-cve --severity-threshold high
```
```
# GitLab CI 示例
security_scan:
script:
- uv-sbom --format markdown --check-cve --severity-threshold high
allow_failure: false
```
**重要说明:**
- 漏洞检查**仅适用于 Markdown 格式**
- 需要互联网连接以查询 OSV API
- 在 `--dry-run` 模式下不可用(跳过网络操作)
- 使用 `--exclude` 防止内部包被发送到 OSV API
**示例输出:**
当发现漏洞时,Markdown 输出中会添加如下部分:
```
## 漏洞报告
**⚠️ Security Issues Detected**
The following packages have known security vulnerabilities:
| Package | Current Version | Fixed Version | CVSS | Severity | CVE ID |
|---------|----------------|---------------|------|----------|--------|
| urllib3 | 2.0.0 | 2.0.7 | 9.8 | 🔴 CRITICAL | CVE-2023-45803 |
| requests | 2.28.0 | 2.31.0 | 7.5 | 🟠 HIGH | CVE-2023-32681 |
---
*Vulnerability data provided by [OSV](https://osv.dev) under CC-BY 4.0*
```
### 漏洞解决指南
当 `--check-cve` 在传递依赖中检测到漏洞时,uv-sbom 会自动生成**漏洞解决指南**。此部分显示哪个直接依赖引入了每个有漏洞的传递包,以便您确切知道需要升级什么。
#### Markdown 输出示例
```
## 漏洞解决指南
The following transitive dependencies have known vulnerabilities. The table shows which direct dependency introduces each vulnerable package.
| Vulnerable Package | Current | Fixed Version | Severity | Introduced By (Direct Dep) | Vulnerability ID |
|--------------------|---------|---------------|----------|---------------------------|-----------------|
| urllib3 | 1.26.15 | >= 2.0.7 | 🟠 HIGH | requests (2.31.0) | [CVE-2024-XXXXX](https://nvd.nist.gov/vuln/detail/CVE-2024-XXXXX) |
| certifi | 2023.7.22 | >= 2024.2.2 | 🟠 HIGH | requests (2.31.0), httpx (0.25.0) | [CVE-2024-YYYYY](https://nvd.nist.gov/vuln/detail/CVE-2024-YYYYY) |
```
#### CycloneDX JSON 输出
在 CycloneDX 格式中,引入依赖作为 `properties` 条目包含:
```
{
"vulnerabilities": [
{
"id": "CVE-2024-XXXXX",
"properties": [
{
"name": "uv-sbom:introduced-by",
"value": "requests@2.31.0"
}
]
}
]
}
```
### 升级顾问 (`--suggest-fix`)
结合使用 `--suggest-fix` 和 `--check-cve`,自动建议应将直接依赖升级到哪个版本解决每个传递依赖漏洞。
**需要**:
- 启用 `--check-cve` 标志
- 安装了 `uv` CLI
- 项目目录中有 `pyproject.toml`
**示例**:
```
uv-sbom generate --check-cve --suggest-fix
```
**输出**:在漏洞解决指南中添加“建议操作”列,显示:
- `⬆️ Upgrade requests → 2.32.3 (resolves urllib3 to 2.2.1)` — 当升级可以修复漏洞时
- `⚠️ Cannot resolve: latest httpx still pins idna < 3.7` — 当没有升级方案时
- `❓ Could not analyze: ` — 当模拟失败时
**使用附带的示例尝试**:
```
# 此示例包含传递性 CVE,旨在展示可升级和无法解决的结果
uv-sbom -p examples/suggest-fix-project --check-cve --suggest-fix -f markdown
```
完整演练请参阅 [`examples/suggest-fix-project/README.md`](examples/suggest-fix-project/README.md)。
**许可证合规性检查输出示例:**
```
## 许可证合规检查
Policy: 3 allowed patterns, 1 denied pattern | Unknown: warn
### 违规(发现 2 项)
| Package | Version | License | Reason |
|---------|---------|---------|--------|
| some-gpl-lib | 1.0.0 | GPL-3.0 | Denied by policy |
| mystery-pkg | 2.1.0 | Unknown | Not in allow list |
```
当未发现漏洞时:
```
## 漏洞报告
**✅ No Known Vulnerabilities**
No security vulnerabilities were found in the scanned packages.
---
*Vulnerability data provided by [OSV](https://osv.dev) under CC-BY 4.0*
```
### 使用 dry-run 验证配置
使用 `--dry-run` 选项在工具与外部注册表通信之前验证您的配置:
```
# 验证排除模式工作正常
uv-sbom --dry-run -e "internal-*" -e "proprietary-pkg"
# 测试包含所有选项的配置
uv-sbom --dry-run --path /path/to/project --format json -e "*-dev"
```
**为什么使用 --dry-run:**
- **验证排除模式**:确保您的 `--exclude` 模式正确匹配您要跳过的包
- **防止信息泄露**:在工具与 PyPI 注册表通信之前确认敏感的内部包已被排除
- **快速验证**:所有输入验证都在没有网络开销的情况下进行
- **早期错误检测**:立即捕获配置问题(缺少 uv.lock、无效模式等)
**在 dry-run 模式下会发生什么:**
- ✅ 读取并解析 `uv.lock` 文件
- ✅ 验证所有命令行参数
- ✅ 检查排除模式并警告未匹配的模式
- ✅ 如果未发现问题则输出成功消息
- ❌ 跳过从 PyPI 获取许可证(无网络通信)
- ❌ 跳过 SBOM 输出生成
## 安全性
### 排除模式输入验证
`-e`/`--exclude` 选项实施以下安全措施以防止恶意输入:
#### 字符限制
模式中仅允许以下字符:
- **字母数字字符**:a-z, A-Z, 0-9, Unicode 字母/数字
- **连字符** (`-`), **下划线** (`_`), **点** (`.`):包名中常见
- **方括号** (`[`, `]`):用于包扩展(例如 `requests[security]`)
- **星号** (`*`):用于通配符匹配
控制字符、Shell 元字符和路径分隔符被阻止,以防止:
- 终端转义序列注入
- 日志注入攻击
- 命令注入(纵深防御)
#### 模式限制
- **最大模式数**:每次调用可指定 64 个模式
- **最大长度**:每个模式 255 个字符
- **最小内容**:模式必须包含至少一个非通配符字符
这些限制可防止以下拒绝服务攻击:
- 过度内存消耗
- 复杂模式匹配导致的 CPU 耗尽
#### 示例
**有效模式**:
```
uv-sbom -e 'pytest' # Exact match
uv-sbom -e 'test-*' # Prefix wildcard
uv-sbom -e '*-dev' # Suffix wildcard
uv-sbom -e 'package[extra]' # Package with extras
```
**无效模式**(被拒绝并报错):
```
uv-sbom -e '' # Empty pattern
uv-sbom -e '***' # Only wildcards
uv-sbom -e 'pkg;rm -rf /' # Contains shell metacharacter
uv-sbom -e "$(cat /etc/passwd)" # Shell command substitution blocked
```
有关更详细的安全信息,包括威胁模型和攻击向量,请参阅 [SECURITY.md](SECURITY.md)。
## 命令行选项
```
Options:
-f, --format Output format: json or markdown [default: json]
-p, --path Path to the project directory [default: current directory]
-o, --output
标签:CycloneDX, DevSecOps, OSV, PyPI, Python, Rust, SBOM, uv, uv.lock, Vercel, 上游代理, 云安全监控, 依赖管理, 可视化界面, 数据集, 无后门, 硬件无关, 网络流量审计, 许可证合规, 跌倒检测, 软件开发工具包, 软件物料清单, 逆向工具, 通知系统, 静态分析