From 03260e0947e7037a0c3f45095c654fb25c0e270f Mon Sep 17 00:00:00 2001 From: Hadi <112569860+anotherhadi@users.noreply.github.com> Date: Tue, 19 May 2026 09:39:50 +0200 Subject: [PATCH] Init copy as HAR Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com> --- internal/ui/components/copyas/formats.go | 105 +++++++++++++++++++++++ internal/ui/components/copyas/model.go | 1 + 2 files changed, 106 insertions(+) diff --git a/internal/ui/components/copyas/formats.go b/internal/ui/components/copyas/formats.go index fd608ce..6895ca6 100644 --- a/internal/ui/components/copyas/formats.go +++ b/internal/ui/components/copyas/formats.go @@ -1,7 +1,9 @@ package copyas import ( + "encoding/json" "fmt" + "net/url" "strings" ) @@ -78,6 +80,8 @@ func formatAs(id, raw, scheme string) string { return toFFUF(pr) case "markdown": return toMarkdown(pr) + case "har": + return toHAR(pr) } return raw } @@ -200,3 +204,104 @@ func toFFUF(pr parsedRequest) string { } 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) +} diff --git a/internal/ui/components/copyas/model.go b/internal/ui/components/copyas/model.go index b2a8680..526fcb2 100644 --- a/internal/ui/components/copyas/model.go +++ b/internal/ui/components/copyas/model.go @@ -45,6 +45,7 @@ var allFormats = []list.Item{ formatItem{"go", "Go", "net/http package"}, formatItem{"ffuf", "FFUF", "web fuzzer: FUZZ in query string"}, formatItem{"markdown", "Markdown", "formatted for documentation"}, + formatItem{"har", "HAR", "HTTP Archive (JSON)"}, } type Model struct {