diff --git a/v2/internal/runner/cloud.go b/v2/internal/runner/cloud.go index 72d14da3..2fe7137a 100644 --- a/v2/internal/runner/cloud.go +++ b/v2/internal/runner/cloud.go @@ -368,3 +368,26 @@ func (r *Runner) processDataSourceItem(repo, token, Type string) (int64, error) } return ID, nil } + +// addCloudReportingSource adds reporting sources to cloud +func (r *Runner) addCloudReportingSource() error { + rcOptions := r.issuesClient.GetReportingOptions() + if rcOptions == nil { + return nil + } + if rcOptions.Jira != nil { + payload, err := jsoniter.Marshal(rcOptions.Jira) + if err != nil { + return err + } + requestObj := nucleicloud.AddReportingSourceRequest{ + Type: "jira", + Payload: payload, + } + if _, err := r.cloudClient.AddReportingSource(requestObj); err != nil { + return errors.Wrap(err, "could not add reporting source") + } + gologger.Info().Msgf("Reporting source and webhook added for %s: %s", "jira", r.options.CloudURL) + } + return nil +} diff --git a/v2/internal/runner/nucleicloud/cloud.go b/v2/internal/runner/nucleicloud/cloud.go index 0fa1f399..266e31d7 100644 --- a/v2/internal/runner/nucleicloud/cloud.go +++ b/v2/internal/runner/nucleicloud/cloud.go @@ -582,3 +582,26 @@ func (c *Client) sendRequest(req *retryablehttp.Request) (*http.Response, error) } return resp, nil } + +// AddReportingSource adds a new data source +func (c *Client) AddReportingSource(req AddReportingSourceRequest) (*AddReportingSourceResponse, error) { + var buf bytes.Buffer + if err := jsoniter.NewEncoder(&buf).Encode(req); err != nil { + return nil, errors.Wrap(err, "could not encode request") + } + httpReq, err := retryablehttp.NewRequest(http.MethodPost, fmt.Sprintf("%s/reporting/add-source", c.baseURL), bytes.NewReader(buf.Bytes())) + if err != nil { + return nil, errors.Wrap(err, "could not make request") + } + resp, err := c.sendRequest(httpReq) + if err != nil { + return nil, errors.Wrap(err, "could not do request") + } + defer resp.Body.Close() + + var data AddReportingSourceResponse + if err := jsoniter.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "could not decode resp") + } + return &data, nil +} diff --git a/v2/internal/runner/nucleicloud/types.go b/v2/internal/runner/nucleicloud/types.go index a4a292c2..6fc8a94a 100644 --- a/v2/internal/runner/nucleicloud/types.go +++ b/v2/internal/runner/nucleicloud/types.go @@ -1,6 +1,7 @@ package nucleicloud import ( + "encoding/json" "time" "github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity" @@ -152,3 +153,14 @@ type ListScanOutput struct { type ExistsInputResponse struct { Reference string `json:"reference"` } + +// AddReportingSourceRequest is a add reporting source request item. +type AddReportingSourceRequest struct { + Type string `json:"type"` + Payload json.RawMessage `json:"payload"` +} + +// AddReportingSourceResponse is a add reporting source response item. +type AddReportingSourceResponse struct { + Ok string `json:"ok"` +} diff --git a/v2/internal/runner/runner.go b/v2/internal/runner/runner.go index 34026c8a..9e09f1c0 100644 --- a/v2/internal/runner/runner.go +++ b/v2/internal/runner/runner.go @@ -516,6 +516,8 @@ func (r *Runner) RunEnumeration() error { err = r.removeTarget(r.options.RemoveTarget) } else if r.options.RemoveTemplate != "" { err = r.removeTemplate(r.options.RemoveTemplate) + } else if r.options.ReportingConfig != "" { + err = r.addCloudReportingSource() } else { if len(store.Templates())+len(store.Workflows())+len(cloudTemplates) == 0 { return errors.New("no templates provided for scan") diff --git a/v2/pkg/reporting/reporting.go b/v2/pkg/reporting/reporting.go index a6504a60..3610b743 100644 --- a/v2/pkg/reporting/reporting.go +++ b/v2/pkg/reporting/reporting.go @@ -272,3 +272,7 @@ func stringSliceContains(slice []string, item string) bool { } return false } + +func (c *Client) GetReportingOptions() *Options { + return c.options +} diff --git a/v2/pkg/reporting/trackers/jira/jira.go b/v2/pkg/reporting/trackers/jira/jira.go index 15f288f9..323fab05 100644 --- a/v2/pkg/reporting/trackers/jira/jira.go +++ b/v2/pkg/reporting/trackers/jira/jira.go @@ -25,26 +25,28 @@ type Integration struct { // Options contains the configuration options for jira client type Options struct { // Cloud value (optional) is set to true when Jira cloud is used - Cloud bool `yaml:"cloud"` + Cloud bool `yaml:"cloud" json:"cloud"` // UpdateExisting value (optional) if true, the existing opened issue is updated - UpdateExisting bool `yaml:"update-existing"` + UpdateExisting bool `yaml:"update-existing" json:"update_existing"` // URL is the URL of the jira server - URL string `yaml:"url" validate:"required"` + URL string `yaml:"url" json:"url" validate:"required"` // AccountID is the accountID of the jira user. - AccountID string `yaml:"account-id" validate:"required"` + AccountID string `yaml:"account-id" json:"account_id" validate:"required"` // Email is the email of the user for jira instance - Email string `yaml:"email" validate:"required,email"` + Email string `yaml:"email" json:"email" validate:"required,email"` // Token is the token for jira instance. - Token string `yaml:"token" validate:"required"` + Token string `yaml:"token" json:"token" validate:"required"` // ProjectName is the name of the project. - ProjectName string `yaml:"project-name" validate:"required"` + ProjectName string `yaml:"project-name" json:"project_name" validate:"required"` // IssueType (optional) is the name of the created issue type - IssueType string `yaml:"issue-type"` + IssueType string `yaml:"issue-type" json:"issue_type"` // SeverityAsLabel (optional) sends the severity as the label of the created // issue. - SeverityAsLabel bool `yaml:"severity-as-label"` + SeverityAsLabel bool `yaml:"severity-as-label" json:"severity_as_label"` + // Severity (optional) is the severity of the issue. + Severity []string `yaml:"severity" json:"severity"` - HttpClient *retryablehttp.Client `yaml:"-"` + HttpClient *retryablehttp.Client `yaml:"-" json:"-"` } // New creates a new issue tracker integration client based on options.