mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-18 03:25:27 +00:00
* Switch -json to -jsonl * Add JSON output file * Update docs for EN and ID * Fix linting issue with error wrap * Add -j flag * Fix call for short flag * Correct typo "Ciper" to "Cipher" (#3468) * migrate dsl helper functions to dsl repo (#3461) * migrate dsl pkg code to dsl repo * fix lint error * upgrade dsl dependency * upgrade deps --------- Co-authored-by: Sandeep Singh <sandeep@projectdiscovery.io> * chore(deps): bump github.com/projectdiscovery/httpx in /v2 (#3469) Bumps [github.com/projectdiscovery/httpx](https://github.com/projectdiscovery/httpx) from 1.2.7 to 1.2.9. - [Release notes](https://github.com/projectdiscovery/httpx/releases) - [Changelog](https://github.com/projectdiscovery/httpx/blob/main/.goreleaser.yml) - [Commits](https://github.com/projectdiscovery/httpx/compare/v1.2.7...v1.2.9) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/httpx dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github.com/weppos/publicsuffix-go in /v2 (#3472) Bumps [github.com/weppos/publicsuffix-go](https://github.com/weppos/publicsuffix-go) from 0.20.0 to 0.30.0. - [Release notes](https://github.com/weppos/publicsuffix-go/releases) - [Changelog](https://github.com/weppos/publicsuffix-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/weppos/publicsuffix-go/compare/v0.20.0...v0.30.0) --- updated-dependencies: - dependency-name: github.com/weppos/publicsuffix-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github.com/projectdiscovery/wappalyzergo in /v2 (#3473) Bumps [github.com/projectdiscovery/wappalyzergo](https://github.com/projectdiscovery/wappalyzergo) from 0.0.81 to 0.0.88. - [Release notes](https://github.com/projectdiscovery/wappalyzergo/releases) - [Commits](https://github.com/projectdiscovery/wappalyzergo/compare/v0.0.81...v0.0.88) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/wappalyzergo dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github.com/projectdiscovery/hmap in /v2 (#3470) Bumps [github.com/projectdiscovery/hmap](https://github.com/projectdiscovery/hmap) from 0.0.10 to 0.0.11. - [Release notes](https://github.com/projectdiscovery/hmap/releases) - [Commits](https://github.com/projectdiscovery/hmap/compare/v0.0.10...v0.0.11) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/hmap dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * debug catalog path * use paths instead of filepath for aws path * deps update (#3477) * deps update * fixing gologger via callback * Moved `json-export` flag to the other exporters * Switch "json[-_]exporter to jsonexporter" --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Ramana Reddy <90540245+RamanaReddy0M@users.noreply.github.com> Co-authored-by: Sandeep Singh <sandeep@projectdiscovery.io> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io> Co-authored-by: Mzack9999 <mzack9999@protonmail.com> Co-authored-by: shubhamrasal <shubhamdharmarasal@gmail.com>
253 lines
7.6 KiB
Go
253 lines
7.6 KiB
Go
package reporting
|
|
|
|
import (
|
|
json_exporter "github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/jsonexporter"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"go.uber.org/multierr"
|
|
"gopkg.in/yaml.v2"
|
|
|
|
"errors"
|
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/dedupe"
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/es"
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/markdown"
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/sarif"
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/splunk"
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/github"
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/gitlab"
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/jira"
|
|
errorutil "github.com/projectdiscovery/utils/errors"
|
|
fileutil "github.com/projectdiscovery/utils/file"
|
|
sliceutil "github.com/projectdiscovery/utils/slice"
|
|
)
|
|
|
|
// Filter filters the received event and decides whether to perform
|
|
// reporting for it or not.
|
|
type Filter struct {
|
|
Severities severity.Severities `yaml:"severity"`
|
|
Tags stringslice.StringSlice `yaml:"tags"`
|
|
}
|
|
|
|
var (
|
|
ErrReportingClientCreation = errors.New("could not create reporting client")
|
|
ErrExportClientCreation = errors.New("could not create exporting client")
|
|
)
|
|
|
|
// GetMatch returns true if a filter matches result event
|
|
func (filter *Filter) GetMatch(event *output.ResultEvent) bool {
|
|
return isSeverityMatch(event, filter) && isTagMatch(event, filter) // TODO revisit this
|
|
}
|
|
|
|
func isTagMatch(event *output.ResultEvent, filter *Filter) bool {
|
|
filterTags := filter.Tags
|
|
if filterTags.IsEmpty() {
|
|
return true
|
|
}
|
|
|
|
tags := event.Info.Tags.ToSlice()
|
|
for _, filterTag := range filterTags.ToSlice() {
|
|
if sliceutil.Contains(tags, filterTag) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func isSeverityMatch(event *output.ResultEvent, filter *Filter) bool {
|
|
resultEventSeverity := event.Info.SeverityHolder.Severity // TODO review
|
|
|
|
if len(filter.Severities) == 0 {
|
|
return true
|
|
}
|
|
|
|
return sliceutil.Contains(filter.Severities, resultEventSeverity)
|
|
}
|
|
|
|
// Tracker is an interface implemented by an issue tracker
|
|
type Tracker interface {
|
|
// CreateIssue creates an issue in the tracker
|
|
CreateIssue(event *output.ResultEvent) error
|
|
}
|
|
|
|
// Exporter is an interface implemented by an issue exporter
|
|
type Exporter interface {
|
|
// Close closes the exporter after operation
|
|
Close() error
|
|
// Export exports an issue to an exporter
|
|
Export(event *output.ResultEvent) error
|
|
}
|
|
|
|
// ReportingClient is a client for nuclei issue tracking module
|
|
type ReportingClient struct {
|
|
trackers []Tracker
|
|
exporters []Exporter
|
|
options *Options
|
|
dedupe *dedupe.Storage
|
|
}
|
|
|
|
// New creates a new nuclei issue tracker reporting client
|
|
func New(options *Options, db string) (Client, error) {
|
|
client := &ReportingClient{options: options}
|
|
|
|
if options.GitHub != nil {
|
|
options.GitHub.HttpClient = options.HttpClient
|
|
tracker, err := github.New(options.GitHub)
|
|
if err != nil {
|
|
return nil, errorutil.NewWithErr(err).Wrap(ErrReportingClientCreation)
|
|
}
|
|
client.trackers = append(client.trackers, tracker)
|
|
}
|
|
if options.GitLab != nil {
|
|
options.GitLab.HttpClient = options.HttpClient
|
|
tracker, err := gitlab.New(options.GitLab)
|
|
if err != nil {
|
|
return nil, errorutil.NewWithErr(err).Wrap(ErrReportingClientCreation)
|
|
}
|
|
client.trackers = append(client.trackers, tracker)
|
|
}
|
|
if options.Jira != nil {
|
|
options.Jira.HttpClient = options.HttpClient
|
|
tracker, err := jira.New(options.Jira)
|
|
if err != nil {
|
|
return nil, errorutil.NewWithErr(err).Wrap(ErrReportingClientCreation)
|
|
}
|
|
client.trackers = append(client.trackers, tracker)
|
|
}
|
|
if options.MarkdownExporter != nil {
|
|
exporter, err := markdown.New(options.MarkdownExporter)
|
|
if err != nil {
|
|
return nil, errorutil.NewWithErr(err).Wrap(ErrExportClientCreation)
|
|
}
|
|
client.exporters = append(client.exporters, exporter)
|
|
}
|
|
if options.SarifExporter != nil {
|
|
exporter, err := sarif.New(options.SarifExporter)
|
|
if err != nil {
|
|
return nil, errorutil.NewWithErr(err).Wrap(ErrExportClientCreation)
|
|
}
|
|
client.exporters = append(client.exporters, exporter)
|
|
}
|
|
if options.JSONExporter != nil {
|
|
exporter, err := json_exporter.New(options.JSONExporter)
|
|
if err != nil {
|
|
return nil, errorutil.NewWithErr(err).Wrap(ErrExportClientCreation)
|
|
}
|
|
client.exporters = append(client.exporters, exporter)
|
|
}
|
|
if options.ElasticsearchExporter != nil {
|
|
options.ElasticsearchExporter.HttpClient = options.HttpClient
|
|
exporter, err := es.New(options.ElasticsearchExporter)
|
|
if err != nil {
|
|
return nil, errorutil.NewWithErr(err).Wrap(ErrExportClientCreation)
|
|
}
|
|
client.exporters = append(client.exporters, exporter)
|
|
}
|
|
if options.SplunkExporter != nil {
|
|
options.SplunkExporter.HttpClient = options.HttpClient
|
|
exporter, err := splunk.New(options.SplunkExporter)
|
|
if err != nil {
|
|
return nil, errorutil.NewWithErr(err).Wrap(ErrExportClientCreation)
|
|
}
|
|
client.exporters = append(client.exporters, exporter)
|
|
}
|
|
|
|
storage, err := dedupe.New(db)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client.dedupe = storage
|
|
return client, nil
|
|
}
|
|
|
|
// CreateConfigIfNotExists creates report-config if it doesn't exists
|
|
func CreateConfigIfNotExists() error {
|
|
config, err := config.GetConfigDir()
|
|
if err != nil {
|
|
return errorutil.NewWithErr(err).Msgf("could not get config directory")
|
|
}
|
|
reportingConfig := filepath.Join(config, "report-config.yaml")
|
|
|
|
if fileutil.FileExists(reportingConfig) {
|
|
return nil
|
|
}
|
|
values := stringslice.StringSlice{Value: []string{}}
|
|
|
|
options := &Options{
|
|
AllowList: &Filter{Tags: values},
|
|
DenyList: &Filter{Tags: values},
|
|
GitHub: &github.Options{},
|
|
GitLab: &gitlab.Options{},
|
|
Jira: &jira.Options{},
|
|
MarkdownExporter: &markdown.Options{},
|
|
SarifExporter: &sarif.Options{},
|
|
ElasticsearchExporter: &es.Options{},
|
|
SplunkExporter: &splunk.Options{},
|
|
}
|
|
reportingFile, err := os.Create(reportingConfig)
|
|
if err != nil {
|
|
return errorutil.NewWithErr(err).Msgf("could not create config file")
|
|
}
|
|
defer reportingFile.Close()
|
|
|
|
err = yaml.NewEncoder(reportingFile).Encode(options)
|
|
return err
|
|
}
|
|
|
|
// RegisterTracker registers a custom tracker to the reporter
|
|
func (c *ReportingClient) RegisterTracker(tracker Tracker) {
|
|
c.trackers = append(c.trackers, tracker)
|
|
}
|
|
|
|
// RegisterExporter registers a custom exporter to the reporter
|
|
func (c *ReportingClient) RegisterExporter(exporter Exporter) {
|
|
c.exporters = append(c.exporters, exporter)
|
|
}
|
|
|
|
// Close closes the issue tracker reporting client
|
|
func (c *ReportingClient) Close() {
|
|
c.dedupe.Close()
|
|
for _, exporter := range c.exporters {
|
|
exporter.Close()
|
|
}
|
|
}
|
|
|
|
// CreateIssue creates an issue in the tracker
|
|
func (c *ReportingClient) CreateIssue(event *output.ResultEvent) error {
|
|
if c.options.AllowList != nil && !c.options.AllowList.GetMatch(event) {
|
|
return nil
|
|
}
|
|
if c.options.DenyList != nil && c.options.DenyList.GetMatch(event) {
|
|
return nil
|
|
}
|
|
|
|
unique, err := c.dedupe.Index(event)
|
|
if unique {
|
|
for _, tracker := range c.trackers {
|
|
if trackerErr := tracker.CreateIssue(event); trackerErr != nil {
|
|
err = multierr.Append(err, trackerErr)
|
|
}
|
|
}
|
|
for _, exporter := range c.exporters {
|
|
if exportErr := exporter.Export(event); exportErr != nil {
|
|
err = multierr.Append(err, exportErr)
|
|
}
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (c *ReportingClient) GetReportingOptions() *Options {
|
|
return c.options
|
|
}
|
|
|
|
func (c *ReportingClient) Clear() {
|
|
c.dedupe.Clear()
|
|
}
|