Commit f761add
GitHub Code Scanning flagged two errors on `.github/workflows/ai_pr_review.yml`:
- #11 actions/untrusted-checkout/high — "Checkout of untrusted code in
trusted context"
- #12 actions/untrusted-checkout-toctou/high — "Insufficient protection
against execution of untrusted code on a privileged workflow
(issue_comment)"
CodeQL flags the structural pattern: the workflow runs `actions/checkout@v6`
with the PR head's repo+sha in a context that has access to OPENAI_API_KEY
and write permissions on PRs/issues. Today we don't execute the checked-out
code (just read it via `git diff`), but the pattern is risky — a future
edit adding "run tests" or "install deps" would turn it into an active RCE
on fork PRs. The PR-author-vs-commenter mismatch (#12) is real: a maintainer
commenting `/ai-review` on a fork PR passes the author_association check,
but the code being checked out is the fork's, which the maintainer didn't
author.
Fix: skip fork PRs entirely. Two-layer gate (different mechanics required
for the three event types):
1. Workflow `if:` for `pull_request` events: require
`github.event.pull_request.head.repo.full_name == github.repository`.
Fork PRs opened against this repo no longer start a workflow run.
2. For `issue_comment` and `pull_request_review_comment` events (event
payloads don't include head-repo info — must API-fetch), the
resolve-pr step now sets a new `is_fork` output. All 7 post-resolve
steps are gated on `steps.pr.outputs.is_fork == 'false'`:
- actions/checkout (closed PR path)
- actions/checkout (open PR path) — the line CodeQL flags
- Pre-fetch base SHA
- Fetch previous AI review
- Build review prompt
- Run Codex
- Post PR comment
For comment-triggered events on fork PRs, the resolve-pr step prints
a yellow `core.notice()` banner in the Actions UI explaining the
skip; no checkout, no codex, no comment.
Background: per a prior thread on the same workflow, fork PRs already fail
today (secrets aren't passed to fork-PR `pull_request` events; comment
triggers on fork PRs would crash because OPENAI_API_KEY is empty). So
skipping cleanly just makes the failure honest — no real loss of behavior.
Tests added (TestWorkflowForkSkip class, 3 contract tests):
- test_workflow_pull_request_if_block_excludes_fork_prs
- test_workflow_resolve_pr_step_sets_is_fork_output
- test_workflow_post_resolve_steps_gated_on_is_fork
(asserts ≥7 occurrences of `is_fork == 'false'` so a future workflow
refactor adding a new PR-content-touching step must extend the gate)
Out of scope:
- Adding `pull_request_target` for fork PRs (would need much more
hardening; not justified for our use case)
- Posting a PR comment explaining the skip (Actions UI notice is enough)
Verification:
- `pytest tests/test_openai_review.py -q` → 214 passed (3 new).
- `python3 -c "import yaml; yaml.safe_load(...)"` → no errors.
- 7 step-level `is_fork == 'false'` gates confirmed.
- CodeQL on this PR should NOT re-flag #11/#12 — early validation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ecd76b7 commit f761add
2 files changed
Lines changed: 105 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
26 | 31 | | |
27 | 32 | | |
28 | | - | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
29 | 37 | | |
30 | 38 | | |
31 | 39 | | |
| |||
75 | 83 | | |
76 | 84 | | |
77 | 85 | | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
78 | 107 | | |
79 | 108 | | |
80 | 109 | | |
| |||
84 | 113 | | |
85 | 114 | | |
86 | 115 | | |
| 116 | + | |
87 | 117 | | |
88 | 118 | | |
89 | 119 | | |
90 | 120 | | |
91 | 121 | | |
92 | 122 | | |
93 | 123 | | |
94 | | - | |
| 124 | + | |
95 | 125 | | |
96 | 126 | | |
97 | 127 | | |
| |||
102 | 132 | | |
103 | 133 | | |
104 | 134 | | |
105 | | - | |
| 135 | + | |
106 | 136 | | |
107 | 137 | | |
108 | 138 | | |
109 | 139 | | |
110 | 140 | | |
| 141 | + | |
111 | 142 | | |
112 | 143 | | |
113 | 144 | | |
| |||
119 | 150 | | |
120 | 151 | | |
121 | 152 | | |
| 153 | + | |
122 | 154 | | |
123 | 155 | | |
124 | 156 | | |
| |||
136 | 168 | | |
137 | 169 | | |
138 | 170 | | |
| 171 | + | |
139 | 172 | | |
140 | 173 | | |
141 | 174 | | |
| |||
415 | 448 | | |
416 | 449 | | |
417 | 450 | | |
| 451 | + | |
418 | 452 | | |
419 | 453 | | |
420 | 454 | | |
| |||
426 | 460 | | |
427 | 461 | | |
428 | 462 | | |
| 463 | + | |
429 | 464 | | |
430 | 465 | | |
431 | 466 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2557 | 2557 | | |
2558 | 2558 | | |
2559 | 2559 | | |
| 2560 | + | |
| 2561 | + | |
| 2562 | + | |
| 2563 | + | |
| 2564 | + | |
| 2565 | + | |
| 2566 | + | |
| 2567 | + | |
| 2568 | + | |
| 2569 | + | |
| 2570 | + | |
| 2571 | + | |
| 2572 | + | |
| 2573 | + | |
| 2574 | + | |
| 2575 | + | |
| 2576 | + | |
| 2577 | + | |
| 2578 | + | |
| 2579 | + | |
| 2580 | + | |
| 2581 | + | |
| 2582 | + | |
| 2583 | + | |
| 2584 | + | |
| 2585 | + | |
| 2586 | + | |
| 2587 | + | |
| 2588 | + | |
| 2589 | + | |
| 2590 | + | |
| 2591 | + | |
| 2592 | + | |
| 2593 | + | |
| 2594 | + | |
| 2595 | + | |
| 2596 | + | |
| 2597 | + | |
| 2598 | + | |
| 2599 | + | |
| 2600 | + | |
| 2601 | + | |
| 2602 | + | |
| 2603 | + | |
| 2604 | + | |
| 2605 | + | |
| 2606 | + | |
| 2607 | + | |
| 2608 | + | |
| 2609 | + | |
| 2610 | + | |
| 2611 | + | |
| 2612 | + | |
| 2613 | + | |
| 2614 | + | |
| 2615 | + | |
| 2616 | + | |
| 2617 | + | |
| 2618 | + | |
| 2619 | + | |
| 2620 | + | |
| 2621 | + | |
| 2622 | + | |
| 2623 | + | |
| 2624 | + | |
| 2625 | + | |
2560 | 2626 | | |
2561 | 2627 | | |
2562 | 2628 | | |
| |||
0 commit comments