diff --git a/.gitignore b/.gitignore index f566d91..4188459 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ dist/ jira-ticket-for-new-vulns* ErrorsFile* listOfTicketCreated* +.DS_Store +.idea/ \ No newline at end of file diff --git a/README.md b/README.md index 28aabdc..8902ffb 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Aimed to be executed at regular interval or with a trigger of your choice (webho **This repository is in maintenance mode, no new features are being developed. Bug & security fixes will continue to be delivered. Open source contributions are welcome for small features & fixes (no breaking changes)** ## Installation -You can either download the binaries fron the from [the release page](https://github.com/snyk-tech-services/jira-tickets-for-new-vulns/releases) +You can either download the binaries from the [the release page](https://github.com/snyk-tech-services/jira-tickets-for-new-vulns/releases) or Use `go install github.com/snyk-tech-services/jira-tickets-for-new-vulns@latest` @@ -115,6 +115,12 @@ Use `go install github.com/snyk-tech-services/jira-tickets-for-new-vulns@latest` *Example*: `--labels=app-1234` +- `--dueDate` *optional* + + Set [Jira ticket labels](https://confluence.atlassian.com/jirasoftwareserver/editing-and-collaborating-on-issues-939938928.html) + + *Example*: `--dueDate=2022-12-01` +- - `--priorityScoreThreshold` *optional* Your minimum [Snyk priority score](https://docs.snyk.io/features/fixing-and-prioritizing-issues/starting-to-fix-vulnerabilities/snyk-priority-score) threshold. Can be a number between `0` and `1000`. @@ -183,12 +189,12 @@ The tool does not support IAC project. It will open issue only for code and open Option to get the JIRA ticket priority set based on issue severity. Defaults map to: -Issue severity | JIRA Priority ------ | ----- -critical | Highest -high | High -medium | Medium -low | Low +| Issue severity | JIRA priority | +|:----------------:|:-------------:| +| critical | Highest | +| high | High | +| medium | Medium | +| low | Low | Use `SNYK_JIRA_PRIORITY_FOR_XXX_VULN` env var to override the default an set your value. > *Example*: @@ -319,4 +325,3 @@ Notes: Using the config file above, running `./snyk-jira-sync-macOs --Org=1234 --configFile=./path/to/folder --token=123` the org ID used by the tool will be `1234` and not `a1b2c3de-99b1-4f3f-bfdb-6ee4b4990513` - See 'Extended options' for default values - diff --git a/end_to_end_test.go b/end_to_end_test.go index 12a87e9..35a9153 100644 --- a/end_to_end_test.go +++ b/end_to_end_test.go @@ -26,7 +26,7 @@ func TestEndToEndFunc(t *testing.T) { os.Args = append(os.Args, "--jiraProjectID=123") os.Args = append(os.Args, "--api="+server.URL) - // Keeping the line below => useful for debug but print too much things + // Keeping the line below => useful for debug but print too many things // os.Args = append(os.Args, "-debug=true") // Get the console output @@ -48,7 +48,7 @@ func TestEndToEndFunc(t *testing.T) { assert.True(t, found) // check if the json is valid - file, err := ioutil.ReadFile(path) + file, err := os.ReadFile(path) if err != nil { panic(err) } diff --git a/jira.go b/jira.go index 9613892..5432a43 100644 --- a/jira.go +++ b/jira.go @@ -31,6 +31,7 @@ type Field struct { Assignees *Assignee `json:"assignee,omitempty"` Priority *PriorityType `json:"priority,omitempty"` Labels []string `json:"labels,omitempty"` + DueDate string `json:"dueDate,omitempty"` } // Assignee is the account ID of the Jira user to assign tickets to @@ -130,6 +131,10 @@ func openJiraTicket(flags flags, projectInfo jsn.Json, vulnForJira interface{}, jiraTicket.Fields.Labels = strings.Split(flags.optionalFlags.labels, ",") } + if flags.optionalFlags.dueDate != "" { + jiraTicket.Fields.DueDate = flags.optionalFlags.dueDate + } + if flags.optionalFlags.assigneeID != "" { var assignee Assignee assignee.AccountId = flags.optionalFlags.assigneeID diff --git a/jira_test.go b/jira_test.go index 97c1877..78b1b54 100644 --- a/jira_test.go +++ b/jira_test.go @@ -1047,3 +1047,59 @@ func TestOpenJiraTicketError50xAndRetryFunc(t *testing.T) { return } + +// Test openJiraTickets function +func TestOpenJiraTicketWithDueDateFunc(t *testing.T) { + assert := assert.New(t) + server := HTTPResponseCheckOpenJiraTickets("/v1/org/123/project/12345678-1234-1234-1234-123456789012/issue/SNYK-JS-MINIMIST-559764/jira-issue") + + defer server.Close() + + projectInfo, _ := jsn.NewJson(readFixture("./fixtures/project.json")) + vulnsForJira := make(map[string]interface{}) + err := json.Unmarshal(readFixture("./fixtures/vulnsForJira.json"), &vulnsForJira) + if err != nil { + panic(err) + } + + // setting mandatory options + Mf := MandatoryFlags{} + Mf.orgID = "123" + Mf.endpointAPI = server.URL + Mf.apiToken = "123" + Mf.jiraProjectID = "123" + Mf.jiraProjectKey = "" + + // setting optional options + Of := optionalFlags{} + Of.severity = "" + Of.priorityScoreThreshold = 0 + Of.issueType = "" + Of.debug = false + Of.jiraTicketType = "Bug" + Of.assigneeID = "" + Of.labels = "" + Of.dueDate = "2029-01-01" + Of.priorityIsSeverity = false + Of.projectID = "" + Of.maturityFilterString = "" + Of.dryRun = false + Of.ifUpgradeAvailableOnly = false + + flags := flags{} + flags.mandatoryFlags = Mf + flags.optionalFlags = Of + + // setting debug + cD := debug{} + cD.setDebug(false) + + numberIssueCreated, jiraResponse, NotCreatedIssueId, tickets := openJiraTickets(flags, projectInfo, vulnsForJira, cD) + + assert.Equal("", NotCreatedIssueId) + assert.NotNil(tickets) + assert.Equal(string(readFixture("./fixtures/results/jiraTicketsOpeningResults")), jiraResponse) + fmt.Println("numberIssueCreated :", numberIssueCreated) + + return +} diff --git a/utils.go b/utils.go index 107c429..590fdd9 100644 --- a/utils.go +++ b/utils.go @@ -100,6 +100,7 @@ func (Of *optionalFlags) setOptionalFlags(debugPtr bool, dryRunPtr bool, v viper Of.maturityFilterString = v.GetString("snyk.maturityFilter") Of.assigneeID = v.GetString("jira.assigneeID") Of.labels = v.GetString("jira.labels") + Of.dueDate = v.GetString("jira.dueDate") Of.priorityIsSeverity = v.GetBool("jira.priorityIsSeverity") Of.priorityScoreThreshold = v.GetInt("snyk.priorityScoreThreshold") Of.debug = debugPtr @@ -156,8 +157,9 @@ func (opt *flags) setOption(args []string) { fs.String("severity", "low", "Optional. Your severity threshold") fs.String("maturityFilter", "", "Optional. include only maturity level(s) separated by commas [mature,proof-of-concept,no-known-exploit,no-data]") fs.String("type", "all", "Optional. Your issue type (all|vuln|license)") - fs.String("assigneeId", "", "Optional. The Jira user accountId to assign issues to.") + fs.String("assigneeId", "", "Optional. The Jira user accountId to assign issues to") fs.String("labels", "", "Optional. Jira ticket labels") + fs.String("dueDate", "", "Optional. The built-in Due Date field") fs.Bool("priorityIsSeverity", false, "Boolean. Use issue severity as priority") fs.Int("priorityScoreThreshold", 0, "Optional. Your min priority score threshold [INT between 0 and 1000]") debugPtr = fs.Bool("debug", false, "Optional. Boolean. enable debug mode") @@ -166,7 +168,11 @@ func (opt *flags) setOption(args []string) { fs.Bool("ifUpgradeAvailableOnly", false, "Optional. Boolean. Opens tickets only for upgradable issues") fs.Bool("ifAutoFixableOnly", false, "Optional. Boolean. Opens tickets for issues that are fixable (no effect when using ifUpgradeAvailableOnly)") configFilePtr = fs.String("configFile", "", "Optional. Config file path. Use config file to set parameters") - fs.Parse(args) + errParse := fs.Parse(args) + if errParse != nil { + log.Println("*** ERROR *** Error parsing command line arguments: ", errParse.Error()) + os.Exit(1) + } // Have to set one by one because the name in the config file doesn't correspond to the flag name // This can be done at any time @@ -186,6 +192,7 @@ func (opt *flags) setOption(args []string) { v.BindPFlag("jira.assigneeID", fs.Lookup("assigneeId")) v.BindPFlag("jira.labels", fs.Lookup("labels")) v.BindPFlag("jira.cveInTitle", fs.Lookup("cveInTitle")) + v.BindPFlag("jira.dueDate", fs.Lookup("dueDate")) v.BindPFlag("jira.priorityIsSeverity", fs.Lookup("priorityIsSeverity")) v.BindPFlag("snyk.priorityScoreThreshold", fs.Lookup("priorityScoreThreshold")) v.BindPFlag("snyk.ifUpgradeAvailableOnly", fs.Lookup("ifUpgradeAvailableOnly")) @@ -676,6 +683,12 @@ func checkJiraValue(JiraValues interface{}) (bool, map[string]interface{}) { log.Printf("*** ERROR *** Please check the format config file, %s is of type %s when it should be a string", key, reflect.TypeOf(value).String()) return false, nil } + case "dueDate": + valueType := reflect.TypeOf(value).String() + if valueType != "string" { + log.Printf("*** ERROR *** Please check the format config file, %s is of type %s when it should be a string", key, reflect.TypeOf(value).String()) + return false, nil + } case "priorityIsSeverity": valueType := reflect.TypeOf(value).String() if valueType != "bool" { diff --git a/utils_def.go b/utils_def.go index b096f4f..342d1d4 100644 --- a/utils_def.go +++ b/utils_def.go @@ -33,6 +33,7 @@ type optionalFlags struct { maturityFilterString string assigneeID string labels string + dueDate string priorityIsSeverity bool priorityScoreThreshold int debug bool