diff --git a/components/consumers/dependency-track/README.md b/components/consumers/dependency-track/README.md index 41c391789..68cf6673d 100644 --- a/components/consumers/dependency-track/README.md +++ b/components/consumers/dependency-track/README.md @@ -2,8 +2,10 @@ This producer imports SBOM results from Dracon producers into [owasp/dependency-track](https://owasp.org/www-project-dependency-track/). It ignores all other results as dependency-track does not do vulnerability management and Dracon does not have any VEX producers yet. -You can use this producer to generate or keep up to date SBOMs for your projects. +You can use this consumer to generate or keep up to date SBOMs for your projects. +This consumer recognises the annotation from the codeowners enricher and will add a project tag with each username found in the codeowners annotations. +The tag format is `Owner:` ## Testing without Dracon diff --git a/components/consumers/dependency-track/kustomization.yaml b/components/consumers/dependency-track/kustomization.yaml index 91fad87de..c22776c41 100644 --- a/components/consumers/dependency-track/kustomization.yaml +++ b/components/consumers/dependency-track/kustomization.yaml @@ -33,6 +33,8 @@ patches: value: $(params.consumer-dependency-track-token) - name: consumer-dependency-track-project-uuid value: $(params.consumer-dependency-track-project-uuid) + - name: consumer-dependency-track-owner-annotation + value: $(params.consumer-dependency-track-owner-annotation) params: - name: consumer-dependency-track-api-url type: string @@ -44,6 +46,9 @@ patches: type: string - name: consumer-dependency-track-project-uuid type: string + - name: consumer-dependency-track-owner-annotation + type: string + default: Owner target: kind: Pipeline # Add anchors to Task. diff --git a/components/consumers/dependency-track/main.go b/components/consumers/dependency-track/main.go index bf503a073..2b704147e 100644 --- a/components/consumers/dependency-track/main.go +++ b/components/consumers/dependency-track/main.go @@ -4,7 +4,9 @@ import ( "context" "encoding/base64" "flag" + "fmt" "log" + "strings" dtrack "github.com/DependencyTrack/client-go" "github.com/google/uuid" @@ -14,12 +16,13 @@ import ( ) var ( - authURL string - apiKey string - projectName string - projectVersion string - projectUUID string - client *dtrack.Client + authURL string + apiKey string + projectName string + projectVersion string + projectUUID string + client *dtrack.Client + ownerAnnotation string ) func init() { @@ -28,6 +31,7 @@ func init() { flag.StringVar(&projectName, "projectName", "", "dependency track project name") flag.StringVar(&projectUUID, "projectUUID", "", "dependency track project name") flag.StringVar(&projectVersion, "projectVersion", "", "dependency track project version") + flag.StringVar(&ownerAnnotation, "ownerAnnotation", "", "if this consumer is in running after any enricher that adds ownership annotations, then provide the annotation-key for this enricher so it can tag owners as tags") flag.Parse() } @@ -66,25 +70,38 @@ func main() { func uploadBOMSFromEnriched(responses []*v1.EnrichedLaunchToolResponse) ([]string, error) { var tokens []string for _, res := range responses { - var bomIssue *v1.Issue + var bomIssue *v1.EnrichedIssue for _, issue := range res.GetIssues() { if issue.GetRawIssue().GetCycloneDXSBOM() != "" && bomIssue == nil { - bomIssue = issue.GetRawIssue() - } else if bomIssue != nil && *bomIssue.CycloneDXSBOM != "" { - log.Fatalf("Tool response for tool %s is malformed, we expected a single issue with an SBOM as part of the tool, got something else instead", + bomIssue = issue + } else if bomIssue != nil && bomIssue.GetRawIssue().GetCycloneDXSBOM() != "" { + log.Printf("Tool response for tool %s is malformed, we expected a single issue with an SBOM as part of the tool, got something else instead", res.GetOriginalResults().GetToolName()) + continue } } - cdxbom, err := cyclonedx.FromDracon(bomIssue) + cdxbom, err := cyclonedx.FromDracon(bomIssue.GetRawIssue()) if err != nil { return tokens, err } - token, err := uploadBOM(bomIssue.GetCycloneDXSBOM(), cdxbom.Metadata.Component.Version) + token, err := uploadBOM(bomIssue.GetRawIssue().GetCycloneDXSBOM(), cdxbom.Metadata.Component.Version) if err != nil { log.Fatal("could not upload bom to dependency track, err:", err) } log.Println("upload token is", token) tokens = append(tokens, token) + if ownerAnnotation != "" { + log.Println("tagging owners") + owners := []string{} + for key, value := range bomIssue.Annotations { + if strings.Contains(key, ownerAnnotation) { + owners = append(owners, value) + } + } + if err := addOwnersTags(owners); err != nil { + log.Println("could not tag owners, err:", err) + } + } } return tokens, nil } @@ -97,8 +114,9 @@ func uploadBOMsFromRaw(responses []*v1.LaunchToolResponse) ([]string, error) { if *issue.CycloneDXSBOM != "" && bomIssue == nil { bomIssue = issue } else if bomIssue != nil && *bomIssue.CycloneDXSBOM != "" { - log.Fatalf("Tool response for tool %s is malformed, we expected a single issue with an SBOM as part of the tool, got multiple issues with sboms instead", + log.Printf("Tool response for tool %s is malformed, we expected a single issue with an SBOM as part of the tool, got multiple issues with sboms instead", res.GetToolName()) + continue } } cdxbom, err := cyclonedx.FromDracon(bomIssue) @@ -115,6 +133,30 @@ func uploadBOMsFromRaw(responses []*v1.LaunchToolResponse) ([]string, error) { return tokens, nil } +func addOwnersTags(owners []string) error { + // addOwnersTags expects a map of -: tagging owners + // it then adds to the projectUUID the owners in the following tag format: Owner: + uuid := uuid.MustParse(projectUUID) + project, err := client.Project.Get(context.Background(), uuid) + if err != nil { + log.Println("could not add project tags error getting project by uuid, err:", err) + return err + } + for _, owner := range owners { + found := false + for _, t := range project.Tags { + if t.Name == fmt.Sprintf("%s:%s", ownerAnnotation, owner) { + found = true + break + } + } + if !found { + project.Tags = append(project.Tags, dtrack.Tag{Name: fmt.Sprintf("%s:%s", ownerAnnotation, owner)}) + } + } + _, err = client.Project.Update(context.Background(), project) + return err +} func uploadBOM(bom string, projectVersion string) (string, error) { if projectVersion == "" { projectVersion = "Unknown" diff --git a/components/consumers/dependency-track/main_test.go b/components/consumers/dependency-track/main_test.go index d98bf250c..f94ee48bf 100644 --- a/components/consumers/dependency-track/main_test.go +++ b/components/consumers/dependency-track/main_test.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "io" "io/ioutil" "net/http" "net/http/httptest" @@ -15,10 +16,10 @@ import ( ) func TestUploadBomsFromRaw(t *testing.T) { - projUIID := uuid.MustParse("7c78f6c9-b4b0-493c-a912-0bb0a4f221f1") + projUUID := uuid.MustParse("7c78f6c9-b4b0-493c-a912-0bb0a4f221f1") expectedRequest := dtrack.BOMUploadRequest{ ProjectName: "test", - ProjectUUID: &projUIID, + ProjectUUID: &projUUID, ProjectVersion: "2022-1", AutoCreate: false, BOM: "eyJib21Gb3JtYXQiOiJDeWNsb25lRFgiLCJzcGVjVmVyc2lvbiI6IjEuNCIsInNlcmlhbE51bWJlciI6InVybjp1dWlkOjNlNjcxNjg3LTM5NWItNDFmNS1hMzBmLWE1ODkyMWE2OWI3OSIsInZlcnNpb24iOjEsIm1ldGFkYXRhIjp7InRpbWVzdGFtcCI6IjIwMjEtMDEtMTBUMTI6MDA6MDBaIiwiY29tcG9uZW50Ijp7ImJvbS1yZWYiOiJhY21lLWFwcGxpY2F0aW9uIiwidHlwZSI6ImFwcGxpY2F0aW9uIiwibmFtZSI6IkFjbWUgQ2xvdWQgRXhhbXBsZSIsInZlcnNpb24iOiIyMDIyLTEifX0sInNlcnZpY2VzIjpbeyJib20tcmVmIjoiYXBpLWdhdGV3YXkiLCJwcm92aWRlciI6eyJuYW1lIjoiQWNtZSBJbmMiLCJ1cmwiOlsiaHR0cHM6Ly9leGFtcGxlLmNvbSJdfSwiZ3JvdXAiOiJjb20uZXhhbXBsZSIsIm5hbWUiOiJBUEkgR2F0ZXdheSIsInZlcnNpb24iOiIyMDIyLTEiLCJkZXNjcmlwdGlvbiI6IkV4YW1wbGUgQVBJIEdhdGV3YXkiLCJlbmRwb2ludHMiOlsiaHR0cHM6Ly9leGFtcGxlLmNvbS8iLCJodHRwczovL2V4YW1wbGUuY29tL2FwcCJdLCJhdXRoZW50aWNhdGVkIjpmYWxzZSwieC10cnVzdC1ib3VuZGFyeSI6dHJ1ZSwiZGF0YSI6W3siZmxvdyI6ImJpLWRpcmVjdGlvbmFsIiwiY2xhc3NpZmljYXRpb24iOiJQSUkifSx7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUElGSSJ9LHsiZmxvdyI6ImJpLWRpcmVjdGlvbmFsIiwiY2xhc3NpZmljYXRpb24iOiJQdWJsaWMifV0sImV4dGVybmFsUmVmZXJlbmNlcyI6W3sidXJsIjoiaHR0cDovL2V4YW1wbGUuY29tL2FwcC9zd2FnZ2VyIiwidHlwZSI6ImRvY3VtZW50YXRpb24ifV0sInNlcnZpY2VzIjpbeyJib20tcmVmIjoibXMtMS5leGFtcGxlLmNvbSIsInByb3ZpZGVyIjp7Im5hbWUiOiJBY21lIEluYyIsInVybCI6WyJodHRwczovL2V4YW1wbGUuY29tIl19LCJncm91cCI6ImNvbS5leGFtcGxlIiwibmFtZSI6Ik1pY3Jvc2VydmljZSAxIiwidmVyc2lvbiI6IjIwMjItMSIsImRlc2NyaXB0aW9uIjoiRXhhbXBsZSBNaWNyb3NlcnZpY2UiLCJlbmRwb2ludHMiOlsiaHR0cHM6Ly9tcy0xLmV4YW1wbGUuY29tIl0sImF1dGhlbnRpY2F0ZWQiOnRydWUsIngtdHJ1c3QtYm91bmRhcnkiOmZhbHNlLCJkYXRhIjpbeyJmbG93IjoiYmktZGlyZWN0aW9uYWwiLCJjbGFzc2lmaWNhdGlvbiI6IlBJSSJ9XSwiZXh0ZXJuYWxSZWZlcmVuY2VzIjpbeyJ1cmwiOiJodHRwczovL21zLTEuZXhhbXBsZS5jb20vc3dhZ2dlciIsInR5cGUiOiJkb2N1bWVudGF0aW9uIn1dfSx7ImJvbS1yZWYiOiJtcy0yLmV4YW1wbGUuY29tIiwicHJvdmlkZXIiOnsibmFtZSI6IkFjbWUgSW5jIiwidXJsIjpbImh0dHBzOi8vZXhhbXBsZS5jb20iXX0sImdyb3VwIjoiY29tLmV4YW1wbGUiLCJuYW1lIjoiTWljcm9zZXJ2aWNlIDIiLCJ2ZXJzaW9uIjoiMjAyMi0xIiwiZGVzY3JpcHRpb24iOiJFeGFtcGxlIE1pY3Jvc2VydmljZSIsImVuZHBvaW50cyI6WyJodHRwczovL21zLTIuZXhhbXBsZS5jb20iXSwiYXV0aGVudGljYXRlZCI6dHJ1ZSwieC10cnVzdC1ib3VuZGFyeSI6ZmFsc2UsImRhdGEiOlt7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUElGSSJ9XSwiZXh0ZXJuYWxSZWZlcmVuY2VzIjpbeyJ1cmwiOiJodHRwczovL21zLTIuZXhhbXBsZS5jb20vc3dhZ2dlciIsInR5cGUiOiJkb2N1bWVudGF0aW9uIn1dfSx7ImJvbS1yZWYiOiJtcy0zLmV4YW1wbGUuY29tIiwicHJvdmlkZXIiOnsibmFtZSI6IkFjbWUgSW5jIiwidXJsIjpbImh0dHBzOi8vZXhhbXBsZS5jb20iXX0sImdyb3VwIjoiY29tLmV4YW1wbGUiLCJuYW1lIjoiTWljcm9zZXJ2aWNlIDMiLCJ2ZXJzaW9uIjoiMjAyMi0xIiwiZGVzY3JpcHRpb24iOiJFeGFtcGxlIE1pY3Jvc2VydmljZSIsImVuZHBvaW50cyI6WyJodHRwczovL21zLTMuZXhhbXBsZS5jb20iXSwiYXV0aGVudGljYXRlZCI6dHJ1ZSwieC10cnVzdC1ib3VuZGFyeSI6ZmFsc2UsImRhdGEiOlt7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUHVibGljIn1dLCJleHRlcm5hbFJlZmVyZW5jZXMiOlt7InVybCI6Imh0dHBzOi8vbXMtMy5leGFtcGxlLmNvbS9zd2FnZ2VyIiwidHlwZSI6ImRvY3VtZW50YXRpb24ifV19LHsiYm9tLXJlZiI6Im1zLTEtcGdzcWwuZXhhbXBsZS5jb20iLCJncm91cCI6Im9yZy5wb3N0Z3Jlc3FsIiwibmFtZSI6IlBvc3RncmVzIiwidmVyc2lvbiI6IjE0LjEiLCJkZXNjcmlwdGlvbiI6IlBvc3RncmVzIGRhdGFiYXNlIGZvciBNaWNyb3NlcnZpY2UgIzEiLCJlbmRwb2ludHMiOlsiaHR0cHM6Ly9tcy0xLXBnc3FsLmV4YW1wbGUuY29tOjU0MzIiXSwiYXV0aGVudGljYXRlZCI6dHJ1ZSwieC10cnVzdC1ib3VuZGFyeSI6ZmFsc2UsImRhdGEiOlt7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUElJIn1dfSx7ImJvbS1yZWYiOiJzMy1leGFtcGxlLmFtYXpvbi5jb20iLCJncm91cCI6ImNvbS5hbWF6b24iLCJuYW1lIjoiUzMiLCJkZXNjcmlwdGlvbiI6IlMzIGJ1Y2tldCIsImVuZHBvaW50cyI6WyJodHRwczovL3MzLWV4YW1wbGUuYW1hem9uLmNvbSJdLCJhdXRoZW50aWNhdGVkIjp0cnVlLCJ4LXRydXN0LWJvdW5kYXJ5Ijp0cnVlLCJkYXRhIjpbeyJmbG93IjoiYmktZGlyZWN0aW9uYWwiLCJjbGFzc2lmaWNhdGlvbiI6IlB1YmxpYyJ9XX1dfV0sImRlcGVuZGVuY2llcyI6W3sicmVmIjoiYWNtZS1hcHBsaWNhdGlvbiIsImRlcGVuZHNPbiI6WyJhcGktZ2F0ZXdheSJdfSx7InJlZiI6ImFwaS1nYXRld2F5IiwiZGVwZW5kc09uIjpbIm1zLTEuZXhhbXBsZS5jb20iLCJtcy0yLmV4YW1wbGUuY29tIiwibXMtMy5leGFtcGxlLmNvbSJdfSx7InJlZiI6Im1zLTEuZXhhbXBsZS5jb20iLCJkZXBlbmRzT24iOlsibXMtMS1wZ3NxbC5leGFtcGxlLmNvbSJdfSx7InJlZiI6Im1zLTIuZXhhbXBsZS5jb20iLCJkZXBlbmRzT24iOltdfSx7InJlZiI6Im1zLTMuZXhhbXBsZS5jb20iLCJkZXBlbmRzT24iOlsiczMtZXhhbXBsZS5hbWF6b24uY29tIl19XX0=", @@ -32,11 +33,9 @@ func TestUploadBomsFromRaw(t *testing.T) { json.Unmarshal(body, &actualRequest) assert.Equal(t, expectedRequest, actualRequest) w.Write([]byte("{\"Token\":\"" + expectedToken + "\"}")) - w.WriteHeader(http.StatusOK) })) defer ts.Close() - - projectUUID = "7c78f6c9-b4b0-493c-a912-0bb0a4f221f1" + projectUUID = projUUID.String() apiKey = "test" projectName = "test" c, err := dtrack.NewClient(ts.URL, dtrack.WithAPIKey(apiKey)) @@ -56,15 +55,14 @@ func TestUploadBomsFromRaw(t *testing.T) { } func TestUploadBomsFromEnriched(t *testing.T) { - projUIID := uuid.MustParse("7c78f6c9-b4b0-493c-a912-0bb0a4f221f1") + projUUID := uuid.MustParse("7c78f6c9-b4b0-493c-a912-0bb0a4f221f1") expectedRequest := dtrack.BOMUploadRequest{ ProjectName: "test", - ProjectUUID: &projUIID, + ProjectUUID: &projUUID, ProjectVersion: "2022-1", AutoCreate: false, BOM: "eyJib21Gb3JtYXQiOiJDeWNsb25lRFgiLCJzcGVjVmVyc2lvbiI6IjEuNCIsInNlcmlhbE51bWJlciI6InVybjp1dWlkOjNlNjcxNjg3LTM5NWItNDFmNS1hMzBmLWE1ODkyMWE2OWI3OSIsInZlcnNpb24iOjEsIm1ldGFkYXRhIjp7InRpbWVzdGFtcCI6IjIwMjEtMDEtMTBUMTI6MDA6MDBaIiwiY29tcG9uZW50Ijp7ImJvbS1yZWYiOiJhY21lLWFwcGxpY2F0aW9uIiwidHlwZSI6ImFwcGxpY2F0aW9uIiwibmFtZSI6IkFjbWUgQ2xvdWQgRXhhbXBsZSIsInZlcnNpb24iOiIyMDIyLTEifX0sInNlcnZpY2VzIjpbeyJib20tcmVmIjoiYXBpLWdhdGV3YXkiLCJwcm92aWRlciI6eyJuYW1lIjoiQWNtZSBJbmMiLCJ1cmwiOlsiaHR0cHM6Ly9leGFtcGxlLmNvbSJdfSwiZ3JvdXAiOiJjb20uZXhhbXBsZSIsIm5hbWUiOiJBUEkgR2F0ZXdheSIsInZlcnNpb24iOiIyMDIyLTEiLCJkZXNjcmlwdGlvbiI6IkV4YW1wbGUgQVBJIEdhdGV3YXkiLCJlbmRwb2ludHMiOlsiaHR0cHM6Ly9leGFtcGxlLmNvbS8iLCJodHRwczovL2V4YW1wbGUuY29tL2FwcCJdLCJhdXRoZW50aWNhdGVkIjpmYWxzZSwieC10cnVzdC1ib3VuZGFyeSI6dHJ1ZSwiZGF0YSI6W3siZmxvdyI6ImJpLWRpcmVjdGlvbmFsIiwiY2xhc3NpZmljYXRpb24iOiJQSUkifSx7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUElGSSJ9LHsiZmxvdyI6ImJpLWRpcmVjdGlvbmFsIiwiY2xhc3NpZmljYXRpb24iOiJQdWJsaWMifV0sImV4dGVybmFsUmVmZXJlbmNlcyI6W3sidXJsIjoiaHR0cDovL2V4YW1wbGUuY29tL2FwcC9zd2FnZ2VyIiwidHlwZSI6ImRvY3VtZW50YXRpb24ifV0sInNlcnZpY2VzIjpbeyJib20tcmVmIjoibXMtMS5leGFtcGxlLmNvbSIsInByb3ZpZGVyIjp7Im5hbWUiOiJBY21lIEluYyIsInVybCI6WyJodHRwczovL2V4YW1wbGUuY29tIl19LCJncm91cCI6ImNvbS5leGFtcGxlIiwibmFtZSI6Ik1pY3Jvc2VydmljZSAxIiwidmVyc2lvbiI6IjIwMjItMSIsImRlc2NyaXB0aW9uIjoiRXhhbXBsZSBNaWNyb3NlcnZpY2UiLCJlbmRwb2ludHMiOlsiaHR0cHM6Ly9tcy0xLmV4YW1wbGUuY29tIl0sImF1dGhlbnRpY2F0ZWQiOnRydWUsIngtdHJ1c3QtYm91bmRhcnkiOmZhbHNlLCJkYXRhIjpbeyJmbG93IjoiYmktZGlyZWN0aW9uYWwiLCJjbGFzc2lmaWNhdGlvbiI6IlBJSSJ9XSwiZXh0ZXJuYWxSZWZlcmVuY2VzIjpbeyJ1cmwiOiJodHRwczovL21zLTEuZXhhbXBsZS5jb20vc3dhZ2dlciIsInR5cGUiOiJkb2N1bWVudGF0aW9uIn1dfSx7ImJvbS1yZWYiOiJtcy0yLmV4YW1wbGUuY29tIiwicHJvdmlkZXIiOnsibmFtZSI6IkFjbWUgSW5jIiwidXJsIjpbImh0dHBzOi8vZXhhbXBsZS5jb20iXX0sImdyb3VwIjoiY29tLmV4YW1wbGUiLCJuYW1lIjoiTWljcm9zZXJ2aWNlIDIiLCJ2ZXJzaW9uIjoiMjAyMi0xIiwiZGVzY3JpcHRpb24iOiJFeGFtcGxlIE1pY3Jvc2VydmljZSIsImVuZHBvaW50cyI6WyJodHRwczovL21zLTIuZXhhbXBsZS5jb20iXSwiYXV0aGVudGljYXRlZCI6dHJ1ZSwieC10cnVzdC1ib3VuZGFyeSI6ZmFsc2UsImRhdGEiOlt7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUElGSSJ9XSwiZXh0ZXJuYWxSZWZlcmVuY2VzIjpbeyJ1cmwiOiJodHRwczovL21zLTIuZXhhbXBsZS5jb20vc3dhZ2dlciIsInR5cGUiOiJkb2N1bWVudGF0aW9uIn1dfSx7ImJvbS1yZWYiOiJtcy0zLmV4YW1wbGUuY29tIiwicHJvdmlkZXIiOnsibmFtZSI6IkFjbWUgSW5jIiwidXJsIjpbImh0dHBzOi8vZXhhbXBsZS5jb20iXX0sImdyb3VwIjoiY29tLmV4YW1wbGUiLCJuYW1lIjoiTWljcm9zZXJ2aWNlIDMiLCJ2ZXJzaW9uIjoiMjAyMi0xIiwiZGVzY3JpcHRpb24iOiJFeGFtcGxlIE1pY3Jvc2VydmljZSIsImVuZHBvaW50cyI6WyJodHRwczovL21zLTMuZXhhbXBsZS5jb20iXSwiYXV0aGVudGljYXRlZCI6dHJ1ZSwieC10cnVzdC1ib3VuZGFyeSI6ZmFsc2UsImRhdGEiOlt7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUHVibGljIn1dLCJleHRlcm5hbFJlZmVyZW5jZXMiOlt7InVybCI6Imh0dHBzOi8vbXMtMy5leGFtcGxlLmNvbS9zd2FnZ2VyIiwidHlwZSI6ImRvY3VtZW50YXRpb24ifV19LHsiYm9tLXJlZiI6Im1zLTEtcGdzcWwuZXhhbXBsZS5jb20iLCJncm91cCI6Im9yZy5wb3N0Z3Jlc3FsIiwibmFtZSI6IlBvc3RncmVzIiwidmVyc2lvbiI6IjE0LjEiLCJkZXNjcmlwdGlvbiI6IlBvc3RncmVzIGRhdGFiYXNlIGZvciBNaWNyb3NlcnZpY2UgIzEiLCJlbmRwb2ludHMiOlsiaHR0cHM6Ly9tcy0xLXBnc3FsLmV4YW1wbGUuY29tOjU0MzIiXSwiYXV0aGVudGljYXRlZCI6dHJ1ZSwieC10cnVzdC1ib3VuZGFyeSI6ZmFsc2UsImRhdGEiOlt7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUElJIn1dfSx7ImJvbS1yZWYiOiJzMy1leGFtcGxlLmFtYXpvbi5jb20iLCJncm91cCI6ImNvbS5hbWF6b24iLCJuYW1lIjoiUzMiLCJkZXNjcmlwdGlvbiI6IlMzIGJ1Y2tldCIsImVuZHBvaW50cyI6WyJodHRwczovL3MzLWV4YW1wbGUuYW1hem9uLmNvbSJdLCJhdXRoZW50aWNhdGVkIjp0cnVlLCJ4LXRydXN0LWJvdW5kYXJ5Ijp0cnVlLCJkYXRhIjpbeyJmbG93IjoiYmktZGlyZWN0aW9uYWwiLCJjbGFzc2lmaWNhdGlvbiI6IlB1YmxpYyJ9XX1dfV0sImRlcGVuZGVuY2llcyI6W3sicmVmIjoiYWNtZS1hcHBsaWNhdGlvbiIsImRlcGVuZHNPbiI6WyJhcGktZ2F0ZXdheSJdfSx7InJlZiI6ImFwaS1nYXRld2F5IiwiZGVwZW5kc09uIjpbIm1zLTEuZXhhbXBsZS5jb20iLCJtcy0yLmV4YW1wbGUuY29tIiwibXMtMy5leGFtcGxlLmNvbSJdfSx7InJlZiI6Im1zLTEuZXhhbXBsZS5jb20iLCJkZXBlbmRzT24iOlsibXMtMS1wZ3NxbC5leGFtcGxlLmNvbSJdfSx7InJlZiI6Im1zLTIuZXhhbXBsZS5jb20iLCJkZXBlbmRzT24iOltdfSx7InJlZiI6Im1zLTMuZXhhbXBsZS5jb20iLCJkZXBlbmRzT24iOlsiczMtZXhhbXBsZS5hbWF6b24uY29tIl19XX0=", } - expectedToken := "7c78f6c9-token" ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { body, _ := ioutil.ReadAll(r.Body) @@ -72,11 +70,10 @@ func TestUploadBomsFromEnriched(t *testing.T) { json.Unmarshal(body, &actualRequest) assert.Equal(t, expectedRequest, actualRequest) w.Write([]byte("{\"Token\":\"" + expectedToken + "\"}")) - w.WriteHeader(http.StatusOK) })) defer ts.Close() - projectUUID = "7c78f6c9-b4b0-493c-a912-0bb0a4f221f1" + projectUUID = projUUID.String() apiKey = "test" projectName = "test" c, err := dtrack.NewClient(ts.URL, dtrack.WithAPIKey(apiKey)) @@ -101,6 +98,87 @@ func TestUploadBomsFromEnriched(t *testing.T) { assert.Equal(t, tokens, []string{expectedToken}) } +func TestUploadBomsFromEnrichedWithOwners(t *testing.T) { + projUUID := uuid.MustParse("7c78f6c9-b4b0-493c-a912-0bb0a4f221f1") + expectedRequest := dtrack.BOMUploadRequest{ + ProjectName: "test", + ProjectUUID: &projUUID, + ProjectVersion: "2022-1", + AutoCreate: false, + BOM: "eyJib21Gb3JtYXQiOiJDeWNsb25lRFgiLCJzcGVjVmVyc2lvbiI6IjEuNCIsInNlcmlhbE51bWJlciI6InVybjp1dWlkOjNlNjcxNjg3LTM5NWItNDFmNS1hMzBmLWE1ODkyMWE2OWI3OSIsInZlcnNpb24iOjEsIm1ldGFkYXRhIjp7InRpbWVzdGFtcCI6IjIwMjEtMDEtMTBUMTI6MDA6MDBaIiwiY29tcG9uZW50Ijp7ImJvbS1yZWYiOiJhY21lLWFwcGxpY2F0aW9uIiwidHlwZSI6ImFwcGxpY2F0aW9uIiwibmFtZSI6IkFjbWUgQ2xvdWQgRXhhbXBsZSIsInZlcnNpb24iOiIyMDIyLTEifX0sInNlcnZpY2VzIjpbeyJib20tcmVmIjoiYXBpLWdhdGV3YXkiLCJwcm92aWRlciI6eyJuYW1lIjoiQWNtZSBJbmMiLCJ1cmwiOlsiaHR0cHM6Ly9leGFtcGxlLmNvbSJdfSwiZ3JvdXAiOiJjb20uZXhhbXBsZSIsIm5hbWUiOiJBUEkgR2F0ZXdheSIsInZlcnNpb24iOiIyMDIyLTEiLCJkZXNjcmlwdGlvbiI6IkV4YW1wbGUgQVBJIEdhdGV3YXkiLCJlbmRwb2ludHMiOlsiaHR0cHM6Ly9leGFtcGxlLmNvbS8iLCJodHRwczovL2V4YW1wbGUuY29tL2FwcCJdLCJhdXRoZW50aWNhdGVkIjpmYWxzZSwieC10cnVzdC1ib3VuZGFyeSI6dHJ1ZSwiZGF0YSI6W3siZmxvdyI6ImJpLWRpcmVjdGlvbmFsIiwiY2xhc3NpZmljYXRpb24iOiJQSUkifSx7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUElGSSJ9LHsiZmxvdyI6ImJpLWRpcmVjdGlvbmFsIiwiY2xhc3NpZmljYXRpb24iOiJQdWJsaWMifV0sImV4dGVybmFsUmVmZXJlbmNlcyI6W3sidXJsIjoiaHR0cDovL2V4YW1wbGUuY29tL2FwcC9zd2FnZ2VyIiwidHlwZSI6ImRvY3VtZW50YXRpb24ifV0sInNlcnZpY2VzIjpbeyJib20tcmVmIjoibXMtMS5leGFtcGxlLmNvbSIsInByb3ZpZGVyIjp7Im5hbWUiOiJBY21lIEluYyIsInVybCI6WyJodHRwczovL2V4YW1wbGUuY29tIl19LCJncm91cCI6ImNvbS5leGFtcGxlIiwibmFtZSI6Ik1pY3Jvc2VydmljZSAxIiwidmVyc2lvbiI6IjIwMjItMSIsImRlc2NyaXB0aW9uIjoiRXhhbXBsZSBNaWNyb3NlcnZpY2UiLCJlbmRwb2ludHMiOlsiaHR0cHM6Ly9tcy0xLmV4YW1wbGUuY29tIl0sImF1dGhlbnRpY2F0ZWQiOnRydWUsIngtdHJ1c3QtYm91bmRhcnkiOmZhbHNlLCJkYXRhIjpbeyJmbG93IjoiYmktZGlyZWN0aW9uYWwiLCJjbGFzc2lmaWNhdGlvbiI6IlBJSSJ9XSwiZXh0ZXJuYWxSZWZlcmVuY2VzIjpbeyJ1cmwiOiJodHRwczovL21zLTEuZXhhbXBsZS5jb20vc3dhZ2dlciIsInR5cGUiOiJkb2N1bWVudGF0aW9uIn1dfSx7ImJvbS1yZWYiOiJtcy0yLmV4YW1wbGUuY29tIiwicHJvdmlkZXIiOnsibmFtZSI6IkFjbWUgSW5jIiwidXJsIjpbImh0dHBzOi8vZXhhbXBsZS5jb20iXX0sImdyb3VwIjoiY29tLmV4YW1wbGUiLCJuYW1lIjoiTWljcm9zZXJ2aWNlIDIiLCJ2ZXJzaW9uIjoiMjAyMi0xIiwiZGVzY3JpcHRpb24iOiJFeGFtcGxlIE1pY3Jvc2VydmljZSIsImVuZHBvaW50cyI6WyJodHRwczovL21zLTIuZXhhbXBsZS5jb20iXSwiYXV0aGVudGljYXRlZCI6dHJ1ZSwieC10cnVzdC1ib3VuZGFyeSI6ZmFsc2UsImRhdGEiOlt7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUElGSSJ9XSwiZXh0ZXJuYWxSZWZlcmVuY2VzIjpbeyJ1cmwiOiJodHRwczovL21zLTIuZXhhbXBsZS5jb20vc3dhZ2dlciIsInR5cGUiOiJkb2N1bWVudGF0aW9uIn1dfSx7ImJvbS1yZWYiOiJtcy0zLmV4YW1wbGUuY29tIiwicHJvdmlkZXIiOnsibmFtZSI6IkFjbWUgSW5jIiwidXJsIjpbImh0dHBzOi8vZXhhbXBsZS5jb20iXX0sImdyb3VwIjoiY29tLmV4YW1wbGUiLCJuYW1lIjoiTWljcm9zZXJ2aWNlIDMiLCJ2ZXJzaW9uIjoiMjAyMi0xIiwiZGVzY3JpcHRpb24iOiJFeGFtcGxlIE1pY3Jvc2VydmljZSIsImVuZHBvaW50cyI6WyJodHRwczovL21zLTMuZXhhbXBsZS5jb20iXSwiYXV0aGVudGljYXRlZCI6dHJ1ZSwieC10cnVzdC1ib3VuZGFyeSI6ZmFsc2UsImRhdGEiOlt7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUHVibGljIn1dLCJleHRlcm5hbFJlZmVyZW5jZXMiOlt7InVybCI6Imh0dHBzOi8vbXMtMy5leGFtcGxlLmNvbS9zd2FnZ2VyIiwidHlwZSI6ImRvY3VtZW50YXRpb24ifV19LHsiYm9tLXJlZiI6Im1zLTEtcGdzcWwuZXhhbXBsZS5jb20iLCJncm91cCI6Im9yZy5wb3N0Z3Jlc3FsIiwibmFtZSI6IlBvc3RncmVzIiwidmVyc2lvbiI6IjE0LjEiLCJkZXNjcmlwdGlvbiI6IlBvc3RncmVzIGRhdGFiYXNlIGZvciBNaWNyb3NlcnZpY2UgIzEiLCJlbmRwb2ludHMiOlsiaHR0cHM6Ly9tcy0xLXBnc3FsLmV4YW1wbGUuY29tOjU0MzIiXSwiYXV0aGVudGljYXRlZCI6dHJ1ZSwieC10cnVzdC1ib3VuZGFyeSI6ZmFsc2UsImRhdGEiOlt7ImZsb3ciOiJiaS1kaXJlY3Rpb25hbCIsImNsYXNzaWZpY2F0aW9uIjoiUElJIn1dfSx7ImJvbS1yZWYiOiJzMy1leGFtcGxlLmFtYXpvbi5jb20iLCJncm91cCI6ImNvbS5hbWF6b24iLCJuYW1lIjoiUzMiLCJkZXNjcmlwdGlvbiI6IlMzIGJ1Y2tldCIsImVuZHBvaW50cyI6WyJodHRwczovL3MzLWV4YW1wbGUuYW1hem9uLmNvbSJdLCJhdXRoZW50aWNhdGVkIjp0cnVlLCJ4LXRydXN0LWJvdW5kYXJ5Ijp0cnVlLCJkYXRhIjpbeyJmbG93IjoiYmktZGlyZWN0aW9uYWwiLCJjbGFzc2lmaWNhdGlvbiI6IlB1YmxpYyJ9XX1dfV0sImRlcGVuZGVuY2llcyI6W3sicmVmIjoiYWNtZS1hcHBsaWNhdGlvbiIsImRlcGVuZHNPbiI6WyJhcGktZ2F0ZXdheSJdfSx7InJlZiI6ImFwaS1nYXRld2F5IiwiZGVwZW5kc09uIjpbIm1zLTEuZXhhbXBsZS5jb20iLCJtcy0yLmV4YW1wbGUuY29tIiwibXMtMy5leGFtcGxlLmNvbSJdfSx7InJlZiI6Im1zLTEuZXhhbXBsZS5jb20iLCJkZXBlbmRzT24iOlsibXMtMS1wZ3NxbC5leGFtcGxlLmNvbSJdfSx7InJlZiI6Im1zLTIuZXhhbXBsZS5jb20iLCJkZXBlbmRzT24iOltdfSx7InJlZiI6Im1zLTMuZXhhbXBsZS5jb20iLCJkZXBlbmRzT24iOlsiczMtZXhhbXBsZS5hbWF6b24uY29tIl19XX0=", + } + expectedProjectUpdate := dtrack.Project{ + UUID: projUUID, + Name: "fooProj", + PURL: "pkg://npm/xyz/asdf@v1.2.2", + Properties: []dtrack.ProjectProperty(nil), + Tags: []dtrack.Tag{ + {Name: "foo:bar"}, + {Name: "Owner:foo"}, + {Name: "Owner:bar"}, + }, + } + expectedToken := "7c78f6c9-token" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.String() == "/api/v1/bom" { + body, _ := io.ReadAll(r.Body) + var actualRequest dtrack.BOMUploadRequest + json.Unmarshal(body, &actualRequest) + assert.Equal(t, expectedRequest, actualRequest) + w.Write([]byte("{\"Token\":\"" + expectedToken + "\"}")) + } else if r.URL.String() == "/api/v1/project/7c78f6c9-b4b0-493c-a912-0bb0a4f221f1" { + project := dtrack.Project{ + UUID: projUUID, + Name: "fooProj", + PURL: "pkg://npm/xyz/asdf@v1.2.2", + Tags: []dtrack.Tag{{Name: "foo:bar"}, {Name: "Owner:foo"}}, + } + res, _ := json.Marshal(project) + w.Write(res) + w.WriteHeader(http.StatusOK) + } else if r.URL.String() == "/api/v1/project" && r.Method == http.MethodPost { + body, _ := io.ReadAll(r.Body) + var req dtrack.Project + json.Unmarshal(body, &req) + assert.Equal(t, req.Tags, expectedProjectUpdate.Tags) + } else { + assert.Fail(t, r.URL.String()) + } // if r.URL.String() == "" + })) + defer ts.Close() + + projectUUID = projUUID.String() + apiKey = "test" + projectName = "test" + c, err := dtrack.NewClient(ts.URL, dtrack.WithAPIKey(apiKey)) + assert.Nil(t, err) + + client = c + issues, err := cyclonedx.ToDracon([]byte(saasBOM), "json") + + assert.Nil(t, err) + ltr := v1.LaunchToolResponse{ + ToolName: "SAT", + Issues: issues, + } + eltr := v1.EnrichedLaunchToolResponse{ + OriginalResults: <r, + Issues: []*v1.EnrichedIssue{ + { + RawIssue: issues[0], + Annotations: map[string]string{ + "Owner-0": "foo", + "Owner-1": "bar", + }, + }, + }, + } + ownerAnnotation = "Owner" + tokens, err := uploadBOMSFromEnriched([]*v1.EnrichedLaunchToolResponse{&eltr}) + assert.Nil(t, err) + assert.Equal(t, tokens, []string{expectedToken}) +} + const saasBOM = `{ "bomFormat": "CycloneDX", "specVersion": "1.4", diff --git a/components/consumers/dependency-track/task.yaml b/components/consumers/dependency-track/task.yaml index ccf4e2d4d..7ea3ebd41 100644 --- a/components/consumers/dependency-track/task.yaml +++ b/components/consumers/dependency-track/task.yaml @@ -17,6 +17,9 @@ spec: type: string - name: consumer-dependency-track-project-uuid type: string + - name: consumer-dependency-track-owner-annotation + type: string + default: "Owner" workspaces: - name: source-code-ws description: The workspace containing the source-code to scan. @@ -31,5 +34,6 @@ spec: "-apiKey", "$(params.consumer-dependency-track-token)", "-projectName", "$(params.consumer-dependency-track-project-name)", "-projectVersion", "$(params.consumer-dependency-track-project-version)", - "-projectUUID", "$(params.consumer-dependency-track-project-uuid)" + "-projectUUID", "$(params.consumer-dependency-track-project-uuid)", + "-ownerAnnotation","$(params.consumer-dependency-track-owner-annotation)" ] diff --git a/components/enrichers/depsdev/main.go b/components/enrichers/depsdev/main.go index 157e685ff..01982471b 100644 --- a/components/enrichers/depsdev/main.go +++ b/components/enrichers/depsdev/main.go @@ -41,7 +41,6 @@ type Check struct { Reason string `json:"reason,omitempty"` Details []interface{} `json:"details,omitempty"` } - // ScorecardV2 is a deps.dev ScoreCardV2 result type ScorecardV2 struct { Date string `json:"date,omitempty"` @@ -57,7 +56,6 @@ type ScorecardV2 struct { Metadata []interface{} `json:"metadata,omitempty"` Score float64 `json:"score,omitempty"` } - // Project is a deps.dev project type Project struct { Type string `json:"type,omitempty"` diff --git a/components/enrichers/policy/main.go b/components/enrichers/policy/main.go index 21a072dc0..f0119d8e1 100644 --- a/components/enrichers/policy/main.go +++ b/components/enrichers/policy/main.go @@ -99,7 +99,7 @@ func run() { log.Fatal(err) } } else { - log.Println("no enriched issues were created") + log.Println("no enriched issues were created for", r.GetToolName()) } if len(r.GetIssues()) > 0 { scanStartTime := r.GetScanInfo().GetScanStartTime().AsTime() diff --git a/pkg/cyclonedx/cyclonedx.go b/pkg/cyclonedx/cyclonedx.go index c3bc3ce9a..82e7eaf86 100644 --- a/pkg/cyclonedx/cyclonedx.go +++ b/pkg/cyclonedx/cyclonedx.go @@ -39,6 +39,7 @@ func ToDracon(inFile []byte, format string) ([]*v1.Issue, error) { } else { target = bom.Metadata.Component.PackageURL } + return []*v1.Issue{ { CycloneDXSBOM: &result,