-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
157 lines (128 loc) · 3.76 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"text/template"
wappalayzer "github.com/projectdiscovery/wappalyzergo"
)
var (
templateStr string
)
type RequestResponse struct {
Request string `json:"request"`
Response string `json:"response"`
}
func init() {
flag.StringVar(&templateStr, "output", "", `Template string for formatting AppInfo. Example: "{{join .Tech.Categories}} -> {{.Name}}\n{{.Tech.Description}}\n {{.Tech.Website}}\n{{.Tech.Icon}}\n{{.Tech.CPE}}\n-----------------------------\n"`)
}
func parseStdin() ([]byte, error) {
stat, err := os.Stdin.Stat()
if err != nil {
return nil, fmt.Errorf("error getting stdin stat: %v", err)
}
if (stat.Mode() & os.ModeCharDevice) != 0 {
return nil, errors.New("feed me with Caido HTTP RequestResponse using pipe")
}
input, err := io.ReadAll(os.Stdin)
if err != nil {
return nil, fmt.Errorf("can't read input: %v", err)
}
// Check for and remove BOM if present
input = bytes.TrimPrefix(input, []byte("\ufeff"))
var requestResponse RequestResponse
if err := json.Unmarshal(input, &requestResponse); err != nil {
return nil, fmt.Errorf("error unmarshalling JSON: %v", err)
}
decodedResponse, err := base64.StdEncoding.DecodeString(requestResponse.Response)
if err != nil {
return nil, fmt.Errorf("error decoding base64: %v", err)
}
return decodedResponse, nil
}
func splitHTTPResponse(response []byte) (http.Header, []byte, error) {
// Split response by headers and body
parts := bytes.SplitN(response, []byte("\r\n\r\n"), 2)
if len(parts) < 2 {
return nil, nil, errors.New("invalid HTTP response format")
}
headers := make(http.Header)
for i, line := range bytes.Split(parts[0], []byte("\r\n")) {
if i == 0 {
continue
}
sepIndex := bytes.IndexByte(line, ':')
if sepIndex == -1 {
return nil, nil, errors.New("invalid header format")
}
key := string(bytes.TrimSpace(line[:sepIndex]))
value := string(bytes.TrimSpace(line[sepIndex+1:]))
headers[key] = append(headers[key], value)
}
return headers, parts[1], nil
}
func analyze(headers map[string][]string, body []byte) (map[string]wappalayzer.AppInfo, error) {
wappalayzerClient, err := wappalayzer.New()
if err != nil {
return nil, fmt.Errorf("error initializing Wappalyzer: %v", err)
}
appInfo := wappalayzerClient.FingerprintWithInfo(headers, body)
return appInfo, nil
}
func joinList(items []string) string {
return strings.Join(items, ",")
}
func formatOutput(techInfo map[string]wappalayzer.AppInfo, tmplStr string) (string, error) {
var output strings.Builder
for name, tech := range techInfo {
if tmplStr == "" {
output.WriteString(name + "\n")
continue
}
var buffer bytes.Buffer
parsedTemplateStr := bytes.ReplaceAll([]byte(tmplStr), []byte(`\n`), []byte("\n"))
tmpl, err := template.New("techInfo").Funcs(template.FuncMap{
"join": joinList,
}).Parse(string(parsedTemplateStr))
if err != nil {
return "", fmt.Errorf("error parsing template: %v", err)
}
err = tmpl.Execute(&buffer, struct {
Name string
Tech wappalayzer.AppInfo
}{name, tech})
if err != nil {
return "", fmt.Errorf("error executing template: %v", err)
}
output.WriteString(buffer.String())
}
return output.String(), nil
}
func main() {
flag.Parse()
decodedResponse, err := parseStdin()
if err != nil {
log.Fatalf("Error parsing stdin: %v", err)
}
headers, body, err := splitHTTPResponse(decodedResponse)
if err != nil {
log.Fatalf("Error splitting HTTP response: %v", err)
}
techInfo, err := analyze(headers, body)
if err != nil {
log.Fatalf("Error analyzing response: %v", err)
}
output, err := formatOutput(techInfo, templateStr)
if err != nil {
log.Fatalf("Error formatting output: %v", err)
}
fmt.Println(output)
}