converting reporting client to interface

This commit is contained in:
Mzack9999 2023-02-07 09:45:49 +01:00
parent 1e5358b1fa
commit d57aec5ec7
11 changed files with 119 additions and 90 deletions

View File

@ -43,7 +43,7 @@ require (
github.com/valyala/fasttemplate v1.2.2 github.com/valyala/fasttemplate v1.2.2
github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37 github.com/weppos/publicsuffix-go v0.15.1-0.20220724114530-e087fba66a37
github.com/xanzy/go-gitlab v0.79.0 github.com/xanzy/go-gitlab v0.79.0
go.uber.org/multierr v1.9.0 go.uber.org/multierr v1.9.0 // indirect
golang.org/x/net v0.5.0 golang.org/x/net v0.5.0
golang.org/x/oauth2 v0.4.0 golang.org/x/oauth2 v0.4.0
golang.org/x/text v0.6.0 golang.org/x/text v0.6.0

View File

@ -70,7 +70,7 @@ type Runner struct {
catalog catalog.Catalog catalog catalog.Catalog
progress progress.Progress progress progress.Progress
colorizer aurora.Aurora colorizer aurora.Aurora
issuesClient *reporting.Client issuesClient reporting.Client
hmapInputProvider *hybrid.Input hmapInputProvider *hybrid.Input
browser *engine.Browser browser *engine.Browser
ratelimiter *ratelimit.Limiter ratelimiter *ratelimit.Limiter

View File

@ -8,7 +8,7 @@ import (
) )
// WriteResult is a helper for writing results to the output // WriteResult is a helper for writing results to the output
func WriteResult(data *output.InternalWrappedEvent, output output.Writer, progress progress.Progress, issuesClient *reporting.Client) bool { func WriteResult(data *output.InternalWrappedEvent, output output.Writer, progress progress.Progress, issuesClient reporting.Client) bool {
// Handle the case where no result found for the template. // Handle the case where no result found for the template.
// In this case, we just show misc information about the failed // In this case, we just show misc information about the failed
// match for the template. // match for the template.

View File

@ -84,7 +84,7 @@ type Options struct {
// Output is the output writer for nuclei // Output is the output writer for nuclei
Output output.Writer Output output.Writer
// IssuesClient is a client for issue exporting // IssuesClient is a client for issue exporting
IssuesClient *reporting.Client IssuesClient reporting.Client
// Progress is the nuclei progress bar implementation. // Progress is the nuclei progress bar implementation.
Progress progress.Progress Progress progress.Progress
// Debug specifies whether debugging output should be shown for interactsh-client // Debug specifies whether debugging output should be shown for interactsh-client
@ -132,7 +132,7 @@ func New(options *Options) (*Client, error) {
} }
// NewDefaultOptions returns the default options for interactsh client // NewDefaultOptions returns the default options for interactsh client
func NewDefaultOptions(output output.Writer, reporting *reporting.Client, progress progress.Progress) *Options { func NewDefaultOptions(output output.Writer, reporting reporting.Client, progress progress.Progress) *Options {
return &Options{ return &Options{
ServerURL: client.DefaultOptions.ServerURL, ServerURL: client.DefaultOptions.ServerURL,
CacheSize: 5000, CacheSize: 5000,

View File

@ -13,9 +13,9 @@ import (
"sync" "sync"
"time" "time"
"github.com/pkg/errors" "errors"
"github.com/remeh/sizedwaitgroup" "github.com/remeh/sizedwaitgroup"
"go.uber.org/multierr"
"moul.io/http2curl" "moul.io/http2curl"
"github.com/projectdiscovery/gologger" "github.com/projectdiscovery/gologger"
@ -36,6 +36,7 @@ import (
templateTypes "github.com/projectdiscovery/nuclei/v2/pkg/templates/types" templateTypes "github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
"github.com/projectdiscovery/nuclei/v2/pkg/types" "github.com/projectdiscovery/nuclei/v2/pkg/types"
"github.com/projectdiscovery/rawhttp" "github.com/projectdiscovery/rawhttp"
errorutil "github.com/projectdiscovery/utils/errors"
stringsutil "github.com/projectdiscovery/utils/strings" stringsutil "github.com/projectdiscovery/utils/strings"
urlutil "github.com/projectdiscovery/utils/url" urlutil "github.com/projectdiscovery/utils/url"
) )
@ -107,7 +108,7 @@ func (request *Request) executeRaceRequest(input *contextargs.Context, previous
err := request.executeRequest(input, httpRequest, previous, false, callback, 0) err := request.executeRequest(input, httpRequest, previous, false, callback, 0)
mutex.Lock() mutex.Lock()
if err != nil { if err != nil {
requestErr = multierr.Append(requestErr, err) requestErr = errors.Join(requestErr, err)
} }
mutex.Unlock() mutex.Unlock()
}(generatedRequests[i]) }(generatedRequests[i])
@ -154,7 +155,7 @@ func (request *Request) executeParallelHTTP(input *contextargs.Context, dynamicV
err := request.executeRequest(input, httpRequest, previous, false, callback, 0) err := request.executeRequest(input, httpRequest, previous, false, callback, 0)
mutex.Lock() mutex.Lock()
if err != nil { if err != nil {
requestErr = multierr.Append(requestErr, err) requestErr = errors.Join(requestErr, err)
} }
mutex.Unlock() mutex.Unlock()
}(generatedHttpRequest) }(generatedHttpRequest)
@ -217,7 +218,7 @@ func (request *Request) executeTurboHTTP(input *contextargs.Context, dynamicValu
err := request.executeRequest(input, httpRequest, previous, false, callback, 0) err := request.executeRequest(input, httpRequest, previous, false, callback, 0)
mutex.Lock() mutex.Lock()
if err != nil { if err != nil {
requestErr = multierr.Append(requestErr, err) requestErr = errors.Join(requestErr, err)
} }
mutex.Unlock() mutex.Unlock()
}(generatedHttpRequest) }(generatedHttpRequest)
@ -231,7 +232,7 @@ func (request *Request) executeTurboHTTP(input *contextargs.Context, dynamicValu
func (request *Request) executeFuzzingRule(input *contextargs.Context, previous output.InternalEvent, callback protocols.OutputEventCallback) error { func (request *Request) executeFuzzingRule(input *contextargs.Context, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
parsed, err := urlutil.Parse(input.MetaInput.Input) parsed, err := urlutil.Parse(input.MetaInput.Input)
if err != nil { if err != nil {
return errors.Wrap(err, "could not parse url") return errorutil.NewWithErr(err).Msgf("could not parse url")
} }
fuzzRequestCallback := func(gr fuzz.GeneratedRequest) bool { fuzzRequestCallback := func(gr fuzz.GeneratedRequest) bool {
hasInteractMatchers := interactsh.HasMatchers(request.CompiledOperators) hasInteractMatchers := interactsh.HasMatchers(request.CompiledOperators)
@ -304,7 +305,7 @@ func (request *Request) executeFuzzingRule(input *contextargs.Context, previous
return nil return nil
} }
if err != nil { if err != nil {
return errors.Wrap(err, "could not execute rule") return errorutil.NewWithErr(err).Msgf("could not execute rule")
} }
} }
} }
@ -558,7 +559,7 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
connConfiguration.Connection.Cookiejar = input.CookieJar connConfiguration.Connection.Cookiejar = input.CookieJar
client, err := httpclientpool.Get(request.options.Options, connConfiguration) client, err := httpclientpool.Get(request.options.Options, connConfiguration)
if err != nil { if err != nil {
return errors.Wrap(err, "could not get http client") return errorutil.NewWithErr(err).Msgf("could not get http client")
} }
httpclient = client httpclient = client
} }
@ -645,7 +646,7 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
dumpedResponseHeaders, err := httputil.DumpResponse(resp, false) dumpedResponseHeaders, err := httputil.DumpResponse(resp, false)
if err != nil { if err != nil {
return errors.Wrap(err, "could not dump http response") return errorutil.NewWithErr(err).Msgf("could not dump http response")
} }
var dumpedResponse []redirectedResponse var dumpedResponse []redirectedResponse
@ -666,7 +667,7 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
if stringsutil.ContainsAny(err.Error(), "gzip: invalid header") { if stringsutil.ContainsAny(err.Error(), "gzip: invalid header") {
gologger.Warning().Msgf("[%s] Server sent an invalid gzip header and it was not possible to read the uncompressed body for %s: %s", request.options.TemplateID, formedURL, err.Error()) gologger.Warning().Msgf("[%s] Server sent an invalid gzip header and it was not possible to read the uncompressed body for %s: %s", request.options.TemplateID, formedURL, err.Error())
} else if !stringsutil.ContainsAny(err.Error(), "unexpected EOF", "user canceled") { // ignore EOF and random error } else if !stringsutil.ContainsAny(err.Error(), "unexpected EOF", "user canceled") { // ignore EOF and random error
return errors.Wrap(err, "could not read http body") return errorutil.NewWithErr(err).Msgf("could not read http body")
} }
} }
gotData = data gotData = data
@ -674,7 +675,7 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
dumpedResponse, err = dumpResponseWithRedirectChain(resp, data) dumpedResponse, err = dumpResponseWithRedirectChain(resp, data)
if err != nil { if err != nil {
return errors.Wrap(err, "could not read http response with redirect chain") return errorutil.NewWithErr(err).Msgf("could not read http response with redirect chain")
} }
} else { } else {
dumpedResponse = []redirectedResponse{{resp: resp, fullResponse: dumpedResponseHeaders, headers: dumpedResponseHeaders}} dumpedResponse = []redirectedResponse{{resp: resp, fullResponse: dumpedResponseHeaders, headers: dumpedResponseHeaders}}
@ -683,7 +684,7 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
// if nuclei-project is enabled store the response if not previously done // if nuclei-project is enabled store the response if not previously done
if request.options.ProjectFile != nil && !fromCache { if request.options.ProjectFile != nil && !fromCache {
if err := request.options.ProjectFile.Set(dumpedRequest, resp, gotData); err != nil { if err := request.options.ProjectFile.Set(dumpedRequest, resp, gotData); err != nil {
return errors.Wrap(err, "could not store in project file") return errorutil.NewWithErr(err).Msgf("could not store in project file")
} }
} }

View File

@ -50,7 +50,7 @@ type ExecuterOptions struct {
// Options contains configuration options for the executer. // Options contains configuration options for the executer.
Options *types.Options Options *types.Options
// IssuesClient is a client for nuclei issue tracker reporting // IssuesClient is a client for nuclei issue tracker reporting
IssuesClient *reporting.Client IssuesClient reporting.Client
// Progress is a progress client for scan reporting // Progress is a progress client for scan reporting
Progress progress.Progress Progress progress.Progress
// RateLimiter is a rate-limiter for limiting sent number of requests. // RateLimiter is a rate-limiter for limiting sent number of requests.

View File

@ -0,0 +1,15 @@
package reporting
import (
"github.com/projectdiscovery/nuclei/v2/pkg/output"
)
// Client is a client for nuclei issue tracking module
type Client interface {
RegisterTracker(tracker Tracker)
RegisterExporter(exporter Exporter)
Close()
Clear()
CreateIssue(event *output.ResultEvent) error
GetReportingOptions() *Options
}

View File

@ -51,6 +51,18 @@ func New(dbPath string) (*Storage, error) {
return storage, nil return storage, nil
} }
func (s *Storage) Clear() {
var keys [][]byte
iter := s.storage.NewIterator(nil, nil)
for iter.Next() {
keys = append(keys, iter.Key())
}
iter.Release()
for _, key := range keys {
s.storage.Delete(key, nil)
}
}
// Close closes the storage for further operations // Close closes the storage for further operations
func (s *Storage) Close() { func (s *Storage) Close() {
s.storage.Close() s.storage.Close()

View File

@ -0,0 +1,36 @@
package reporting
import (
"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"
"github.com/projectdiscovery/retryablehttp-go"
)
// Options is a configuration file for nuclei reporting module
type Options struct {
// AllowList contains a list of allowed events for reporting module
AllowList *Filter `yaml:"allow-list"`
// DenyList contains a list of denied events for reporting module
DenyList *Filter `yaml:"deny-list"`
// GitHub contains configuration options for GitHub Issue Tracker
GitHub *github.Options `yaml:"github"`
// GitLab contains configuration options for GitLab Issue Tracker
GitLab *gitlab.Options `yaml:"gitlab"`
// Jira contains configuration options for Jira Issue Tracker
Jira *jira.Options `yaml:"jira"`
// MarkdownExporter contains configuration options for Markdown Exporter Module
MarkdownExporter *markdown.Options `yaml:"markdown"`
// SarifExporter contains configuration options for Sarif Exporter Module
SarifExporter *sarif.Options `yaml:"sarif"`
// ElasticsearchExporter contains configuration options for Elasticsearch Exporter Module
ElasticsearchExporter *es.Options `yaml:"elasticsearch"`
// SplunkExporter contains configuration options for splunkhec Exporter Module
SplunkExporter *splunk.Options `yaml:"splunkhec"`
HttpClient *retryablehttp.Client `yaml:"-"`
}

View File

@ -3,12 +3,11 @@ package reporting
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/pkg/errors"
"go.uber.org/multierr"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"errors"
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config" "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/severity"
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice" "github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
@ -21,34 +20,11 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/github" "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/gitlab"
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/jira" "github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/jira"
"github.com/projectdiscovery/retryablehttp-go" errorutil "github.com/projectdiscovery/utils/errors"
fileutil "github.com/projectdiscovery/utils/file" fileutil "github.com/projectdiscovery/utils/file"
sliceutil "github.com/projectdiscovery/utils/slice"
) )
// Options is a configuration file for nuclei reporting module
type Options struct {
// AllowList contains a list of allowed events for reporting module
AllowList *Filter `yaml:"allow-list"`
// DenyList contains a list of denied events for reporting module
DenyList *Filter `yaml:"deny-list"`
// GitHub contains configuration options for GitHub Issue Tracker
GitHub *github.Options `yaml:"github"`
// GitLab contains configuration options for GitLab Issue Tracker
GitLab *gitlab.Options `yaml:"gitlab"`
// Jira contains configuration options for Jira Issue Tracker
Jira *jira.Options `yaml:"jira"`
// MarkdownExporter contains configuration options for Markdown Exporter Module
MarkdownExporter *markdown.Options `yaml:"markdown"`
// SarifExporter contains configuration options for Sarif Exporter Module
SarifExporter *sarif.Options `yaml:"sarif"`
// ElasticsearchExporter contains configuration options for Elasticsearch Exporter Module
ElasticsearchExporter *es.Options `yaml:"elasticsearch"`
// SplunkExporter contains configuration options for splunkhec Exporter Module
SplunkExporter *splunk.Options `yaml:"splunkhec"`
HttpClient *retryablehttp.Client `yaml:"-"`
}
// Filter filters the received event and decides whether to perform // Filter filters the received event and decides whether to perform
// reporting for it or not. // reporting for it or not.
type Filter struct { type Filter struct {
@ -56,9 +32,9 @@ type Filter struct {
Tags stringslice.StringSlice `yaml:"tags"` Tags stringslice.StringSlice `yaml:"tags"`
} }
const ( var (
reportingClientCreationErrorMessage = "could not create reporting client" ErrReportingClientCreation = errors.New("could not create reporting client")
exportClientCreationErrorMessage = "could not create exporting client" ErrExportClientCreation = errors.New("could not create exporting client")
) )
// GetMatch returns true if a filter matches result event // GetMatch returns true if a filter matches result event
@ -73,8 +49,8 @@ func isTagMatch(event *output.ResultEvent, filter *Filter) bool {
} }
tags := event.Info.Tags.ToSlice() tags := event.Info.Tags.ToSlice()
for _, tag := range filterTags.ToSlice() { for _, filterTag := range filterTags.ToSlice() {
if stringSliceContains(tags, tag) { if sliceutil.Contains(tags, filterTag) {
return true return true
} }
} }
@ -89,13 +65,7 @@ func isSeverityMatch(event *output.ResultEvent, filter *Filter) bool {
return true return true
} }
for _, current := range filter.Severities { return sliceutil.Contains(filter.Severities, resultEventSeverity)
if current == resultEventSeverity {
return true
}
}
return false
} }
// Tracker is an interface implemented by an issue tracker // Tracker is an interface implemented by an issue tracker
@ -112,8 +82,8 @@ type Exporter interface {
Export(event *output.ResultEvent) error Export(event *output.ResultEvent) error
} }
// Client is a client for nuclei issue tracking module // ReportingClient is a client for nuclei issue tracking module
type Client struct { type ReportingClient struct {
trackers []Tracker trackers []Tracker
exporters []Exporter exporters []Exporter
options *Options options *Options
@ -121,14 +91,14 @@ type Client struct {
} }
// New creates a new nuclei issue tracker reporting client // New creates a new nuclei issue tracker reporting client
func New(options *Options, db string) (*Client, error) { func New(options *Options, db string) (Client, error) {
client := &Client{options: options} client := &ReportingClient{options: options}
if options.GitHub != nil { if options.GitHub != nil {
options.GitHub.HttpClient = options.HttpClient options.GitHub.HttpClient = options.HttpClient
tracker, err := github.New(options.GitHub) tracker, err := github.New(options.GitHub)
if err != nil { if err != nil {
return nil, errors.Wrap(err, reportingClientCreationErrorMessage) return nil, errors.Join(err, ErrReportingClientCreation)
} }
client.trackers = append(client.trackers, tracker) client.trackers = append(client.trackers, tracker)
} }
@ -136,7 +106,7 @@ func New(options *Options, db string) (*Client, error) {
options.GitLab.HttpClient = options.HttpClient options.GitLab.HttpClient = options.HttpClient
tracker, err := gitlab.New(options.GitLab) tracker, err := gitlab.New(options.GitLab)
if err != nil { if err != nil {
return nil, errors.Wrap(err, reportingClientCreationErrorMessage) return nil, errors.Join(err, ErrReportingClientCreation)
} }
client.trackers = append(client.trackers, tracker) client.trackers = append(client.trackers, tracker)
} }
@ -144,21 +114,21 @@ func New(options *Options, db string) (*Client, error) {
options.Jira.HttpClient = options.HttpClient options.Jira.HttpClient = options.HttpClient
tracker, err := jira.New(options.Jira) tracker, err := jira.New(options.Jira)
if err != nil { if err != nil {
return nil, errors.Wrap(err, reportingClientCreationErrorMessage) return nil, errors.Join(err, ErrReportingClientCreation)
} }
client.trackers = append(client.trackers, tracker) client.trackers = append(client.trackers, tracker)
} }
if options.MarkdownExporter != nil { if options.MarkdownExporter != nil {
exporter, err := markdown.New(options.MarkdownExporter) exporter, err := markdown.New(options.MarkdownExporter)
if err != nil { if err != nil {
return nil, errors.Wrap(err, exportClientCreationErrorMessage) return nil, errors.Join(err, ErrExportClientCreation)
} }
client.exporters = append(client.exporters, exporter) client.exporters = append(client.exporters, exporter)
} }
if options.SarifExporter != nil { if options.SarifExporter != nil {
exporter, err := sarif.New(options.SarifExporter) exporter, err := sarif.New(options.SarifExporter)
if err != nil { if err != nil {
return nil, errors.Wrap(err, exportClientCreationErrorMessage) return nil, errors.Join(err, ErrExportClientCreation)
} }
client.exporters = append(client.exporters, exporter) client.exporters = append(client.exporters, exporter)
} }
@ -166,7 +136,7 @@ func New(options *Options, db string) (*Client, error) {
options.ElasticsearchExporter.HttpClient = options.HttpClient options.ElasticsearchExporter.HttpClient = options.HttpClient
exporter, err := es.New(options.ElasticsearchExporter) exporter, err := es.New(options.ElasticsearchExporter)
if err != nil { if err != nil {
return nil, errors.Wrap(err, exportClientCreationErrorMessage) return nil, errors.Join(err, ErrExportClientCreation)
} }
client.exporters = append(client.exporters, exporter) client.exporters = append(client.exporters, exporter)
} }
@ -174,7 +144,7 @@ func New(options *Options, db string) (*Client, error) {
options.SplunkExporter.HttpClient = options.HttpClient options.SplunkExporter.HttpClient = options.HttpClient
exporter, err := splunk.New(options.SplunkExporter) exporter, err := splunk.New(options.SplunkExporter)
if err != nil { if err != nil {
return nil, errors.Wrap(err, exportClientCreationErrorMessage) return nil, errors.Join(err, ErrExportClientCreation)
} }
client.exporters = append(client.exporters, exporter) client.exporters = append(client.exporters, exporter)
} }
@ -191,7 +161,7 @@ func New(options *Options, db string) (*Client, error) {
func CreateConfigIfNotExists() error { func CreateConfigIfNotExists() error {
config, err := config.GetConfigDir() config, err := config.GetConfigDir()
if err != nil { if err != nil {
return errors.Wrap(err, "could not get config directory") return errorutil.NewWithErr(err).Msgf("could not get config directory")
} }
reportingConfig := filepath.Join(config, "report-config.yaml") reportingConfig := filepath.Join(config, "report-config.yaml")
@ -213,7 +183,7 @@ func CreateConfigIfNotExists() error {
} }
reportingFile, err := os.Create(reportingConfig) reportingFile, err := os.Create(reportingConfig)
if err != nil { if err != nil {
return errors.Wrap(err, "could not create config file") return errorutil.NewWithErr(err).Msgf("could not create config file")
} }
defer reportingFile.Close() defer reportingFile.Close()
@ -222,17 +192,17 @@ func CreateConfigIfNotExists() error {
} }
// RegisterTracker registers a custom tracker to the reporter // RegisterTracker registers a custom tracker to the reporter
func (c *Client) RegisterTracker(tracker Tracker) { func (c *ReportingClient) RegisterTracker(tracker Tracker) {
c.trackers = append(c.trackers, tracker) c.trackers = append(c.trackers, tracker)
} }
// RegisterExporter registers a custom exporter to the reporter // RegisterExporter registers a custom exporter to the reporter
func (c *Client) RegisterExporter(exporter Exporter) { func (c *ReportingClient) RegisterExporter(exporter Exporter) {
c.exporters = append(c.exporters, exporter) c.exporters = append(c.exporters, exporter)
} }
// Close closes the issue tracker reporting client // Close closes the issue tracker reporting client
func (c *Client) Close() { func (c *ReportingClient) Close() {
c.dedupe.Close() c.dedupe.Close()
for _, exporter := range c.exporters { for _, exporter := range c.exporters {
exporter.Close() exporter.Close()
@ -240,7 +210,7 @@ func (c *Client) Close() {
} }
// CreateIssue creates an issue in the tracker // CreateIssue creates an issue in the tracker
func (c *Client) CreateIssue(event *output.ResultEvent) error { func (c *ReportingClient) CreateIssue(event *output.ResultEvent) error {
if c.options.AllowList != nil && !c.options.AllowList.GetMatch(event) { if c.options.AllowList != nil && !c.options.AllowList.GetMatch(event) {
return nil return nil
} }
@ -252,27 +222,22 @@ func (c *Client) CreateIssue(event *output.ResultEvent) error {
if unique { if unique {
for _, tracker := range c.trackers { for _, tracker := range c.trackers {
if trackerErr := tracker.CreateIssue(event); trackerErr != nil { if trackerErr := tracker.CreateIssue(event); trackerErr != nil {
err = multierr.Append(err, trackerErr) err = errors.Join(err, trackerErr)
} }
} }
for _, exporter := range c.exporters { for _, exporter := range c.exporters {
if exportErr := exporter.Export(event); exportErr != nil { if exportErr := exporter.Export(event); exportErr != nil {
err = multierr.Append(err, exportErr) err = errors.Join(err, exportErr)
} }
} }
} }
return err return err
} }
func stringSliceContains(slice []string, item string) bool { func (c *ReportingClient) GetReportingOptions() *Options {
for _, i := range slice {
if strings.EqualFold(i, item) {
return true
}
}
return false
}
func (c *Client) GetReportingOptions() *Options {
return c.options return c.options
} }
func (c *ReportingClient) Clear() {
c.dedupe.Clear()
}

View File

@ -3,6 +3,7 @@ package templates
import ( import (
"encoding/json" "encoding/json"
"errors"
validate "github.com/go-playground/validator/v10" validate "github.com/go-playground/validator/v10"
"github.com/projectdiscovery/nuclei/v2/pkg/model" "github.com/projectdiscovery/nuclei/v2/pkg/model"
@ -18,7 +19,6 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/whois" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/whois"
"github.com/projectdiscovery/nuclei/v2/pkg/templates/types" "github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
"github.com/projectdiscovery/nuclei/v2/pkg/workflows" "github.com/projectdiscovery/nuclei/v2/pkg/workflows"
"go.uber.org/multierr"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -154,7 +154,7 @@ func (template *Template) Type() types.ProtocolType {
func (template *Template) MarshalYAML() ([]byte, error) { func (template *Template) MarshalYAML() ([]byte, error) {
out, marshalErr := yaml.Marshal(template) out, marshalErr := yaml.Marshal(template)
errValidate := validate.New().Struct(template) errValidate := validate.New().Struct(template)
return out, multierr.Append(marshalErr, errValidate) return out, errors.Join(marshalErr, errValidate)
} }
// MarshalYAML forces recursive struct validation after unmarshal operation // MarshalYAML forces recursive struct validation after unmarshal operation
@ -173,7 +173,7 @@ func (template *Template) UnmarshalYAML(unmarshal func(interface{}) error) error
func (template *Template) MarshalJSON() ([]byte, error) { func (template *Template) MarshalJSON() ([]byte, error) {
out, marshalErr := json.Marshal(template) out, marshalErr := json.Marshal(template)
errValidate := validate.New().Struct(template) errValidate := validate.New().Struct(template)
return out, multierr.Append(marshalErr, errValidate) return out, errors.Join(marshalErr, errValidate)
} }
// UnmarshalJSON forces recursive struct validation after unmarshal operation // UnmarshalJSON forces recursive struct validation after unmarshal operation