tedg-dev/GitHub-SBOM-fetcher
GitHub: tedg-dev/GitHub-SBOM-fetcher
基于 GitHub SBOM API 的自动化工具,用于递归获取仓库及其依赖项的软件物料清单并生成安全与合规报告。
Stars: 0 | Forks: 0
# GitHub SBOM API 获取工具
**用于从 GitHub 仓库及其依赖项中收集软件物料清单 (SBOM) 的自动化工具**
使用 GitHub 官方 SBOM API 和包注册中心 API,实现全面、可维护的依赖项发现和 SBOM 收集。
## 功能特点
✅ **完整的依赖项发现** - 从 GitHub SBOM API 获取完整的依赖树
✅ **智能包映射** - 使用 npm/PyPI 注册中心 API 将包映射到 GitHub 仓库
✅ **智能去重** - 每个仓库仅下载一次,并跟踪版本关系
✅ **私有仓库支持** - 适用于需要身份验证的仓库
✅ **诊断报告** - 包含映射诊断信息的详细执行报告
✅ **生产就绪** - 模块化架构,全面的错误处理
## 快速入门
**使用安装脚本**(推荐):
```
# 设置环境和依赖
./setup_environment.sh
# 设置并交互运行(提示输入 owner/repo)
./setup_environment.sh --run
# 设置并运行测试
./setup_environment.sh --test
```
**手动安装**:
```
# 安装
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# 配置(使用你的 GitHub token 创建 keys.json)
cp key.sample.json keys.json
# 使用你的 token 编辑 keys.json
# 运行
python -m sbom_fetcher --gh-user OWNER --gh-repo REPO --account your-account
# 带有诊断日志
python -m sbom_fetcher --gh-user OWNER --gh-repo REPO --account your-account --debug
```
## 工作原理
### 1. 获取根 SBOM
使用 GitHub Dependency Graph API 检索目标仓库的 SBOM:
```
GET /repos/{owner}/{repo}/dependency-graph/sbom
```
### 2. 提取依赖项
解析 SBOM(SPDX 2.3 格式)以提取所有包:
- 包名、版本、生态系统(npm, PyPI 等)
- 包 URL (purls):`pkg:npm/lodash@4.17.5`
### 3. 将包映射到 GitHub 仓库
查询包注册中心以查找权威的仓库 URL:
**对于 npm 包:**
```
GET https://registry.npmjs.org/{package-name}
→ Extract "repository" field
→ Validate GitHub URL and extract owner/repo
```
**对于 PyPI 包:**
```
GET https://pypi.org/pypi/{package-name}/json
→ Extract project_urls["Source"] or "Homepage"
→ Validate GitHub URL and extract owner/repo
```
**诊断日志:**
- ✅ 映射成功:`pkg:npm/lodash → lodash/lodash`
- ❌ 映射失败:显示具体原因(无 repository 字段、非 GitHub URL 等)
### 4. 仓库去重
按仓库对包进行分组,以避免重复下载:
- 多个版本的 `lodash` → 单次下载 `lodash/lodash`
- 在 `version_mapping.json` 中跟踪版本关系
- 节省 50% 以上的存储空间,减少下载时间
### 5. 下载依赖项 SBOM
为每个唯一的仓库获取 SBOM:
```
GET /repos/{owner}/{repo}/dependency-graph/sbom
```
处理:
- 自动延迟的速率限制
- 下载失败(未启用 dep graph、HTTP 错误)
- 分支名称发现(main 还是 master)
### 6. 生成报告
创建综合执行报告,显示:
- 统计数据(已映射的包、唯一的仓库、重复项)
- 失败的下载及原因
- **带有诊断信息的未映射包**:
- 映射失败的原因(缺少 npm 元数据、非 GitHub 仓库等)
- 包注册中心查询 URL
- 注册中心响应
- 解释说明某些包可能有仓库但缺少注册中心元数据
## 输出结构
```
sboms/
sbom_export_2025-12-05_09.19.04/
tedg-dev_beatBot/
# Root SBOM
tedg-dev_beatBot_root.json
# Execution report with diagnostics
tedg-dev_beatBot_execution_report.md
# Version tracking
version_mapping.json
# Dependency SBOMs
dependencies/
lodash_lodash_main.json
caolan_async_master.json
...
```
### 输出示例
```
======================================================================
STEP 1: Fetching Root SBOM
======================================================================
✅ Root SBOM fetched successfully
======================================================================
STEP 2: Parsing Dependency Packages
======================================================================
Found 229 valid packages in SBOM
======================================================================
STEP 3: Mapping Packages to GitHub Repositories
======================================================================
Mapped 222 packages to GitHub repos
Packages without GitHub repos: 7
Unmapped packages:
- boolbase (npm) v1.0.0
- eyes (npm) v0.1.8
- @ffmpeg-installer/darwin-x64 (npm) v4.0.4
...
======================================================================
STEP 4: Downloading Dependency SBOMs (Deduplicated)
======================================================================
Total packages: 222
Unique repositories: 166
Duplicates to skip: 56
[1/166] Fetching SBOM for lodash/lodash (versions: 3.10.1, 4.17.5, ...)
[2/166] Fetching SBOM for caolan/async (versions: 0.1.22, 2.6.0, ...)
...
======================================================================
SUMMARY
======================================================================
Root SBOM dependency repositories: 229
Mapped to GitHub repos: 222
Unique repositories: 166
Duplicate versions skipped: 56
Packages without GitHub repos: 7
SBOMs downloaded successfully: 164
SBOMs failed (permanent): 2
Elapsed time: 4m 20s
```
## 了解包映射
### 为什么某些包未能映射
此工具依赖于**包注册中心元数据**来发现 GitHub 仓库:
1. **查询注册中心**:`https://registry.npmjs.org/boolbase`
2. **提取仓库**:查找 `"repository"` 字段
3. **验证**:确认其为 GitHub URL
4. **映射**:提取 owner/repo
**以下情况包将无法映射:**
- npm/PyPI 元数据包含 `"repository": null`
- 旧的/未维护的包(在 repository 字段成为标准之前发布)
- 特定平台的二进制文件(没有可链接的源代码)
- 发布者疏忽(维护者未包含 repository 元数据)
**重要提示:** 某些未映射的包确实拥有 GitHub 仓库,但包注册中心没有链接到这些仓库。如果没有此元数据,该工具将无法发现仓库位置。
**示例:**
- `boolbase` 有一个 GitHub 仓库:https://github.com/fb55/boolbase
- 但其 npm 元数据为:`"repository": null`
- **结果**:无法映射(注册中心未提供仓库位置)
### 诊断报告
执行报告包含一个详细的“无法映射到 GitHub 的包”部分,内容包括:
- **映射的工作原理**(4 步过程)
- **映射失败的原因**(常见原因)
- **每个包的诊断信息**:
- 注册中心查询 URL
- 注册中心响应
- 结果解释
- **解决方案**:联系维护者、使用替代工具、手动获取
**这种透明度有助于区分:**
- 没有 GitHub 仓库的包(正确未映射)
- 有仓库但缺少注册中心元数据的包(包元数据中的限制)
## 配置
### GitHub Token
在项目根目录创建 `keys.json`,包含一个或多个账户:
**多账户格式**(推荐):
```
{
"accounts": [
{
"username": "your-personal-account",
"token": "ghp_yourPersonalAccessToken"
},
{
"username": "your-work-account",
"token": "ghp_yourWorkAccessToken"
}
]
}
```
**旧版单账户格式**(仍然支持):
```
{
"username": "your-github-username",
"token": "ghp_yourPersonalAccessToken"
}
```
**Token 要求:**
- **Classic PAT**:需包含 `repo` 范围以用于私有仓库
- **Fine-grained PAT**:仓库权限:
- Contents(读取)
- Dependency graph(读取)
**安全性:**
- 使用 `key.sample.json` 作为模板
- 切勿提交 `keys.json`(已包含在 `.gitignore` 中)
- 定期轮换 token
### 为组织访问配置 SSO
**如果您需要访问使用 SAML 单点登录 (SSO) 的组织中的仓库:**
1. **创建您的 Personal Access Token:**
- 前往:https://github.com/settings/tokens
- 点击 "Generate new token" (Classic)
- 选择所需范围:`repo`、`read:org`
- 生成并复制该 token
2. **为 SSO 授权该 token:**
- 访问:https://github.com/settings/tokens
- 在列表中找到您的 token
- 点击 token 旁边的 **"Configure SSO"**
- 为您需要访问的每个组织点击 **"Authorize"**
- 完成 SAML 身份验证流程
3. **添加到 keys.json:**
{
"accounts": [
{
"username": "your-work-account",
"token": "ghp_yourSSOAuthorizedToken"
}
]
}
4. **使用工具:**
python -m sbom_fetcher --gh-user YourOrg --gh-repo repo-name --account your-work-account
**常见 SSO 错误:**
```
❌ Access forbidden (403): Resource protected by organization SAML enforcement.
You must grant your Personal Access token access to an organization.
```
**解决方案:** 按照上述步骤 2-3 为组织授权您的 token。
**文档:** https://docs.github.com/articles/authenticating-to-a-github-organization-with-saml-single-sign-on/
## 命令行选项
```
python -m sbom_fetcher [OPTIONS]
```
**必填项:**
```
--gh-user USER Repository owner
--gh-repo REPO Repository name
--account ACCOUNT Account username from keys.json (e.g., your-personal-account)
```
**可选项:**
```
--key-file FILE Path to keys.json (default: keys.json)
--output-dir DIR Output directory (default: sboms)
--debug Enable diagnostic logging
```
**示例:**
```
# 使用个人账户的基本用法
python -m sbom_fetcher --gh-user tedg-dev --gh-repo beatBot --account your-personal-account
# 使用工作账户访问受 SSO 保护的组织
python -m sbom_fetcher --gh-user YourCompany --gh-repo project --account your-work-account
# 带有诊断
python -m sbom_fetcher --gh-user tedg-dev --gh-repo beatBot --account your-personal-account --debug
# 自定义输出目录
python -m sbom_fetcher --gh-user tedg-dev --gh-repo beatBot --account your-personal-account --output-dir ./my_sboms
# 使用自定义 keys 文件
python -m sbom_fetcher --gh-user tedg-dev --gh-repo beatBot --account your-personal-account --key-file ./custom-keys.json
```
## 架构
### 模块化设计
```
src/sbom_fetcher/
domain/
models.py # Core data structures
services/
github_client.py # GitHub API interactions
parsers.py # SBOM parsing
mappers.py # Package → GitHub mapping
mapper_factory.py # Ecosystem-specific mappers
sbom_service.py # Main orchestration
reporters.py # Report generation
infrastructure/
config.py # Configuration management
repository.py # File system operations
```
### 关键组件
**1. GitHub Client** (`github_client.py`)
- 从 GitHub API 获取 SBOM
- 处理身份验证、速率限制
- 发现默认分支名称(main/master)
- 缓存 API 响应
**2. Package Mappers** (`mappers.py`、`mapper_factory.py`)
- NPM:查询 npm 注册中心 API
- PyPI:查询 PyPI JSON API
- 支持范围包(`@org/pkg`)的 URL 编码
- 处理多种 repository 字段格式
- 每个决策点的**诊断日志**
**3. SBOM Service** (`sbom_service.py`)
- 编排 6 步工作流程
- 跟踪未映射的包及其原因
- 处理去重
- 生成综合统计信息
**4. Reporters** (`reporters.py`)
- Markdown 执行报告
- **详细的诊断部分**:
- 映射的工作原理
- 包映射失败的原因
- 每个包的注册中心查询和响应
- 解决方案和替代方案
## 故障排除
### 调试模式
启用详细日志记录以诊断问题:
```
python -m sbom_fetcher --gh-user OWNER --gh-repo REPO --account your-account --debug
```
**显示:**
- npm/PyPI 注册中心查询和响应
- 包映射决策
- 每个包成功或失败的原因
- GitHub API 交互
### 常见问题
**"Failed to load GitHub token"**
- 确保 `keys.json` 存在且是有效的 JSON
- 检查文件权限
**403 Forbidden**
- 验证 token 是否具有正确的权限
- 检查 token 是否已过期
**404 Not Found - Repository**
- 验证仓库是否存在
- 对于私有仓库,请确保 token 具有访问权限
**"Dependency graph not enabled"**
- 必须启用 GitHub 的 dependency graph
- 检查:`https://github.com/{owner}/{repo}/network/dependencies`
**"Resource protected by organization SAML enforcement"**
- 您的 token 需要此组织的 SSO 授权
- 请参阅上面的“为组织访问配置 SSO”部分
- 访问:https://github.com/settings/tokens 并授权您的 token
**没有 GitHub 仓库的包**
- 对于特定平台的二进制文件属于正常现象
- 检查执行报告以获取诊断详细信息
- 某些包可能有仓库但缺少注册中心元数据
### 了解未映射的包
执行报告确切显示了**每个包未能映射的原因**:
```
#### boolbase (v1.0.0)
- **Package Registry Query:** `https://registry.npmjs.org/boolbase`
- **Registry Response:** Package metadata contains `"repository": null`
- **Result:** ❌ Cannot determine GitHub repository location from package metadata
**Important**: This package may have a GitHub repository, but the npm
registry metadata does not link to it.
```
**解决方案:**
1. 联系包维护者以添加 repository 字段
2. 使用其他 SBOM 工具(syft, grype)
3. 如果您知道仓库 URL,请手动获取
## 版本去重
**GitHub API 限制**:仅返回仓库的**当前状态**(默认分支),而不是特定版本的 SBOM。
**示例:**
- 您的项目使用 `lodash@0.9.2` (2013) 和 `lodash@4.17.5` (2018)
- 两者都映射到 `lodash/lodash` 仓库
- GitHub API 返回**当前状态**(最新的 main/master 分支)
- 而不是历史版本
**我们的处理方式:**
1. 每个仓库仅下载一次
2. 按分支命名文件:`lodash_lodash_main.json`
3. 在 `version_mapping.json` 中跟踪版本关系
**优势:**
- ✅ 减少 50% 存储空间(无重复内容)
- ✅ 执行速度更快(下载次数更少)
- ✅ 准确反映 API 行为
- ✅ 清晰的版本到 SBOM 映射
**对于特定版本的分析:** 请使用包含确切依赖树的包锁定文件(`package-lock.json`、`Pipfile.lock`)。
## 测试
```
# 安装 dev dependencies
pip install -r requirements-dev.txt
# 运行测试
pytest -q
# 带有覆盖率
pytest --cov=src/sbom_fetcher --cov-report=term-missing
# 特定测试
pytest tests/test_sbom_service.py -v
```
测试使用模拟的网络调用——不会产生真实的 GitHub 流量。
## 文档
有关详细文档,请参阅 `docs/` 文件夹。
## 用例
### 安全审计
- 收集 SBOM 用于漏洞扫描
- 跟踪跨版本的依赖项
- 识别没有 GitHub 仓库的包
### 合规性与许可证审查
- 用于合规性工具的 SPDX 2.3 格式
- 用于审计的完整依赖树
- 跟踪直接和传递依赖项
### 依赖分析
- 了解依赖关系
- 识别重复依赖
- 将包映射到源仓库
### 私有仓库管理
- 适用于私有仓库(基于 API)
- 组织范围的 SBOM 收集
- 跟踪内部和外部依赖项
## 贡献
欢迎贡献!请:
1. Fork 该仓库
2. 创建一个 feature 分支
3. 为新功能添加测试
4. 确保测试通过:`pytest -q`
5. 提交 pull request
## 许可证
MIT License - 详见 LICENSE 文件
## 支持
- **问题**:[GitHub Issues](https://github.com/tedg-dev/GitHub-SBOM-fetcher/issues)
- **文档**:本 README 和 `docs/` 文件夹
- **调试模式**:使用 `--debug` 标志获取详细的诊断信息
标签:GitHub API, GPT, npm, PyPI, Python, SBOM, SPDX, WebSocket, 依赖分析, 依赖发现, 包管理器, 合规追踪, 安全报告, 开源合规, 无后门, 漏洞管理, 硬件无关, 组件映射, 统一API, 跌倒检测, 软件开发, 软件物料清单, 逆向工具