- 
                Notifications
    
You must be signed in to change notification settings  - Fork 2.9k
 
Add ncat command to replicate raw request #5243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
| 
           Thanks for your contribution @cn-kali-team , we appreciate it!  | 
    
| 
           @Mzack9999 fyi  | 
    
          
WalkthroughRefactors curl generation to prefer the original generated request when available, and adds an alternate printf|ncat-based path for unsafe/raw requests with URL parsing and shell-escaping; control flow now selects between http2curl and the raw/ncat path. Changes
 Sequence Diagram(s)sequenceDiagram
  autonumber
  participant Caller
  participant Generator as CurlGenerator
  participant GenReq as generatedRequest
  participant Resp as resp
  participant http2curl as http2curl
  participant ncat as ncat
  Caller->>Generator: GenerateCurlCommand()
  alt generatedRequest present & standard
    Generator->>GenReq: use generatedRequest.request.Request
    Generator->>http2curl: build curl
    http2curl-->>Generator: curl string
  else unsafe/raw request available
    Generator->>Generator: parse URL (net/url), bashEscape(raw bytes)
    Generator->>ncat: assemble printf | ncat command (add -ssl if https)
    ncat-->>Generator: ncat-command string
  else fallback (resp.Request)
    Generator->>Resp: use resp.Request (body preserved if needed)
    Generator->>http2curl: build curl
    http2curl-->>Generator: curl string
  end
  Generator-->>Caller: command string (curl or ncat-command)
    Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
 Pre-merge checks and finishing touches❌ Failed checks (1 warning)
 ✅ Passed checks (2 passed)
 ✨ Finishing touches
 🧪 Generate unit tests (beta)
 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. Comment   | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
pkg/protocols/http/request.go(3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: Format Go code using go fmt
Run static analysis with go vet
Files:
pkg/protocols/http/request.go
pkg/protocols/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
Each protocol implementation must provide a Request interface with methods Compile(), ExecuteWithResults(), Match(), and Extract()
Files:
pkg/protocols/http/request.go
🧬 Code graph analysis (1)
pkg/protocols/http/request.go (2)
pkg/protocols/http/raw/raw.go (2)
Request(24-32)Parse(35-139)pkg/protocols/http/http.go (1)
Request(35-240)
⏰ 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). (3)
- GitHub Check: Tests (ubuntu-latest)
 - GitHub Check: Tests (macOS-latest)
 - GitHub Check: Tests (windows-latest)
 
| for _, line := range bytes.Split(unsafeRawBytes, new_line) { | ||
| ncat += bashEscape(append(line[:], new_line[:]...)) | ||
| ncat += "\\\r\n" | ||
| } | ||
| ncat += "|" | ||
| ncat_cmd := []string{"ncat"} | ||
| rawurl, err := url.Parse(formedURL) | ||
| if err != nil { | ||
| ncat_cmd = append(ncat_cmd, "127.0.0.1") | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't mutate the raw payload before piping it to ncat.
bytes.Split strips the CRLF delimiters, so adding append(line[:], new_line...) reintroduces \r\n even for the final chunk that never had it (e.g., most request bodies). The reproduced payload now contains an extra CRLF, so the bytes you send differ from UnsafeRawBytes, breaking the “replicate raw request” promise. The append(line[:], …) also mutates the backing array for later slices. Please emit the original buffer as-is instead of rebuilding it per line.
-			new_line := []byte{'\r', '\n'}
-			for _, line := range bytes.Split(unsafeRawBytes, new_line) {
-				ncat += bashEscape(append(line[:], new_line[:]...))
-				ncat += "\\\r\n"
-			}
-			ncat += "|"
+			ncat += bashEscape(unsafeRawBytes)
+			ncat += " |"📝 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.
| for _, line := range bytes.Split(unsafeRawBytes, new_line) { | |
| ncat += bashEscape(append(line[:], new_line[:]...)) | |
| ncat += "\\\r\n" | |
| } | |
| ncat += "|" | |
| ncat_cmd := []string{"ncat"} | |
| rawurl, err := url.Parse(formedURL) | |
| if err != nil { | |
| ncat_cmd = append(ncat_cmd, "127.0.0.1") | |
| ncat += bashEscape(unsafeRawBytes) | |
| ncat += " |" | |
| ncat_cmd := []string{"ncat"} | |
| rawurl, err := url.Parse(formedURL) | 
        
          
                pkg/protocols/http/request.go
              
                Outdated
          
        
      | if rawurl.Scheme == "https" { | ||
| ncat_cmd = append(ncat_cmd, "--ssl") | ||
| } | ||
| ncat_cmd = append(ncat_cmd, rawurl.Hostname()) | ||
| ncat_cmd = append(ncat_cmd, rawurl.Port()) | ||
| } | ||
| ncat += strings.Join(ncat_cmd, " ") | ||
| curlCommand = ncat | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Provide a port when building the ncat command.
When the template URL omits an explicit port (the common case), rawurl.Port() returns "", so the generated command becomes ncat example.com  and exits with “missing port” before the payload is sent. Default to 80/443 (depending on the scheme) and keep appending --ssl only once.
 			ncat_cmd := []string{"ncat"}
 			rawurl, err := url.Parse(formedURL)
 			if err != nil {
 				ncat_cmd = append(ncat_cmd, "127.0.0.1")
 				ncat_cmd = append(ncat_cmd, "80")
 			} else {
-				if rawurl.Scheme == "https" {
-					ncat_cmd = append(ncat_cmd, "--ssl")
-				}
-				ncat_cmd = append(ncat_cmd, rawurl.Hostname())
-				ncat_cmd = append(ncat_cmd, rawurl.Port())
+				port := rawurl.Port()
+				if port == "" {
+					if rawurl.Scheme == "https" {
+						ncat_cmd = append(ncat_cmd, "--ssl")
+						port = "443"
+					} else {
+						port = "80"
+					}
+				} else if rawurl.Scheme == "https" {
+					ncat_cmd = append(ncat_cmd, "--ssl")
+				}
+				ncat_cmd = append(ncat_cmd, rawurl.Hostname(), port)
 			}
 			ncat += strings.Join(ncat_cmd, " ")📝 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.
| if rawurl.Scheme == "https" { | |
| ncat_cmd = append(ncat_cmd, "--ssl") | |
| } | |
| ncat_cmd = append(ncat_cmd, rawurl.Hostname()) | |
| ncat_cmd = append(ncat_cmd, rawurl.Port()) | |
| } | |
| ncat += strings.Join(ncat_cmd, " ") | |
| curlCommand = ncat | |
| ncat_cmd := []string{"ncat"} | |
| rawurl, err := url.Parse(formedURL) | |
| if err != nil { | |
| ncat_cmd = append(ncat_cmd, "127.0.0.1") | |
| ncat_cmd = append(ncat_cmd, "80") | |
| } else { | |
| port := rawurl.Port() | |
| if port == "" { | |
| if rawurl.Scheme == "https" { | |
| ncat_cmd = append(ncat_cmd, "--ssl") | |
| port = "443" | |
| } else { | |
| port = "80" | |
| } | |
| } else if rawurl.Scheme == "https" { | |
| ncat_cmd = append(ncat_cmd, "--ssl") | |
| } | |
| ncat_cmd = append(ncat_cmd, rawurl.Hostname(), port) | |
| } | |
| ncat += strings.Join(ncat_cmd, " ") | |
| curlCommand = ncat | 
🤖 Prompt for AI Agents
In pkg/protocols/http/request.go around lines 937 to 944, the ncat command
builder appends rawurl.Port() which can be empty and causes "missing port"
errors; change it to compute port := rawurl.Port(); if port == "" { if
rawurl.Scheme == "https" { port = "443" } else { port = "80" } } then append
that port (not an empty string) to ncat_cmd, and ensure "--ssl" is only added
once when the scheme is https (i.e., add --ssl based on scheme before adding
hostname/port).
| func bashEscape(b []byte) string { | ||
| str := "" | ||
| for i := 0; i < len(b); i++ { | ||
| if b[i] < 32 || b[i] > 126 { | ||
| s := strconv.QuoteToASCII(string(b[i])) | ||
| s = strings.TrimPrefix(s, "\"") | ||
| s = strings.TrimSuffix(s, "\"") | ||
| str += s | ||
| } else { | ||
| str += string(b[i]) | ||
| } | ||
| } | ||
| str = "'" + strings.ReplaceAll(str, "\"", "\\\"") + "'" | ||
| return str | ||
| } | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bashEscape breaks when the payload contains single quotes.
Wrapping everything in '…' without escaping ' means any request carrying an apostrophe (common in JSON, form data, etc.) yields an invalid shell command. Please escape ' (and \) explicitly—ANSI-C $'…' quoting keeps the bytes intact.
-func bashEscape(b []byte) string {
-	str := ""
-	for i := 0; i < len(b); i++ {
-		if b[i] < 32 || b[i] > 126 {
-			s := strconv.QuoteToASCII(string(b[i]))
-			s = strings.TrimPrefix(s, "\"")
-			s = strings.TrimSuffix(s, "\"")
-			str += s
-		} else {
-			str += string(b[i])
-		}
-	}
-	str = "'" + strings.ReplaceAll(str, "\"", "\\\"") + "'"
-	return str
-}
+func bashEscape(b []byte) string {
+	var sb strings.Builder
+	sb.WriteString("$'")
+	for _, ch := range b {
+		switch {
+		case ch == '\'':
+			sb.WriteString("\\'")
+		case ch == '\\':
+			sb.WriteString("\\\\")
+		case ch < 32 || ch > 126:
+			sb.WriteString(fmt.Sprintf("\\x%02x", ch))
+		default:
+			sb.WriteByte(ch)
+		}
+	}
+	sb.WriteByte('\'')
+	return sb.String()
+}🤖 Prompt for AI Agents
In pkg/protocols/http/request.go around lines 1304–1318, bashEscape currently
wraps the result in single quotes which breaks when the payload contains single
quotes; change it to use ANSI-C quoting ($'...') and ensure single quotes and
backslashes are escaped and non-printable bytes are represented with \xHH
sequences. Concretely: iterate over bytes, append printable ASCII bytes as-is
but convert backslash '\' to "\\\\" and single-quote '\'' to "\\'"; for bytes
outside 0x20–0x7E, append a hex escape like "\\x%02x"; finally return the string
wrapped as "$'"+payload+"'" so the shell preserves the intended bytes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
pkg/protocols/http/request.go (3)
926-930: Don't mutate the raw payload before piping it to ncat.
bytes.Splitstrips the CRLF delimiters, so addingappend(line[:], new_line...)reintroduces\r\neven for the final chunk that never had it (e.g., most request bodies). The reproduced payload now contains an extra CRLF, so the bytes you send differ fromUnsafeRawBytes, breaking the "replicate raw request" promise. Theappend(line[:], …)also mutates the backing array for later slices.Apply this diff to emit the original buffer as-is:
- new_line := []byte{'\r', '\n'} - for _, line := range bytes.Split(unsafeRawBytes, new_line) { - ncat += bashEscape(append(line[:], new_line[:]...)) - ncat += "\\\r\n" - } - ncat += "|" + ncat += bashEscape(unsafeRawBytes) + ncat += " |"
937-941: Provide a port when building the ncat command.When the template URL omits an explicit port (the common case),
rawurl.Port()returns"", so the generated command becomesncat example.comand exits with "missing port" before the payload is sent. Default to 80/443 (depending on the scheme) and keep appending--sslonly once.Apply this diff:
- if rawurl.Scheme == "https" { - ncat_cmd = append(ncat_cmd, "--ssl") - } - ncat_cmd = append(ncat_cmd, rawurl.Hostname()) - ncat_cmd = append(ncat_cmd, rawurl.Port()) + port := rawurl.Port() + if port == "" { + if rawurl.Scheme == "https" { + ncat_cmd = append(ncat_cmd, "--ssl") + port = "443" + } else { + port = "80" + } + } else if rawurl.Scheme == "https" { + ncat_cmd = append(ncat_cmd, "--ssl") + } + ncat_cmd = append(ncat_cmd, rawurl.Hostname(), port)
1307-1321: bashEscape breaks when the payload contains single quotes.Wrapping everything in
'…'without escaping'means any request carrying an apostrophe (common in JSON, form data, etc.) yields an invalid shell command. Please escape'(and\) explicitly—ANSI-C$'…'quoting keeps the bytes intact.Apply this diff:
-func bashEscape(b []byte) string { - str := "" - for i := 0; i < len(b); i++ { - if b[i] < 32 || b[i] > 126 { - s := strconv.QuoteToASCII(string(b[i])) - s = strings.TrimPrefix(s, "\"") - s = strings.TrimSuffix(s, "\"") - str += s - } else { - str += string(b[i]) - } - } - str = "'" + strings.ReplaceAll(str, "\"", "\\\"") + "'" - return str -} +func bashEscape(b []byte) string { + var sb strings.Builder + sb.WriteString("$'") + for _, ch := range b { + switch { + case ch == '\'': + sb.WriteString("\\'") + case ch == '\\': + sb.WriteString("\\\\") + case ch < 32 || ch > 126: + sb.WriteString(fmt.Sprintf("\\x%02x", ch)) + default: + sb.WriteByte(ch) + } + } + sb.WriteByte('\'') + return sb.String() +}
🧹 Nitpick comments (1)
pkg/protocols/http/request.go (1)
917-917: Remove dead resp.Request.Body assignment at pkg/protocols/http/request.go:917. The assignment isn’t used by the curl generation or anywhere else; remove this dead code.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
pkg/protocols/http/request.go(4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: Format Go code using go fmt
Run static analysis with go vet
Files:
pkg/protocols/http/request.go
pkg/protocols/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
Each protocol implementation must provide a Request interface with methods Compile(), ExecuteWithResults(), Match(), and Extract()
Files:
pkg/protocols/http/request.go
🧬 Code graph analysis (1)
pkg/protocols/http/request.go (2)
pkg/protocols/http/raw/raw.go (1)
Request(24-32)pkg/protocols/http/http.go (1)
Request(35-240)
🔇 Additional comments (2)
pkg/protocols/http/request.go (2)
1038-1040: LGTM!The conditional check ensures that
ncat-commandis only added to the output when it has been successfully generated.
11-11: LGTM!The
net/urlimport is correctly added to support URL parsing in the ncat command generation logic (line 932).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case of error parsing the URL we should not default to 127.0.0.1:80 but abort the creation or use a placeholder like:
HOSTNAME:PORT
Proposed changes
Checklist
Template:
Test Run:
Output
Summary by CodeRabbit