Skip to content

Deduplicate multiformat fuzz HTTP inputs - #7580

Open
Mzack9999 wants to merge 1 commit into
devfrom
4962-fuzz-dedupe
Open

Deduplicate multiformat fuzz HTTP inputs#7580
Mzack9999 wants to merge 1 commit into
devfrom
4962-fuzz-dedupe

Conversation

@Mzack9999

@Mzack9999 Mzack9999 commented Jul 24, 2026

Copy link
Copy Markdown
Member

Summary

  • Add shared request fingerprinting (pkg/input/dedupe) and use it in multiformat HTTP inputs (burp, openapi, swagger, jsonl, yaml) so equivalent requests are skipped during count/iterate
  • Point live DAST server dedupe at the same helper

Closes #4962

Summary by CodeRabbit

  • New Features

    • Added automatic deduplication for HTTP requests during input processing and iteration.
    • Requests are recognized as duplicates despite differences in query parameter order or dynamic headers.
    • Request bodies and stable headers are considered when distinguishing unique requests.
    • Duplicate counts are tracked and reported during input processing.
  • Bug Fixes

    • Prevented repeated equivalent requests from being processed multiple times.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

HTTP request deduplication

Layer / File(s) Summary
Shared request fingerprinting
pkg/input/dedupe/dedupe.go, pkg/input/dedupe/dedupe_test.go
Adds concurrent request deduplication using normalized URLs, filtered headers, HTTP methods, and optional bodies, with tests for equivalent and distinct requests.
Server deduplicator integration
internal/server/dedupe.go
Replaces the server-local hashing implementation with the shared RequestDeduplicator.
HTTP input provider deduplication
pkg/input/provider/http/multiformat.go, pkg/input/provider/http/multiformat_test.go
Deduplicates provider parsing and iteration, tracks duplicate counts, logs removals, and tests JSONL inputs.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant JSONL as JSONL input
  participant Provider as NewHttpInputProvider
  participant Deduper as RequestDeduplicator
  participant Iterate as HttpInputProvider.Iterate
  JSONL->>Provider: parse HTTP requests
  Provider->>Deduper: IsDuplicate(request)
  Deduper-->>Provider: duplicate status
  Provider-->>Iterate: unique requests and dupeCount
  Iterate->>Deduper: IsDuplicate(request)
  Deduper-->>Iterate: duplicate status
  Iterate-->>Provider: unique inputs
Loading

Poem

I’m a rabbit with a tidy request queue,
Sorting paths and query strings too.
Cookies may hop, but bodies stay true,
Duplicate hops become one or two.
Sniff-sniff—clean inputs spring anew!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: deduplicating fuzz HTTP inputs.
Linked Issues check ✅ Passed The PR implements URL deduping for fuzzing by adding shared request fingerprinting and applying it to HTTP input parsing and iteration.
Out of Scope Changes check ✅ Passed The changes stay focused on request deduplication and related tests without introducing unrelated functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 4962-fuzz-dedupe

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (1)
pkg/input/dedupe/dedupe.go (1)

125-142: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Host casing and URL fragment are not normalized.

NormalizeURL preserves the original host casing and fragment. Per RFC 3986 hosts are case-insensitive, and fragments are never sent to the server, so two otherwise-identical requests differing only in host case or fragment will not be deduplicated (a missed-dedup false negative, not a false positive). Low risk since it only reduces dedup effectiveness rather than dropping legitimate requests; consider lowercasing scheme/host and stripping the fragment for more complete normalization.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/input/dedupe/dedupe.go` around lines 125 - 142, Update NormalizeURL to
lowercase the URL scheme and host, and clear the fragment before returning the
normalized string. Preserve the existing query sorting, root-path defaulting,
nil handling, and cloned URL behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/input/dedupe/dedupe.go`:
- Around line 97-122: Update the fingerprint construction in the request hashing
function to delimit every serialized field, including method, normalizedURL,
each sorted header key and value, and body, before computing the SHA-256 hash.
Use an unambiguous separator or length-prefixed encoding consistently so
distinct field boundaries cannot produce the same hash input, while preserving
the existing header ordering and duplicate-detection flow.

---

Nitpick comments:
In `@pkg/input/dedupe/dedupe.go`:
- Around line 125-142: Update NormalizeURL to lowercase the URL scheme and host,
and clear the fragment before returning the normalized string. Preserve the
existing query sorting, root-path defaulting, nil handling, and cloned URL
behavior.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b061612-8e1b-46a7-84de-76f59d1b7a39

📥 Commits

Reviewing files that changed from the base of the PR and between bcf2089 and 593195a.

📒 Files selected for processing (5)
  • internal/server/dedupe.go
  • pkg/input/dedupe/dedupe.go
  • pkg/input/dedupe/dedupe_test.go
  • pkg/input/provider/http/multiformat.go
  • pkg/input/provider/http/multiformat_test.go

Comment on lines +97 to +122
var hashContent strings.Builder
method := "GET"
var body string
var headers mapsutil.OrderedMap[string, string]
if req.Request != nil {
if req.Request.Method != "" {
method = strings.ToUpper(req.Request.Method)
}
body = req.Request.Body
headers = req.Request.Headers
}
hashContent.WriteString(method)
hashContent.WriteString(normalizedURL)

for _, header := range sortedNonDynamicHeaders(headers) {
hashContent.WriteString(header.Key)
hashContent.WriteString(header.Value)
}

if len(body) > 0 {
hashContent.WriteString(body)
}

hash := sha256.Sum256([]byte(hashContent.String()))
return hex.EncodeToString(hash[:]), nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fingerprint fields are concatenated without delimiters — collision risk.

method, normalizedURL, each header key/value, and body are written back-to-back into hashContent with no separators (Lines 108-118). Different (header-key, header-value) pairs can serialize to the identical string (e.g. key="ab", value="c" vs key="a", value="bc"), producing the same SHA-256 fingerprint for genuinely different requests. Since IsDuplicate treats identical fingerprints as duplicates, this can silently drop distinct requests from fuzzing/DAST coverage — the exact failure mode this feature is meant to avoid.

🐛 Proposed fix: add delimiters between fields
 	hashContent.WriteString(method)
+	hashContent.WriteString("\x00")
 	hashContent.WriteString(normalizedURL)

 	for _, header := range sortedNonDynamicHeaders(headers) {
+		hashContent.WriteString("\x00")
 		hashContent.WriteString(header.Key)
+		hashContent.WriteString("\x00")
 		hashContent.WriteString(header.Value)
 	}

 	if len(body) > 0 {
+		hashContent.WriteString("\x00")
 		hashContent.WriteString(body)
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var hashContent strings.Builder
method := "GET"
var body string
var headers mapsutil.OrderedMap[string, string]
if req.Request != nil {
if req.Request.Method != "" {
method = strings.ToUpper(req.Request.Method)
}
body = req.Request.Body
headers = req.Request.Headers
}
hashContent.WriteString(method)
hashContent.WriteString(normalizedURL)
for _, header := range sortedNonDynamicHeaders(headers) {
hashContent.WriteString(header.Key)
hashContent.WriteString(header.Value)
}
if len(body) > 0 {
hashContent.WriteString(body)
}
hash := sha256.Sum256([]byte(hashContent.String()))
return hex.EncodeToString(hash[:]), nil
}
var hashContent strings.Builder
method := "GET"
var body string
var headers mapsutil.OrderedMap[string, string]
if req.Request != nil {
if req.Request.Method != "" {
method = strings.ToUpper(req.Request.Method)
}
body = req.Request.Body
headers = req.Request.Headers
}
hashContent.WriteString(method)
hashContent.WriteString("\x00")
hashContent.WriteString(normalizedURL)
for _, header := range sortedNonDynamicHeaders(headers) {
hashContent.WriteString("\x00")
hashContent.WriteString(header.Key)
hashContent.WriteString("\x00")
hashContent.WriteString(header.Value)
}
if len(body) > 0 {
hashContent.WriteString("\x00")
hashContent.WriteString(body)
}
hash := sha256.Sum256([]byte(hashContent.String()))
return hex.EncodeToString(hash[:]), nil
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/input/dedupe/dedupe.go` around lines 97 - 122, Update the fingerprint
construction in the request hashing function to delimit every serialized field,
including method, normalizedURL, each sorted header key and value, and body,
before computing the SHA-256 hash. Use an unambiguous separator or
length-prefixed encoding consistently so distinct field boundaries cannot
produce the same hash input, while preserving the existing header ordering and
duplicate-detection flow.

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.

url deduping for fuzzing

1 participant