From 84153c1f18c033b762cf4c8681f681b718bac63c Mon Sep 17 00:00:00 2001 From: sg Date: Thu, 10 Oct 2024 23:48:04 +0100 Subject: [PATCH] close #409, refactor Jira consumer, remove addToDescription, instead offer a default template --- components/consumers/jira/task.yaml | 13 +++++-- components/consumers/jira/utils/utils.go | 4 -- .../pipelines/jira-project/kustomization.yaml | 14 +++++++ .../pipelines/jira-project/pipelinerun.yaml | 37 ++++++++++++++++++ pkg/jira/config.yaml | 14 ------- pkg/jira/config/config_test.go | 9 ----- pkg/jira/config/types.go | 1 - pkg/jira/jira/api.go | 2 +- pkg/jira/jira/api_test.go | 19 +++------- pkg/jira/jira/apiutils.go | 36 ++++++++++-------- pkg/jira/jira/apiutils_test.go | 38 +++++++------------ 11 files changed, 101 insertions(+), 86 deletions(-) create mode 100644 examples/pipelines/jira-project/kustomization.yaml create mode 100644 examples/pipelines/jira-project/pipelinerun.yaml diff --git a/components/consumers/jira/task.yaml b/components/consumers/jira/task.yaml index 7fdd18b75..b5bb27969 100644 --- a/components/consumers/jira/task.yaml +++ b/components/consumers/jira/task.yaml @@ -17,7 +17,7 @@ spec: type: string - name: consumer-jira-user type: string - - name: consumer-jira-config + - name: consumer-jira-project-name type: string default: "" workspaces: @@ -30,7 +30,15 @@ spec: script: | mkdir -p $(workspaces.output.path)/.dracon/consumers/jira cat <<'EOF' > $(workspaces.output.path)/.dracon/consumers/jira/config.json - $(params.consumer-jira-config) + { + "defaultValues": { + "project": "$(params.consumer-jira-project-name)", + "issueType": "Task", + "customFields": null + }, + "descriptionTemplate": "", + "mappings": null + } EOF cat $(workspaces.output.path)/.dracon/consumers/jira/config.json - name: run-consumer @@ -46,7 +54,6 @@ spec: value: $(params.consumer-jira-url) - name: DRACON_JIRA_CONFIG_PATH value: $(workspaces.output.path)/.dracon/consumers/jira/config.json - args: [ "-in", "$(workspaces.output.path)/.dracon/enrichers/", diff --git a/components/consumers/jira/utils/utils.go b/components/consumers/jira/utils/utils.go index 14db83fb9..10ed296b3 100644 --- a/components/consumers/jira/utils/utils.go +++ b/components/consumers/jira/utils/utils.go @@ -35,10 +35,6 @@ func ProcessMessages(allowDuplicates, allowFP bool, sevThreshold int) ([]documen return nil, 0, err } messages, discarded := ProcessEnrichedMessages(responses, allowDuplicates, allowFP, sevThreshold) - if err != nil { - log.Print("Could not Process Enriched messages: ", err) - return nil, 0, err - } return messages, discarded, nil } diff --git a/examples/pipelines/jira-project/kustomization.yaml b/examples/pipelines/jira-project/kustomization.yaml new file mode 100644 index 000000000..3ce46b1c6 --- /dev/null +++ b/examples/pipelines/jira-project/kustomization.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +nameSuffix: -jira-project +components: + - pkg:helm/dracon-oss-components/base + - pkg:helm/dracon-oss-components/git-clone + - pkg:helm/dracon-oss-components/producer-golang-gosec + - pkg:helm/dracon-oss-components/producer-golang-nancy + - pkg:helm/dracon-oss-components/producer-aggregator + - pkg:helm/dracon-oss-components/enricher-custom-annotation + - pkg:helm/dracon-oss-components/enricher-aggregator + - pkg:helm/dracon-oss-components/consumer-stdout-json + - pkg:helm/dracon-oss-components/consumer-jira diff --git a/examples/pipelines/jira-project/pipelinerun.yaml b/examples/pipelines/jira-project/pipelinerun.yaml new file mode 100644 index 000000000..02f765a46 --- /dev/null +++ b/examples/pipelines/jira-project/pipelinerun.yaml @@ -0,0 +1,37 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + generateName: dracon-jira-project- +spec: + pipelineRef: + name: dracon-jira-project + params: + - name: git-clone-url + value: https://github.com/ocurity/e2e-monorepo.git + - name: consumer-jira-url + value: "$jira_url" + - name: consumer-jira-api-token + value: "$jira_token" + - name: consumer-jira-user + value: "$jira_user" + - name: consumer-jira-project-name + value: $jira_project_name + - name: enricher-custom-annotation-base-annotation + value: | + { + "found by other tools": "semgrep,toolmctoolface", + "training": "some training", + "knowledgebase": "cheatsheets" + } + - name: enricher-custom-annotation-name + value: "magic" + workspaces: + - name: output + volumeClaimTemplate: + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi diff --git a/pkg/jira/config.yaml b/pkg/jira/config.yaml index 69d7db888..ad50c3dbd 100644 --- a/pkg/jira/config.yaml +++ b/pkg/jira/config.yaml @@ -39,20 +39,6 @@ defaultValues: ## simple-value: will translate to the special jira use case of "customfield_id":"value" ## simple-value is provided as a workaround when single-value does not work with your jira setup -## Uncomment the fields you want included in the Issue's Description -addToDescription: - - scan_start_time - # - scan_id - - tool_name - # - source - - target - - type - # - severity_text - # - cvss - - confidence_text - # - first_found - # - false_positive - ## You can map fields from the Dracon Result into customfields specific to your Jira Workspace ## Note: You don't have to map all (or any) of the fields mappings: diff --git a/pkg/jira/config/config_test.go b/pkg/jira/config/config_test.go index f1e08daf4..0daf864d9 100644 --- a/pkg/jira/config/config_test.go +++ b/pkg/jira/config/config_test.go @@ -26,15 +26,6 @@ var sampleConfig = Config{ JiraField: "customfield_10001", FieldType: "float", }}, - DescriptionExtras: []string{ - "scan_start_time", - "tool_name", - "target", - "type", - "confidence_text", - "annotations", - "hash", - }, SyncMappings: []JiraToDraconVulnMappings{ { JiraStatus: "Test", diff --git a/pkg/jira/config/types.go b/pkg/jira/config/types.go index 9b1900891..851a4238d 100644 --- a/pkg/jira/config/types.go +++ b/pkg/jira/config/types.go @@ -4,7 +4,6 @@ package config type Config struct { DefaultValues DefaultValues `json:"defaultValues"` Mappings []Mappings `json:"mappings"` - DescriptionExtras []string `json:"addToDescription"` DescriptionTemplate string `json:"descriptionTemplate"` SyncMappings []JiraToDraconVulnMappings `json:"syncMappings"` } diff --git a/pkg/jira/jira/api.go b/pkg/jira/jira/api.go index 6a026b195..30df8e3a6 100644 --- a/pkg/jira/jira/api.go +++ b/pkg/jira/jira/api.go @@ -56,7 +56,7 @@ func (c Client) assembleIssue(draconResult document.Document) *jira.Issue { } } summary, extra := makeSummary(draconResult) - description := makeDescription(draconResult, c.Config.DescriptionExtras, c.Config.DescriptionTemplate) + description := makeDescription(draconResult, c.Config.DescriptionTemplate) if extra != "" { description = fmt.Sprintf(".... %s\n%s", extra, description) } diff --git a/pkg/jira/jira/api_test.go b/pkg/jira/jira/api_test.go index 5a1dd2f24..e2b7114f6 100644 --- a/pkg/jira/jira/api_test.go +++ b/pkg/jira/jira/api_test.go @@ -32,9 +32,8 @@ var ( JiraField: "customfield_10001", FieldType: "float", }}, - DescriptionExtras: []string{"target", "tool_name"}, } - t, _ = time.Parse("0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z") + t, _ = time.Parse(time.RFC3339, "2024-10-10T20:06:33Z") sampleResult = document.Document{ ScanStartTime: t, ScanID: "babbb83-4627-41c6-8ba0-70ee866290e9", @@ -50,6 +49,8 @@ var ( FirstFound: t, FalsePositive: "true", CVE: "CVE-0000-99999", + Annotations: map[string]string{"foo": "bar", "foobar": "baz"}, + Count: "2", } expIssue = jira.Issue{ @@ -60,18 +61,8 @@ var ( Type: jira.IssueType{ Name: "Vulnerability", }, - Description: "Dracon found 'Unit Test Title'" + - " at '//foo1/bar1:baz2'," + - " severity 'SEVERITY_INFO'," + - " rule id: 'test type'," + - " CVSS '0'" + - " Confidence 'CONFIDENCE_INFO'" + - " Original Description: this is a test description," + - " Cve CVE-0000-99999,\n" + - "{code:}\n" + - "target: //foo1/bar1:baz2\n" + - "tool_name: spotbugs\n{code}\n", - Summary: "bar1:baz2 Unit Test Title", + Description: "spotbugs detected 'Unit Test Title' at //foo1/bar1:baz2 during scan started at: 2024-10-10 20:06:33 +0000 UTC with id babbb83-4627-41c6-8ba0-70ee866290e9.\nConfidence: Info\nThis issue has been detected 2 times before, first found on 2024-10-10T20:06:33Z\nOriginal Description is: 'this is a test description'\nspotbugs reported severity as Info\nSmithy enrichers added the following annotations:\nfoo:bar\nfoobar:baz\n", + Summary: "bar1:baz2 Unit Test Title", Components: []*jira.Component{ {Name: "c1"}, {Name: "c2"}, diff --git a/pkg/jira/jira/apiutils.go b/pkg/jira/jira/apiutils.go index a2140ea06..bd810d60c 100644 --- a/pkg/jira/jira/apiutils.go +++ b/pkg/jira/jira/apiutils.go @@ -3,11 +3,12 @@ package jira import ( "bytes" "encoding/json" - "fmt" "log" + "log/slog" "path/filepath" "strconv" "strings" + "time" jira "github.com/andygrunwald/go-jira" "github.com/trivago/tgo/tcontainer" @@ -112,7 +113,25 @@ func draconResultToSTRMaps(draconResult document.Document) (map[string]string, s } // makeDescription creates the description of an issue's enhanced with extra information from the Dracon Result. -func makeDescription(draconResult document.Document, extras []string, template string) string { +func makeDescription(draconResult document.Document, template string) string { + if template == "" { + template = draconResult.ToolName + ` detected '{{.RawIssue.Title}}' at {{.RawIssue.Target}} during scan started at: ` + draconResult.ScanStartTime.String() + ` with id ` + draconResult.ScanID + `. +Confidence: ` + draconResult.ConfidenceText + count, err := strconv.Atoi(draconResult.Count) + if err != nil { + slog.Error("could not convert value to int", slog.String("cound", draconResult.Count), slog.String("error", err.Error())) + } + if count > 0 { + template += ` +This issue has been detected {{.Count}} times before, first found on ` + draconResult.FirstFound.Format(time.RFC3339) + } + template += ` +Original Description is: '{{.RawIssue.Description}}' +` + draconResult.ToolName + ` reported severity as ` + draconResult.SeverityText + ` +Smithy enrichers added the following annotations: +{{ range $key, $value := .Annotations }}{{ $key }}:{{ $value }} +{{ end }}` + } if draconResult.Count == "" { draconResult.Count = "0" } @@ -155,19 +174,6 @@ func makeDescription(draconResult document.Document, extras []string, template s log.Fatal("Could not template enriched issue ", err) } desc := *description - // Append the extra fields to the description - strMap, annotations := draconResultToSTRMaps(draconResult) - if len(extras) > 0 { - desc += "{code:}" + "\n" - for _, s := range extras { - if s == "annotations" { - desc += fmt.Sprintf("%s: %*s\n", s, 25-len(s)+len(annotations), annotations) - } else { - desc += fmt.Sprintf("%s: %*s\n", s, 25-len(s)+len(strMap[s]), strMap[s]) - } - } - desc += "{code}" + "\n" - } return desc } diff --git a/pkg/jira/jira/apiutils_test.go b/pkg/jira/jira/apiutils_test.go index 287197c5e..9539cad4a 100644 --- a/pkg/jira/jira/apiutils_test.go +++ b/pkg/jira/jira/apiutils_test.go @@ -4,7 +4,7 @@ import ( "testing" jira "github.com/andygrunwald/go-jira" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/trivago/tgo/tcontainer" ) @@ -33,7 +33,7 @@ func TestSetDefaultFields(t *testing.T) { }, } - assert.EqualValues(t, res, exp) + require.EqualValues(t, res, exp) } func TestMakeCustomField(t *testing.T) { @@ -53,35 +53,23 @@ func TestMakeCustomField(t *testing.T) { res4 := makeCustomField("simple-value", []string{"test-value"}) exp4 := "test-value" - assert.EqualValues(t, res1, exp1) - assert.EqualValues(t, res2, exp2) - assert.Equal(t, res3, exp3) - assert.Equal(t, res4, exp4) + require.EqualValues(t, res1, exp1) + require.EqualValues(t, res2, exp2) + require.Equal(t, res3, exp3) + require.Equal(t, res4, exp4) } func TestMakeDescription(t *testing.T) { - extras := []string{"tool_name", "target", "confidence_text"} - res := makeDescription(sampleResult, extras, "") - exp := "Dracon found 'Unit Test Title' at '//foo1/bar1:baz2'," + - " severity 'SEVERITY_INFO'," + - " rule id: 'test type'," + - " CVSS '0' " + - "Confidence 'CONFIDENCE_INFO'" + - " Original Description: this is a test description," + - " Cve CVE-0000-99999,\n" + - "{code:}\n" + - "tool_name: spotbugs\n" + - "target: //foo1/bar1:baz2\n" + - "confidence_text: Info\n" + - "{code}\n" - assert.Equal(t, res, exp) + res := makeDescription(sampleResult, "") + exp := "spotbugs detected 'Unit Test Title' at //foo1/bar1:baz2 during scan started at: 2024-10-10 20:06:33 +0000 UTC with id babbb83-4627-41c6-8ba0-70ee866290e9.\nConfidence: Info\nThis issue has been detected 2 times before, first found on 2024-10-10T20:06:33Z\nOriginal Description is: 'this is a test description'\nspotbugs reported severity as Info\nSmithy enrichers added the following annotations:\nfoo:bar\nfoobar:baz\n" + require.Equal(t, res, exp) } func TestMakeSummary(t *testing.T) { res, extra := makeSummary(sampleResult) exp := "bar1:baz2 Unit Test Title" - assert.Equal(t, res, exp) - assert.Equal(t, extra, "") + require.Equal(t, res, exp) + require.Equal(t, extra, "") longTitle := make([]rune, 300) truncatedSummary := make([]rune, 254) @@ -107,7 +95,7 @@ func TestMakeSummary(t *testing.T) { sampleResult.Title = string(longTitle) res, extra = makeSummary(sampleResult) - assert.Equal(t, string(truncatedSummary), res) + require.Equal(t, string(truncatedSummary), res) - assert.Equal(t, string(expectedExtra), extra) + require.Equal(t, string(expectedExtra), extra) }