Skip to content

Commit

Permalink
feat: add version, use project auto-creation
Browse files Browse the repository at this point in the history
With this change the SBOM is now just uploaded to Dependency Track with
the "auto-creat" option set to true. So, each project/version combinations
gets an entry in Dependency Track.

The Docker repository (registry + image) is used as the project name, the
Docker tag is used for the version.
  • Loading branch information
derkoe authored and ckotzbauer committed Feb 1, 2022
1 parent 7fbd4c8 commit 492e99e
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 72 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ go 1.17
require (
github.com/anchore/syft v0.36.0
github.com/docker/cli v20.10.12+incompatible
github.com/google/uuid v1.3.0
github.com/novln/docker-parser v1.0.0
github.com/nscuro/dtrack-client v0.3.0
github.com/onsi/ginkgo/v2 v2.1.1
Expand Down Expand Up @@ -46,6 +45,7 @@ require (
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
Expand Down
2 changes: 1 addition & 1 deletion internal/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (c *CronService) runBackgroundService() {
}

for _, t := range c.targets {
t.ProcessSbom(image.ImageID, sbom)
t.ProcessSbom(image, sbom)
}
}

Expand Down
3 changes: 2 additions & 1 deletion internal/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
)

type ContainerImage struct {
Image string
ImageID string
Auth []byte
Pods []corev1.Pod
Expand Down Expand Up @@ -106,7 +107,7 @@ func (client *KubeClient) LoadImageInfos(namespaces []corev1.Namespace, podLabel
if !client.hasAnnotation(annotations, c) {
img, ok := images[c.ImageID]
if !ok {
img = ContainerImage{ImageID: c.ImageID, Auth: pullSecrets, Pods: []corev1.Pod{}}
img = ContainerImage{Image: c.Image, ImageID: c.ImageID, Auth: pullSecrets, Pods: []corev1.Pod{}}
}

img.Pods = append(img.Pods, pod)
Expand Down
88 changes: 21 additions & 67 deletions internal/target/dtrack_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@ import (
"context"
"encoding/base64"
"fmt"
"strings"

"github.com/google/uuid"
parser "github.com/novln/docker-parser"
dtrack "github.com/nscuro/dtrack-client"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"

"github.com/ckotzbauer/sbom-operator/internal"
"github.com/ckotzbauer/sbom-operator/internal/kubernetes"
)

type DependencyTrackTarget struct {
baseUrl string
apiKey string
imageToProject map[string]uuid.UUID
baseUrl string
apiKey string
}

func NewDependencyTrackTarget() *DependencyTrackTarget {
Expand All @@ -40,85 +39,40 @@ func (g *DependencyTrackTarget) ValidateConfig() error {
}

func (g *DependencyTrackTarget) Initialize() {
client, _ := dtrack.NewClient(g.baseUrl, dtrack.WithAPIKey(g.apiKey))
g.imageToProject = make(map[string]uuid.UUID)

const pageSize = 10
pageNumber := 1
for {
projectsPage, err := client.Project.GetAll(context.TODO(), dtrack.PageOptions{
PageNumber: pageNumber,
PageSize: pageSize,
})
if err != nil {
logrus.Errorf("Could not fetch projects: %v", err)
return
}

for _, project := range projectsPage.Projects {
for _, property := range project.Properties {
if property.Name == "image-name" {
g.imageToProject[property.Value] = project.UUID
}
}
}

if pageNumber*pageSize >= projectsPage.TotalCount {
break
}

pageNumber++
}
}

func (g *DependencyTrackTarget) ProcessSbom(imageID, sbom string) {
if sbom == "" {
logrus.Infof("Empty SBOM - skip image (image=%s)", imageID)
func (g *DependencyTrackTarget) ProcessSbom(image kubernetes.ContainerImage, sbom string) {
fullRef, err := parser.Parse(image.Image)
if err != nil {
logrus.WithError(err).Errorf("Could not parse imageID %s", image.ImageID)
return
}

client, _ := dtrack.NewClient(g.baseUrl, dtrack.WithAPIKey(g.apiKey))
logrus.Infof("Sending SBOM to Dependency Track (image=%s)", imageID)
imageName := fullRef.Repository()
tagName := fullRef.Tag()
if tagName == "" {
tagName = "latest"
}

if !strings.ContainsRune(imageID, '@') {
logrus.Warnf("Image id %s does not contain an @sha256", imageID)
if sbom == "" {
logrus.Infof("Empty SBOM - skip image (image=%s)", image.ImageID)
return
}
imageSplit := strings.Split(imageID, "@")
imageName := imageSplit[0]

projectId := g.imageToProject[imageName]
if projectId == uuid.Nil {
project, err := client.Project.Create(context.TODO(),
dtrack.Project{
Active: true,
Classifier: "APPLICATION",
Name: imageName,
Properties: []dtrack.ProjectProperty{
{Name: "image-name", Group: "container", Value: imageName, Type: "STRING"},
},
// TODO check if to add PURL: "pkg:docker/" + imageID,
})
if err != nil {
logrus.Errorf("Could not create project (%s): %v", imageName, err)
}
projectId = project.UUID
g.imageToProject[imageName] = projectId
}
client, _ := dtrack.NewClient(g.baseUrl, dtrack.WithAPIKey(g.apiKey))

if projectId == uuid.Nil {
logrus.Warnf("No project id for image %s", imageName)
return
}
logrus.Infof("Sending SBOM to Dependency Track (project=%s, version=%s)", imageName, tagName)

sbomBase64 := base64.StdEncoding.EncodeToString([]byte(sbom))
uploadToken, err := client.BOM.Upload(context.TODO(), dtrack.BOMUploadRequest{ProjectUUID: &projectId, BOM: sbomBase64, AutoCreate: false})
uploadToken, err := client.BOM.Upload(
context.TODO(),
dtrack.BOMUploadRequest{ProjectName: imageName, ProjectVersion: tagName, AutoCreate: true, BOM: sbomBase64},
)
if err != nil {
logrus.Errorf("Could not upload BOM: %v", err)
}
logrus.Infof("Uploaded SBOM (upload-token=%s)", uploadToken)
}

func (g *DependencyTrackTarget) Cleanup(allImages []string) {
g.imageToProject = nil
}
4 changes: 3 additions & 1 deletion internal/target/git_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/ckotzbauer/sbom-operator/internal"
"github.com/ckotzbauer/sbom-operator/internal/kubernetes"
"github.com/ckotzbauer/sbom-operator/internal/syft"
"github.com/ckotzbauer/sbom-operator/internal/target/git"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -79,7 +80,8 @@ func (g *GitTarget) Initialize() {
viper.GetString(internal.ConfigKeyGitBranch))
}

func (g *GitTarget) ProcessSbom(imageID, sbom string) {
func (g *GitTarget) ProcessSbom(image kubernetes.ContainerImage, sbom string) {
imageID := image.ImageID
filePath := g.imageIDToFilePath(imageID)

dir := filepath.Dir(filePath)
Expand Down
6 changes: 5 additions & 1 deletion internal/target/target.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package target

import (
"github.com/ckotzbauer/sbom-operator/internal/kubernetes"
)

type Target interface {
Initialize()
ValidateConfig() error
ProcessSbom(imageID, sbom string)
ProcessSbom(imageID kubernetes.ContainerImage, sbom string)
Cleanup(allImages []string)
}

0 comments on commit 492e99e

Please sign in to comment.