Add Jazzer coverage-guided fuzzing for untrusted-input parsers#118
Conversation
Adds Jazzer (Apache-2.0) JUnit 5 fuzz harnesses for the highest-value untrusted-input surfaces, selected with CodeGraph: - ArtifactTokenParserFuzzTest: fuzzes ArtifactLinkService.verifyReadToken (attacker-controlled HMAC token; forges valid signatures to reach the Base64URL-decode/UUID/epoch-parse path). Invariant: only ArtifactTokenException. - DocumentValidationFuzzTest: fuzzes DefaultDocumentValidationService (filename/extension + policy-override headers). Invariant: only IllegalArgumentException. - TenantClaimsFuzzTest: fuzzes TenantContext.fromHeaders + TenantAccessService.require (tenant/subject/permission + signed-claims timestamp/signature). Invariant: fromHeaders never throws; require only throws ResponseStatusException. Fuzzing immediately surfaced an unhandled java.time.DateTimeException in the token parser: Instant.ofEpochSecond(Long.parseLong(...)) escaped the catch (IllegalArgumentException) block on an out-of-range epoch second, turning a 401 into a 500. Fixed by also catching DateTimeException in ArtifactLinkService.parseAndVerify; the triggering input is committed as a regression seed. Seed corpora, a bounded CI fuzz job (.github/workflows/fuzz.yml: 60s/target on PRs, 10min/target nightly), docs/fuzzing.md, and a foundational coverage-guided fuzzing paper (docs/papers) are included. Regression replay of the seed corpus runs as part of the normal `mvn test` build (348 tests green). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RTAMs4bpSZS77Xe3RQjv9P
|
Current-head salvage is now pushed as Verification evidence:
All previously reported mutable Action references are pinned to immutable SHAs on the current head. |
|
Current-head 7f9ef32 fixes the actual Jazzer finding from job 86649790273. Finding and fix:
Verification:
Please evaluate only current SHA 7f9ef32. |
There was a problem hiding this comment.
Pull request overview
OpenCode model providers were unavailable for this same-head run, but deterministic current-head evidence is clean: coverage evidence passed, peer GitHub Checks are complete, medium-or-higher code-scanning alerts are clear, mergeability is clean, and reviewer threads are resolved or outdated.
Findings
No blocking findings.
Evidence
- Result: APPROVE
- Reason: current-head model-unavailable evidence fallback; coverage, docstring, peer GitHub Checks, code-scanning alerts, mergeability, and review threads were clear for current head.
- Scope:
unsupported - Changed files:
0 - Model-pool outcome:
exhausted - Head SHA:
767855d94834c707278b656d9c9ae1a032283086 - Workflow run: 29213020076
- Workflow attempt: 1
This fallback does not suppress failed checks, medium-or-higher code-scanning alerts, merge conflicts, unresolved reviewer threads, or failed coverage evidence; any of those conditions still publish REQUEST_CHANGES or leave the approval state unchanged.
Summary
Adds Jazzer (Apache-2.0) coverage-guided fuzzing, integrated with JUnit 5, for the viewer's highest-value untrusted-input surfaces. Targets were chosen using CodeGraph to find the boundaries where attacker-controlled bytes are parsed.
Each harness asserts that arbitrary input only ever yields the documented failure mode, never an unhandled runtime exception.
ArtifactTokenParserFuzzTestArtifactLinkService.verifyReadToken— splits + HMAC-verifies + Base64URL-decodes + parses 10 token fields (UUID, epochInstants). Forges valid signatures to reach the deep parse path.ArtifactTokenException(4xx) escapesDocumentValidationFuzzTestDefaultDocumentValidationService.validateOrThrow— filename/extension + policy-override headersIllegalArgumentExceptionescapesTenantClaimsFuzzTestTenantContext.fromHeaders+TenantAccessService.require— tenant/subject/permission + signed-claims timestamp/signaturefromHeadersnever throws;requireonly throwsResponseStatusExceptionBug found and fixed
Fuzzing immediately surfaced an unhandled
java.time.DateTimeExceptionin the artifact-token parser.Instant.ofEpochSecond(Long.parseLong(...))throwsDateTimeException(which is not anIllegalArgumentException) on an out-of-range but syntactically valid epoch second, escaping thecatch (IllegalArgumentException)block inArtifactLinkService.parseAndVerifyand turning a would-be 401 into a 500. Fixed by also catchingDateTimeException; the triggering input is committed as a regression seed (regression-datetime-epoch-overflow).How it runs
mvn test(default):@FuzzTestmethods run in regression mode, replaying the committed seed corpus as fast unit tests — no libFuzzer driver, cross-platform. 348 tests green (was 336).JAZZER_FUZZ=1 mvn test -Dtest=<Harness>: libFuzzer drives each target for its bounded@FuzzTest(maxDuration = "60s")budget.CI
.github/workflows/fuzz.ymlfuzzes each target for 60s on PRs/pushes and 10min/target nightly (bounded to keep cost low). New crashers fail the job and upload the reproducer as an artifact.Notes
docs/papers/(Böhme et al., Coverage-based Greybox Fuzzing as Markov Chain, CCS 2016 — the AFLFast paper) anddocs/fuzzing.md.codegraph init+codegraph explore).🤖 Generated with Claude Code
https://claude.ai/code/session_01RTAMs4bpSZS77Xe3RQjv9P