依赖管理与 Dependabot

要管理你的依赖关系,可以使用软件组成分析(SCA)工具。GitHub 提供了依赖图、Dependabot 警报和 Dependabot 安全更新来管理你的软件依赖关系。

  • 依赖图帮助你理解你的依赖树。

  • Dependabot 警报检查你的依赖项是否存在已知的漏洞,如果 Dependabot 发现漏洞,它会通知你。

  • 如果你启用了 Dependabot 安全更新,当依赖包的作者发布漏洞修复时,Dependabot 将自动创建拉取请求,更新你的依赖项。

依赖图对于公共仓库默认启用,但对于私有仓库需要手动启用。Dependabot 警报和更新也需要为所有仓库启用。你可以在“设置 | 安全与分析”中启用这些功能(见图 14.1)。

image 2024 12 27 16 15 06 269
Figure 1. 图 14.1 – 启用依赖图和 Dependabot

在组织级别,你可以为所有仓库启用这些选项,并将其设置为新仓库的默认值。

探索你的依赖项

启用依赖图后,它将开始查找依赖关系。以下包生态系统受到支持(见表 14.1):

image 2024 12 27 16 15 48 421
Figure 2. 表 14.1 – 依赖图和 Dependabot 支持的格式

要探索你的依赖关系,你可以导航到“洞察 | 依赖图”。在“依赖项”标签下,你可以找到仓库中找到的所有依赖项的清单。如果你点击每个依赖项,你可以查看它的依赖关系树。如果该依赖项有已知漏洞,你可以在右侧看到该漏洞。漏洞会有一个分配的严重性等级和一个通用漏洞和暴露(CVE)标识符。你可以使用该标识符在国家漏洞数据库([nvd.nist.gov](CVE-2021-3749(https://nvd.nist.gov/vuln/detail/CVE-2021-3749))或 GitHub 咨询数据库([https://github.com/advisories](https://github.com/advisories))。如果该漏洞有修复,依赖图会建议你将依赖项升级到的版本(见图 14.2)。

image 2024 12 27 16 16 10 751
Figure 3. 图 14.2 – 使用依赖图探索你的依赖项

在组织级别,在“洞察 | 依赖项”下,你可以找到所有启用了依赖图的仓库的所有依赖项。除了仓库的洞察信息外,你还可以在这里找到所有使用的许可证。这可以帮助你检查你产品的许可证合规性(见图 14.3)。

image 2024 12 27 16 16 27 743
Figure 4. 图 14.3 – 组织级别的依赖项洞察

如果你想利用 GitHub 通知其他依赖于你包的用户,你可以在 “安全 | 安全咨询 | 新建草稿安全咨询” 中起草一个新的安全咨询。安全咨询包括标题、描述、生态系统、包名、受影响的版本(如:< 1.2.3)、修复版本(如 1.2.3)和严重性。你可以选择添加多个常见弱点枚举(CWE)(参见 [https://cwe.mitre.org/](https://cwe.mitre.org/))。如果你已经有 CVE ID,可以在这里添加;如果没有,你可以选择稍后添加。

草稿仅对仓库所有者可见,直到发布。一旦发布,公共仓库的安全咨询将对所有人可见,并将添加到 GitHub 咨询数据库([https://github.com/advisories](https://github.com/advisories))。对于私有仓库,只有拥有仓库访问权限的人可以看到它们,且在你请求正式的 CVE 标识符之前,不会将它们添加到咨询数据库。

Dependabot

Dependabot 是 GitHub 上的一个机器人,可以检查你的依赖项是否存在已知的漏洞。它还可以自动创建拉取请求,以保持你的依赖项最新。

Dependabot 支持多种包生态系统,包括 npm、GitHub Actions、Docker、git 子模块、.NET(NuGet)、pip、Terraform、Bundler、Maven 等。完整的支持包列表可以参考 [GitHub文档](https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/about-dependabot-version-updates#supported-repositories-and-ecosystems)。

要启用 Dependabot,可以在 .github 目录中创建一个 dependabot.yml 文件。你需要选择包生态系统和包含包文件的目录(例如 package.json 文件)。还需要指定 Dependabot 检查更新的频率:每日、每周或每月。

version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"

你可以使用 Dependabot 密钥对私有注册表进行身份验证。在“设置 | 秘密 | Dependabot”下添加新密钥(见图 14.4):

image 2024 12 27 16 19 14 669
Figure 5. 图 14.4 – 添加 Dependabot 密钥

然后,在 dependabot.yml 文件中添加注册表,并从密钥上下文中访问它:

version: 2
registries:
  my-npm-pkg:
    type: npm-registry
    url: https://npm.pkg.github.com
    token: ${{secrets.PAT}}

updates:
  - package-ecosystem: "npm"
    directory: "/"
    registries:
      - my-npm-pkg
    schedule:
      interval: "daily"

你可以配置更多选项以定制 Dependabot —— 例如允许或拒绝某些包、对拉取请求应用元数据(如标签、里程碑和审阅者)、自定义提交信息,或者更改合并策略。有关所有配置选项的完整列表,请参见 [GitHub文档](https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates)。

你可以在“洞察 | 依赖图 | Dependabot”下查看 Dependabot 更新的状态。每个更新条目都有一行显示状态,并且如果出现问题,还会显示警告图标。点击状态可以查看完整的日志(见图 14.5):

image 2024 12 27 16 20 43 947
Figure 6. 图 14.5 – 查看 Dependabot 状态和日志文件

你可以在“安全 | Dependabot 警报”下找到所有 Dependabot 警报。点击每个条目查看详细信息。如果 Dependabot 已经创建了一个拉取请求来修复漏洞,你会看到一个带有展开菜单的链接(见图 14.6):

image 2024 12 27 16 21 01 797
Figure 7. 图 14.6 – 查看 Dependabot 警报

请注意,这个列表中只有安全警报 —— 不是所有创建的更新依赖项的拉取请求。这里也有许多安全警报没有修复。有时,唯一的修复方法是降级,并且如果某个依赖项声明了更高的最低版本,就没有自动修复(见图 14.7):

image 2024 12 27 16 21 19 497
Figure 8. 图 14.7 – 无修复的漏洞详细信息

如果你仔细查看 Dependabot 的拉取请求,你会注意到有很多附加信息。当然,实际的变更仅仅是更新了清单文件中的版本号。但在描述中,它会添加该包的发布说明(如果有的话),以及新版本中所有提交的完整列表。Dependabot 还会添加一个兼容性评分,表示此更新与代码兼容的可能性(见图 14.8):

image 2024 12 27 16 21 40 307
Figure 9. 图 14.8 – Dependabot 拉取请求的详细信息

在描述中,你还会找到一系列可以通过在拉取请求中评论来发送给机器人的命令。你可以使用以下任何命令:

  • @dependabot cancel merge:取消之前请求的合并。

  • @dependabot close:关闭拉取请求并防止 Dependabot 重新创建它。你也可以手动关闭拉取请求来实现相同的效果。

  • @dependabot ignore this dependency:关闭拉取请求并防止 Dependabot 为该依赖项再创建任何拉取请求(除非你重新打开拉取请求或自己升级到建议的版本)。

  • @dependabot ignore this major version:关闭拉取请求并防止 Dependabot 为该主版本再创建任何拉取请求(除非你重新打开拉取请求或自己升级到该主版本)。

  • @dependabot ignore this minor version:关闭拉取请求并防止 Dependabot 为该次版本再创建任何拉取请求(除非你重新打开拉取请求或自己升级到该次版本)。

  • @dependabot merge:在你的 CI 测试通过后合并拉取请求。

  • @dependabot rebase:重新基准化拉取请求。

  • @dependabot recreate:重新创建拉取请求,覆盖对拉取请求所做的所有编辑。

  • @dependabot reopen:如果拉取请求已关闭,则重新打开拉取请求。

  • @dependabot squash and merge:在 CI 测试通过后,将拉取请求压缩并合并。

只需在拉取请求中评论其中一个命令,Dependabot 就会为你完成其余的操作。

使用 GitHub Actions 自动化 Dependabot 更新

你可以使用 GitHub Actions 为 Dependabot 更新添加更多自动化功能,但有一些事项需要注意。如果 Dependabot 触发了一个工作流,那么 GitHub Actor 就是 Dependabot(github.actor == "Dependabot[bot]")。这意味着默认情况下,GITHUB_TOKEN 只有只读权限,如果需要写入权限,你必须显式授予。填充在 Secret 上下文中的密钥是 Dependabot 密钥!GitHub Actions 的密钥不可用于此工作流。

以下是一个示例工作流,它仅在 Dependabot 的拉取请求触发时运行,并授予写入权限给拉取请求、问题和项目:

name: Dependabot automation
on: pull_request
permissions:
  pull-requests: write
  issues: write
  repository-projects: write
jobs:
  Dependabot:
    runs-on: ubuntu-latest
    if: ${{ github.actor == 'Dependabot[bot]' }}

你可以使用 Dependabot/fetch-metadata 动作提取有关被更新依赖项的信息。下面是一个示例,它使用这些信息为拉取请求应用标签:

steps:
  - name: Dependabot metadata
    id: md
    uses: Dependabot/fetch-metadata@v1.1.1
    with:
      github-token: "${{ secrets.GITHUB_TOKEN }}"
  - name: Add label for production dependencies
    if: ${{ steps.md.outputs.dependency-type == 'direct:production' }}
    run: gh pr edit "$PR_URL" --add-label "production"
    env:
      PR_URL: ${{ github.event.pull_request.html_url }}

通过 GitHub CLI,添加自动化非常简单。例如,你可以自动批准和自动合并所有新的修补程序:

- name: Enable auto-merge for Dependabot PRs
  if: ${{ steps.md.outputs.update-type == 'versionupdate:semver-patch' }}
  run: |
    gh pr review --approve "$PR_URL"
    gh pr merge --auto --merge "$PR_URL"
  env:
    PR_URL: ${{github.event.pull_request.html_url}}
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

GitHub Actions 和 Dependabot 的组合非常强大,可以去除几乎所有手动操作,保持软件的更新。结合一个良好的 CI 构建和你信任的测试套件,你基本可以自动合并所有通过测试的 Dependabot 拉取请求。

使用 Dependabot 保持你的 GitHub Actions 最新

GitHub Actions 也是你必须管理的依赖项。每个动作都被固定在一个版本上(如 uses: Dependabot/fetch-metadata@v1.1.1 中的 @ 后部分)。版本也可以是一个分支名称——但这样会导致工作流不稳定,因为你的动作可能在你不知情的情况下发生变化。最好将版本固定为标签或单独的提交 SHA。你可以让 Dependabot 检查更新并为你创建拉取请求,就像对待其他生态系统一样。在你的 dependabot.yml 文件中添加以下部分:

version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "daily"

如果你的 GitHub Actions 有新版本,Dependabot 会为你创建拉取请求。