|
20 | 20 | crashes_found: ${{ steps.check.outputs.crashes_found }} |
21 | 21 | crash_count: ${{ steps.check.outputs.crash_count }} |
22 | 22 | first_crash_name: ${{ steps.check.outputs.first_crash_name }} |
| 23 | + artifact_url: ${{ steps.upload_artifacts.outputs.artifact-url }} |
23 | 24 | steps: |
24 | 25 | - uses: runs-on/action@v2 |
25 | 26 | with: |
|
75 | 76 | echo "No crashes found" |
76 | 77 | fi |
77 | 78 | - name: Archive crash artifacts |
| 79 | + id: upload_artifacts |
78 | 80 | if: steps.check.outputs.crashes_found == 'true' |
79 | 81 | uses: actions/upload-artifact@v4 |
80 | 82 | with: |
@@ -110,152 +112,196 @@ jobs: |
110 | 112 | permissions: |
111 | 113 | issues: write |
112 | 114 | contents: read |
| 115 | + id-token: write # Required for Claude Code Action OIDC authentication |
| 116 | + pull-requests: read # For gh api commands |
113 | 117 | steps: |
114 | 118 | - name: Checkout repository |
115 | 119 | uses: actions/checkout@v5 |
116 | 120 |
|
117 | | - - name: Download crash artifacts |
118 | | - uses: actions/download-artifact@v4 |
119 | | - with: |
120 | | - name: io-fuzzing-crash-artifacts |
121 | | - path: ./crash_artifacts |
122 | | - |
123 | 121 | - name: Download fuzzer logs |
124 | 122 | uses: actions/download-artifact@v4 |
125 | 123 | with: |
126 | 124 | name: io-fuzzing-logs |
127 | 125 | path: ./logs |
128 | 126 |
|
129 | | - - name: Extract first crash only |
130 | | - run: | |
131 | | - # Only keep the first crash file for analysis |
132 | | - FIRST_CRASH="${{ needs.io_fuzz.outputs.first_crash_name }}" |
133 | | - echo "Processing only first crash: $FIRST_CRASH" |
134 | | -
|
135 | | - # Create a clean directory with just the first crash |
136 | | - mkdir -p ./first_crash |
137 | | - if [ -f "crash_artifacts/$FIRST_CRASH" ]; then |
138 | | - cp "crash_artifacts/$FIRST_CRASH" ./first_crash/ |
139 | | - echo "Copied first crash to ./first_crash/" |
140 | | - else |
141 | | - echo "Warning: Could not find crash file $FIRST_CRASH" |
142 | | - echo "Available files:" |
143 | | - ls -la crash_artifacts/ || echo "No files in crash_artifacts" |
144 | | - fi |
145 | | -
|
146 | | - - name: Show crash info |
147 | | - run: | |
148 | | - echo "=== Crash Summary ===" |
149 | | - echo "Total crashes found: ${{ needs.io_fuzz.outputs.crash_count }}" |
150 | | - echo "Processing first crash: ${{ needs.io_fuzz.outputs.first_crash_name }}" |
151 | | - echo "" |
152 | | - echo "=== First crash file size ===" |
153 | | - ls -lh first_crash/ || echo "No crash file" |
154 | | - echo "" |
155 | | - echo "=== Fuzzer log preview (last 100 lines, where crashes are reported) ===" |
156 | | - tail -100 logs/fuzz_output.log || echo "No log file" |
157 | | -
|
158 | | - - name: Create GitHub issue |
| 127 | + # Single Claude Code Action that does everything: |
| 128 | + # - Analyzes crash from logs |
| 129 | + # - Checks for duplicate issues |
| 130 | + # - Creates new issue OR comments on existing issue |
| 131 | + - name: Analyze and report crash with Claude |
159 | 132 | env: |
160 | | - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
161 | | - run: | |
162 | | - # Extract crash type from filename |
163 | | - CRASH_FILE="${{ needs.io_fuzz.outputs.first_crash_name }}" |
164 | | - if [[ "$CRASH_FILE" == crash-* ]]; then |
165 | | - CRASH_TYPE="Crash" |
166 | | - elif [[ "$CRASH_FILE" == leak-* ]]; then |
167 | | - CRASH_TYPE="Memory Leak" |
168 | | - elif [[ "$CRASH_FILE" == timeout-* ]]; then |
169 | | - CRASH_TYPE="Timeout" |
170 | | - elif [[ "$CRASH_FILE" == oom-* ]]; then |
171 | | - CRASH_TYPE="Out of Memory" |
172 | | - else |
173 | | - CRASH_TYPE="Unknown" |
174 | | - fi |
175 | | -
|
176 | | - # Create issue with gh CLI |
177 | | - gh issue create \ |
178 | | - --repo ${{ github.repository }} \ |
179 | | - --title "Fuzzing $CRASH_TYPE: file_io - $(date -u +%Y-%m-%d)" \ |
180 | | - --label "bug,fuzzing,file_io-fuzz,needs-triage" \ |
181 | | - --body-file - <<'EOF' |
182 | | - ## Fuzzing Crash Report |
183 | | -
|
184 | | - The `file_io` fuzzing target detected a $CRASH_TYPE during a scheduled fuzzing run. |
185 | | -
|
186 | | - ### Summary |
187 | | -
|
188 | | - - **Crash Type**: $CRASH_TYPE |
189 | | - - **Target**: `file_io` |
190 | | - - **Crash File**: `${{ needs.io_fuzz.outputs.first_crash_name }}` |
191 | | - - **Total Crashes Found**: ${{ needs.io_fuzz.outputs.crash_count }} |
192 | | - - **Workflow Run**: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} |
193 | | - - **Timestamp**: $(date -u +"%Y-%m-%d %H:%M:%S UTC") |
194 | | - - **Branch**: ${{ github.ref_name }} |
195 | | - - **Commit**: ${{ github.sha }} |
196 | | -
|
197 | | - ### Crash Artifacts |
198 | | -
|
199 | | - Download crash artifacts from the workflow run: |
200 | | - **https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}** |
201 | | -
|
202 | | - Artifacts available: |
203 | | - - `io-fuzzing-crash-artifacts` - All crash files found (includes ${{ needs.io_fuzz.outputs.crash_count }} crashes) |
204 | | - - `io-fuzzing-logs` - Complete fuzzer output with stack traces |
205 | | -
|
206 | | - ### Reproduction Steps |
207 | | -
|
208 | | - 1. Download the `io-fuzzing-crash-artifacts` from the workflow run above |
209 | | - 2. Extract the crash file to your local `fuzz/artifacts/file_io/` directory |
210 | | - 3. Reproduce the crash locally: |
211 | | -
|
212 | | - ```bash |
213 | | - cargo +nightly fuzz run file_io fuzz/artifacts/file_io/${{ needs.io_fuzz.outputs.first_crash_name }} |
214 | | - ``` |
215 | | -
|
216 | | - 4. Get full backtrace: |
217 | | -
|
218 | | - ```bash |
219 | | - RUST_BACKTRACE=full cargo +nightly fuzz run file_io fuzz/artifacts/file_io/${{ needs.io_fuzz.outputs.first_crash_name }} |
220 | | - ``` |
221 | | -
|
222 | | - 5. Minimize the test case (optional): |
223 | | -
|
224 | | - ```bash |
225 | | - cargo +nightly fuzz tmin file_io fuzz/artifacts/file_io/${{ needs.io_fuzz.outputs.first_crash_name }} |
226 | | - ``` |
227 | | -
|
228 | | - ### Investigation Checklist |
229 | | -
|
230 | | - - [ ] Download crash artifacts from workflow run |
231 | | - - [ ] Reproduce crash locally with full backtrace |
232 | | - - [ ] Analyze stack trace and identify root cause |
233 | | - - [ ] Determine severity (security vs stability) |
234 | | - - [ ] Check if this is a duplicate of an existing issue |
235 | | - - [ ] Minimize test case if needed |
236 | | - - [ ] Create fix PR with reference to this issue |
237 | | - - [ ] Add regression test |
238 | | - - [ ] Verify fix with: `cargo +nightly fuzz run file_io <crash-file>` |
239 | | -
|
240 | | - ### Environment |
241 | | -
|
242 | | - - **Runner**: ubuntu24-full-arm64 |
243 | | - - **Rust Toolchain**: nightly |
244 | | - - **Fuzz Duration**: 7200 seconds (2 hours) |
245 | | - - **Fuzzer**: cargo-fuzz (libFuzzer) |
246 | | -
|
247 | | - ### Additional Context |
248 | | -
|
249 | | - This issue was automatically created by the fuzzing workflow. If multiple crashes were found, this issue represents the first crash detected. All crash artifacts are available for download. |
250 | | -
|
251 | | - **Note**: If this issue is a duplicate of an existing bug, please close it and reference the original issue. |
252 | | -
|
253 | | - --- |
| 133 | + CRASH_FILE: ${{ needs.io_fuzz.outputs.first_crash_name }} |
| 134 | + CRASH_COUNT: ${{ needs.io_fuzz.outputs.crash_count }} |
| 135 | + WORKFLOW_RUN: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} |
| 136 | + ARTIFACT_URL: ${{ needs.io_fuzz.outputs.artifact_url }} |
| 137 | + BRANCH: ${{ github.ref_name }} |
| 138 | + COMMIT: ${{ github.sha }} |
| 139 | + uses: anthropics/claude-code-action@v1 |
| 140 | + with: |
| 141 | + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} |
| 142 | + github_token: ${{ secrets.GITHUB_TOKEN }} |
| 143 | + show_full_output: true |
| 144 | + prompt: | |
| 145 | + # Fuzzer Crash Analysis and Reporting |
| 146 | +
|
| 147 | + A fuzzing run for the `file_io` target has detected a crash. Analyze it and report by creating or updating a GitHub issue. |
| 148 | +
|
| 149 | + ## Step 1: Analyze the Crash |
| 150 | +
|
| 151 | + 1. Read the fuzzer log: `logs/fuzz_output.log` |
| 152 | + 2. Extract: |
| 153 | + - Stack trace (lines with `#0`, `#1`, etc.) |
| 154 | + - Error message ("panicked at" or "ERROR:") |
| 155 | + - Crash location (top user code frame, not std/core/libfuzzer) |
| 156 | + - Debug output (look for "Output of `std::fmt::Debug`:" section before the crash) |
| 157 | + 3. Read the source code at the crash location to understand root cause |
| 158 | +
|
| 159 | + ## Step 2: Check for Duplicates |
| 160 | +
|
| 161 | + 1. List existing fuzzer issues: |
| 162 | + ```bash |
| 163 | + gh issue list --repo ${{ github.repository }} --label fuzzer --state open --json number,title,body --limit 50 |
| 164 | + ``` |
| 165 | +
|
| 166 | + 2. For each existing issue, compare: |
| 167 | + - **Crash location**: Same file + function? (line numbers can differ) |
| 168 | + - **Error pattern**: Same error after normalizing values? |
| 169 | + - "index 5 out of bounds" = "index 12 out of bounds" (SAME) |
| 170 | + - "len is 100" = "len is 5" (SAME) |
| 171 | + - Read source code if needed to verify same root cause |
| 172 | +
|
| 173 | + 3. Determine duplication level: |
| 174 | + - **EXACT DUPLICATE**: Same crash location + same error pattern → Add reaction |
| 175 | + - **SIMILAR**: Same general area but unclear → Add comment |
| 176 | + - **NEW BUG**: Different location or different error type → Create new issue |
| 177 | +
|
| 178 | + ## Step 3: Take Action |
| 179 | +
|
| 180 | + ### If EXACT DUPLICATE (high confidence): |
| 181 | + Update or create a tracking comment to count occurrences: |
| 182 | +
|
| 183 | + 1. First, check if there's already a tracking comment (look for a comment starting with "<!-- occurrences: ") |
| 184 | + 2. If found, extract the count, increment it, and edit the comment |
| 185 | + 3. If not found, create a new comment |
| 186 | +
|
| 187 | + Comment format: |
| 188 | + ```markdown |
| 189 | + <!-- occurrences: N --> |
| 190 | + **Crash seen N time(s)** |
| 191 | +
|
| 192 | + Latest occurrence: |
| 193 | + - Crash file: $CRASH_FILE |
| 194 | + - Artifact: $ARTIFACT_URL |
| 195 | + - Branch: $BRANCH |
| 196 | + - Commit: $COMMIT |
| 197 | + ``` |
| 198 | +
|
| 199 | + Use: |
| 200 | + - `gh api repos/${{ github.repository }}/issues/ISSUE_NUM/comments` to list comments |
| 201 | + - `gh api repos/${{ github.repository }}/issues/comments/COMMENT_ID -X PATCH` to update |
| 202 | + - `gh issue comment ISSUE_NUM` to create new |
| 203 | +
|
| 204 | + ### If SIMILAR (medium confidence): |
| 205 | + Comment on the existing issue with analysis: |
| 206 | + ```bash |
| 207 | + gh issue comment ISSUE_NUM --repo ${{ github.repository }} --body "..." |
| 208 | + ``` |
| 209 | +
|
| 210 | + Include in comment: |
| 211 | + - Note that another crash was detected |
| 212 | + - Your confidence level and reasoning |
| 213 | + - Key differences if any |
| 214 | + - Crash file: $CRASH_FILE |
| 215 | + - Workflow: $WORKFLOW_RUN |
| 216 | +
|
| 217 | + ### If NEW BUG (not a duplicate): |
| 218 | + Create a new issue with `gh issue create`: |
| 219 | + ```bash |
| 220 | + gh issue create --repo ${{ github.repository }} \ |
| 221 | + --title "Fuzzing Crash: [brief description]" \ |
| 222 | + --label "bug,fuzzer" \ |
| 223 | + --body "..." |
| 224 | + ``` |
| 225 | +
|
| 226 | + Issue body must include: |
| 227 | + ```markdown |
| 228 | + ## Fuzzing Crash Report |
| 229 | +
|
| 230 | + ### Analysis |
| 231 | +
|
| 232 | + **Crash Location**: `file.rs:function_name` |
| 233 | +
|
| 234 | + **Error Message**: |
| 235 | + ``` |
| 236 | + [error message] |
| 237 | + ``` |
| 238 | +
|
| 239 | + **Stack Trace**: |
| 240 | + ``` |
| 241 | + [top 5-7 frames - keep in code block to prevent markdown rendering issues] |
| 242 | + ``` |
| 243 | +
|
| 244 | + Note: Keep stack traces in code blocks to prevent `#0`, `#1` from being interpreted as markdown headers. |
| 245 | +
|
| 246 | + **Root Cause**: [Your analysis] |
| 247 | +
|
| 248 | + **Debug Output** (if available): |
| 249 | + ``` |
| 250 | + [Include any Debug: output or structured debug information from the fuzzer log] |
| 251 | + ``` |
| 252 | +
|
| 253 | + ### Summary |
| 254 | +
|
| 255 | + - **Target**: `file_io` |
| 256 | + - **Crash File**: `$CRASH_FILE` |
| 257 | + - **Branch**: $BRANCH |
| 258 | + - **Commit**: $COMMIT |
| 259 | + - **Crash Artifact**: $ARTIFACT_URL |
| 260 | +
|
| 261 | + ### Reproduction |
| 262 | +
|
| 263 | + 1. Download the crash artifact: |
| 264 | + - **Direct download**: $ARTIFACT_URL |
| 265 | + - Or find `io-fuzzing-crash-artifacts` at: $WORKFLOW_RUN |
| 266 | + - Extract the zip file |
| 267 | +
|
| 268 | + 2. Reproduce locally: |
| 269 | + ```bash |
| 270 | + # The artifact contains file_io/$CRASH_FILE |
| 271 | + cargo +nightly fuzz run --sanitizer=none file_io file_io/$CRASH_FILE |
| 272 | + ``` |
| 273 | +
|
| 274 | + 3. Get full backtrace: |
| 275 | + ```bash |
| 276 | + RUST_BACKTRACE=full cargo +nightly fuzz run --sanitizer=none file_io file_io/$CRASH_FILE |
| 277 | + ``` |
| 278 | +
|
| 279 | + --- |
| 280 | + *Auto-created by fuzzing workflow with Claude analysis* |
| 281 | + ``` |
| 282 | +
|
| 283 | + ## Important Guidelines |
| 284 | +
|
| 285 | + - Be conservative: when unsure, prefer creating a new issue over marking as duplicate |
| 286 | + - Focus on ROOT CAUSE, not specific values in error messages |
| 287 | + - You have full repo access - read source code to understand crashes |
| 288 | + - Only use +1 reaction for truly identical crashes |
| 289 | +
|
| 290 | + ## Environment Variables |
| 291 | +
|
| 292 | + - CRASH_FILE: $CRASH_FILE |
| 293 | + - CRASH_COUNT: $CRASH_COUNT |
| 294 | + - WORKFLOW_RUN: $WORKFLOW_RUN |
| 295 | + - ARTIFACT_URL: $ARTIFACT_URL (direct link to crash artifact) |
| 296 | + - BRANCH: $BRANCH |
| 297 | + - COMMIT: $COMMIT |
| 298 | +
|
| 299 | + Start by reading `logs/fuzz_output.log`. |
| 300 | + claude_args: | |
| 301 | + --model claude-sonnet-4-5-20250929 |
| 302 | + --max-turns 25 |
| 303 | + --allowedTools "Read,Write,Bash(gh issue list:*),Bash(gh issue view:*),Bash(gh issue create:*),Bash(gh issue comment:*),Bash(gh api:*),Bash(echo:*),Bash(cat:*),Bash(jq:*),Bash(grep:*),Bash(cargo +nightly fuzz run:*),Bash(RUST_BACKTRACE=* cargo +nightly fuzz run:*)" |
254 | 304 |
|
255 | | - *Automatically created by fuzzing workflow* |
256 | | - *Workflow file: [fuzz.yml](https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/fuzz.yml)* |
257 | | - *Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}* |
258 | | - EOF |
259 | 305 |
|
260 | 306 | ops_fuzz: |
261 | 307 | name: "Array Operations Fuzz" |
|
0 commit comments