Skip to content

chore(deps): bump the actions-minor-patch group with 3 updates #67

chore(deps): bump the actions-minor-patch group with 3 updates

chore(deps): bump the actions-minor-patch group with 3 updates #67

Workflow file for this run

name: Fuzz (Jazzer)
# SR-144: Jazzer fuzz job against OffApiClient's JSON decode path. Framing
# per issue #144: this is regression-catch quality, not OSS-Fuzz-grade
# fuzzing. Two jobs (#230): `fuzz` runs the full ~5-minute coverage-guided
# fuzz on the weekly schedule + manual dispatch; `regression` replays the
# committed seed corpus (no fuzzing, seconds) on every PR as a fast guard
# that the fuzz harness still wires up. Findings/reports are uploaded as
# workflow artifacts. Neither job is in any required-status-checks list yet
# — feature PRs do not block on them (the PR regression check may be promoted
# to required later, once observed green).
on:
workflow_dispatch:
# #230: also run on PRs in fast regression mode (replay the committed seed
# corpus, no fuzzing) so a broken fuzz classpath can't slip through unrun
# again — the original break was invisible because this workflow was
# schedule-only. Intentionally no `paths:` filter: it matches the repo's
# other workflows and keeps this check safe to later promote to a required
# status check (a required check gated by `paths:` can deadlock PRs that
# don't touch those paths). The full ~5-minute fuzz stays schedule/dispatch-
# only — see the `if:` guards on the two jobs below.
pull_request:
schedule:
# Monday 04:15 UTC — deliberately offset from security.yml (Mon 06:00)
# and codeql.yml (Wed 06:00) so the three scheduled security workflows
# don't pile into the same minute on the GitHub-Actions queue. 04:15
# is also off-peak, which matters because Jazzer needs ~5 minutes of
# actual CPU time and shared runners are slower mid-business-day.
- cron: '15 4 * * 1'
concurrency:
group: fuzz-${{ github.ref }}
cancel-in-progress: true
# Workflow-level least-privilege scope (SR-18 convention, mirrors ci.yml).
# The fuzz job only reads — no SARIF upload, no PR comment, no release —
# so `contents: read` is sufficient. Artifact upload uses the implicit
# `actions: write` from the upload-artifact action's own permission
# (granted to the GITHUB_TOKEN at the workflow run level, not requested
# here).
permissions:
contents: read
jobs:
fuzz:
name: Jazzer fuzz (OffApiClient)
# #230: the full coverage-guided fuzz (~5 min) runs only on the weekly
# schedule and manual dispatch — never on PRs, where the fast `regression`
# job below runs instead.
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
# Hard ceiling on the whole job: 7 minutes. The @FuzzTest annotation's
# `maxDuration = "5m"` is the per-method Jazzer ceiling; the additional
# 2 minutes covers JDK + Gradle bootstrap, dependency resolution, and
# the JUnit Platform / Jazzer JVM warmup. If a future fuzz method's
# annotation is widened without thinking, this Actions-level timeout
# is the belt-and-braces stop.
timeout-minutes: 7
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
distribution: temurin
java-version: '21'
- uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
- name: Run Jazzer fuzz tests
# The :app:fuzzTest task sets JAZZER_FUZZ=1 internally so this runs
# in actual fuzzing mode (not just regression replay). --no-daemon
# keeps the JVM lifecycle predictable for the 7-minute timeout.
# --stacktrace ensures any Gradle-level failure (task-resolution,
# classpath mismatch) surfaces a full trace into the job log.
run: |
chmod +x gradlew
./gradlew :app:fuzzTest --no-daemon --stacktrace
- name: Upload findings (if any)
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: jazzer-findings
# Jazzer drops crashing inputs into the inputs directory and writes
# the surrounding stack trace + Gradle test report under
# build/reports/tests/fuzzTest/. Both paths are uploaded so the
# maintainer can pull the artifact, replay the crashing input
# locally (in regression mode, JAZZER_FUZZ unset), and commit it
# back into the seed corpus.
path: |
app/build/reports/tests/fuzzTest/
app/build/test-results/fuzzTest/
app/src/test/resources/de/docgerdsoft/pantrytracker/data/remote/OffApiClientFuzzTestInputs/
if-no-files-found: ignore
retention-days: 30
regression:
name: Fuzz regression (seed corpus)
# #230: fast guard — replay the committed seed corpus through the fuzz
# harness in regression mode (JAZZER_FUZZ unset via -PfuzzRegression; no
# generative fuzzing, finishes in seconds, writes nothing). Runs on PRs and
# manual dispatch; the weekly `schedule` runs the full `fuzz` job instead.
if: github.event_name != 'schedule'
runs-on: ubuntu-latest
# Generous ceiling: the cost here is the Gradle build (compile the unit-test
# source set), not the replay itself (~1s). Belt-and-braces stop only.
timeout-minutes: 15
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
distribution: temurin
java-version: '21'
- uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
- name: Run fuzz regression (replay seed corpus, no fuzzing)
# -PfuzzRegression leaves JAZZER_FUZZ unset, so Jazzer only replays the
# committed corpus under OffApiClientFuzzTestInputs/ and asserts the
# decode path doesn't crash on them. --no-daemon keeps the JVM lifecycle
# predictable; --stacktrace surfaces any Gradle-level wiring failure
# (the #230 class of bug) as a full trace in the job log.
run: |
chmod +x gradlew
./gradlew :app:fuzzTest -PfuzzRegression --no-daemon --stacktrace
- name: Upload regression report (on failure)
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: jazzer-regression-report
path: |
app/build/reports/tests/fuzzTest/
app/build/test-results/fuzzTest/
if-no-files-found: ignore
retention-days: 30