Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/reporting/format/format_utils.go
2070 views
1
package format
2
3
import (
4
"bytes"
5
"fmt"
6
"strconv"
7
"strings"
8
9
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
10
"github.com/projectdiscovery/nuclei/v3/pkg/model"
11
"github.com/projectdiscovery/nuclei/v3/pkg/output"
12
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/markdown/util"
13
"github.com/projectdiscovery/nuclei/v3/pkg/types"
14
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
15
unitutils "github.com/projectdiscovery/utils/unit"
16
)
17
18
// Summary returns a formatted built one line summary of the event
19
func Summary(event *output.ResultEvent) string {
20
return fmt.Sprintf("%s (%s) found on %s", types.ToString(event.Info.Name), GetMatchedTemplateName(event), event.Host)
21
}
22
23
// GetMatchedTemplateName returns the matched template name from a result event
24
// together with the found matcher and extractor name, if present
25
func GetMatchedTemplateName(event *output.ResultEvent) string {
26
matchedTemplateName := event.TemplateID
27
if event.MatcherName != "" {
28
matchedTemplateName += ":" + event.MatcherName
29
}
30
31
if event.ExtractorName != "" {
32
matchedTemplateName += ":" + event.ExtractorName
33
}
34
35
return matchedTemplateName
36
}
37
38
type reportMetadataEditorHook func(event *output.ResultEvent, formatter ResultFormatter) string
39
40
var (
41
// ReportGenerationMetadataHooks are the hooks for adding metadata to the report
42
ReportGenerationMetadataHooks []reportMetadataEditorHook
43
)
44
45
func CreateReportDescription(event *output.ResultEvent, formatter ResultFormatter, omitRaw bool) string {
46
template := GetMatchedTemplateName(event)
47
builder := &bytes.Buffer{}
48
fmt.Fprintf(builder, "%s: %s matched at %s\n\n", formatter.MakeBold("Details"), formatter.MakeBold(template), event.Host)
49
50
attributes := utils.NewEmptyInsertionOrderedStringMap(3)
51
attributes.Set("Protocol", strings.ToUpper(event.Type))
52
attributes.Set("Full URL", event.Matched)
53
attributes.Set("Timestamp", event.Timestamp.Format("Mon Jan 2 15:04:05 -0700 MST 2006"))
54
attributes.ForEach(func(key string, data interface{}) {
55
fmt.Fprintf(builder, "%s: %s\n\n", formatter.MakeBold(key), types.ToString(data))
56
})
57
58
if len(ReportGenerationMetadataHooks) > 0 {
59
for _, hook := range ReportGenerationMetadataHooks {
60
builder.WriteString(hook(event, formatter))
61
}
62
}
63
64
builder.WriteString(formatter.MakeBold("Template Information"))
65
builder.WriteString("\n\n")
66
builder.WriteString(CreateTemplateInfoTable(&event.Info, formatter))
67
68
if !omitRaw {
69
if event.Request != "" {
70
builder.WriteString(formatter.CreateCodeBlock("Request", types.ToHexOrString(event.Request), "http"))
71
}
72
if event.Response != "" {
73
var responseString string
74
// If the response is larger than 5 kb, truncate it before writing.
75
maxKbSize := 5 * unitutils.Kilo
76
if len(event.Response) > maxKbSize {
77
responseString = event.Response[:maxKbSize]
78
responseString += ".... Truncated ...."
79
} else {
80
responseString = event.Response
81
}
82
builder.WriteString(formatter.CreateCodeBlock("Response", responseString, "http"))
83
}
84
}
85
86
if len(event.ExtractedResults) > 0 || len(event.Metadata) > 0 || event.AnalyzerDetails != "" {
87
builder.WriteString("\n")
88
builder.WriteString(formatter.MakeBold("Extra Information"))
89
builder.WriteString("\n\n")
90
91
if len(event.ExtractedResults) > 0 {
92
builder.WriteString(formatter.MakeBold("Extracted results:"))
93
builder.WriteString("\n\n")
94
95
for _, v := range event.ExtractedResults {
96
builder.WriteString("- ")
97
builder.WriteString(v)
98
builder.WriteString("\n")
99
}
100
builder.WriteString("\n")
101
}
102
if event.AnalyzerDetails != "" {
103
builder.WriteString(formatter.MakeBold("Analyzer Details:"))
104
builder.WriteString("\n\n")
105
106
builder.WriteString(event.AnalyzerDetails)
107
builder.WriteString("\n")
108
}
109
if len(event.Metadata) > 0 {
110
builder.WriteString(formatter.MakeBold("Metadata:"))
111
builder.WriteString("\n\n")
112
for k, v := range event.Metadata {
113
builder.WriteString("- ")
114
builder.WriteString(k)
115
builder.WriteString(": ")
116
builder.WriteString(types.ToString(v))
117
builder.WriteString("\n")
118
}
119
builder.WriteString("\n")
120
}
121
}
122
if event.Interaction != nil {
123
fmt.Fprintf(builder, "%s\n%s", formatter.MakeBold("Interaction Data"), formatter.CreateHorizontalLine())
124
builder.WriteString(event.Interaction.Protocol)
125
if event.Interaction.QType != "" {
126
_, _ = fmt.Fprintf(builder, " (%s)", event.Interaction.QType)
127
}
128
fmt.Fprintf(builder, " Interaction from %s at %s", event.Interaction.RemoteAddress, event.Interaction.UniqueID)
129
130
if event.Interaction.RawRequest != "" {
131
builder.WriteString(formatter.CreateCodeBlock("Interaction Request", event.Interaction.RawRequest, ""))
132
}
133
if event.Interaction.RawResponse != "" {
134
builder.WriteString(formatter.CreateCodeBlock("Interaction Response", event.Interaction.RawResponse, ""))
135
}
136
}
137
138
reference := event.Info.Reference
139
if reference != nil && !reference.IsEmpty() {
140
builder.WriteString("\nReferences: \n")
141
142
referenceSlice := reference.ToSlice()
143
for i, item := range referenceSlice {
144
builder.WriteString("- ")
145
builder.WriteString(item)
146
if len(referenceSlice)-1 != i {
147
builder.WriteString("\n")
148
}
149
}
150
}
151
builder.WriteString("\n")
152
153
if event.CURLCommand != "" {
154
builder.WriteString(
155
formatter.CreateCodeBlock("CURL command", types.ToHexOrString(event.CURLCommand), "sh"),
156
)
157
}
158
159
builder.WriteString("\n" + formatter.CreateHorizontalLine() + "\n")
160
_, _ = fmt.Fprintf(builder, "Generated by %s", formatter.CreateLink("Nuclei "+config.Version, "https://github.com/projectdiscovery/nuclei"))
161
data := builder.String()
162
return data
163
}
164
165
func CreateTemplateInfoTable(templateInfo *model.Info, formatter ResultFormatter) string {
166
rows := [][]string{
167
{"Name", templateInfo.Name},
168
{"Authors", templateInfo.Authors.String()},
169
{"Tags", templateInfo.Tags.String()},
170
{"Severity", templateInfo.SeverityHolder.Severity.String()},
171
}
172
173
if !utils.IsBlank(templateInfo.Description) {
174
rows = append(rows, []string{"Description", lineBreakToHTML(templateInfo.Description)})
175
}
176
177
if !utils.IsBlank(templateInfo.Remediation) {
178
rows = append(rows, []string{"Remediation", lineBreakToHTML(templateInfo.Remediation)})
179
}
180
181
classification := templateInfo.Classification
182
if classification != nil {
183
if classification.CVSSMetrics != "" {
184
rows = append(rows, []string{"CVSS-Metrics", generateCVSSMetricsFromClassification(classification)})
185
}
186
187
rows = append(rows, generateCVECWEIDLinksFromClassification(classification)...)
188
rows = append(rows, []string{"CVSS-Score", strconv.FormatFloat(classification.CVSSScore, 'f', 2, 64)})
189
}
190
191
for key, value := range templateInfo.Metadata {
192
switch value := value.(type) {
193
case string:
194
if !utils.IsBlank(value) {
195
rows = append(rows, []string{key, value})
196
}
197
}
198
}
199
200
table, _ := formatter.CreateTable([]string{"Key", "Value"}, rows)
201
202
return table
203
}
204
205
func generateCVSSMetricsFromClassification(classification *model.Classification) string {
206
var cvssLinkPrefix string
207
if strings.Contains(classification.CVSSMetrics, "CVSS:3.0") {
208
cvssLinkPrefix = "https://www.first.org/cvss/calculator/3.0#"
209
} else if strings.Contains(classification.CVSSMetrics, "CVSS:3.1") {
210
cvssLinkPrefix = "https://www.first.org/cvss/calculator/3.1#"
211
}
212
213
if cvssLinkPrefix == "" {
214
return classification.CVSSMetrics
215
} else {
216
return util.CreateLink(classification.CVSSMetrics, cvssLinkPrefix+classification.CVSSMetrics)
217
}
218
}
219
220
func generateCVECWEIDLinksFromClassification(classification *model.Classification) [][]string {
221
cwes := classification.CWEID.ToSlice()
222
223
cweIDs := make([]string, 0, len(cwes))
224
for _, value := range cwes {
225
parts := strings.Split(value, "-")
226
if len(parts) != 2 {
227
continue
228
}
229
cweIDs = append(cweIDs, util.CreateLink(strings.ToUpper(value), fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", parts[1])))
230
}
231
232
var rows [][]string
233
234
if len(cweIDs) > 0 {
235
rows = append(rows, []string{"CWE-ID", strings.Join(cweIDs, ",")})
236
}
237
238
cves := classification.CVEID.ToSlice()
239
cveIDs := make([]string, 0, len(cves))
240
for _, value := range cves {
241
cveIDs = append(cveIDs, util.CreateLink(strings.ToUpper(value), fmt.Sprintf("https://cve.mitre.org/cgi-bin/cvename.cgi?name=%s", value)))
242
}
243
if len(cveIDs) > 0 {
244
rows = append(rows, []string{"CVE-ID", strings.Join(cveIDs, ",")})
245
}
246
247
return rows
248
}
249
250
func lineBreakToHTML(text string) string {
251
return strings.ReplaceAll(text, "\n", "<br>")
252
}
253
254