SIGN IN SIGN UP

fix: publish Castiron checks for external-fork pull requests (#2464)

## Summary

GitHub omits `workflow_run.pull_requests` for external-fork runs, and
querying the upstream repository's commit-association endpoint returns
no pull requests for those fork commits. That leaves the required
`Castiron / budget-only change` and `Castiron / custom-code budget`
contexts permanently expected even when the candidate workflow succeeds.

- Centralize trusted Python pull-request association in
`custom_code_report.py` and reuse it from trusted report generation,
comment publication, and budget evaluation.
- Resolve missing associations from the authenticated source run's
`head_repository`, including legitimately renamed forks, then
independently re-fetch every candidate PR from `openai/openai-node`.
- Apply equivalent validation in both privileged JavaScript publishers:
required commit statuses and failure-report comments.
- Refresh the candidate workflow's pinned reporter SHA-256 after the
trusted reporter change.

## Security model

Fork-side associations and PR numbers are discovery hints, never
authorization. The trusted paths:

1. Re-fetch the workflow run from the upstream Actions API and verify
its repository, immutable candidate SHA, workflow path, completion, and
run attempt as applicable.
2. Strictly validate the source repository's owner/name syntax and
consistency with authenticated `head_repository` metadata; reject
traversal-like components and spoofed identities.
3. Re-fetch each hinted PR from the upstream repository and require an
open PR with the exact candidate SHA, the exact source head repository,
the intended upstream repository and `main` base ref, and exactly one
valid current association.
4. Require the current `main` base SHA wherever budget evaluation or
status publication needs freshness; preserve existing stale-run behavior
and merge-group validation.

The existing trusted `workflow_run`/main-checkout boundary, bare Git
object store, candidate-artifact isolation, least-privilege job
permissions, merge-queue protections, and exact required status names
remain unchanged. No candidate workflow definition, mutable ref,
contributor artifact, or fork-supplied PR number is trusted.

## Affected contributor PRs

- #2463: live fork workflow run `32877584723` has `pull_requests: []`;
the updated resolver correctly finds its upstream PR through
`cmun2/openai-node`.
- #2444 and #2431: independently reproduced the same fork-only
association behavior; both PRs merged while this fix was being prepared.

## Verification

- `env PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s
scripts/castiron -p 'test_custom_code*.py'` — 60 tests pass, with one
pre-existing skip.
- `go run github.com/rhysd/actionlint/cmd/actionlint@latest
.github/workflows/castiron-custom-code.yml
.github/workflows/castiron-custom-code-comment.yml` — both workflows
pass actionlint v1.7.12.
- `ruff format --check scripts/castiron/custom_code_report.py
scripts/castiron/custom_code_budget.py
scripts/castiron/test_custom_code_report.py
scripts/castiron/test_custom_code_budget.py`.
- Ruff lint passes when ignoring only the same pre-existing baseline
rule findings; `git diff --check` passes.
- Executable publisher and Python regression coverage includes external
forks with empty run associations, fork-side lookup, same-repository and
renamed-fork PRs, malformed/spoofed repositories, unrelated source
heads, ambiguous/duplicate/invalid associations, stale heads/bases/run
attempts, exact required contexts, and merge groups.
H
Hayden committed
a90581ed85f236a2440102fe64320f1831844b50
Parent: bf4386c
Committed by GitHub <noreply@github.com> on 8/25/2026, 6:02:36 PM