Path: blob/dev/pkg/reporting/format/format_utils.go
2070 views
package format12import (3"bytes"4"fmt"5"strconv"6"strings"78"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"9"github.com/projectdiscovery/nuclei/v3/pkg/model"10"github.com/projectdiscovery/nuclei/v3/pkg/output"11"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/markdown/util"12"github.com/projectdiscovery/nuclei/v3/pkg/types"13"github.com/projectdiscovery/nuclei/v3/pkg/utils"14unitutils "github.com/projectdiscovery/utils/unit"15)1617// Summary returns a formatted built one line summary of the event18func Summary(event *output.ResultEvent) string {19return fmt.Sprintf("%s (%s) found on %s", types.ToString(event.Info.Name), GetMatchedTemplateName(event), event.Host)20}2122// GetMatchedTemplateName returns the matched template name from a result event23// together with the found matcher and extractor name, if present24func GetMatchedTemplateName(event *output.ResultEvent) string {25matchedTemplateName := event.TemplateID26if event.MatcherName != "" {27matchedTemplateName += ":" + event.MatcherName28}2930if event.ExtractorName != "" {31matchedTemplateName += ":" + event.ExtractorName32}3334return matchedTemplateName35}3637type reportMetadataEditorHook func(event *output.ResultEvent, formatter ResultFormatter) string3839var (40// ReportGenerationMetadataHooks are the hooks for adding metadata to the report41ReportGenerationMetadataHooks []reportMetadataEditorHook42)4344func CreateReportDescription(event *output.ResultEvent, formatter ResultFormatter, omitRaw bool) string {45template := GetMatchedTemplateName(event)46builder := &bytes.Buffer{}47fmt.Fprintf(builder, "%s: %s matched at %s\n\n", formatter.MakeBold("Details"), formatter.MakeBold(template), event.Host)4849attributes := utils.NewEmptyInsertionOrderedStringMap(3)50attributes.Set("Protocol", strings.ToUpper(event.Type))51attributes.Set("Full URL", event.Matched)52attributes.Set("Timestamp", event.Timestamp.Format("Mon Jan 2 15:04:05 -0700 MST 2006"))53attributes.ForEach(func(key string, data interface{}) {54fmt.Fprintf(builder, "%s: %s\n\n", formatter.MakeBold(key), types.ToString(data))55})5657if len(ReportGenerationMetadataHooks) > 0 {58for _, hook := range ReportGenerationMetadataHooks {59builder.WriteString(hook(event, formatter))60}61}6263builder.WriteString(formatter.MakeBold("Template Information"))64builder.WriteString("\n\n")65builder.WriteString(CreateTemplateInfoTable(&event.Info, formatter))6667if !omitRaw {68if event.Request != "" {69builder.WriteString(formatter.CreateCodeBlock("Request", types.ToHexOrString(event.Request), "http"))70}71if event.Response != "" {72var responseString string73// If the response is larger than 5 kb, truncate it before writing.74maxKbSize := 5 * unitutils.Kilo75if len(event.Response) > maxKbSize {76responseString = event.Response[:maxKbSize]77responseString += ".... Truncated ...."78} else {79responseString = event.Response80}81builder.WriteString(formatter.CreateCodeBlock("Response", responseString, "http"))82}83}8485if len(event.ExtractedResults) > 0 || len(event.Metadata) > 0 || event.AnalyzerDetails != "" {86builder.WriteString("\n")87builder.WriteString(formatter.MakeBold("Extra Information"))88builder.WriteString("\n\n")8990if len(event.ExtractedResults) > 0 {91builder.WriteString(formatter.MakeBold("Extracted results:"))92builder.WriteString("\n\n")9394for _, v := range event.ExtractedResults {95builder.WriteString("- ")96builder.WriteString(v)97builder.WriteString("\n")98}99builder.WriteString("\n")100}101if event.AnalyzerDetails != "" {102builder.WriteString(formatter.MakeBold("Analyzer Details:"))103builder.WriteString("\n\n")104105builder.WriteString(event.AnalyzerDetails)106builder.WriteString("\n")107}108if len(event.Metadata) > 0 {109builder.WriteString(formatter.MakeBold("Metadata:"))110builder.WriteString("\n\n")111for k, v := range event.Metadata {112builder.WriteString("- ")113builder.WriteString(k)114builder.WriteString(": ")115builder.WriteString(types.ToString(v))116builder.WriteString("\n")117}118builder.WriteString("\n")119}120}121if event.Interaction != nil {122fmt.Fprintf(builder, "%s\n%s", formatter.MakeBold("Interaction Data"), formatter.CreateHorizontalLine())123builder.WriteString(event.Interaction.Protocol)124if event.Interaction.QType != "" {125_, _ = fmt.Fprintf(builder, " (%s)", event.Interaction.QType)126}127fmt.Fprintf(builder, " Interaction from %s at %s", event.Interaction.RemoteAddress, event.Interaction.UniqueID)128129if event.Interaction.RawRequest != "" {130builder.WriteString(formatter.CreateCodeBlock("Interaction Request", event.Interaction.RawRequest, ""))131}132if event.Interaction.RawResponse != "" {133builder.WriteString(formatter.CreateCodeBlock("Interaction Response", event.Interaction.RawResponse, ""))134}135}136137reference := event.Info.Reference138if reference != nil && !reference.IsEmpty() {139builder.WriteString("\nReferences: \n")140141referenceSlice := reference.ToSlice()142for i, item := range referenceSlice {143builder.WriteString("- ")144builder.WriteString(item)145if len(referenceSlice)-1 != i {146builder.WriteString("\n")147}148}149}150builder.WriteString("\n")151152if event.CURLCommand != "" {153builder.WriteString(154formatter.CreateCodeBlock("CURL command", types.ToHexOrString(event.CURLCommand), "sh"),155)156}157158builder.WriteString("\n" + formatter.CreateHorizontalLine() + "\n")159_, _ = fmt.Fprintf(builder, "Generated by %s", formatter.CreateLink("Nuclei "+config.Version, "https://github.com/projectdiscovery/nuclei"))160data := builder.String()161return data162}163164func CreateTemplateInfoTable(templateInfo *model.Info, formatter ResultFormatter) string {165rows := [][]string{166{"Name", templateInfo.Name},167{"Authors", templateInfo.Authors.String()},168{"Tags", templateInfo.Tags.String()},169{"Severity", templateInfo.SeverityHolder.Severity.String()},170}171172if !utils.IsBlank(templateInfo.Description) {173rows = append(rows, []string{"Description", lineBreakToHTML(templateInfo.Description)})174}175176if !utils.IsBlank(templateInfo.Remediation) {177rows = append(rows, []string{"Remediation", lineBreakToHTML(templateInfo.Remediation)})178}179180classification := templateInfo.Classification181if classification != nil {182if classification.CVSSMetrics != "" {183rows = append(rows, []string{"CVSS-Metrics", generateCVSSMetricsFromClassification(classification)})184}185186rows = append(rows, generateCVECWEIDLinksFromClassification(classification)...)187rows = append(rows, []string{"CVSS-Score", strconv.FormatFloat(classification.CVSSScore, 'f', 2, 64)})188}189190for key, value := range templateInfo.Metadata {191switch value := value.(type) {192case string:193if !utils.IsBlank(value) {194rows = append(rows, []string{key, value})195}196}197}198199table, _ := formatter.CreateTable([]string{"Key", "Value"}, rows)200201return table202}203204func generateCVSSMetricsFromClassification(classification *model.Classification) string {205var cvssLinkPrefix string206if strings.Contains(classification.CVSSMetrics, "CVSS:3.0") {207cvssLinkPrefix = "https://www.first.org/cvss/calculator/3.0#"208} else if strings.Contains(classification.CVSSMetrics, "CVSS:3.1") {209cvssLinkPrefix = "https://www.first.org/cvss/calculator/3.1#"210}211212if cvssLinkPrefix == "" {213return classification.CVSSMetrics214} else {215return util.CreateLink(classification.CVSSMetrics, cvssLinkPrefix+classification.CVSSMetrics)216}217}218219func generateCVECWEIDLinksFromClassification(classification *model.Classification) [][]string {220cwes := classification.CWEID.ToSlice()221222cweIDs := make([]string, 0, len(cwes))223for _, value := range cwes {224parts := strings.Split(value, "-")225if len(parts) != 2 {226continue227}228cweIDs = append(cweIDs, util.CreateLink(strings.ToUpper(value), fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", parts[1])))229}230231var rows [][]string232233if len(cweIDs) > 0 {234rows = append(rows, []string{"CWE-ID", strings.Join(cweIDs, ",")})235}236237cves := classification.CVEID.ToSlice()238cveIDs := make([]string, 0, len(cves))239for _, value := range cves {240cveIDs = append(cveIDs, util.CreateLink(strings.ToUpper(value), fmt.Sprintf("https://cve.mitre.org/cgi-bin/cvename.cgi?name=%s", value)))241}242if len(cveIDs) > 0 {243rows = append(rows, []string{"CVE-ID", strings.Join(cveIDs, ",")})244}245246return rows247}248249func lineBreakToHTML(text string) string {250return strings.ReplaceAll(text, "\n", "<br>")251}252253254