Skip to content

Conversation

@ayanrajpoot10
Copy link
Contributor

@ayanrajpoot10 ayanrajpoot10 commented Oct 22, 2025

fixes #2296

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Host header synchronization in unsafe mode to ensure proper header handling.

@auto-assign auto-assign bot requested a review from dogancanbakir October 22, 2025 14:09
@coderabbitai
Copy link

coderabbitai bot commented Oct 22, 2025

Walkthrough

A targeted fix synchronizes the Host header in unsafe mode by explicitly setting both r.Host and r.Header["Host"] when the header name is "host" and unsafe mode is enabled, ensuring consistency between the two header representations in rawhttp requests.

Changes

Cohort / File(s) Summary
Host header synchronization in unsafe mode
common/httpx/httpx.go
Adds explicit sync of Host header to both r.Host and r.Header when Options.Unsafe is true and header name is "host", preventing the header from being overwritten in unsafe/rawhttp mode.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5 minutes

Poem

A rabbit's fix, so lean and clean,
Host headers now stay pristine!
Both r.Host and Header aligned,
Unsafe mode, perfectly designed. 🐰

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Linked Issues Check ❓ Inconclusive The linked issue #2296 requests that the Host header from a raw request file be preserved when using -target and -unsafe together, with a specific request to add conditional logic to avoid overriding req.Host when the raw request already defines a Host header. The raw summary indicates the PR adds synchronization to ensure the Host header is present in both r.Host and r.Header in unsafe mode, but does not explicitly detail whether conditional logic was implemented to prevent the -target override scenario described in the issue, nor does it confirm that a Host header from the raw request is preserved as originally requested. Review the actual code implementation to confirm that the fix addresses the specific issue requirement: conditional logic that preserves a Host header from the raw request file when -target and -unsafe are used together, not just synchronization between r.Host and r.Header. Verify that the implementation prevents req.Host from being unconditionally overridden by target.CustomHost in unsafe mode.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "fix: ensure Host header is set in unsafe mode for rawhttp requests" directly and clearly summarizes the main change described in the raw summary, which adds explicit synchronization of the Host header when using unsafe mode. The title is specific, concise (66 characters, 12 words), and uses descriptive terminology that clearly communicates the nature of the fix. A reviewer scanning the history would understand that this change addresses Host header handling in unsafe/rawhttp mode.
Out of Scope Changes Check ✅ Passed According to the raw summary, the changes are limited to adding explicit synchronization of the Host header when using unsafe mode in the common/httpx/httpx.go file. The modifications are focused on ensuring the Host header is present in both r.Host and r.Header when the header name is "host" and Options.Unsafe is true, with no changes to signatures of exported entities or handling of other headers. These changes are directly related to and in scope with the issue #2296, which addresses Host header handling in unsafe mode for raw requests.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0111b9 and 497c399.

📒 Files selected for processing (1)
  • common/httpx/httpx.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • common/httpx/httpx.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Test Builds (ubuntu-latest)
  • GitHub Check: Test Builds (macOS-latest)
  • GitHub Check: Test Builds (windows-latest)
  • GitHub Check: Functional Test (macOS-latest)
  • GitHub Check: Functional Test (windows-latest)
  • GitHub Check: Analyze (go)
  • GitHub Check: Functional Test (ubuntu-latest)

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

@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.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b8fd15 and cf3a978.

📒 Files selected for processing (1)
  • runner/runner.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
runner/runner.go (1)
common/customheader/customheader.go (1)
  • CustomHeaders (10-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Test Builds (windows-latest)
  • GitHub Check: Test Builds (macOS-latest)
  • GitHub Check: release-test
  • GitHub Check: Functional Test (windows-latest)
  • GitHub Check: Functional Test (ubuntu-latest)
  • GitHub Check: Functional Test (macOS-latest)
  • GitHub Check: Analyze (go)

runner/runner.go Outdated
Comment on lines 1640 to 1651
// Don't override req.Host in unsafe mode if CustomHeaders has Host
// This allows AutomaticHostHeader(false) to work (see lines 244-251)
if target.CustomHost != "" {
req.Host = target.CustomHost
if scanopts.Unsafe {
// In unsafe mode, only set req.Host if no Host in CustomHeaders
if _, hasHost := hp.CustomHeaders["host"]; !hasHost {
req.Host = target.CustomHost
}
} else {
req.Host = target.CustomHost
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Host header detection logic is incorrect and the fix won't work as intended.

The check at line 1645 uses hp.CustomHeaders["host"] to look for an exact lowercase key "host", but this will always fail because:

  1. In unsafe mode (lines 201-203): CustomHeaders keys are the entire header string (e.g., "Host: example.com"), not just "host"
  2. When parsing raw requests (lines 236-238): Headers are stored with their original casing (e.g., "Host"), not lowercase "host"

This means the condition !hasHost will always be true, and req.Host will still be overridden by target.CustomHost, defeating the purpose of this fix.

The correct approach is shown in lines 246-251, which iterates through the keys and checks with case-insensitive prefix matching:

Apply this diff to fix the logic:

 	// Don't override req.Host in unsafe mode if CustomHeaders has Host
 	// This allows AutomaticHostHeader(false) to work (see lines 244-251)
 	if target.CustomHost != "" {
 		if scanopts.Unsafe {
 			// In unsafe mode, only set req.Host if no Host in CustomHeaders
-			if _, hasHost := hp.CustomHeaders["host"]; !hasHost {
+			hasHostHeader := false
+			for name := range hp.CustomHeaders {
+				nameLower := strings.TrimSpace(strings.ToLower(name))
+				if strings.HasPrefix(nameLower, "host") {
+					hasHostHeader = true
+					break
+				}
+			}
+			if !hasHostHeader {
 				req.Host = target.CustomHost
 			}
 		} else {
 			req.Host = target.CustomHost
 		}
 	}
📝 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
// Don't override req.Host in unsafe mode if CustomHeaders has Host
// This allows AutomaticHostHeader(false) to work (see lines 244-251)
if target.CustomHost != "" {
req.Host = target.CustomHost
if scanopts.Unsafe {
// In unsafe mode, only set req.Host if no Host in CustomHeaders
if _, hasHost := hp.CustomHeaders["host"]; !hasHost {
req.Host = target.CustomHost
}
} else {
req.Host = target.CustomHost
}
}
// Don't override req.Host in unsafe mode if CustomHeaders has Host
// This allows AutomaticHostHeader(false) to work (see lines 244-251)
if target.CustomHost != "" {
if scanopts.Unsafe {
// In unsafe mode, only set req.Host if no Host in CustomHeaders
hasHostHeader := false
for name := range hp.CustomHeaders {
nameLower := strings.TrimSpace(strings.ToLower(name))
if strings.HasPrefix(nameLower, "host") {
hasHostHeader = true
break
}
}
if !hasHostHeader {
req.Host = target.CustomHost
}
} else {
req.Host = target.CustomHost
}
}
🤖 Prompt for AI Agents
In runner/runner.go around lines 1640 to 1651, the boolean check using
hp.CustomHeaders["host"] is incorrect because CustomHeaders can contain full
header strings or original-cased keys; replace it with iteration over
hp.CustomHeaders keys and perform a case-insensitive prefix/equality check
(e.g., lowercase key and check if it == "host" or strings.HasPrefix(lowerKey,
"host:")) to detect an existing Host header; only set req.Host =
target.CustomHost in unsafe mode when no such Host header is found.

@ayanrajpoot10 ayanrajpoot10 changed the title fix: prevent overriding req.Host in unsafe mode when CustomHeaders ha… fix: ensure Host header is set in unsafe mode for rawhttp requests Oct 22, 2025
Copy link
Member

@Mzack9999 Mzack9999 left a comment

Choose a reason for hiding this comment

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

@ayanrajpoot10 Thanks for your PR. This function is already performed automatically by the rawhttp library (ref: https://github.com/projectdiscovery/rawhttp/blob/132ebe7ef211155e77497e4432c5cb13dec1ba79/options.go#L31). Did you face any situation in which the action was not performed properly?

@ayanrajpoot10
Copy link
Contributor Author

@ayanrajpoot10 Thanks for your PR. This function is already performed automatically by the rawhttp library (ref: https://github.com/projectdiscovery/rawhttp/blob/132ebe7ef211155e77497e4432c5cb13dec1ba79/options.go#L31). Did you face any situation in which the action was not performed properly?

image image

In the first screenshot, raw.txt shows Host: de1.wstunnel.xyz, but when the request is made, it changes to arturo.ns.cloudflare.com, which is our target — that’s the issue. In the second screenshot, after making this change, the Host header remains as de1.wstunnel.xyz, which means the issue is resolved. This suggests that there may be something wrong with the rawhttp library.

@ayanrajpoot10
Copy link
Contributor Author

@Mzack9999 The problem is that httpx.SetCustomHeaders only set req.Host and didn’t add it to req.Header, so when httpx.doUnsafeWithOptions passed only req.Header to rawhttp, the Host header was missing, and rawhttp.AutomaticHostHeader overwrote it with the target URL’s host.

@ayanrajpoot10
Copy link
Contributor Author

rawhttp does not handle req.Host like Go’s standard HTTP package does; instead, it only uses req.Header to get the host value.

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.

Host header overwritten in unsafe mode when using -target with raw requests

2 participants