diff --git a/v2/cmd/nuclei/issue-tracker-config.yaml b/v2/cmd/nuclei/issue-tracker-config.yaml index fd3cab27..acc991df 100644 --- a/v2/cmd/nuclei/issue-tracker-config.yaml +++ b/v2/cmd/nuclei/issue-tracker-config.yaml @@ -19,6 +19,8 @@ # project-name: "" # # issue-label is the label of the created issue type # issue-label: "" +# # severity-as-label set the sevetiry as the label of the created issue type +# severity-as-label: false # gitlab contains configuration options for gitlab issue tracker #gitlab: @@ -32,6 +34,8 @@ # project-id: "" # # issue-label is the label of the created issue type # issue-label: "" +# # severity-as-label set the sevetiry as the label of the created issue type +# severity-as-label: false # jira contains configuration options for jira issue tracker #jira: @@ -67,4 +71,4 @@ # # Username for the elasticsearch instance # username: test # # Password is the password for elasticsearch instance -# password: test \ No newline at end of file +# password: test diff --git a/v2/pkg/reporting/exporters/es/elasticsearch.go b/v2/pkg/reporting/exporters/es/elasticsearch.go index cf625eb6..6753b0cf 100644 --- a/v2/pkg/reporting/exporters/es/elasticsearch.go +++ b/v2/pkg/reporting/exporters/es/elasticsearch.go @@ -49,6 +49,10 @@ type Exporter struct { // New creates and returns a new exporter for elasticsearch func New(option *Options) (*Exporter, error) { var ei *Exporter + err := validateOptions(options) + if err != nil { + return nil, errors.New("could not parse config") + } client := &http.Client{ Timeout: 5 * time.Second, @@ -81,6 +85,25 @@ func New(option *Options) (*Exporter, error) { return ei, nil } +func validateOptions(options *Options) error { + if options.IP == "" { + return errors.New("IP name is mandatory") + } + if options.Port == 0 { + return errors.New("Port name is mandatory") + } + if options.Username == "" { + return errors.New("Username name is mandatory") + } + if options.Password == "" { + return errors.New("Password name is mandatory") + } + if options.IndexName == "" { + return errors.New("IndexName name is mandatory") + } + return nil +} + // Export exports a passed result event to elasticsearch func (i *Exporter) Export(event *output.ResultEvent) error { // creating a request @@ -105,9 +128,9 @@ func (i *Exporter) Export(event *output.ResultEvent) error { res, err := i.elasticsearch.Do(req) if err != nil { - return err + return err } - + b, err = ioutil.ReadAll(res.Body) if err != nil { return errors.New(err.Error() + "error thrown by elasticsearch " + string(b)) diff --git a/v2/pkg/reporting/trackers/github/github.go b/v2/pkg/reporting/trackers/github/github.go index c794fc1e..bf9ed076 100644 --- a/v2/pkg/reporting/trackers/github/github.go +++ b/v2/pkg/reporting/trackers/github/github.go @@ -22,18 +22,21 @@ type Integration struct { // Options contains the configuration options for github issue tracker client type Options struct { - // BaseURL is the optional self-hosted github application url + // BaseURL (optional) is the self-hosted github application url BaseURL string `yaml:"base-url"` - // Username is the mandatory username of the github user + // Username (mandatory) is the username of the github user Username string `yaml:"username"` - // Owner is the mandatory owner name of the repository for issues. + // Owner (manadatory)is the owner name of the repository for issues. Owner string `yaml:"owner"` - // Token is the mandatory token for github account. + // Token (mandatory) is the token for github account. Token string `yaml:"token"` - // ProjectName is the mandatory name of the repository. + // ProjectName (mandatory) name of the repository. ProjectName string `yaml:"project-name"` - // IssueLabel is the mandatory label of the created issue type + // IssueLabel (optional) it the label of the created issue type IssueLabel string `yaml:"issue-label"` + // SeverityAsLabel (optional) send the severity as the label of the created + // issue. + SeverityAsLabel bool `yaml:"severity-as-label"` } // New creates a new issue tracker integration client based on options. @@ -72,9 +75,6 @@ func validateOptions(options *Options) error { if options.ProjectName == "" { return errors.New("ProjectName name is mandatory") } - if options.IssueLabel == "" { - return errors.New("IssueLabel name is mandatory") - } return nil } @@ -82,12 +82,19 @@ func validateOptions(options *Options) error { func (i *Integration) CreateIssue(event *output.ResultEvent) error { summary := format.Summary(event) description := format.MarkdownDescription(event) + labels := []string{} severityLabel := fmt.Sprintf("Severity: %s", event.Info.SeverityHolder.Severity.String()) + if i.options.SeverityAsLabel && severityLabel != "" { + labels = append(labels, severityLabel) + } + if label := i.options.IssueLabel; label != "" { + labels = append(labels, label) + } req := &github.IssueRequest{ Title: &summary, Body: &description, - Labels: &[]string{i.options.IssueLabel, severityLabel}, + Labels: &labels, Assignees: &[]string{i.options.Username}, } _, _, err := i.client.Issues.Create(context.Background(), i.options.Owner, i.options.ProjectName, req) diff --git a/v2/pkg/reporting/trackers/gitlab/gitlab.go b/v2/pkg/reporting/trackers/gitlab/gitlab.go index cc1c8bcc..a12fe07c 100644 --- a/v2/pkg/reporting/trackers/gitlab/gitlab.go +++ b/v2/pkg/reporting/trackers/gitlab/gitlab.go @@ -19,16 +19,19 @@ type Integration struct { // Options contains the configuration options for gitlab issue tracker client type Options struct { - // BaseURL is the optional self-hosted gitlab application url + // BaseURL (optional) is the self-hosted gitlab application url BaseURL string `yaml:"base-url"` - // Username is the mandatory username of the gitlab user + // Username (mandatory) is the username of the gitlab user Username string `yaml:"username"` - // Token is the mandatory token for gitlab account. + // Token (mandatory) is the token for gitlab account. Token string `yaml:"token"` - // ProjectName is the mandatory name of the repository. + // ProjectName (mandatory) is the name of the repository. ProjectName string `yaml:"project-name"` - // IssueLabel is the mandatory label of the created issue type + // IssueLabel (mandatory) is the label of the created issue type IssueLabel string `yaml:"issue-label"` + // SeverityAsLabel (optional) send the severity as the label of the created + // issue. + SeverityAsLabel bool `yaml:"severity-as-label"` } // New creates a new issue tracker integration client based on options. @@ -62,9 +65,6 @@ func validateOptions(options *Options) error { if options.ProjectName == "" { return errors.New("ProjectName name is mandatory") } - if options.IssueLabel == "" { - return errors.New("IssueLabel name is mandatory") - } return nil } @@ -72,12 +72,19 @@ func validateOptions(options *Options) error { func (i *Integration) CreateIssue(event *output.ResultEvent) error { summary := format.Summary(event) description := format.MarkdownDescription(event) + labels := []string{} severityLabel := fmt.Sprintf("Severity: %s", event.Info.SeverityHolder.Severity.String()) + if i.options.SeverityAsLabel && severityLabel != "" { + labels = append(labels, severityLabel) + } + if label := i.options.IssueLabel; label != "" { + labels = append(labels, label) + } _, _, err := i.client.Issues.CreateIssue(i.options.ProjectName, &gitlab.CreateIssueOptions{ Title: &summary, Description: &description, - Labels: gitlab.Labels{i.options.IssueLabel, severityLabel}, + Labels: labels, AssigneeIDs: []int{i.userID}, }) diff --git a/v2/pkg/reporting/trackers/jira/jira.go b/v2/pkg/reporting/trackers/jira/jira.go index 8f56d71e..befcfb9d 100644 --- a/v2/pkg/reporting/trackers/jira/jira.go +++ b/v2/pkg/reporting/trackers/jira/jira.go @@ -2,6 +2,7 @@ package jira import ( "bytes" + "errors" "fmt" "io/ioutil" "strings" @@ -42,6 +43,10 @@ type Options struct { // New creates a new issue tracker integration client based on options. func New(options *Options) (*Integration, error) { + err := validateOptions(options) + if err != nil { + return nil, errors.New("could not parse config") + } username := options.Email if !options.Cloud { username = options.AccountID @@ -57,10 +62,33 @@ func New(options *Options) (*Integration, error) { return &Integration{jira: jiraClient, options: options}, nil } +func validateOptions(options *Options) error { + if options.URL == "" { + return errors.New("URL name is mandatory") + } + if options.AccountID == "" { + return errors.New("AccountID name is mandatory") + } + if options.Email == "" { + return errors.New("Email name is mandatory") + } + if options.ProjectName == "" { + return errors.New("ProjectName name is mandatory") + } + return nil +} + // CreateNewIssue creates a new issue in the tracker func (i *Integration) CreateNewIssue(event *output.ResultEvent) error { summary := format.Summary(event) - severityLabel := fmt.Sprintf("Severity:%s", event.Info.SeverityHolder.Severity.String()) + labels := []string{} + severityLabel := fmt.Sprintf("Severity: %s", event.Info.SeverityHolder.Severity.String()) + if severityLabel != "" { + labels = append(labels, severityLabel) + } + if label := i.options.IssueType; label != "" { + labels = append(labels, label) + } fields := &jira.IssueFields{ Assignee: &jira.User{AccountID: i.options.AccountID}, @@ -69,7 +97,7 @@ func (i *Integration) CreateNewIssue(event *output.ResultEvent) error { Type: jira.IssueType{Name: i.options.IssueType}, Project: jira.Project{Key: i.options.ProjectName}, Summary: summary, - Labels: []string{severityLabel}, + Labels: labels, } // On-prem version of Jira server does not use AccountID if !i.options.Cloud {