nuclei/v2/pkg/reporting/exporters/sarif/sarif.go

141 lines
3.7 KiB
Go
Raw Normal View History

2021-06-05 12:31:08 +00:00
package sarif
import (
2021-06-05 17:30:59 +00:00
"crypto/sha1"
"encoding/hex"
"io/ioutil"
2021-06-05 12:31:08 +00:00
"os"
2021-06-05 14:36:23 +00:00
"path"
"strings"
2021-06-05 12:31:08 +00:00
"sync"
"github.com/owenrumney/go-sarif/sarif"
"github.com/pkg/errors"
"github.com/projectdiscovery/nuclei/v2/pkg/output"
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/format"
)
2021-06-05 14:38:52 +00:00
// Exporter is an exporter for nuclei sarif output format.
2021-06-05 12:31:08 +00:00
type Exporter struct {
sarif *sarif.Report
run *sarif.Run
mutex *sync.Mutex
2021-06-05 17:30:59 +00:00
home string
tempFile string
options *Options
2021-06-05 12:31:08 +00:00
}
// Options contains the configuration options for sarif exporter client
type Options struct {
// File is the file to export found sarif result to
File string `yaml:"file"`
}
// New creates a new disk exporter integration client based on options.
func New(options *Options) (*Exporter, error) {
report, err := sarif.New(sarif.Version210)
if err != nil {
return nil, errors.Wrap(err, "could not create sarif exporter")
}
2021-06-05 17:30:59 +00:00
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()
2021-06-05 14:36:23 +00:00
home, err := os.UserHomeDir()
if err != nil {
return nil, errors.Wrap(err, "could not get home dir")
}
templatePath := path.Join(home, "nuclei-templates")
2021-06-05 12:31:08 +00:00
run := sarif.NewRun("nuclei", "https://github.com/projectdiscovery/nuclei")
2021-06-05 17:30:59 +00:00
return &Exporter{options: options, tempFile: tempFileName, home: templatePath, sarif: report, run: run, mutex: &sync.Mutex{}}, nil
2021-06-05 12:31:08 +00:00
}
2021-06-05 14:38:52 +00:00
// Export exports a passed result event to sarif structure
2021-06-05 12:31:08 +00:00
func (i *Exporter) Export(event *output.ResultEvent) error {
2021-06-05 14:36:23 +00:00
templatePath := strings.TrimPrefix(event.TemplatePath, i.home)
2021-06-05 17:30:59 +00:00
h := sha1.New()
h.Write([]byte(event.Host))
templateID := event.TemplateID + "-" + hex.EncodeToString(h.Sum(nil))
fullDescription := format.MarkdownDescription(event)
2021-06-05 14:36:23 +00:00
sarifSeverity := getSarifSeverity(event)
var ruleName string
if s, ok := event.Info["name"]; ok {
ruleName = s.(string)
}
2021-06-05 12:31:08 +00:00
2021-06-05 14:36:23 +00:00
var templateURL string
if strings.HasPrefix(event.TemplatePath, i.home) {
templateURL = "https://github.com/projectdiscovery/nuclei-templates/blob/master" + templatePath
}
2021-06-05 12:31:08 +00:00
var ruleDescription string
if d, ok := event.Info["description"]; ok {
ruleDescription = d.(string)
}
2021-06-05 14:38:52 +00:00
i.mutex.Lock()
defer i.mutex.Unlock()
2021-06-05 17:30:59 +00:00
_ = i.run.AddRule(templateID).
2021-06-05 12:31:08 +00:00
WithDescription(ruleName).
2021-06-05 17:30:59 +00:00
WithHelp(fullDescription).
2021-06-05 14:36:23 +00:00
WithHelpURI(templateURL).
2021-06-05 17:30:59 +00:00
WithFullDescription(sarif.NewMultiformatMessageString(ruleDescription))
2021-06-05 14:36:23 +00:00
2021-06-05 17:30:59 +00:00
_ = i.run.AddResult(templateID).
WithMessage(sarif.NewMessage().WithText(event.Host)).
2021-06-05 12:31:08 +00:00
WithLevel(sarifSeverity).
WithLocation(sarif.NewLocation().WithMessage(sarif.NewMessage().WithText(event.Host)).WithPhysicalLocation(
sarif.NewPhysicalLocation().
2021-06-05 17:30:59 +00:00
WithArtifactLocation(sarif.NewArtifactLocation().WithUri(i.tempFile)).
2021-06-05 12:31:08 +00:00
WithRegion(sarif.NewRegion().WithStartColumn(1).WithStartLine(1).WithEndLine(1).WithEndColumn(1)),
))
return nil
}
2021-06-05 14:36:23 +00:00
// getSarifSeverity returns the sarif severity
func getSarifSeverity(event *output.ResultEvent) string {
var ruleSeverity string
if s, ok := event.Info["severity"]; ok {
ruleSeverity = s.(string)
}
switch ruleSeverity {
case "info":
return "none"
case "low", "medium":
return "warning"
case "high", "critical":
return "error"
default:
return "none"
}
}
2021-06-05 12:31:08 +00:00
// Close closes the exporter after operation
func (i *Exporter) Close() error {
2021-06-05 14:38:52 +00:00
i.mutex.Lock()
defer i.mutex.Unlock()
2021-06-05 12:31:08 +00:00
i.sarif.AddRun(i.run)
2021-06-05 17:30:59 +00:00
if len(i.run.Results) == 0 {
return nil // do not write when no results
}
2021-06-05 12:31:08 +00:00
file, err := os.Create(i.options.File)
if err != nil {
return errors.Wrap(err, "could not create sarif output file")
}
defer file.Close()
return i.sarif.Write(file)
}