3434 steps :
3535 - name : Checkout repository
3636 uses : actions/checkout@v4
37+ with :
38+ # Full history so the Claude analysis step can `git log --grep` and `git show`
39+ # precedent CVE-fix commits (e.g., to mirror past mitigation patterns exactly).
40+ fetch-depth : 0
3741
3842 - name : Log in to GHCR
3943 uses : docker/login-action@v3
99103 id : triage
100104 env :
101105 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
102- TEST_CVE_ID : ${{ inputs.test_cve_id }}
106+ # Use github.event.inputs (not inputs) so this is well-defined on schedule too.
107+ TEST_CVE_ID : ${{ github.event.inputs.test_cve_id || '' }}
103108 run : |
104109 set -euo pipefail
105110
@@ -156,7 +161,7 @@ jobs:
156161 service_account : ${{ vars.GCP_SA_EMAIL }}
157162
158163 - name : Ensure issue labels exist
159- if : steps.triage.outputs.cve_ids != '' && inputs.dry_run != true
164+ if : steps.triage.outputs.cve_ids != '' && (github.event. inputs.dry_run || 'false') != ' true'
160165 env :
161166 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
162167 run : |
@@ -167,7 +172,9 @@ jobs:
167172
168173 - name : Claude analysis on Vertex AI
169174 if : steps.triage.outputs.cve_ids != ''
170- uses : anthropics/claude-code-action@v1
175+ # Pinned to commit SHA (== v1 as of 2026-04-27) for supply-chain safety;
176+ # bump this SHA when picking up new claude-code-action releases.
177+ uses : anthropics/claude-code-action@567fe954a4527e81f132d87d1bdbcc94f7737434 # v1
171178 env :
172179 CLAUDE_CODE_USE_VERTEX : ' 1'
173180 CLOUD_ML_REGION : global
@@ -181,15 +188,16 @@ jobs:
181188 "permissions": {
182189 "allow": [
183190 "Read",
184- "Write",
191+ "Write(/tmp/issues/**)",
192+ "Bash(mkdir:/tmp/issues*)",
185193 "Bash(git log:*)",
186194 "Bash(git show:*)",
187195 "Bash(git rev-parse:*)",
196+ "Bash(git grep:*)",
188197 "Bash(grep:*)",
189198 "Bash(find:*)",
190199 "Bash(jq:*)",
191200 "Bash(ls:*)",
192- "Bash(mkdir:*)",
193201 "Bash(cat:*)",
194202 "Bash(head:*)",
195203 "Bash(tail:*)"
@@ -230,9 +238,18 @@ jobs:
230238 applied to similar packages.
231239 6. `docker/requirements/*.txt` — conda dependency lists. Use `grep` to find
232240 which file pulls in the affected package.
233- 7. Recent git history — `git log --all --oneline --grep <package>`,
234- `git log --all --oneline --grep CVE-`, and `git show <sha>` to inspect prior
235- fix patterns. ALWAYS verify a commit SHA exists before citing it.
241+ 7. Recent git history — full history is available (the workflow checks out
242+ with `fetch-depth: 0`). Use:
243+ - `git log --all --oneline --grep <package>` to find prior commits
244+ touching the affected package
245+ - `git log --all --oneline --grep CVE-` to find prior CVE-fix commits
246+ - **`git show <sha>` to inspect the FULL DIFF of any precedent fix you
247+ plan to cite. Read the diff, not just the commit message.** Many fixes
248+ combine multiple elements (e.g., file removal + reinstall) — your
249+ recommendation must mirror ALL elements of the precedent, not just the
250+ headline change.
251+ - ALWAYS verify a commit SHA exists with `git show <sha>` before citing it
252+ in the report.
236253
237254 ## Required structure for each report
238255
@@ -278,35 +295,51 @@ jobs:
278295 if-no-files-found : warn
279296
280297 - name : File GitHub issues
281- if : steps.triage.outputs.cve_ids != '' && inputs.dry_run != true
298+ if : steps.triage.outputs.cve_ids != '' && (github.event. inputs.dry_run || 'false') != ' true'
282299 env :
283300 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
284301 TEST_MODE : ${{ steps.triage.outputs.test_mode }}
285302 run : |
286303 set -uo pipefail
304+ shopt -s nullglob
305+
306+ if [ ! -d /tmp/issues ]; then
307+ echo "::error::/tmp/issues does not exist — Claude analysis step likely failed"
308+ exit 1
309+ fi
287310
288- if [ ! -d /tmp/issues ] || [ -z "$(ls -A /tmp/issues 2>/dev/null)" ]; then
289- echo "::error::No analysis files in /tmp/issues — Claude may have failed silently"
311+ md_files=(/tmp/issues/*.md)
312+ if [ ${#md_files[@]} -eq 0 ]; then
313+ echo "::error::No .md analysis files in /tmp/issues — Claude may have failed silently"
290314 exit 1
291315 fi
292316
293317 failed=0
294- for f in /tmp/issues/*.md ; do
318+ for f in "${md_files[@]}" ; do
295319 cve=$(basename "$f" .md)
296320 title=$(head -1 "$f" | sed 's/^# *//')
297321 body=$(tail -n +2 "$f")
298322
299- labels="security,cve"
323+ # Dedup-integrity guard: title MUST contain the CVE ID, otherwise the next
324+ # scheduled run won't recognize it as already-triaged via title-substring search.
325+ if ! echo "$title" | grep -qF "$cve"; then
326+ echo "::error::Issue title for $cve does not contain the CVE ID — refusing to file (would break dedup)"
327+ echo " Title was: $title"
328+ failed=$((failed+1))
329+ continue
330+ fi
331+
332+ label_args=(--label security --label cve)
300333 if [ "$TEST_MODE" = "true" ]; then
301- labels="$labels, test"
334+ label_args+=(--label test)
302335 fi
303336
304337 echo "Creating issue for $cve: $title"
305338 if ! gh issue create \
306339 --repo "$GITHUB_REPOSITORY" \
307340 --title "$title" \
308341 --body "$body" \
309- --label "$labels "; then
342+ "${label_args[@]} "; then
310343 echo "::error::Failed to create issue for $cve"
311344 failed=$((failed+1))
312345 fi
0 commit comments