Skip to content

Commit cc38f07

Browse files
committed
feat: add validation and crash reproduction checks
Add pre-Claude validation steps: - Validate issue has fuzzer label and required fields - Download and verify crash artifact exists - Reproduce the crash to confirm it still exists - Skip Claude if crash cannot be reproduced (already fixed) - Provide crash reproduction log to Claude for analysis This ensures we only run expensive Claude analysis on valid, reproducible crashes. Signed-off-by: Joe Isaacs <[email protected]>
1 parent 4b37b7a commit cc38f07

File tree

1 file changed

+125
-21
lines changed

1 file changed

+125
-21
lines changed

.github/workflows/fuzzer-fix-automation.yml

Lines changed: 125 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,121 @@ jobs:
100100
echo "Extracted: target=$TARGET, crash_file=$CRASH_FILE"
101101
rm -f issue_body.txt
102102
103+
- name: Validate issue details
104+
id: validate
105+
env:
106+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
107+
run: |
108+
ISSUE_NUM="${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.issue_number || github.event.issue.number }}"
109+
110+
# Check if issue exists and has fuzzer label
111+
ISSUE_LABELS=$(gh issue view "$ISSUE_NUM" --repo ${{ github.repository }} --json labels --jq '.labels[].name')
112+
113+
if ! echo "$ISSUE_LABELS" | grep -q "fuzzer"; then
114+
echo "❌ Issue #$ISSUE_NUM does not have 'fuzzer' label"
115+
exit 1
116+
fi
117+
118+
echo "✅ Issue #$ISSUE_NUM has 'fuzzer' label"
119+
120+
# Check if we have required crash details
121+
if [ -z "${{ steps.extract.outputs.crash_file }}" ]; then
122+
echo "❌ Could not extract crash file name from issue"
123+
exit 1
124+
fi
125+
126+
if [ -z "${{ steps.extract.outputs.artifact_url }}" ]; then
127+
echo "❌ Could not extract artifact URL from issue"
128+
exit 1
129+
fi
130+
131+
echo "✅ Extracted crash details: target=${{ steps.extract.outputs.target }}, crash_file=${{ steps.extract.outputs.crash_file }}"
132+
133+
- name: Download and verify crash artifact
134+
id: download
135+
env:
136+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
137+
run: |
138+
# Extract run ID from artifact URL
139+
ARTIFACT_URL="${{ steps.extract.outputs.artifact_url }}"
140+
RUN_ID=$(echo "$ARTIFACT_URL" | grep -oP 'runs/\K[0-9]+')
141+
ARTIFACT_ID=$(echo "$ARTIFACT_URL" | grep -oP 'artifacts/\K[0-9]+')
142+
143+
echo "Downloading artifact $ARTIFACT_ID from run $RUN_ID"
144+
145+
# Download the artifact
146+
gh run download "$RUN_ID" --name "${{ steps.extract.outputs.target }}-fuzzing-crash-artifacts" --repo ${{ github.repository }}
147+
148+
# Verify crash file exists
149+
CRASH_FILE_PATH="${{ steps.extract.outputs.target }}/${{ steps.extract.outputs.crash_file }}"
150+
if [ ! -f "$CRASH_FILE_PATH" ]; then
151+
echo "❌ Crash file not found: $CRASH_FILE_PATH"
152+
ls -la "${{ steps.extract.outputs.target }}/" || true
153+
exit 1
154+
fi
155+
156+
echo "✅ Downloaded crash file: $CRASH_FILE_PATH"
157+
echo "crash_file_path=$CRASH_FILE_PATH" >> $GITHUB_OUTPUT
158+
159+
- name: Reproduce crash
160+
id: reproduce
161+
continue-on-error: true
162+
run: |
163+
echo "Attempting to reproduce crash with fuzzer..."
164+
165+
# Run fuzzer with crash file (no sanitizer, just reproduce)
166+
timeout 30s cargo +nightly fuzz run --sanitizer=none "${{ steps.extract.outputs.target }}" "${{ steps.download.outputs.crash_file_path }}" -- -runs=1 2>&1 | tee crash_reproduction.log
167+
168+
FUZZ_EXIT_CODE=${PIPESTATUS[0]}
169+
170+
if [ $FUZZ_EXIT_CODE -eq 0 ]; then
171+
echo "⚠️ Fuzzer did not crash - may have been fixed already"
172+
echo "crash_reproduced=false" >> $GITHUB_OUTPUT
173+
else
174+
echo "✅ Crash reproduced (exit code: $FUZZ_EXIT_CODE)"
175+
echo "crash_reproduced=true" >> $GITHUB_OUTPUT
176+
fi
177+
178+
- name: Check if crash still exists
179+
if: steps.reproduce.outputs.crash_reproduced == 'false'
180+
env:
181+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
182+
run: |
183+
ISSUE_NUM="${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.issue_number || github.event.issue.number }}"
184+
185+
gh issue comment "$ISSUE_NUM" --repo ${{ github.repository }} --body "## 🤖 Automated Analysis
186+
187+
I attempted to reproduce this crash but the fuzzer completed successfully without crashing.
188+
189+
**This likely means the issue has already been fixed.**
190+
191+
### Verification Steps
192+
193+
I ran:
194+
\`\`\`bash
195+
cargo +nightly fuzz run --sanitizer=none ${{ steps.extract.outputs.target }} ${{ steps.download.outputs.crash_file_path }} -- -runs=1
196+
\`\`\`
197+
198+
The fuzzer exited with code 0 (success).
199+
200+
### Next Steps
201+
202+
- Verify if a recent commit fixed this issue
203+
- If confirmed fixed, close this issue
204+
- If not fixed, the crash may be non-deterministic and requires further investigation"
205+
206+
echo "Crash could not be reproduced - skipping fix attempt"
207+
exit 0
208+
103209
- name: Attempt to fix crash with Claude
210+
if: steps.reproduce.outputs.crash_reproduced == 'true'
104211
env:
105212
ISSUE_NUMBER: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.issue_number || github.event.issue.number }}
106213
ISSUE_TITLE: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && steps.fetch_issue.outputs.issue_title || github.event.issue.title }}
107214
ISSUE_BODY: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && steps.fetch_issue.outputs.issue_body || github.event.issue.body }}
108215
TARGET: ${{ steps.extract.outputs.target }}
109216
CRASH_FILE: ${{ steps.extract.outputs.crash_file }}
217+
CRASH_FILE_PATH: ${{ steps.download.outputs.crash_file_path }}
110218
ARTIFACT_URL: ${{ steps.extract.outputs.artifact_url }}
111219
uses: anthropics/claude-code-action@v1
112220
with:
@@ -134,32 +242,28 @@ jobs:
134242
- **Target**: ${{ env.TARGET }}
135243
- **Crash File**: ${{ env.CRASH_FILE }}
136244
137-
## Step 1: Download the Crash Artifact
245+
## ✅ Pre-Validated Information
138246
139-
The issue body contains an artifact URL. Extract it and download the crash file:
247+
**Good news!** The crash artifact has already been downloaded and the crash has been reproduced.
140248
141-
```bash
142-
# The issue body should contain the crash artifact URL
143-
# Download it using gh or curl
144-
# Example:
145-
gh run download RUN_ID --name ${{ env.TARGET }}-fuzzing-crash-artifacts
146-
```
249+
- **Crash file location**: `${{ env.CRASH_FILE_PATH }}`
250+
- **Crash reproduction log**: `crash_reproduction.log`
147251
148-
Look for the artifact URL in the issue body. If you can't download it automatically, note this in your comment and provide manual instructions.
252+
The crash has been confirmed to still exist on the current codebase, so you can proceed with analysis and fixing.
149253
150-
## Step 2: Reproduce the Crash
254+
## Step 1: Analyze the Crash
151255
152-
Once you have the crash file, reproduce it:
256+
Read the crash reproduction log to see the actual crash output:
153257
154258
```bash
155-
cargo +nightly fuzz run --sanitizer=none ${{ env.TARGET }} <path-to-crash-file>
259+
cat crash_reproduction.log
156260
```
157261
158-
Capture the output and verify you can reproduce the panic/crash.
262+
This will show you the panic message, stack trace, and any debug output.
159263
160-
## Step 3: Analyze the Root Cause
264+
## Step 2: Analyze the Root Cause
161265
162-
1. Read the **Stack Trace** from the issue body
266+
1. Read the **Stack Trace** from the crash reproduction log
163267
2. Identify the **Crash Location** (file and line)
164268
3. Read the source code at that location
165269
4. Understand what input caused the crash (check the Debug Output in the issue)
@@ -170,7 +274,7 @@ jobs:
170274
- Integer overflow?
171275
- etc.
172276
173-
## Step 4: Assess Fixability
277+
## Step 3: Assess Fixability
174278
175279
Determine if this is something you can fix:
176280
@@ -188,15 +292,15 @@ jobs:
188292
- Multiple files/modules affected
189293
- Unclear requirements
190294
191-
## Step 5: If Fixable - Create the Fix
295+
## Step 4: If Fixable - Create the Fix
192296
193297
1. **Modify the source code** to fix the issue
194298
2. **Add validation** or bounds checks as needed
195299
3. **Handle the edge case** properly
196300
4. **Follow the project's code style** (see CLAUDE.md)
197301
5. **Keep changes minimal** - only fix the specific issue
198302
199-
## Step 6: Write Regression Tests
303+
## Step 5: Write Regression Tests
200304
201305
Create tests that:
202306
1. **Would fail before your fix** (reproduce the crash)
@@ -221,7 +325,7 @@ jobs:
221325
}
222326
```
223327
224-
## Step 7: Verify Your Fix
328+
## Step 6: Verify Your Fix
225329
226330
1. Run the new regression test:
227331
```bash
@@ -230,7 +334,7 @@ jobs:
230334
231335
2. Run the fuzzer with the crash file:
232336
```bash
233-
cargo +nightly fuzz run --sanitizer=none ${{ env.TARGET }} <path-to-crash-file> -- -runs=100
337+
cargo +nightly fuzz run --sanitizer=none ${{ env.TARGET }} ${{ env.CRASH_FILE_PATH }} -- -runs=100
234338
```
235339
236340
3. Run related tests:
@@ -248,7 +352,7 @@ jobs:
248352
cargo +nightly fmt --all
249353
```
250354
251-
## Step 8: Post Your Analysis
355+
## Step 7: Post Your Analysis
252356
253357
Comment on issue #${{ env.ISSUE_NUMBER }} with your findings:
254358

0 commit comments

Comments
 (0)