diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml new file mode 100644 index 0000000..36221b6 --- /dev/null +++ b/.github/workflows/fuzz.yml @@ -0,0 +1,77 @@ +name: fuzz + +# Coverage-guided fuzzing of the untrusted-input parsers (Jazzer, Apache-2.0). +# +# - Pull requests / pushes: run each Jazzer @FuzzTest in fuzzing mode for a +# short, bounded budget (maxDuration = 60s per target, set on the annotation) +# so the job stays cheap. Newly discovered crashers fail the build. +# - Nightly (cron): a longer budget for deeper exploration. +# +# Regression replay of the seed corpus also runs as part of the normal +# `mvn test` build (JAZZER_FUZZ unset) and needs no libFuzzer driver. + +on: + pull_request: + paths: + - 'src/**' + - 'pom.xml' + - '.github/workflows/fuzz.yml' + push: + branches: [ main ] + paths: + - 'src/**' + - 'pom.xml' + - '.github/workflows/fuzz.yml' + schedule: + - cron: '0 3 * * *' # nightly deep run + workflow_dispatch: {} + +permissions: + contents: read + +concurrency: + group: fuzz-${{ github.ref }} + cancel-in-progress: true + +jobs: + fuzz: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - ArtifactTokenParserFuzzTest + - DocumentValidationFuzzTest + - TenantClaimsFuzzTest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + + - name: Set up JDK 21 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 + with: + distribution: temurin + java-version: '21' + cache: maven + + # Nightly scheduled runs fuzz longer; PR/push runs use the 60s annotation budget. + - name: Fuzz ${{ matrix.target }} + env: + JAZZER_FUZZ: '1' + run: | + EXTRA="" + if [ "${{ github.event_name }}" = "schedule" ]; then + # Deeper nightly exploration (10 min per target). + EXTRA="-Djazzer.max_total_time=600" + fi + mvn -B -ntp -Dtest='${{ matrix.target }}' $EXTRA test + + - name: Upload crash reproducers + if: failure() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: fuzz-findings-${{ matrix.target }} + path: | + **/crash-* + **/timeout-* + **/oom-* + if-no-files-found: ignore diff --git a/.gitignore b/.gitignore index 8eb4e32..2059ecb 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,9 @@ certs/ *.crt *.cer *.jks + +# Jazzer generated corpus +.cifuzz-corpus/ + +# CodeGraph local index +.codegraph/ diff --git a/docs/fuzzing.md b/docs/fuzzing.md new file mode 100644 index 0000000..33da746 --- /dev/null +++ b/docs/fuzzing.md @@ -0,0 +1,68 @@ +# Fuzzing + +Coverage-guided fuzzing of the viewer's untrusted-input parsers, using +[Jazzer](https://github.com/CodeIntelligenceTesting/jazzer) (Apache-2.0) +integrated with JUnit 5 (`jazzer-junit`). + +## Research basis + +The seed-corpus and bounded coverage-guided strategy is grounded in Marcel +Böhme, Van-Thuan Pham, and Abhik Roychoudhury, “Coverage-based Greybox +Fuzzing as Markov Chain,” *Proceedings of the 2016 ACM SIGSAC Conference on +Computer and Communications Security (CCS '16)*, pp. 1032–1043, +doi:10.1145/2976749.2978428. The preserved source PDF is +`docs/papers/boehme-2016-coverage-based-greybox-fuzzing-as-markov-chain.pdf` +(SHA-256 `fb3a7a74280659f86f83a934cae7bd35660e4c699dbd1dbd6825834bfe132151`). +Its core result motivates retaining low-frequency-path crash seeds as durable +regressions while giving scheduled fuzzing a longer exploration budget than +per-PR fuzzing. + +## Targets + +The surfaces were selected with CodeGraph as the highest-value untrusted-input +boundaries (attacker-controlled tokens, filenames, and request headers). Each +harness asserts that arbitrary input only ever produces the *documented* +failure mode -- never an unhandled runtime exception. + +| Harness | Surface under test | Invariant | +| --- | --- | --- | +| `ArtifactTokenParserFuzzTest` | `ArtifactLinkService.verifyReadToken` -- splits, HMAC-verifies, Base64URL-decodes and parses 10 token fields (UUID, epoch `Instant`s). Forges valid signatures to reach the deep parse path. | Only `ArtifactTokenException` (a mapped 4xx) escapes. | +| `DocumentValidationFuzzTest` | `DefaultDocumentValidationService.validateOrThrow` -- filename/extension parsing + policy-override headers. | Only `IllegalArgumentException` (incl. `UnsupportedDocumentFormatException`) escapes. | +| `TenantClaimsFuzzTest` | `TenantContext.fromHeaders` + `TenantAccessService.require` -- tenant/subject/permission headers and the signed-claims timestamp/signature. | `fromHeaders` never throws; `require` only fails with `ResponseStatusException`. | + +Seed corpora live under +`src/test/resources/com/clearfolio/viewer/fuzz/Inputs//`. + +## How it runs + +- **Normal build (`mvn test`)**: the `@FuzzTest` methods run in *regression* + mode -- they replay the seed corpus (and any committed crash reproducers) as + fast, deterministic unit tests. No libFuzzer driver required, so this works on + every developer platform and keeps the default build cheap. + +- **Fuzzing mode (`JAZZER_FUZZ=1`)**: libFuzzer drives each target for the + bounded budget declared on its `@FuzzTest(maxDuration = "60s")` annotation. + +```bash +# Regression replay of all fuzz seeds (part of the normal suite): +mvn test -Dtest='*FuzzTest' + +# Actively fuzz a single target for its bounded budget: +JAZZER_FUZZ=1 mvn test -Dtest='ArtifactTokenParserFuzzTest' +``` + +## CI + +`.github/workflows/fuzz.yml` fuzzes each target for 60s on pull requests and +pushes to `main`, and for 10 minutes per target nightly. A newly discovered +crasher fails the job; the reproducer is uploaded as a build artifact. Commit +the reproducer into the harness's `Inputs//` directory to turn it into a +permanent regression test. + +## Findings + +The initial harness immediately surfaced an unhandled `java.time.DateTimeException` +in the artifact-token parser: `Instant.ofEpochSecond(Long.parseLong(...))` threw +on an out-of-range (but syntactically valid) epoch second, escaping the +`catch (IllegalArgumentException)` block and turning a would-be 401 into a 500. +Fixed by also catching `DateTimeException` in `ArtifactLinkService.parseAndVerify`. diff --git a/docs/papers/boehme-2016-coverage-based-greybox-fuzzing-as-markov-chain.pdf b/docs/papers/boehme-2016-coverage-based-greybox-fuzzing-as-markov-chain.pdf new file mode 100644 index 0000000..a550049 Binary files /dev/null and b/docs/papers/boehme-2016-coverage-based-greybox-fuzzing-as-markov-chain.pdf differ diff --git a/pom.xml b/pom.xml index 2bd40db..a2bc7dd 100644 --- a/pom.xml +++ b/pom.xml @@ -160,6 +160,14 @@ test + + + com.code-intelligence + jazzer-junit + 0.30.0 + test + + diff --git a/src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java b/src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java index 3b650ff..6e68f49 100644 --- a/src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java +++ b/src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java @@ -6,6 +6,7 @@ import java.security.MessageDigest; import java.security.SecureRandom; import java.time.Clock; +import java.time.DateTimeException; import java.time.Instant; import java.util.Arrays; import java.util.Base64; @@ -357,7 +358,9 @@ private ArtifactTokenClaims parseAndVerify(String token) { Instant.ofEpochSecond(Long.parseLong(decode(parts[8]))), Instant.ofEpochSecond(Long.parseLong(decode(parts[9]))) ); - } catch (IllegalArgumentException ex) { + } catch (IllegalArgumentException | DateTimeException ex) { + // IllegalArgumentException: malformed Base64URL, UUID, or numeric fields. + // DateTimeException: epoch-second value outside the supported Instant range. throw new ArtifactTokenException(HttpStatus.UNAUTHORIZED, "artifact token invalid"); } } diff --git a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java index 5763a4b..2ceb1fe 100644 --- a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java +++ b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java @@ -129,7 +129,11 @@ private String extensionOf(final String fileName) { throw new IllegalArgumentException("File name contains null byte."); } - String normalized = java.nio.file.Path.of(fileName.strip()).getFileName().toString(); + java.nio.file.Path leafName = java.nio.file.Path.of(fileName.strip()).getFileName(); + if (leafName == null) { + return ""; + } + String normalized = leafName.toString(); int lastDot = normalized.lastIndexOf('.'); if (lastDot <= 0 || lastDot == normalized.length() - 1) { return ""; diff --git a/src/test/java/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTest.java b/src/test/java/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTest.java new file mode 100644 index 0000000..af09b16 --- /dev/null +++ b/src/test/java/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTest.java @@ -0,0 +1,99 @@ +package com.clearfolio.viewer.fuzz; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.UUID; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.junit.FuzzTest; + +import com.clearfolio.viewer.artifact.ArtifactLinkService; +import com.clearfolio.viewer.artifact.ArtifactTokenException; +import com.clearfolio.viewer.artifact.InMemoryArtifactStore; + +/** + * Fuzzes {@link ArtifactLinkService}'s artifact-token verification path. + * + *

The token string is fully attacker-controlled (query parameter or bearer + * header). {@link ArtifactLinkService#verifyReadToken} splits it, HMAC-verifies + * it, then Base64URL-decodes and parses ten fields (including a {@link UUID}, + * two {@code Long}s and two {@link java.time.Instant}s). The invariant under + * test: for any input the method must fail with the controlled + * {@link ArtifactTokenException} (mapped to a 4xx) and never leak an + * unhandled runtime exception (which would surface as a 500). + * + *

To exercise the deep decode/parse path -- not just the signature gate -- + * the harness forges structurally valid, correctly-signed tokens using a known + * secret, mirroring {@code ArtifactLinkService.sign}. This is what surfaced the + * previously uncaught {@link java.time.DateTimeException} thrown by + * {@code Instant.ofEpochSecond} on out-of-range epoch seconds. + */ +final class ArtifactTokenParserFuzzTest { + + private static final String SECRET = "clearfolio-fuzz-artifact-secret"; + private static final Base64.Encoder ENCODER = Base64.getUrlEncoder().withoutPadding(); + + private final ArtifactLinkService service = + new ArtifactLinkService(new InMemoryArtifactStore(), SECRET); + + @FuzzTest(maxDuration = "60s") + void verifyReadTokenNeverThrowsUnhandled(FuzzedDataProvider data) { + boolean forgeSignature = data.consumeBoolean(); + UUID routeDocId = new UUID(data.consumeLong(), data.consumeLong()); + + String token; + if (forgeSignature) { + // Build a token with a valid HMAC so parsing runs to completion. + String version = data.consumeBoolean() ? "v1" : data.consumeString(8); + UUID docId = new UUID(data.consumeLong(), data.consumeLong()); + String[] fields = { + version, + data.consumeString(24), // tokenId + data.consumeString(24), // tenantId + data.consumeString(24), // subjectId + data.consumeBoolean() ? docId.toString() + : data.consumeString(40), // docId (valid UUID or garbage) + data.consumeString(24), // scope + data.consumeString(24), // purpose + data.consumeString(64), // artifactChecksum + Long.toString(data.consumeLong()), // issuedAt epoch seconds + Long.toString(data.consumeLong()), // expiresAt epoch seconds + }; + token = sign(fields); + } else { + // Fully arbitrary token: exercises the split + signature-gate path. + token = data.consumeRemainingAsString(); + } + + try { + service.verifyReadToken(routeDocId, null, new byte[0], token); + } catch (ArtifactTokenException expected) { + // Controlled failure -> 4xx. Correct behavior for hostile input. + } + } + + private static String sign(String[] fields) { + StringBuilder payload = new StringBuilder(); + for (int i = 0; i < fields.length; i++) { + if (i > 0) { + payload.append('.'); + } + payload.append(ENCODER.encodeToString(fields[i].getBytes(StandardCharsets.UTF_8))); + } + String signature = hmac(payload.toString()); + return payload + "." + signature; + } + + private static String hmac(String payload) { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); + return ENCODER.encodeToString(mac.doFinal(payload.getBytes(StandardCharsets.UTF_8))); + } catch (java.security.GeneralSecurityException ex) { + throw new IllegalStateException("test HMAC failed", ex); + } + } +} diff --git a/src/test/java/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTest.java b/src/test/java/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTest.java new file mode 100644 index 0000000..7598632 --- /dev/null +++ b/src/test/java/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTest.java @@ -0,0 +1,53 @@ +package com.clearfolio.viewer.fuzz; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.junit.FuzzTest; + +import com.clearfolio.viewer.config.ConversionProperties; +import com.clearfolio.viewer.controller.InMemoryMultipartFile; +import com.clearfolio.viewer.service.DefaultDocumentValidationService; +import com.clearfolio.viewer.service.PolicyOverrideRequest; + +/** + * Fuzzes {@link DefaultDocumentValidationService#validateOrThrow}, the upload + * gate that runs before any conversion work. + * + *

Every input is untrusted: the original filename (parsed for its + * extension), the raw file bytes, and the three policy-override HTTP headers. + * The documented contract is that invalid uploads are rejected with an + * {@link IllegalArgumentException} (its subclass + * {@code UnsupportedDocumentFormatException} included) -- so the invariant is + * that no other throwable ever escapes for arbitrary input. + */ +final class DocumentValidationFuzzTest { + + private final DefaultDocumentValidationService validator = createValidator(); + + private static DefaultDocumentValidationService createValidator() { + ConversionProperties properties = new ConversionProperties(); + properties.setPolicyOverrideSecret("fuzz-test-secret"); + return new DefaultDocumentValidationService(properties); + } + + @FuzzTest(maxDuration = "60s") + void validateOrThrowOnlyRejectsWithIllegalArgument(FuzzedDataProvider data) { + String filename = data.consumeBoolean() ? null : data.consumeString(64); + String contentType = data.consumeBoolean() ? null : data.consumeString(32); + byte[] content = data.consumeBytes(data.consumeInt(0, 4096)); + + String policyOverride = data.consumeBoolean() ? null : data.consumeString(16); + String approvalToken = data.consumeBoolean() ? null : data.consumeString(32); + String approverId = data.consumeRemainingAsString(); + + InMemoryMultipartFile file = + new InMemoryMultipartFile("file", filename, contentType, content); + PolicyOverrideRequest override = + PolicyOverrideRequest.of(policyOverride, approvalToken, approverId); + + try { + validator.validateOrThrow(file, override); + } catch (IllegalArgumentException expected) { + // Documented rejection path (covers UnsupportedDocumentFormatException). + } + } +} diff --git a/src/test/java/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTest.java b/src/test/java/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTest.java new file mode 100644 index 0000000..c44c6e0 --- /dev/null +++ b/src/test/java/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTest.java @@ -0,0 +1,61 @@ +package com.clearfolio.viewer.fuzz; + +import java.util.Optional; + +import org.springframework.http.HttpHeaders; +import org.springframework.web.server.ResponseStatusException; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.junit.FuzzTest; + +import com.clearfolio.viewer.auth.TenantAccessService; +import com.clearfolio.viewer.auth.TenantContext; +import com.clearfolio.viewer.auth.TenantPermissions; + +/** + * Fuzzes tenant-claim parsing from untrusted request headers. + * + *

{@link TenantContext#fromHeaders} normalizes the tenant/subject/permission + * headers, and {@link TenantAccessService#require} additionally parses the + * signed-claims {@code Issued-At} timestamp and compares an HMAC signature. + * Invariants under test: + *

    + *
  • {@code fromHeaders} never throws for any header combination; and
  • + *
  • {@code require} only ever fails with {@link ResponseStatusException} + * (never an unhandled {@link NumberFormatException} from the timestamp or + * any other runtime exception).
  • + *
+ */ +final class TenantClaimsFuzzTest { + + private final TenantAccessService accessService = + new TenantAccessService("clearfolio-fuzz-claims-secret", 300L); + + @FuzzTest(maxDuration = "60s") + void headerClaimsParsingIsRobust(FuzzedDataProvider data) { + HttpHeaders headers = new HttpHeaders(); + addFuzzedHeader(headers, TenantContext.TENANT_ID_HEADER, data); + addFuzzedHeader(headers, TenantContext.SUBJECT_ID_HEADER, data); + addFuzzedHeader(headers, TenantContext.PERMISSIONS_HEADER, data); + addFuzzedHeader(headers, TenantContext.CLAIMS_ISSUED_AT_HEADER, data); + addFuzzedHeader(headers, TenantContext.CLAIMS_SIGNATURE_HEADER, data); + + // Must never throw: pure normalization of arbitrary header values. + Optional context = TenantContext.fromHeaders(headers); + if (context.isPresent()) { + context.get().canonicalPermissions(); + } + + try { + accessService.require(headers, TenantPermissions.JOB_CREATE); + } catch (ResponseStatusException expected) { + // Controlled 4xx rejection for missing/invalid/forged claims. + } + } + + private static void addFuzzedHeader(HttpHeaders headers, String name, FuzzedDataProvider data) { + if (data.consumeBoolean()) { + headers.add(name, data.consumeString(48)); + } + } +} diff --git a/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java b/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java index 98a89ac..a7f21b8 100644 --- a/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java +++ b/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java @@ -268,6 +268,27 @@ void rejectsMissingExtension() { assertEquals("File extension is required.", ex.getMessage()); } + @Test + void rejectsFilesystemRootFilenameWithDocumentedValidationError() { + ConversionProperties conversionProperties = new ConversionProperties(); + DefaultDocumentValidationService validationService = + new DefaultDocumentValidationService(conversionProperties); + + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> validationService.validateOrThrow( + new MockMultipartFile( + "file", + "/", + "application/octet-stream", + new byte[] {1} + ) + ) + ); + + assertEquals("File extension is required.", ex.getMessage()); + } + @Test void rejectsBlankFilenameOrMissingName() { ConversionProperties conversionProperties = new ConversionProperties(); diff --git a/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/regression-datetime-epoch-overflow b/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/regression-datetime-epoch-overflow new file mode 100644 index 0000000..61a3f41 Binary files /dev/null and b/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/regression-datetime-epoch-overflow differ diff --git a/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/seed-forged b/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/seed-forged new file mode 100644 index 0000000..b0bf93d Binary files /dev/null and b/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/seed-forged differ diff --git a/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/seed-garbage b/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/seed-garbage new file mode 100644 index 0000000..2d089b5 --- /dev/null +++ b/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/seed-garbage @@ -0,0 +1 @@ +not-a-token \ No newline at end of file diff --git a/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/seed-raw-tenparts b/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/seed-raw-tenparts new file mode 100644 index 0000000..7869c54 --- /dev/null +++ b/src/test/resources/com/clearfolio/viewer/fuzz/ArtifactTokenParserFuzzTestInputs/verifyReadTokenNeverThrowsUnhandled/seed-raw-tenparts @@ -0,0 +1 @@ +v1.aaa.bbb.ccc.ddd.eee.fff.ggg.hhh.iii.sig \ No newline at end of file diff --git a/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/crash-f7c4f9deab4174f1cb363d9882a302e303b91070 b/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/crash-f7c4f9deab4174f1cb363d9882a302e303b91070 new file mode 100644 index 0000000..775d99b Binary files /dev/null and b/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/crash-f7c4f9deab4174f1cb363d9882a302e303b91070 differ diff --git a/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/seed-blocked-override b/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/seed-blocked-override new file mode 100644 index 0000000..b63f2fa Binary files /dev/null and b/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/seed-blocked-override differ diff --git a/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/seed-noext b/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/seed-noext new file mode 100644 index 0000000..34dea4f --- /dev/null +++ b/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/seed-noext @@ -0,0 +1 @@ +noext \ No newline at end of file diff --git a/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/seed-pdf b/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/seed-pdf new file mode 100644 index 0000000..2067328 Binary files /dev/null and b/src/test/resources/com/clearfolio/viewer/fuzz/DocumentValidationFuzzTestInputs/validateOrThrowOnlyRejectsWithIllegalArgument/seed-pdf differ diff --git a/src/test/resources/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTestInputs/headerClaimsParsingIsRobust/seed-bad-timestamp b/src/test/resources/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTestInputs/headerClaimsParsingIsRobust/seed-bad-timestamp new file mode 100644 index 0000000..f15c2c7 Binary files /dev/null and b/src/test/resources/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTestInputs/headerClaimsParsingIsRobust/seed-bad-timestamp differ diff --git a/src/test/resources/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTestInputs/headerClaimsParsingIsRobust/seed-empty b/src/test/resources/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTestInputs/headerClaimsParsingIsRobust/seed-empty new file mode 100644 index 0000000..40b450d Binary files /dev/null and b/src/test/resources/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTestInputs/headerClaimsParsingIsRobust/seed-empty differ diff --git a/src/test/resources/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTestInputs/headerClaimsParsingIsRobust/seed-full b/src/test/resources/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTestInputs/headerClaimsParsingIsRobust/seed-full new file mode 100644 index 0000000..31446a7 --- /dev/null +++ b/src/test/resources/com/clearfolio/viewer/fuzz/TenantClaimsFuzzTestInputs/headerClaimsParsingIsRobust/seed-full @@ -0,0 +1 @@ +acmealicejob:create100sig \ No newline at end of file