fuzz #44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: fuzz | |
| on: | |
| schedule: | |
| - cron: "0 4 * * *" # nightly 04:00 UTC | |
| workflow_dispatch: | |
| inputs: | |
| duration: | |
| description: "Per-target fuzz duration (e.g. 5m, 30s)" | |
| required: false | |
| default: "5m" | |
| permissions: | |
| contents: read | |
| jobs: | |
| detect: | |
| name: detect fuzz targets | |
| runs-on: ubuntu-latest | |
| outputs: | |
| targets: ${{ steps.targets.outputs.targets }} | |
| has-targets: ${{ steps.targets.outputs.has-targets }} | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version: "1.26" | |
| cache: true | |
| - name: Discover Fuzz targets | |
| id: targets | |
| shell: bash | |
| run: | | |
| # Emit one JSON entry per (package, FuzzXxx) pair so the matrix can fan out. | |
| # `go test -list` prints fuzz names *before* the trailing "ok <pkg>" line, | |
| # so we iterate per-package via `go list` rather than parsing the combined | |
| # stream. | |
| targets=() | |
| while IFS= read -r pkg; do | |
| [ -z "$pkg" ] && continue | |
| while IFS= read -r fuzz; do | |
| [ -z "$fuzz" ] && continue | |
| targets+=("$pkg::$fuzz") | |
| done < <(go test -list 'Fuzz.*' "$pkg" 2>/dev/null | awk '/^Fuzz/') | |
| done < <(go list ./... 2>/dev/null) | |
| if [ ${#targets[@]} -eq 0 ]; then | |
| echo "targets=[]" >> "$GITHUB_OUTPUT" | |
| echo "has-targets=false" >> "$GITHUB_OUTPUT" | |
| echo "No fuzz targets discovered." | |
| else | |
| json=$(printf '%s\n' "${targets[@]}" | jq -R . | jq -s -c .) | |
| echo "targets=$json" >> "$GITHUB_OUTPUT" | |
| echo "has-targets=true" >> "$GITHUB_OUTPUT" | |
| echo "Discovered: $json" | |
| fi | |
| fuzz: | |
| name: fuzz ${{ matrix.target }} | |
| needs: detect | |
| if: needs.detect.outputs.has-targets == 'true' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: ${{ fromJson(needs.detect.outputs.targets) }} | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version: "1.26" | |
| cache: true | |
| - name: Fuzz | |
| shell: bash | |
| # Values from `inputs` and `matrix` flow through env to avoid | |
| # `${{ }}` interpolation inside a shell `run:` block — the | |
| # standard remediation for the run-shell-injection rule. | |
| env: | |
| DURATION: ${{ inputs.duration }} | |
| TARGET: ${{ matrix.target }} | |
| run: | | |
| duration="${DURATION:-5m}" | |
| pkg="$TARGET" | |
| fuzz_pkg="${pkg%%::*}" | |
| fuzz_name="${pkg##*::}" | |
| echo "Fuzzing $fuzz_name in $fuzz_pkg for $duration" | |
| go test "$fuzz_pkg" -run='^$' -fuzz="^${fuzz_name}\$" -fuzztime="$duration" | |
| - name: Upload corpus on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: fuzz-corpus-${{ matrix.target }} | |
| path: testdata/fuzz/ | |
| if-no-files-found: ignore | |
| retention-days: 14 |