Skip to content

feat(test): add fuzz tests for attacker-reachable code paths and nightly CI#665

Merged
wizzomafizzo merged 3 commits into
mainfrom
fuzz-tests-and-utf8-fixes
Apr 14, 2026
Merged

feat(test): add fuzz tests for attacker-reachable code paths and nightly CI#665
wizzomafizzo merged 3 commits into
mainfrom
fuzz-tests-and-utf8-fixes

Conversation

@wizzomafizzo

@wizzomafizzo wizzomafizzo commented Apr 14, 2026

Copy link
Copy Markdown
Member

Summary

  • Add 12 new fuzz tests covering attacker-reachable parsing code (JSON-RPC entry, encrypted sessions, PAKE handshake, MQTT handler, advargs parser, mapping matchers, MQTT path parsing, ZapLink responses, run handler, RPC ID unmarshaling)
  • Expand task fuzz from 6 to 36 targets with continue-on-failure loop
  • Add nightly fuzz CI workflow (2 min/target, creates GitHub issue on crash)
  • Fix 8 invalid UTF-8 passthrough bugs found by fuzzing across virtualpath, helpers, tags, and RPCID
  • Include fuzz corpus regression entries for all fixed bugs

Summary by CodeRabbit

  • New Features

    • Added an automated fuzz-testing workflow with scheduled and manual runs, failure artifact capture, and failure reporting.
  • Testing

    • Greatly expanded fuzz coverage across API, encryption, MQTT, parsing, mapping, zapscript, and reader components; added new corpus seeds.
    • Enhanced task runner to execute a broader set of fuzz targets.
  • Bug Fixes

    • Strengthened UTF-8 input validation across filename, path, URI, and virtual-path handling.
  • Documentation

    • Updated fuzz testing instructions and examples.

…tly CI

Add 12 new fuzz tests targeting security-relevant parsing code: JSON-RPC
entry point, encrypted session establishment, PAKE handshake, MQTT message
handling, advanced args parsing, mapping matchers, MQTT path/protocol
parsing, ZapLink response parsing, run handler, and RPC ID unmarshaling.

Expand `task fuzz` from 6 to 36 targets (17 previously missing existing
tests + 12 new) using a continue-on-failure loop so all targets run even
when one crashes.

Add nightly CI workflow (.github/workflows/fuzz.yml) that runs all fuzz
targets for 2 minutes each, uploads crash corpus as artifacts, and creates
a GitHub issue on failure.

Fix invalid UTF-8 passthrough bugs found by fuzzing:
- virtualpath.ParseVirtualPathStr: reject invalid UTF-8 in ID/name
- helpers.getPathExt/getPathBase/getPathDir: reject invalid UTF-8 input
- helpers.DecodeURIIfNeeded/FilenameFromPath: reject invalid UTF-8 input
- tags.extractSpecialPatterns: reject invalid UTF-8 filenames
- models.RPCID.UnmarshalJSON: reject empty input (lossy round-trip)

Include fuzz corpus regression entries for all fixed bugs.
@coderabbitai

coderabbitai Bot commented Apr 14, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ebc1415c-f3b2-41b5-ba07-fa11b71faa8e

📥 Commits

Reviewing files that changed from the base of the PR and between ec0d085 and 2b419c4.

📒 Files selected for processing (1)
  • pkg/testing/FUZZ_TESTING.md
✅ Files skipped from review due to trivial changes (1)
  • pkg/testing/FUZZ_TESTING.md

📝 Walkthrough

Walkthrough

Adds a new GitHub Actions fuzzing workflow and expands in-repo fuzz infrastructure: many new Go fuzz tests across API, middleware, helpers, readers, service, and zapscript packages; UTF-8 validation hardening in path/URI helpers; new fuzz corpus seeds; and Taskfile/CI/task docs updates to run the extended fuzz targets.

Changes

Cohort / File(s) Summary
CI/CD & Task Infrastructure
\.github/workflows/fuzz.yml, AGENTS.md, Taskfile.dist.yml
New daily/manual GitHub Actions workflow for fuzzing with artifact upload and issue creation on failure; actionlint permission note added to AGENTS.md; Taskfile replaced fixed fuzz invocations with a loop running an expanded enumerated list of fuzz targets.
API & Middleware Fuzz Tests
pkg/api/methods/run_fuzz_test.go, pkg/api/server_fuzz_test.go, pkg/api/pairing_fuzz_test.go, pkg/api/middleware/encryption_fuzz_test.go
Added fuzz targets exercising HandleRun, processRequestObject, pairing session start/startSession, and encryption session establishment with seeded cases and property/determinism assertions.
RPC Model Fuzzing
pkg/api/models/rpcid.go, pkg/api/models/rpcid_fuzz_test.go
RPCID.UnmarshalJSON now rejects empty/whitespace-only input; added fuzz test validating rejection properties and JSON round-trip determinism.
Path & URI Helpers (UTF‑8 hardening)
pkg/database/tags/filename_parser.go, pkg/helpers/paths.go, pkg/helpers/uris.go, pkg/helpers/virtualpath/virtualpath.go
Introduced utf8 validity checks and early returns; added decodePathComponent enforcing UTF‑8 and returning explicit errors for invalid byte sequences.
Helper Fuzz Corpus Seeds
pkg/database/tags/testdata/fuzz/..., pkg/helpers/testdata/fuzz/..., pkg/helpers/virtualpath/testdata/fuzz/...
Added multiple corpus seed files with edge-case/invalid-UTF8 byte sequences to guide several fuzz targets.
Helpers Fuzz Test Adjustments
pkg/helpers/uris_fuzz_test.go
Narrowed expectation: equality-only assertion for DecodeURIIfNeeded applies only when input is valid UTF‑8; removed the FuzzParseVirtualPathStr test from this file.
MQTT Reader Fuzz Tests
pkg/readers/mqtt/common_fuzz_test.go, pkg/readers/mqtt/mqtt_fuzz_test.go
Added fuzz targets for ParseMQTTPath and ParseMQTTProtocol with determinism checks, and a message-handler fuzz that validates token emission/metadata.
Service & ZapScript Fuzz Tests
pkg/service/mappings_fuzz_test.go, pkg/zapscript/advargs/advargs_fuzz_test.go, pkg/zapscript/zaplinks_fuzz_test.go
Added fuzz tests for mapping matchers, advargs parsing with deep-equality checks, and well-known fetch handling with a mock HTTP doer and determinism assertions.
Docs / Testing Instructions
pkg/testing/FUZZ_TESTING.md, pkg/testing/README.md
Updated documented fuzz-run package paths to the narrower ./pkg/helpers/virtualpath/.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰
I hop through bytes with tiny paws at play,
Seeding edges, chasing bugs away.
UTF‑8 fences now keep the paths aligned,
Fuzz seeds scattered—no crash left behind.
Celebrate the run, a rabbit’s tidy day!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main changes: adding fuzz tests for attacker-reachable code paths and nightly CI infrastructure.
Docstring Coverage ✅ Passed Docstring coverage is 95.65% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fuzz-tests-and-utf8-fixes

Comment @coderabbitai help to get the list of available commands and usage tips.

@sentry

sentry Bot commented Apr 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.10345% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/helpers/virtualpath/virtualpath.go 86.66% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
pkg/zapscript/advargs/advargs_fuzz_test.go (1)

81-98: Consider: Verify parsed values match, not just error presence.

The determinism check only compares (err == nil) between runs. For stronger guarantees, consider also verifying that successfully parsed structs have identical field values. This would catch non-deterministic parsing that produces different results on success.

💡 Optional: Add value comparison for successful parses
+import "reflect"
+
 // Determinism: same input must produce same result for each dest type
 var launchArgs2 zapscript.LaunchRandomArgs
 err1b := Parse(raw, &launchArgs2, ctx)
 if (err1 == nil) != (err1b == nil) {
     t.Errorf("non-deterministic error for LaunchRandomArgs with key=%q value=%q", key, value)
 }
+if err1 == nil && err1b == nil && !reflect.DeepEqual(launchArgs, launchArgs2) {
+    t.Errorf("non-deterministic result for LaunchRandomArgs with key=%q value=%q", key, value)
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/zapscript/advargs/advargs_fuzz_test.go` around lines 81 - 98, The
determinism check only compares error presence; update the tests so when Parse
succeeds for a type (e.g., when err1 == nil and err1b == nil for
LaunchRandomArgs, or similarly for PlaylistArgs and GlobalArgs) you also
deep-compare the two parsed structs (launchArgs vs launchArgs2, playlistArgs vs
playlistArgs2, globalArgs vs globalArgs2) and fail the test if they differ; use
a Go deep-equal check (or reflect.DeepEqual) to compare fields and include the
key/value in the error message for identification.
pkg/zapscript/zaplinks_fuzz_test.go (1)

106-110: Consider comparing Trusted slice for complete determinism validation.

The determinism check compares ZapScript and Auth but not the Trusted slice. While JSON slice parsing is typically deterministic, adding a length/content check would provide complete coverage.

♻️ Optional: Add Trusted slice comparison
 		if err == nil && err2 == nil {
 			if wk.ZapScript != wk2.ZapScript || wk.Auth != wk2.Auth {
 				t.Errorf("non-deterministic result for body %q", body)
 			}
+			if len(wk.Trusted) != len(wk2.Trusted) {
+				t.Errorf("non-deterministic Trusted length for body %q", body)
+			}
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/zapscript/zaplinks_fuzz_test.go` around lines 106 - 110, The determinism
check currently compares wk.ZapScript and wk.Auth but omits wk.Trusted; update
the test inside the fuzz loop to also validate wk.Trusted vs wk2.Trusted by
checking lengths and element-wise equality (or use reflect.DeepEqual) so the
condition flags non-deterministic results when Trusted differs; reference the
wk.Trusted and wk2.Trusted fields in the same if-block that checks
ZapScript/Auth and include a clear failure message identifying Trusted
mismatches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Taskfile.dist.yml`:
- Around line 97-108: Remove the duplicate fuzz test named
FuzzParseVirtualPathStr from pkg/helpers/uris_fuzz_test.go (the one that targets
the virtualpath implementation) so the virtualpath function is only fuzzed by
pkg/helpers/virtualpath/virtualpath_fuzz_test.go; delete the test function
FuzzParseVirtualPathStr and its seed data in that file and update any references
(e.g., Taskfile entries listing "FuzzParseVirtualPathStr ./pkg/helpers") to
point only to the virtualpath fuzz test or remove the redundant listing so there
is a single canonical fuzz target for the virtualpath package.

---

Nitpick comments:
In `@pkg/zapscript/advargs/advargs_fuzz_test.go`:
- Around line 81-98: The determinism check only compares error presence; update
the tests so when Parse succeeds for a type (e.g., when err1 == nil and err1b ==
nil for LaunchRandomArgs, or similarly for PlaylistArgs and GlobalArgs) you also
deep-compare the two parsed structs (launchArgs vs launchArgs2, playlistArgs vs
playlistArgs2, globalArgs vs globalArgs2) and fail the test if they differ; use
a Go deep-equal check (or reflect.DeepEqual) to compare fields and include the
key/value in the error message for identification.

In `@pkg/zapscript/zaplinks_fuzz_test.go`:
- Around line 106-110: The determinism check currently compares wk.ZapScript and
wk.Auth but omits wk.Trusted; update the test inside the fuzz loop to also
validate wk.Trusted vs wk2.Trusted by checking lengths and element-wise equality
(or use reflect.DeepEqual) so the condition flags non-deterministic results when
Trusted differs; reference the wk.Trusted and wk2.Trusted fields in the same
if-block that checks ZapScript/Auth and include a clear failure message
identifying Trusted mismatches.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 919c117d-0ea2-47bb-b0df-ab7110c71ee4

📥 Commits

Reviewing files that changed from the base of the PR and between 6c4f3ef and 8781c87.

📒 Files selected for processing (26)
  • .github/workflows/fuzz.yml
  • AGENTS.md
  • Taskfile.dist.yml
  • pkg/api/methods/run_fuzz_test.go
  • pkg/api/middleware/encryption_fuzz_test.go
  • pkg/api/models/rpcid.go
  • pkg/api/models/rpcid_fuzz_test.go
  • pkg/api/pairing_fuzz_test.go
  • pkg/api/server_fuzz_test.go
  • pkg/database/tags/filename_parser.go
  • pkg/database/tags/testdata/fuzz/FuzzExtractSpecialPatterns/98fce631eb9c5dd5
  • pkg/helpers/paths.go
  • pkg/helpers/testdata/fuzz/FuzzDecodeURIIfNeeded/ba0160f680fd7fcb
  • pkg/helpers/testdata/fuzz/FuzzFilenameFromPath/5f644fdcef1c73a8
  • pkg/helpers/testdata/fuzz/FuzzGetPathBase/718f6ecdbb5c1f5b
  • pkg/helpers/testdata/fuzz/FuzzGetPathDir/e40f64e0e1794357
  • pkg/helpers/testdata/fuzz/FuzzGetPathExt/bf289923489eb280
  • pkg/helpers/testdata/fuzz/FuzzParseVirtualPathStr/a6d9e8e6ad9ae59f
  • pkg/helpers/uris.go
  • pkg/helpers/uris_fuzz_test.go
  • pkg/helpers/virtualpath/virtualpath.go
  • pkg/readers/mqtt/common_fuzz_test.go
  • pkg/readers/mqtt/mqtt_fuzz_test.go
  • pkg/service/mappings_fuzz_test.go
  • pkg/zapscript/advargs/advargs_fuzz_test.go
  • pkg/zapscript/zaplinks_fuzz_test.go

Comment thread Taskfile.dist.yml Outdated
…ecks

Remove redundant FuzzParseVirtualPathStr from pkg/helpers (canonical
copy lives in pkg/helpers/virtualpath), move its corpus file, and
update Taskfile + docs. Add reflect.DeepEqual struct comparison to
advargs fuzz determinism checks and Trusted field comparison to
zaplinks fuzz test.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/testing/FUZZ_TESTING.md (1)

71-80: ⚠️ Potential issue | 🟡 Minor

Keep all FuzzParseVirtualPathStr examples on the same package path.

Line 71 is correct, but Lines 74, 77, and 80 still point to ./pkg/helpers/. For this target, the examples should stay on ./pkg/helpers/virtualpath/ to avoid copy/paste failures.

📘 Suggested doc fix
-# Time-boxed fuzzing (30 seconds)
-go test -fuzz=FuzzParseVirtualPathStr -fuzztime=30s ./pkg/helpers/
+# Time-boxed fuzzing (30 seconds)
+go test -fuzz=FuzzParseVirtualPathStr -fuzztime=30s ./pkg/helpers/virtualpath/

-# Run with more parallel workers (8 cores)
-go test -fuzz=FuzzParseVirtualPathStr -parallel=8 ./pkg/helpers/
+# Run with more parallel workers (8 cores)
+go test -fuzz=FuzzParseVirtualPathStr -parallel=8 ./pkg/helpers/virtualpath/

-# Disable minimization for faster fuzzing
-go test -fuzz=FuzzParseVirtualPathStr -fuzzminimizetime=0 ./pkg/helpers/
+# Disable minimization for faster fuzzing
+go test -fuzz=FuzzParseVirtualPathStr -fuzzminimizetime=0 ./pkg/helpers/virtualpath/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testing/FUZZ_TESTING.md` around lines 71 - 80, Update the fuzzing
examples so all invocations of the FuzzParseVirtualPathStr target use the exact
package path ./pkg/helpers/virtualpath/ instead of ./pkg/helpers/; specifically
adjust the three commands that currently reference ./pkg/helpers/ (the
-fuzztime, -parallel, and -fuzzminimizetime examples) to point to
./pkg/helpers/virtualpath/ so they match the first example and the actual fuzz
target FuzzParseVirtualPathStr.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@pkg/testing/FUZZ_TESTING.md`:
- Around line 71-80: Update the fuzzing examples so all invocations of the
FuzzParseVirtualPathStr target use the exact package path
./pkg/helpers/virtualpath/ instead of ./pkg/helpers/; specifically adjust the
three commands that currently reference ./pkg/helpers/ (the -fuzztime,
-parallel, and -fuzzminimizetime examples) to point to
./pkg/helpers/virtualpath/ so they match the first example and the actual fuzz
target FuzzParseVirtualPathStr.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1b0a0c52-e036-48d1-b8bb-5dd28828a97a

📥 Commits

Reviewing files that changed from the base of the PR and between 8781c87 and ec0d085.

📒 Files selected for processing (7)
  • Taskfile.dist.yml
  • pkg/helpers/uris_fuzz_test.go
  • pkg/helpers/virtualpath/testdata/fuzz/FuzzParseVirtualPathStr/a6d9e8e6ad9ae59f
  • pkg/testing/FUZZ_TESTING.md
  • pkg/testing/README.md
  • pkg/zapscript/advargs/advargs_fuzz_test.go
  • pkg/zapscript/zaplinks_fuzz_test.go
✅ Files skipped from review due to trivial changes (2)
  • pkg/testing/README.md
  • pkg/helpers/virtualpath/testdata/fuzz/FuzzParseVirtualPathStr/a6d9e8e6ad9ae59f
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/helpers/uris_fuzz_test.go
  • Taskfile.dist.yml
  • pkg/zapscript/advargs/advargs_fuzz_test.go
  • pkg/zapscript/zaplinks_fuzz_test.go

Comment thread pkg/helpers/uris.go
@wizzomafizzo
wizzomafizzo merged commit 088e742 into main Apr 14, 2026
11 checks passed
@wizzomafizzo
wizzomafizzo deleted the fuzz-tests-and-utf8-fixes branch April 14, 2026 01:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant