mirror of
https://github.com/anotherhadi/spilltea.git
synced 2026-05-20 01:32:33 +02:00
a147e8b972
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
308 lines
7.5 KiB
Go
308 lines
7.5 KiB
Go
package copyas
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
type header struct{ key, value string }
|
|
|
|
type parsedRequest struct {
|
|
method string
|
|
path string
|
|
host string
|
|
scheme string
|
|
headers []header
|
|
body string
|
|
}
|
|
|
|
func parseRaw(raw, scheme string) parsedRequest {
|
|
lines := strings.Split(strings.ReplaceAll(raw, "\r\n", "\n"), "\n")
|
|
pr := parsedRequest{scheme: scheme}
|
|
if len(lines) == 0 {
|
|
return pr
|
|
}
|
|
|
|
parts := strings.SplitN(lines[0], " ", 3)
|
|
if len(parts) >= 1 {
|
|
pr.method = strings.TrimSpace(parts[0])
|
|
}
|
|
if len(parts) >= 2 {
|
|
pr.path = strings.TrimSpace(parts[1])
|
|
}
|
|
|
|
i := 1
|
|
for i < len(lines) {
|
|
line := strings.TrimRight(lines[i], "\r")
|
|
if line == "" {
|
|
i++
|
|
break
|
|
}
|
|
if kv := strings.SplitN(line, ": ", 2); len(kv) == 2 {
|
|
k := strings.TrimSpace(kv[0])
|
|
v := strings.TrimSpace(kv[1])
|
|
pr.headers = append(pr.headers, header{k, v})
|
|
if strings.EqualFold(k, "host") {
|
|
pr.host = v
|
|
}
|
|
}
|
|
i++
|
|
}
|
|
|
|
if i < len(lines) {
|
|
pr.body = strings.TrimRight(strings.Join(lines[i:], "\n"), "\n")
|
|
}
|
|
return pr
|
|
}
|
|
|
|
func (pr parsedRequest) fullURL() string {
|
|
scheme := pr.scheme
|
|
if scheme == "" {
|
|
scheme = "https"
|
|
}
|
|
return scheme + "://" + pr.host + pr.path
|
|
}
|
|
|
|
func formatAs(id, raw, scheme string) string {
|
|
pr := parseRaw(raw, scheme)
|
|
switch id {
|
|
case "raw":
|
|
return raw
|
|
case "curl":
|
|
return toCurl(pr)
|
|
case "python":
|
|
return toPython(pr)
|
|
case "go":
|
|
return toGo(pr)
|
|
case "ffuf":
|
|
return toFFUF(pr)
|
|
case "markdown":
|
|
return toMarkdown(pr)
|
|
case "har":
|
|
return toHAR(pr)
|
|
}
|
|
return raw
|
|
}
|
|
|
|
func toMarkdown(pr parsedRequest) string {
|
|
var sb strings.Builder
|
|
fmt.Fprintf(&sb, "### %s %s\n\n", pr.method, pr.fullURL())
|
|
sb.WriteString("```\n")
|
|
sb.WriteString(pr.method + " " + pr.path + " HTTP/1.1\n")
|
|
for _, h := range pr.headers {
|
|
sb.WriteString(fmt.Sprintf("%s: %s\n", h.key, h.value))
|
|
}
|
|
if pr.body != "" {
|
|
sb.WriteString("\n" + pr.body)
|
|
}
|
|
sb.WriteString("\n```")
|
|
return sb.String()
|
|
}
|
|
|
|
func toCurl(pr parsedRequest) string {
|
|
var sb strings.Builder
|
|
fmt.Fprintf(&sb, "curl -X %s '%s'", pr.method, pr.fullURL())
|
|
for _, h := range pr.headers {
|
|
if strings.EqualFold(h.key, "content-length") {
|
|
continue
|
|
}
|
|
fmt.Fprintf(&sb, " \\\n -H '%s: %s'", h.key, h.value)
|
|
}
|
|
if pr.body != "" {
|
|
body := strings.ReplaceAll(pr.body, "'", "'\\''")
|
|
fmt.Fprintf(&sb, " \\\n --data '%s'", body)
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
func toPython(pr parsedRequest) string {
|
|
var sb strings.Builder
|
|
sb.WriteString("import requests\n\n")
|
|
fmt.Fprintf(&sb, "url = %q\n", pr.fullURL())
|
|
|
|
sb.WriteString("headers = {\n")
|
|
for _, h := range pr.headers {
|
|
if strings.EqualFold(h.key, "content-length") {
|
|
continue
|
|
}
|
|
fmt.Fprintf(&sb, " %q: %q,\n", h.key, h.value)
|
|
}
|
|
sb.WriteString("}\n")
|
|
|
|
method := strings.ToLower(pr.method)
|
|
if pr.body != "" {
|
|
fmt.Fprintf(&sb, "data = %q\n\n", pr.body)
|
|
fmt.Fprintf(&sb, "response = requests.%s(url, headers=headers, data=data)\n", method)
|
|
} else {
|
|
fmt.Fprintf(&sb, "\nresponse = requests.%s(url, headers=headers)\n", method)
|
|
}
|
|
sb.WriteString("print(response.status_code)\n")
|
|
sb.WriteString("print(response.text)\n")
|
|
return sb.String()
|
|
}
|
|
|
|
func toGo(pr parsedRequest) string {
|
|
var sb strings.Builder
|
|
sb.WriteString("package main\n\nimport (\n")
|
|
if pr.body != "" {
|
|
sb.WriteString("\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n")
|
|
} else {
|
|
sb.WriteString("\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\n")
|
|
}
|
|
sb.WriteString("func main() {\n")
|
|
|
|
if pr.body != "" {
|
|
fmt.Fprintf(&sb, "\tbody := strings.NewReader(%q)\n", pr.body)
|
|
fmt.Fprintf(&sb, "\treq, err := http.NewRequest(%q, %q, body)\n", pr.method, pr.fullURL())
|
|
} else {
|
|
fmt.Fprintf(&sb, "\treq, err := http.NewRequest(%q, %q, nil)\n", pr.method, pr.fullURL())
|
|
}
|
|
sb.WriteString("\tif err != nil { panic(err) }\n")
|
|
|
|
for _, h := range pr.headers {
|
|
if strings.EqualFold(h.key, "host") || strings.EqualFold(h.key, "content-length") {
|
|
continue
|
|
}
|
|
fmt.Fprintf(&sb, "\treq.Header.Set(%q, %q)\n", h.key, h.value)
|
|
}
|
|
|
|
sb.WriteString("\n\tclient := &http.Client{}\n")
|
|
sb.WriteString("\tresp, err := client.Do(req)\n")
|
|
sb.WriteString("\tif err != nil { panic(err) }\n")
|
|
sb.WriteString("\tdefer resp.Body.Close()\n")
|
|
sb.WriteString("\tbody2, _ := io.ReadAll(resp.Body)\n")
|
|
sb.WriteString("\tfmt.Printf(\"Status: %d\\n\", resp.StatusCode)\n")
|
|
sb.WriteString("\tfmt.Println(string(body2))\n")
|
|
sb.WriteString("}\n")
|
|
return sb.String()
|
|
}
|
|
|
|
func toFFUF(pr parsedRequest) string {
|
|
// Place FUZZ in the path: replace query string or append ?FUZZ
|
|
fuzzURL := pr.scheme + "://" + pr.host
|
|
if idx := strings.Index(pr.path, "?"); idx != -1 {
|
|
fuzzURL += pr.path[:idx] + "?FUZZ"
|
|
} else {
|
|
fuzzURL += pr.path + "?FUZZ"
|
|
}
|
|
|
|
var sb strings.Builder
|
|
fmt.Fprintf(&sb, "ffuf -u '%s'", fuzzURL)
|
|
sb.WriteString(" \\\n -w wordlist.txt")
|
|
fmt.Fprintf(&sb, " \\\n -X %s", pr.method)
|
|
for _, h := range pr.headers {
|
|
if strings.EqualFold(h.key, "content-length") {
|
|
continue
|
|
}
|
|
fmt.Fprintf(&sb, " \\\n -H '%s: %s'", h.key, h.value)
|
|
}
|
|
if pr.body != "" {
|
|
body := strings.ReplaceAll(pr.body, "'", "'\\''")
|
|
fmt.Fprintf(&sb, " \\\n -d '%s'", body)
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
func toHAR(pr parsedRequest) string {
|
|
type harNameValue struct {
|
|
Name string `json:"name"`
|
|
Value string `json:"value"`
|
|
}
|
|
type harPostData struct {
|
|
MimeType string `json:"mimeType"`
|
|
Text string `json:"text"`
|
|
}
|
|
type harRequest struct {
|
|
Method string `json:"method"`
|
|
URL string `json:"url"`
|
|
HTTPVersion string `json:"httpVersion"`
|
|
Headers []harNameValue `json:"headers"`
|
|
QueryString []harNameValue `json:"queryString"`
|
|
Cookies []harNameValue `json:"cookies"`
|
|
HeadersSize int `json:"headersSize"`
|
|
BodySize int `json:"bodySize"`
|
|
PostData *harPostData `json:"postData,omitempty"`
|
|
}
|
|
type harEntry struct {
|
|
StartedDateTime string `json:"startedDateTime"`
|
|
Time int `json:"time"`
|
|
Request harRequest `json:"request"`
|
|
Cache struct{} `json:"cache"`
|
|
Timings struct {
|
|
Send int `json:"send"`
|
|
Wait int `json:"wait"`
|
|
Receive int `json:"receive"`
|
|
} `json:"timings"`
|
|
}
|
|
type harLog struct {
|
|
Version string `json:"version"`
|
|
Creator struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
} `json:"creator"`
|
|
Entries []harEntry `json:"entries"`
|
|
}
|
|
type harRoot struct {
|
|
Log harLog `json:"log"`
|
|
}
|
|
|
|
headers := make([]harNameValue, 0, len(pr.headers))
|
|
for _, h := range pr.headers {
|
|
headers = append(headers, harNameValue{h.key, h.value})
|
|
}
|
|
|
|
var qs []harNameValue
|
|
if idx := strings.Index(pr.path, "?"); idx != -1 {
|
|
vals, err := url.ParseQuery(pr.path[idx+1:])
|
|
if err == nil {
|
|
for k, vs := range vals {
|
|
for _, v := range vs {
|
|
qs = append(qs, harNameValue{k, v})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if qs == nil {
|
|
qs = []harNameValue{}
|
|
}
|
|
|
|
req := harRequest{
|
|
Method: pr.method,
|
|
URL: pr.fullURL(),
|
|
HTTPVersion: "HTTP/1.1",
|
|
Headers: headers,
|
|
QueryString: qs,
|
|
Cookies: []harNameValue{},
|
|
HeadersSize: -1,
|
|
BodySize: len(pr.body),
|
|
}
|
|
if pr.body != "" {
|
|
mimeType := "application/octet-stream"
|
|
for _, h := range pr.headers {
|
|
if strings.EqualFold(h.key, "content-type") {
|
|
mimeType = h.value
|
|
break
|
|
}
|
|
}
|
|
req.PostData = &harPostData{MimeType: mimeType, Text: pr.body}
|
|
}
|
|
|
|
root := harRoot{Log: harLog{
|
|
Version: "1.2",
|
|
Entries: []harEntry{{
|
|
StartedDateTime: "1970-01-01T00:00:00.000Z",
|
|
Time: -1,
|
|
Request: req,
|
|
}},
|
|
}}
|
|
root.Log.Creator.Name = "spilltea"
|
|
|
|
b, err := json.MarshalIndent(root, "", " ")
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(b)
|
|
}
|