Commit c36b27a
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 78b1ef0 commit c36b27a
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 | | |
| |||
243 | 276 | | |
244 | 277 | | |
245 | 278 | | |
| 279 | + | |
246 | 280 | | |
247 | 281 | | |
248 | 282 | | |
| |||
254 | 288 | | |
255 | 289 | | |
256 | 290 | | |
| 291 | + | |
257 | 292 | | |
258 | 293 | | |
259 | 294 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2238 | 2238 | | |
2239 | 2239 | | |
2240 | 2240 | | |
| 2241 | + | |
| 2242 | + | |
| 2243 | + | |
| 2244 | + | |
| 2245 | + | |
| 2246 | + | |
| 2247 | + | |
| 2248 | + | |
| 2249 | + | |
| 2250 | + | |
| 2251 | + | |
| 2252 | + | |
| 2253 | + | |
| 2254 | + | |
| 2255 | + | |
| 2256 | + | |
| 2257 | + | |
| 2258 | + | |
| 2259 | + | |
| 2260 | + | |
| 2261 | + | |
| 2262 | + | |
| 2263 | + | |
| 2264 | + | |
| 2265 | + | |
| 2266 | + | |
| 2267 | + | |
| 2268 | + | |
| 2269 | + | |
| 2270 | + | |
| 2271 | + | |
| 2272 | + | |
| 2273 | + | |
| 2274 | + | |
| 2275 | + | |
| 2276 | + | |
| 2277 | + | |
| 2278 | + | |
| 2279 | + | |
| 2280 | + | |
| 2281 | + | |
| 2282 | + | |
| 2283 | + | |
| 2284 | + | |
| 2285 | + | |
| 2286 | + | |
| 2287 | + | |
| 2288 | + | |
| 2289 | + | |
| 2290 | + | |
| 2291 | + | |
| 2292 | + | |
| 2293 | + | |
| 2294 | + | |
| 2295 | + | |
| 2296 | + | |
| 2297 | + | |
| 2298 | + | |
| 2299 | + | |
| 2300 | + | |
| 2301 | + | |
| 2302 | + | |
| 2303 | + | |
| 2304 | + | |
| 2305 | + | |
| 2306 | + | |
2241 | 2307 | | |
2242 | 2308 | | |
2243 | 2309 | | |
| |||
0 commit comments