Skip to content

fix(fuzz): prevent path mutation across sequential Rebuild calls (#6398)#7253

Merged
Mzack9999 merged 3 commits intoprojectdiscovery:devfrom
promisingcoder:fix/fuzz-numeric-path-parts
Mar 20, 2026
Merged

fix(fuzz): prevent path mutation across sequential Rebuild calls (#6398)#7253
Mzack9999 merged 3 commits intoprojectdiscovery:devfrom
promisingcoder:fix/fuzz-numeric-path-parts

Conversation

@promisingcoder
Copy link
Copy Markdown
Contributor

@promisingcoder promisingcoder commented Mar 17, 2026

Summary

Fixes #6398 — Fuzzing skips numeric path parts due to shared state mutation across sequential Rebuild calls.

Problem

When fuzzing URLs with numeric path segments (e.g. /api/v2/users), the path parts slice was being mutated in place during iteration. Subsequent Rebuild calls would see the already-modified path, causing numeric segments to be skipped.

Fix

Clone the path parts slice before mutation in Rebuild to prevent cross-call contamination.

Testing

Added unit tests covering numeric path parts fuzzing to verify all segments are properly fuzzed.

/claim #6398

Summary by CodeRabbit

  • Bug Fixes

    • Prevented request-path mutations from affecting path reconstruction so rebuilding during segment-level fuzzing yields consistent, expected paths.
  • Tests

    • Added tests verifying rebuilt paths remain correct when individual segments are fuzzed and the request is cloned or updated.

…jectdiscovery#6398)

retryablehttp.Request.Clone() shares the URL pointer, so when
Rebuild() calls UpdateRelPath() on the cloned request it mutates
q.req.URL.Path. On subsequent Rebuild() calls the original path
segments are wrong, causing corrupted fuzz requests (especially
visible with numeric path parts like /user/55/profile).

Snapshot the original path during Parse() and use the snapshot in
Rebuild() instead of reading from the mutable request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@auto-assign auto-assign bot requested a review from Mzack9999 March 17, 2026 12:32
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bce384c7-d2e5-460a-82e9-50f23fbae34d

📥 Commits

Reviewing files that changed from the base of the PR and between 7a2535b and ae64ef8.

📒 Files selected for processing (1)
  • pkg/fuzz/component/path_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/fuzz/component/path_test.go

Walkthrough

Adds an originalPath snapshot to the Path component; Parse captures the request path into it, Rebuild uses that snapshot to derive original segments, and Clone propagates originalPath. A test ensures Rebuild does not mutate the original path when fuzzing segments.

Changes

Cohort / File(s) Summary
Path Component State Management
pkg/fuzz/component/path.go
Add originalPath string to Path; Parse snapshots req.Path into originalPath; Rebuild computes original segments from originalPath (preventing rebuilds from being affected by request mutations); Clone copies originalPath.
Path Component Testing
pkg/fuzz/component/path_test.go
Add TestPathComponent_RebuildDoesNotMutateOriginal which fuzzes each path segment individually, calls Rebuild(), and asserts the expected rebuilt paths exist (verifies original path is preserved across rebuilds).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

I nibble at paths with careful paws,
I hide the trail to hold its cause,
Fuzzed bits wander, then drift away,
The original waits to greet the day. 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the fix: preventing path mutation across sequential Rebuild calls, which directly addresses the shared state issue in fuzzing.
Linked Issues check ✅ Passed The PR implementation matches issue #6398 requirements: it snapshots the original path and ensures Rebuild uses unmodified path data, preventing numeric segments from being skipped during sequential fuzzing iterations.
Out of Scope Changes check ✅ Passed All changes are scoped to the path component fuzzing logic: storing originalPath, using it in Rebuild, and propagating it in Clone; no unrelated modifications are present.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
pkg/fuzz/component/path_test.go (1)

83-124: Good regression test; consider adding explicit mutation check.

The test correctly simulates single-mode fuzzing and validates that all three segments (including the numeric 55) are properly fuzzed. The use of require.Contains handles Go's non-deterministic map iteration order.

However, the test name promises "RebuildDoesNotMutateOriginal" but only verifies fuzzing works—it doesn't explicitly assert the original wasn't mutated. Consider adding a direct verification for stronger coverage.

📝 Optional: Add explicit mutation check
 	// All three segments must have been fuzzed
 	require.Len(t, fuzzedPaths, 3, "expected 3 fuzzed paths for 3 segments")

+	// Verify original request path was not mutated
+	require.Equal(t, "/user/55/profile", req.URL.Path, "original request path should not be mutated")
+
 	// Verify that each segment was individually fuzzed
 	require.Contains(t, fuzzedPaths, "/user FUZZED/55/profile")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/component/path_test.go` around lines 83 - 124, Test name promises
the original Path isn't mutated but currently only checks fuzzed outputs;
capture the original segment values before fuzzing (using path.Iterate into a
snapshot map or reading from req), run the existing fuzzing loop, then assert
the snapshot still equals the current path state by re-iterating path.Iterate or
calling path.Parse(req) and comparing segment values; reference
TestPathComponent_RebuildDoesNotMutateOriginal, the path variable, segments map,
SetValue, Rebuild and Iterate to locate where to take the snapshot and where to
compare afterward.
pkg/fuzz/component/path.go (1)

144-149: Clone implementation is correct; consider documenting context choice.

The Clone method correctly propagates originalPath and clones the value. Using context.Background() is consistent with Rebuild() (line 136), but it discards any context attached to the original request (e.g., deadlines, tracing). If this is intentional for isolation purposes, a brief comment would clarify the design choice.

📝 Optional: Add clarifying comment
 // Clones current state to a new component
 func (q *Path) Clone() Component {
 	return &Path{
 		value:        q.value.Clone(),
 		originalPath: q.originalPath,
+		// Use Background context for isolation; cloned components
+		// operate independently of the original request's lifecycle.
 		req:          q.req.Clone(context.Background()),
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/fuzz/component/path.go` around lines 144 - 149, Add a brief comment above
Path.Clone explaining the deliberate use of context.Background() when calling
q.req.Clone(context.Background()), referencing the Path.Clone method and the
Rebuild() behavior for consistency; state that this intentionally discards any
caller deadlines/tracing to ensure isolation (or alternatively note how to
preserve the original request context if that was intended), so future readers
understand the tradeoff.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/fuzz/component/path_test.go`:
- Around line 83-124: Test name promises the original Path isn't mutated but
currently only checks fuzzed outputs; capture the original segment values before
fuzzing (using path.Iterate into a snapshot map or reading from req), run the
existing fuzzing loop, then assert the snapshot still equals the current path
state by re-iterating path.Iterate or calling path.Parse(req) and comparing
segment values; reference TestPathComponent_RebuildDoesNotMutateOriginal, the
path variable, segments map, SetValue, Rebuild and Iterate to locate where to
take the snapshot and where to compare afterward.

In `@pkg/fuzz/component/path.go`:
- Around line 144-149: Add a brief comment above Path.Clone explaining the
deliberate use of context.Background() when calling
q.req.Clone(context.Background()), referencing the Path.Clone method and the
Rebuild() behavior for consistency; state that this intentionally discards any
caller deadlines/tracing to ensure isolation (or alternatively note how to
preserve the original request context if that was intended), so future readers
understand the tradeoff.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9a6dd940-a73c-40d7-b03d-a51678676f63

📥 Commits

Reviewing files that changed from the base of the PR and between 3497738 and 3f8f831.

📒 Files selected for processing (2)
  • pkg/fuzz/component/path.go
  • pkg/fuzz/component/path_test.go

Made-with: Cursor

# Conflicts:
#	pkg/fuzz/component/path.go
@neo-by-projectdiscovery-dev
Copy link
Copy Markdown

neo-by-projectdiscovery-dev bot commented Mar 20, 2026

Neo - PR Security Review

Caution

Review could not be completed

Review could not be completed. Please retry with @pdneo review.

Suggestion: Try again with @pdneo review.

Comment @pdneo help for available commands.

@Mzack9999 Mzack9999 merged commit 67e8806 into projectdiscovery:dev Mar 20, 2026
20 of 21 checks passed
@promisingcoder
Copy link
Copy Markdown
Contributor Author

/claim #6398

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Fuzzing templates skips numeric path parts

2 participants