Skip to content

Commit 0e7facb

Browse files
authored
Merge pull request #723 from projectdiscovery/feat/port-burpxml
feat(parsers/burp): port Burp Suite XML parser
2 parents 23680eb + c41994e commit 0e7facb

4 files changed

Lines changed: 663 additions & 0 deletions

File tree

parsers/burp/xml/strings.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package burpxml
2+
3+
import "fmt"
4+
5+
func (i Item) ToStrings(noReq, noResp bool) []string {
6+
arr := []string{
7+
i.Time,
8+
i.URL,
9+
i.Host.Name,
10+
i.Host.IP,
11+
i.Port,
12+
i.Protocol,
13+
i.Path,
14+
i.Extension,
15+
}
16+
17+
if !noReq {
18+
arr = append(arr, i.Request.ToStrings()...)
19+
}
20+
21+
arr = append(arr, []string{
22+
i.Status,
23+
i.ResponseLength,
24+
i.MimeType,
25+
}...)
26+
27+
if !noResp {
28+
arr = append(arr, i.Response.ToStrings()...)
29+
}
30+
31+
arr = append(arr, i.Comment)
32+
return arr
33+
}
34+
35+
func (r Request) ToStrings() []string {
36+
if r.Body != "" {
37+
return []string{r.Body}
38+
}
39+
return []string{r.Base64Encoded, r.Raw}
40+
}
41+
42+
func (r Response) ToStrings() []string {
43+
if r.Body != "" {
44+
return []string{r.Body}
45+
}
46+
return []string{r.Base64Encoded, r.Raw}
47+
}
48+
49+
func (i Item) FlatString() string {
50+
return fmt.Sprintf(`%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s`,
51+
i.Time,
52+
i.URL,
53+
i.Host.Name,
54+
i.Host.IP,
55+
i.Port,
56+
i.Protocol,
57+
i.Path,
58+
i.Extension,
59+
i.Request.FlatString(),
60+
i.Status,
61+
i.ResponseLength,
62+
i.MimeType,
63+
i.Response.FlatString(),
64+
i.Comment,
65+
)
66+
}
67+
68+
func (r Request) FlatString() string {
69+
if r.Body != "" {
70+
return r.Body
71+
}
72+
return fmt.Sprintf(`%s,%s`, r.Base64Encoded, r.Raw)
73+
}
74+
75+
func (r Response) FlatString() string {
76+
if r.Body != "" {
77+
return r.Body
78+
}
79+
return fmt.Sprintf(`%s,%s`, r.Base64Encoded, r.Raw)
80+
}
81+
82+
func (i Item) String() string {
83+
return fmt.Sprintf(`Item{
84+
Time = %s,
85+
Url = %s,
86+
Host = %s,
87+
IP = %s,
88+
Port = %s,
89+
Proto = %s,
90+
Path = %s,
91+
Ext = %s,
92+
%s,
93+
Status = %s,
94+
RespLen = %s,
95+
MIME = %s,
96+
%s,
97+
Comment = %s,
98+
}`,
99+
i.Time,
100+
i.URL,
101+
i.Host.Name,
102+
i.Host.IP,
103+
i.Port,
104+
i.Protocol,
105+
i.Path,
106+
i.Extension,
107+
i.Request.String(),
108+
i.Status,
109+
i.ResponseLength,
110+
i.MimeType,
111+
i.Response.String(),
112+
i.Comment,
113+
)
114+
}
115+
116+
func (r Request) String() string {
117+
s := "Request{\n"
118+
if r.Body != "" {
119+
s += fmt.Sprintf(" Body = %s,\n", r.Body)
120+
} else {
121+
s += fmt.Sprintf(" Base64 = %s,\nBody = %s,\n", r.Base64Encoded, r.Raw)
122+
}
123+
s += "}"
124+
return s
125+
}
126+
127+
func (r Response) String() string {
128+
s := "Response{\n"
129+
if r.Body != "" {
130+
s += fmt.Sprintf(" Body = %s,\n", r.Body)
131+
} else {
132+
s += fmt.Sprintf(" Base64 = %s,\nBody = %s,\n", r.Base64Encoded, r.Raw)
133+
}
134+
s += "}"
135+
return s
136+
}

parsers/burp/xml/types.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package burpxml
2+
3+
import (
4+
"encoding/csv"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
)
9+
10+
type Request struct {
11+
Base64Encoded string `xml:"base64,attr" json:"base64_encoded,omitempty"`
12+
Raw string `xml:",chardata" json:"raw,omitempty"`
13+
Body string `xml:"-" json:"body,omitempty"`
14+
}
15+
16+
type Response struct {
17+
Base64Encoded string `xml:"base64,attr" json:"base64_encoded,omitempty"`
18+
Raw string `xml:",chardata" json:"raw,omitempty"`
19+
Body string `xml:"-" json:"body,omitempty"`
20+
}
21+
22+
type Host struct {
23+
IP string `xml:"ip,attr" json:"ip,omitempty"`
24+
Name string `xml:",chardata" json:"name,omitempty"`
25+
}
26+
27+
type Item struct {
28+
Time string `xml:"time" json:"time,omitempty"`
29+
URL string `xml:"url" json:"url,omitempty"`
30+
Host Host `xml:"host" json:"host,omitzero"`
31+
Port string `xml:"port" json:"port,omitempty"`
32+
Protocol string `xml:"protocol" json:"protocol,omitempty"`
33+
Path string `xml:"path" json:"path,omitempty"`
34+
Extension string `xml:"extension" json:"extension,omitempty"`
35+
Request Request `xml:"request" json:"request,omitzero"`
36+
Status string `xml:"status" json:"status,omitempty"`
37+
ResponseLength string `xml:"responselength" json:"response_length,omitempty"`
38+
MimeType string `xml:"mimetype" json:"mime_type,omitempty"`
39+
Response Response `xml:"response" json:"response,omitzero"`
40+
Comment string `xml:"comment" json:"comment,omitempty"`
41+
}
42+
43+
type Items struct {
44+
Items []Item `xml:"item" json:"items,omitempty"`
45+
}
46+
47+
func (items *Items) ToJSON(w io.Writer) error {
48+
enc := json.NewEncoder(w)
49+
enc.SetIndent("", " ")
50+
if err := enc.Encode(items); err != nil {
51+
return fmt.Errorf("json encode: %w", err)
52+
}
53+
return nil
54+
}
55+
56+
type CSVOptions struct {
57+
ExcludeRequest bool
58+
ExcludeResponse bool
59+
}
60+
61+
func (items *Items) ToCSV(w io.Writer, opts CSVOptions) error {
62+
enc := csv.NewWriter(w)
63+
defer enc.Flush()
64+
65+
for _, item := range items.Items {
66+
record := item.toRecord(opts)
67+
if err := enc.Write(record); err != nil {
68+
return fmt.Errorf("csv write: %w", err)
69+
}
70+
}
71+
72+
return enc.Error()
73+
}
74+
75+
func (item *Item) toRecord(opts CSVOptions) []string {
76+
record := []string{
77+
item.Time,
78+
item.URL,
79+
item.Host.Name,
80+
item.Host.IP,
81+
item.Port,
82+
item.Protocol,
83+
item.Path,
84+
item.Extension,
85+
}
86+
87+
if !opts.ExcludeRequest {
88+
record = append(record, item.Request.content())
89+
}
90+
91+
record = append(record, item.Status, item.ResponseLength, item.MimeType)
92+
93+
if !opts.ExcludeResponse {
94+
record = append(record, item.Response.content())
95+
}
96+
97+
record = append(record, item.Comment)
98+
return record
99+
}
100+
101+
func (r *Request) content() string {
102+
if r.Body != "" {
103+
return r.Body
104+
}
105+
return r.Raw
106+
}
107+
108+
func (r *Response) content() string {
109+
if r.Body != "" {
110+
return r.Body
111+
}
112+
return r.Raw
113+
}

parsers/burp/xml/xml.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package burpxml
2+
3+
import (
4+
"encoding/base64"
5+
"encoding/xml"
6+
"fmt"
7+
"io"
8+
"strconv"
9+
)
10+
11+
type XMLParseOptions struct {
12+
DecodeBase64 bool
13+
}
14+
15+
func ParseXML(r io.Reader, opts XMLParseOptions) (*Items, error) {
16+
var items Items
17+
if err := xml.NewDecoder(r).Decode(&items); err != nil {
18+
return nil, fmt.Errorf("xml decode: %w", err)
19+
}
20+
21+
if !opts.DecodeBase64 {
22+
return &items, nil
23+
}
24+
25+
for i := range items.Items {
26+
if err := decodeItemBodies(&items.Items[i]); err != nil {
27+
return nil, err
28+
}
29+
}
30+
31+
return &items, nil
32+
}
33+
34+
func decodeItemBodies(item *Item) error {
35+
decoded, err := decodeBase64Field(item.Request.Base64Encoded, item.Request.Raw)
36+
if err != nil {
37+
return fmt.Errorf("decode request: %w", err)
38+
}
39+
item.Request.Body = decoded
40+
41+
decoded, err = decodeBase64Field(item.Response.Base64Encoded, item.Response.Raw)
42+
if err != nil {
43+
return fmt.Errorf("decode response: %w", err)
44+
}
45+
item.Response.Body = decoded
46+
47+
return nil
48+
}
49+
50+
func decodeBase64Field(base64Flag, raw string) (string, error) {
51+
if base64Flag == "" {
52+
return raw, nil
53+
}
54+
55+
isBase64, err := strconv.ParseBool(base64Flag)
56+
if err != nil {
57+
return "", fmt.Errorf("parse base64 flag: %w", err)
58+
}
59+
60+
if !isBase64 {
61+
return raw, nil
62+
}
63+
64+
decoded, err := base64.StdEncoding.DecodeString(raw)
65+
if err != nil {
66+
return "", fmt.Errorf("base64 decode: %w", err)
67+
}
68+
69+
return string(decoded), nil
70+
}

0 commit comments

Comments
 (0)