From 67c128dbfb5ea978cc5b490ccae2bde5f8bc33c1 Mon Sep 17 00:00:00 2001 From: Ming Date: Sat, 11 Jul 2026 08:40:21 +0800 Subject: [PATCH 1/4] ci: pin GitHub App token action --- .github/workflows/sync-labels.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml index d1b15ca..1d92463 100644 --- a/.github/workflows/sync-labels.yml +++ b/.github/workflows/sync-labels.yml @@ -36,11 +36,14 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 with: persist-credentials: false - - uses: actions/create-github-app-token@v3 + + - name: Create label synchronization token id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 with: client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} - name: Apply MathArts label synchronization uses: ./.github/actions/sync-labels From 8ec2421672facddadb77d27992e205bde4db6ff3 Mon Sep 17 00:00:00 2001 From: Ming Date: Sat, 11 Jul 2026 08:40:28 +0800 Subject: [PATCH 2/4] ci: validate immutable action references --- .github/scripts/validate-action-pins.rb | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/scripts/validate-action-pins.rb diff --git a/.github/scripts/validate-action-pins.rb b/.github/scripts/validate-action-pins.rb new file mode 100644 index 0000000..7042bfe --- /dev/null +++ b/.github/scripts/validate-action-pins.rb @@ -0,0 +1,45 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +ACTION_FILE_GLOBS = [ + ".github/workflows/*.{yml,yaml}", + ".github/actions/**/action.{yml,yaml}" +].freeze +LOCAL_REFERENCE_PREFIXES = ["./", "docker://"].freeze +FULL_COMMIT_SHA = /\A[0-9a-fA-F]{40}\z/ +USES_PATTERN = /^\s*(?:-\s*)?uses\s*:\s*(?:"([^"]+)"|'([^']+)'|([^#\s]+))/ + +files = ACTION_FILE_GLOBS.flat_map { |pattern| Dir.glob(pattern) }.uniq.sort +errors = [] +external_references = 0 + +if files.empty? + warn "未找到需要检查的 GitHub Actions 工作流或 Action 元数据。" + exit 1 +end + +files.each do |file| + File.foreach(file, encoding: "UTF-8").with_index(1) do |line, number| + match = line.match(USES_PATTERN) + next unless match + + reference = match.captures.compact.first.to_s.strip + next if reference.start_with?(*LOCAL_REFERENCE_PREFIXES) + + external_references += 1 + action, separator, revision = reference.rpartition("@") + next if separator == "@" && !action.empty? && FULL_COMMIT_SHA.match?(revision) + + message = "外部 Action 必须固定到完整的 40 位 Commit SHA:#{reference}" + puts "::error file=#{file},line=#{number},title=Mutable GitHub Action reference::#{message}" + errors << [file, number, reference] + end +end + +if errors.empty? + puts "已验证 #{external_references} 个外部 GitHub Action 引用,全部固定到完整 Commit SHA。" + exit 0 +end + +warn "发现 #{errors.length} 个可移动或无效的外部 GitHub Action 引用。" +exit 1 From 3d8c94fa361118f831baf9bd8918776c551e7955 Mon Sep 17 00:00:00 2001 From: Ming Date: Sat, 11 Jul 2026 08:40:39 +0800 Subject: [PATCH 3/4] ci: enforce immutable action references --- .github/workflows/validate-documentation.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/validate-documentation.yml b/.github/workflows/validate-documentation.yml index 43a95ab..83e94ce 100644 --- a/.github/workflows/validate-documentation.yml +++ b/.github/workflows/validate-documentation.yml @@ -91,6 +91,9 @@ jobs: - name: Check Issue Form labels run: ruby .github/scripts/validate-issue-labels.rb + - name: Check immutable GitHub Action references + run: ruby .github/scripts/validate-action-pins.rb + - name: Test label synchronization behavior run: ruby .github/actions/sync-labels/test_sync_labels.rb From 779ba18fcbee1903a874cb317c7b5aa8d570ce92 Mon Sep 17 00:00:00 2001 From: Ming Date: Sat, 11 Jul 2026 08:40:53 +0800 Subject: [PATCH 4/4] docs: document GitHub App label credentials --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0e8f2ca..02ccda4 100644 --- a/README.md +++ b/README.md @@ -49,10 +49,18 @@ MathArts 的组织级标签由两个权威来源共同维护: 标签自动化分为两个权限边界: -* [`preview-labels.yml`](.github/workflows/preview-labels.yml) 在推送、定时任务或手动触发时使用只读 `github.token` 预览公开可见仓库的标签漂移,不读取跨仓库写令牌; +* [`preview-labels.yml`](.github/workflows/preview-labels.yml) 在推送、定时任务或手动触发时使用只读 `github.token` 预览公开可见仓库的标签漂移,不读取 GitHub App 私钥; * [`sync-labels.yml`](.github/workflows/sync-labels.yml) 只允许从 `main` 手动触发真实变更,并绑定 `label-governance-production` Environment。 -真实同步所需的 `LABEL_SYNC_WRITE_TOKEN` 必须配置为 `label-governance-production` 的 Environment Secret,而不是普通仓库 Secret。该 Environment 应在仓库设置中启用 Required reviewers,并禁止发起者自行批准。 +真实同步通过 GitHub App 生成短时效 Installation Access Token。`label-governance-production` Environment 必须配置: + +* Environment Variable `APP_CLIENT_ID`; +* Environment Secret `APP_PRIVATE_KEY`; +* Required reviewers,并在账户方案支持时禁止发起者自行批准。 + +GitHub App 应只安装到参与组织级标签治理的仓库,并采用最小仓库权限;当前标签同步至少需要 `Issues: Read and write`,`Metadata: Read` 由 GitHub 自动授予。轮换 GitHub App 私钥时,应同步更新 `APP_PRIVATE_KEY`。 + +所有外部 GitHub Action 和可复用工作流引用必须固定到完整的 40 位 Commit SHA,并在行尾注释人类可读版本。`.github/scripts/validate-action-pins.rb` 负责强制检查,Dependabot 负责提出后续版本更新。 ## 维护原则