Skip to content

Commit

Permalink
Markdown Export Sorting (#3961)
Browse files Browse the repository at this point in the history
* Sort markdown exports by host, severity, or template

* Switch default to empty string

* use fileutil to create folder

---------

Co-authored-by: Tarun Koyalwar <[email protected]>
  • Loading branch information
kchason and tarunKoyalwar authored Jul 21, 2023
1 parent 9558e22 commit 759ee3d
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 2 deletions.
7 changes: 7 additions & 0 deletions v2/internal/runner/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,13 @@ func readEnvInputVars(options *types.Options) {
options.GitLabTemplateDisableDownload = getBoolEnvValue("DISABLE_NUCLEI_TEMPLATES_GITLAB_DOWNLOAD")
options.AwsTemplateDisableDownload = getBoolEnvValue("DISABLE_NUCLEI_TEMPLATES_AWS_DOWNLOAD")
options.AzureTemplateDisableDownload = getBoolEnvValue("DISABLE_NUCLEI_TEMPLATES_AZURE_DOWNLOAD")

// Options to modify the behavior of exporters
options.MarkdownExportSortMode = strings.ToLower(os.Getenv("MARKDOWN_EXPORT_SORT_MODE"))
// If the user has not specified a valid sort mode, use the default
if options.MarkdownExportSortMode != "template" && options.MarkdownExportSortMode != "severity" && options.MarkdownExportSortMode != "host" {
options.MarkdownExportSortMode = ""
}
}

func getBoolEnvValue(key string) bool {
Expand Down
2 changes: 2 additions & 0 deletions v2/internal/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,12 +346,14 @@ func createReportingOptions(options *types.Options) (*reporting.Options, error)
reportingOptions.MarkdownExporter = &markdown.Options{
Directory: options.MarkdownExportDirectory,
IncludeRawPayload: !options.OmitRawRequests,
SortMode: options.MarkdownExportSortMode,
}
} else {
reportingOptions = &reporting.Options{}
reportingOptions.MarkdownExporter = &markdown.Options{
Directory: options.MarkdownExportDirectory,
IncludeRawPayload: !options.OmitRawRequests,
SortMode: options.MarkdownExportSortMode,
}
}
}
Expand Down
36 changes: 34 additions & 2 deletions v2/pkg/reporting/exporters/markdown/markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import (
"path/filepath"
"strings"

"github.com/projectdiscovery/gologger"

"github.com/projectdiscovery/nuclei/v2/pkg/output"
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/markdown/util"
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/format"
fileutil "github.com/projectdiscovery/utils/file"
stringsutil "github.com/projectdiscovery/utils/strings"
)

Expand All @@ -25,6 +28,7 @@ type Options struct {
// Directory is the directory to export found results to
Directory string `yaml:"directory"`
IncludeRawPayload bool `yaml:"include-raw-payload"`
SortMode string `yaml:"sort-mode"`
}

// New creates a new markdown exporter integration client based on options.
Expand Down Expand Up @@ -69,7 +73,35 @@ func (exporter *Exporter) Export(event *output.ResultEvent) error {
defer file.Close()

filename := createFileName(event)
host := util.CreateLink(event.Host, filename)

// If the sort mode is set to severity, host, or template, then we need to get a safe version of the name for a
// subdirectory to store the file in.
// This will allow us to sort the files into subdirectories based on the sort mode. The subdirectory will need to
// be created if it does not exist.
fileUrl := filename
subdirectory := ""
switch exporter.options.SortMode {
case "severity":
subdirectory = event.Info.SeverityHolder.Severity.String()
case "host":
subdirectory = event.Host
case "template":
subdirectory = event.TemplateID
}
if subdirectory != "" {
// Sanitize the subdirectory name to remove any characters that are not allowed in a directory name
subdirectory = sanitizeFilename(subdirectory)

// Prepend the subdirectory name to the filename for the fileUrl
fileUrl = filepath.Join(subdirectory, filename)

// Create the subdirectory if it does not exist
if err = fileutil.CreateFolders(filepath.Join(exporter.directory, subdirectory)); err != nil {
gologger.Warning().Msgf("Could not create subdirectory for markdown report: %s", err)
}
}

host := util.CreateLink(event.Host, fileUrl)
finding := event.TemplateID + " " + event.MatcherName
severity := event.Info.SeverityHolder.Severity.String()

Expand All @@ -85,7 +117,7 @@ func (exporter *Exporter) Export(event *output.ResultEvent) error {
dataBuilder.WriteString(format.CreateReportDescription(event, util.MarkdownFormatter{}))
data := dataBuilder.Bytes()

return os.WriteFile(filepath.Join(exporter.directory, filename), data, 0644)
return os.WriteFile(filepath.Join(exporter.directory, subdirectory, filename), data, 0644)
}

func createFileName(event *output.ResultEvent) string {
Expand Down
2 changes: 2 additions & 0 deletions v2/pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ type Options struct {
ReportingConfig string
// MarkdownExportDirectory is the directory to export reports in Markdown format
MarkdownExportDirectory string
// MarkdownExportSortMode is the method to sort the markdown reports (options: severity, template, host, none)
MarkdownExportSortMode string
// SarifExport is the file to export sarif output format to
SarifExport string
// CloudURL is the URL for the nuclei cloud endpoint
Expand Down

0 comments on commit 759ee3d

Please sign in to comment.