mirror of https://github.com/daffainfo/nuclei.git
Improvements to sarif report
parent
1977d41a1e
commit
d9bb1393d3
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:"-"`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue