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

1 participant