fix(ci): stop running fork code with the base repo token

assert-main-merged checked out the PR head and then executed scripts/git/assert/main_merged.sh from that same tree. Under pull_request_target the job holds the base repository's token, so a fork PR could run anything it liked with write access. This is the run's only critical scanner finding.

The workflow file itself comes from the base branch and is trusted; only the checked-out files are not. Two checkouts now separate them: the script comes from .base-tooling, the tree it inspects is pr-head, and neither checkout persists credentials. Both repositories are public, so the script's git fetch still resolves anonymously, and git rev-parse --show-toplevel follows the working directory rather than the script's location - both verified by execution.

The same pass drops the top-level write scopes to contents: read, moves packages: write onto the two fork image jobs, and downgrades the orchestrator call site to actions: read now that call-orchestrator no longer declares actions: write.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5 committed 2026-08-08 12:12:10 +02:00
1 parent 60c4f582cf
commit 158d57342e
1 file changed
+17 -5
@@ -23,10 +23,7 @@ concurrency:
cancel-in-progress: true
permissions:
actions: write
contents: read
packages: write
security-events: write
jobs:
detect-pr-scope:
@@ -69,17 +66,26 @@ jobs:
if: ${{ github.event_name == 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository }}
runs-on: ubuntu-latest
steps:
- name: ⬇️ Checkout the base branch for its tooling
uses: actions/checkout@v7.0.0
with:
path: .base-tooling
persist-credentials: false
- name: ⬇️ Checkout PR head
uses: actions/checkout@v7.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
path: pr-head
persist-credentials: false
- name: ✅ Assert the base branch is fully merged into the PR head
working-directory: pr-head
env:
INFINITO_MAIN_REMOTE: origin
INFINITO_MAIN_BRANCH: ${{ github.event.pull_request.base.ref }}
run: bash scripts/git/assert/main_merged.sh
run: bash "${GITHUB_WORKSPACE}/.base-tooling/scripts/git/assert/main_merged.sh"
pr-scope-gate:
name: 🚦 Gate PR scope
@@ -134,7 +140,7 @@ jobs:
- detect-affected-roles
if: ${{ github.event_name == 'pull_request' && needs.detect-pr-scope.outputs.run_ci_orchestrator == 'true' }}
permissions:
actions: write
actions: read
contents: read
packages: write
security-events: write
@@ -168,6 +174,9 @@ jobs:
build-ci-images-fork:
needs: prepare-fork-merge-ref
permissions:
contents: read
packages: write
uses: ./.github/workflows/call-images-build-ci.yml
with:
distros: "arch debian ubuntu fedora centos"
@@ -179,6 +188,9 @@ jobs:
images-mirror-missing-fork:
needs: prepare-fork-merge-ref
permissions:
contents: read
packages: write
uses: ./.github/workflows/call-images-mirror-missing.yml
with:
repo_root: _pr_source