Improvements to sarif report

dev
Ice3man543 2021-06-05 23:00:59 +05:30
parent 1977d41a1e
commit d9bb1393d3
6 changed files with 70 additions and 90 deletions

View File

@ -39,7 +39,7 @@ func NewExecuter(requests []*templates.Template, options *protocols.ExecuterOpti
executer.operators = append(executer.operators, &clusteredOperator{ executer.operators = append(executer.operators, &clusteredOperator{
templateID: req.ID, templateID: req.ID,
templateInfo: req.Info, templateInfo: req.Info,
templatePath: req.Options.TemplatePath, templatePath: req.Path,
operator: req.RequestsHTTP[0].CompiledOperators, operator: req.RequestsHTTP[0].CompiledOperators,
}) })
} }

View File

@ -1,7 +1,9 @@
package sarif package sarif
import ( import (
"bytes" "crypto/sha1"
"encoding/hex"
"io/ioutil"
"os" "os"
"path" "path"
"strings" "strings"
@ -11,7 +13,6 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/projectdiscovery/nuclei/v2/pkg/output" "github.com/projectdiscovery/nuclei/v2/pkg/output"
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/format" "github.com/projectdiscovery/nuclei/v2/pkg/reporting/format"
"github.com/projectdiscovery/nuclei/v2/pkg/types"
) )
// Exporter is an exporter for nuclei sarif output format. // Exporter is an exporter for nuclei sarif output format.
@ -20,8 +21,9 @@ type Exporter struct {
run *sarif.Run run *sarif.Run
mutex *sync.Mutex mutex *sync.Mutex
home string home string
options *Options tempFile string
options *Options
} }
// Options contains the configuration options for sarif exporter client // Options contains the configuration options for sarif exporter client
@ -36,6 +38,15 @@ func New(options *Options) (*Exporter, error) {
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not create sarif exporter") return nil, errors.Wrap(err, "could not create sarif exporter")
} }
tempFile, err := ioutil.TempFile("", "sarif-test-*")
if err != nil {
return nil, errors.Wrap(err, "could not create sarif temp file")
}
defer tempFile.Close()
tempFile.WriteString("github.com/projectdiscovery/nuclei Scan Result")
tempFileName := tempFile.Name()
home, err := os.UserHomeDir() home, err := os.UserHomeDir()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get home dir") return nil, errors.Wrap(err, "could not get home dir")
@ -43,16 +54,19 @@ func New(options *Options) (*Exporter, error) {
templatePath := path.Join(home, "nuclei-templates") templatePath := path.Join(home, "nuclei-templates")
run := sarif.NewRun("nuclei", "https://github.com/projectdiscovery/nuclei") run := sarif.NewRun("nuclei", "https://github.com/projectdiscovery/nuclei")
return &Exporter{options: options, home: templatePath, sarif: report, run: run, mutex: &sync.Mutex{}}, nil return &Exporter{options: options, tempFile: tempFileName, home: templatePath, sarif: report, run: run, mutex: &sync.Mutex{}}, nil
} }
// Export exports a passed result event to sarif structure // Export exports a passed result event to sarif structure
func (i *Exporter) Export(event *output.ResultEvent) error { func (i *Exporter) Export(event *output.ResultEvent) error {
templatePath := strings.TrimPrefix(event.TemplatePath, i.home) templatePath := strings.TrimPrefix(event.TemplatePath, i.home)
description := getSarifResultMessage(event, templatePath) h := sha1.New()
h.Write([]byte(event.Host))
templateID := event.TemplateID + "-" + hex.EncodeToString(h.Sum(nil))
fullDescription := format.MarkdownDescription(event)
sarifSeverity := getSarifSeverity(event) sarifSeverity := getSarifSeverity(event)
sarifRuleHelpURIs := getSarifRuleHelpURIFromReferences(event)
var ruleName string var ruleName string
if s, ok := event.Info["name"]; ok { if s, ok := event.Info["name"]; ok {
@ -68,33 +82,22 @@ func (i *Exporter) Export(event *output.ResultEvent) error {
if d, ok := event.Info["description"]; ok { if d, ok := event.Info["description"]; ok {
ruleDescription = d.(string) ruleDescription = d.(string)
} }
builder := &strings.Builder{}
builder.WriteString(ruleDescription)
if sarifRuleHelpURIs != "" {
builder.WriteString("\nReferences: \n")
builder.WriteString(sarifRuleHelpURIs)
}
if templateURL != "" {
builder.WriteString("\nTemplate URL: ")
builder.WriteString(templateURL)
}
ruleHelp := builder.String()
i.mutex.Lock() i.mutex.Lock()
defer i.mutex.Unlock() defer i.mutex.Unlock()
_ = i.run.AddRule(event.TemplateID). _ = i.run.AddRule(templateID).
WithDescription(ruleName). WithDescription(ruleName).
WithHelp(ruleHelp). WithHelp(fullDescription).
WithHelpURI(templateURL). WithHelpURI(templateURL).
WithFullDescription(sarif.NewMultiformatMessageString(sarifRuleHelpURIs)) WithFullDescription(sarif.NewMultiformatMessageString(ruleDescription))
_ = i.run.AddResult(event.TemplateID). _ = i.run.AddResult(templateID).
WithMessage(sarif.NewMessage().WithText(description)). WithMessage(sarif.NewMessage().WithText(event.Host)).
WithLevel(sarifSeverity). WithLevel(sarifSeverity).
WithLocation(sarif.NewLocation().WithMessage(sarif.NewMessage().WithText(event.Host)).WithPhysicalLocation( WithLocation(sarif.NewLocation().WithMessage(sarif.NewMessage().WithText(event.Host)).WithPhysicalLocation(
sarif.NewPhysicalLocation(). sarif.NewPhysicalLocation().
WithArtifactLocation(sarif.NewArtifactLocation().WithUri(event.Type)). WithArtifactLocation(sarif.NewArtifactLocation().WithUri(i.tempFile)).
WithRegion(sarif.NewRegion().WithStartColumn(1).WithStartLine(1).WithEndLine(1).WithEndColumn(1)), WithRegion(sarif.NewRegion().WithStartColumn(1).WithStartLine(1).WithEndLine(1).WithEndColumn(1)),
)) ))
return nil return nil
@ -119,77 +122,15 @@ func getSarifSeverity(event *output.ResultEvent) string {
} }
} }
// getSarifRuleHelpURIFromReferences returns the sarif rule help uri
func getSarifRuleHelpURIFromReferences(event *output.ResultEvent) string {
if d, ok := event.Info["reference"]; ok {
switch v := d.(type) {
case string:
return v
case []interface{}:
slice := types.ToStringSlice(v)
return strings.Join(slice, "\n")
}
}
return ""
}
// getSarifResultMessage gets a sarif result message from event
func getSarifResultMessage(event *output.ResultEvent, templatePath string) string {
template := format.GetMatchedTemplate(event)
builder := &bytes.Buffer{}
builder.WriteString(template)
builder.WriteString(" matched at ")
builder.WriteString(event.Host)
builder.WriteString(" (")
builder.WriteString(strings.ToUpper(event.Type))
builder.WriteString(") => ")
builder.WriteString(event.Matched)
if len(event.ExtractedResults) > 0 || len(event.Metadata) > 0 {
if len(event.ExtractedResults) > 0 {
builder.WriteString(" **Extracted results**:<br><br>")
for _, v := range event.ExtractedResults {
builder.WriteString("- ")
builder.WriteString(v)
builder.WriteString("<br>")
}
builder.WriteString("<br>")
}
if len(event.Metadata) > 0 {
builder.WriteString(" **Metadata**:<br>")
for k, v := range event.Metadata {
builder.WriteString("- ")
builder.WriteString(k)
builder.WriteString(": ")
builder.WriteString(types.ToString(v))
builder.WriteString("<br>")
}
builder.WriteString("<br>")
}
}
if event.Interaction != nil {
builder.WriteString("**Interaction Data**\n---\n")
builder.WriteString(event.Interaction.Protocol)
}
builder.WriteString(" <br>To Reproduce - `nuclei -t ")
builder.WriteString(strings.TrimPrefix(templatePath, "/"))
builder.WriteString(" -target \"")
builder.WriteString(event.Host)
builder.WriteString("\"`")
data := builder.String()
return data
}
// Close closes the exporter after operation // Close closes the exporter after operation
func (i *Exporter) Close() error { func (i *Exporter) Close() error {
i.mutex.Lock() i.mutex.Lock()
defer i.mutex.Unlock() defer i.mutex.Unlock()
i.sarif.AddRun(i.run) i.sarif.AddRun(i.run)
if len(i.run.Results) == 0 {
return nil // do not write when no results
}
file, err := os.Create(i.options.File) file, err := os.Create(i.options.File)
if err != nil { if err != nil {
return errors.Wrap(err, "could not create sarif output file") return errors.Wrap(err, "could not create sarif output file")

View File

@ -110,6 +110,24 @@ func MarkdownDescription(event *output.ResultEvent) string {
builder.WriteString("\n```\n") builder.WriteString("\n```\n")
} }
} }
if d, ok := event.Info["reference"]; ok {
builder.WriteString("\nReference: \n")
switch v := d.(type) {
case string:
builder.WriteString("- ")
builder.WriteString(v)
case []interface{}:
slice := types.ToStringSlice(v)
for i, item := range slice {
builder.WriteString("- ")
builder.WriteString(item)
if len(slice)-1 != i {
builder.WriteString("\n")
}
}
}
}
builder.WriteString("\n---\nGenerated by [Nuclei](https://github.com/projectdiscovery/nuclei)") builder.WriteString("\n---\nGenerated by [Nuclei](https://github.com/projectdiscovery/nuclei)")
data := builder.String() data := builder.String()

View File

@ -153,6 +153,24 @@ func jiraFormatDescription(event *output.ResultEvent) string {
builder.WriteString("\n{code}\n") builder.WriteString("\n{code}\n")
} }
} }
if d, ok := event.Info["reference"]; ok {
builder.WriteString("\nReference: \n")
switch v := d.(type) {
case string:
builder.WriteString("- ")
builder.WriteString(v)
case []interface{}:
slice := types.ToStringSlice(v)
for i, item := range slice {
builder.WriteString("- ")
builder.WriteString(item)
if len(slice)-1 != i {
builder.WriteString("\n")
}
}
}
}
builder.WriteString("\n---\nGenerated by [Nuclei|https://github.com/projectdiscovery/nuclei]") builder.WriteString("\n---\nGenerated by [Nuclei|https://github.com/projectdiscovery/nuclei]")
data := builder.String() data := builder.String()
return data return data

View File

@ -142,6 +142,7 @@ func Parse(filePath string, options protocols.ExecuterOptions) (*Template, error
if template.Executer == nil && template.CompiledWorkflow == nil { if template.Executer == nil && template.CompiledWorkflow == nil {
return nil, errors.New("cannot create template executer") return nil, errors.New("cannot create template executer")
} }
template.Path = filePath
return template, nil return template, nil
} }

View File

@ -35,4 +35,6 @@ type Template struct {
TotalRequests int `yaml:"-" json:"-"` TotalRequests int `yaml:"-" json:"-"`
// Executer is the actual template executor for running template requests // Executer is the actual template executor for running template requests
Executer protocols.Executer `yaml:"-" json:"-"` Executer protocols.Executer `yaml:"-" json:"-"`
Path string `yaml:"-" json:"-"`
} }