Merge pull request #753 from gano3s/dev

Do not create a new issue in Jira if it is already opened
dev
Sandeep Singh 2021-09-01 15:33:32 +05:30 committed by GitHub
commit 8300486bec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 4 deletions

View File

@ -33,8 +33,10 @@
# jira contains configuration options for jira issue tracker # jira contains configuration options for jira issue tracker
#jira: #jira:
# # Cloud is the boolean which tells if Jira instance is running in the cloud or on-prem version is used # # cloud is the boolean which tells if Jira instance is running in the cloud or on-prem version is used
# cloud: true # cloud: true
# # update-existing is the boolean which tells if the existing, opened issue should be updated or new one should be created
# update-existing: false
# # URL is the jira application url # # URL is the jira application url
# url: "" # url: ""
# # account-id is the account-id of the jira user or username in case of on-prem Jira # # account-id is the account-id of the jira user or username in case of on-prem Jira

View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
"github.com/andygrunwald/go-jira" "github.com/andygrunwald/go-jira"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v2/pkg/output" "github.com/projectdiscovery/nuclei/v2/pkg/output"
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/format" "github.com/projectdiscovery/nuclei/v2/pkg/reporting/format"
"github.com/projectdiscovery/nuclei/v2/pkg/types" "github.com/projectdiscovery/nuclei/v2/pkg/types"
@ -23,6 +23,8 @@ type Integration struct {
type Options struct { type Options struct {
// Cloud value is set to true when Jira cloud is used // Cloud value is set to true when Jira cloud is used
Cloud bool `yaml:"cloud"` Cloud bool `yaml:"cloud"`
// UpdateExisting value if true, the existing opened issue is updated
UpdateExisting bool `yaml:"update-existing"`
// URL is the URL of the jira server // URL is the URL of the jira server
URL string `yaml:"url"` URL string `yaml:"url"`
// AccountID is the accountID of the jira user. // AccountID is the accountID of the jira user.
@ -54,8 +56,8 @@ func New(options *Options) (*Integration, error) {
return &Integration{jira: jiraClient, options: options}, nil return &Integration{jira: jiraClient, options: options}, nil
} }
// CreateIssue creates an issue in the tracker // CreateNewIssue creates a new issue in the tracker
func (i *Integration) CreateIssue(event *output.ResultEvent) error { func (i *Integration) CreateNewIssue(event *output.ResultEvent) error {
summary := format.Summary(event) summary := format.Summary(event)
fields := &jira.IssueFields{ fields := &jira.IssueFields{
@ -92,6 +94,52 @@ func (i *Integration) CreateIssue(event *output.ResultEvent) error {
return nil return nil
} }
// CreateIssue creates an issue in the tracker or updates the existing one
func (i *Integration) CreateIssue(event *output.ResultEvent) error {
if i.options.UpdateExisting {
issueID, err := i.FindExistingIssue(event)
if err != nil {
return err
} else if issueID != "" {
_, _, err = i.jira.Issue.AddComment(issueID, &jira.Comment{
Body: jiraFormatDescription(event),
})
return err
}
}
return i.CreateNewIssue(event)
}
// FindExistingIssue checks if the issue already exists and returns its ID
func (i *Integration) FindExistingIssue(event *output.ResultEvent) (string, error) {
template := format.GetMatchedTemplate(event)
jql := fmt.Sprintf("summary ~ \"%s\" AND summary ~ \"%s\" AND status = \"Open\"", template, event.Host)
searchOptions := &jira.SearchOptions{
MaxResults: 1, // if any issue exists, then we won't create a new one
}
chunk, resp, err := i.jira.Issue.Search(jql, searchOptions)
if err != nil {
var data string
if resp != nil && resp.Body != nil {
d, _ := ioutil.ReadAll(resp.Body)
data = string(d)
}
return "", fmt.Errorf("%s => %s", err, data)
}
switch resp.Total {
case 0:
return "", nil
case 1:
return chunk[0].ID, nil
default:
gologger.Warning().Msgf("Discovered multiple opened issues %s for the host %s: The issue [%s] will be updated.", template, event.Host, chunk[0].ID)
return chunk[0].ID, nil
}
}
// jiraFormatDescription formats a short description of the generated // jiraFormatDescription formats a short description of the generated
// event by the nuclei scanner in Jira format. // event by the nuclei scanner in Jira format.
func jiraFormatDescription(event *output.ResultEvent) string { // TODO remove the code duplication: format.go <-> jira.go func jiraFormatDescription(event *output.ResultEvent) string { // TODO remove the code duplication: format.go <-> jira.go