From 77758b3db0b639b4529ed42b045dfa1df75f50bb Mon Sep 17 00:00:00 2001 From: Ralf Pannemans Date: Thu, 23 May 2024 13:44:22 +0200 Subject: [PATCH] feat(cnbBuild): Only use sbom-cataloger with cnb images (#4934) --- cmd/cnbBuild.go | 10 +++++++++- pkg/syft/syft.go | 44 +++++++++++++++++++++++++++++++++---------- pkg/syft/syft_test.go | 2 +- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/cmd/cnbBuild.go b/cmd/cnbBuild.go index f5d6a3e76a..6ee7a5d3d1 100644 --- a/cmd/cnbBuild.go +++ b/cmd/cnbBuild.go @@ -346,7 +346,15 @@ func callCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, buildSummary.Print() if config.CreateBOM { - err = syft.GenerateSBOM(config.SyftDownloadURL, filepath.Dir(config.DockerConfigJSON), utils, utils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags) + log.Entry().Debugf("Creating sbom for %d images\n", len(commonPipelineEnvironment.container.imageNameTags)) + syftScanner, err := syft.CreateSyftScanner(config.SyftDownloadURL, utils, httpClient) + if err != nil { + log.SetErrorCategory(log.ErrorCompliance) + return errors.Wrap(err, "failed to create syft scanner file") + } + // images produces with cnb have sboms + syftScanner.AddArgument("--override-default-catalogers=sbom-cataloger") + err = syftScanner.ScanImages(filepath.Dir(config.DockerConfigJSON), utils, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags) if err != nil { log.SetErrorCategory(log.ErrorCompliance) return errors.Wrap(err, "failed to create BOM file") diff --git a/pkg/syft/syft.go b/pkg/syft/syft.go index 48a2f6b484..2d0f01935c 100644 --- a/pkg/syft/syft.go +++ b/pkg/syft/syft.go @@ -15,34 +15,58 @@ import ( "github.com/pkg/errors" ) -func GenerateSBOM(syftDownloadURL, dockerConfigDir string, execRunner command.ExecRunner, fileUtils piperutils.FileUtils, httpClient piperhttp.Sender, registryURL string, images []string) error { - if registryURL == "" { - return errors.New("syft: regisitry url must not be empty") - } +type SyftScanner struct { + syftFile string + additionalArgs []string +} - if len(images) == 0 { - return errors.New("syft: no images provided") +func GenerateSBOM(syftDownloadURL, dockerConfigDir string, execRunner command.ExecRunner, fileUtils piperutils.FileUtils, httpClient piperhttp.Sender, registryURL string, images []string) error { + scanner, err := CreateSyftScanner(syftDownloadURL, fileUtils, httpClient) + if err != nil { + return err } + return scanner.ScanImages(dockerConfigDir, execRunner, registryURL, images) +} - execRunner.AppendEnv([]string{fmt.Sprintf("DOCKER_CONFIG=%s", dockerConfigDir)}) +func CreateSyftScanner(syftDownloadURL string, fileUtils piperutils.FileUtils, httpClient piperhttp.Sender) (*SyftScanner, error) { tmpDir, err := fileUtils.TempDir("", "syft") if err != nil { - return err + return nil, err } syftFile := filepath.Join(tmpDir, "syft") err = install(syftDownloadURL, syftFile, fileUtils, httpClient) if err != nil { - return errors.Wrap(err, "failed to install syft") + return nil, errors.Wrap(err, "failed to install syft") } + return &SyftScanner{syftFile: syftFile}, nil +} + +func (s *SyftScanner) AddArgument(arg string) { + s.additionalArgs = append(s.additionalArgs, arg) +} + +func (s *SyftScanner) ScanImages(dockerConfigDir string, execRunner command.ExecRunner, registryURL string, images []string) error { + if registryURL == "" { + return errors.New("syft: registry url must not be empty") + } + + if len(images) == 0 { + return errors.New("syft: no images provided") + } + + execRunner.AppendEnv([]string{fmt.Sprintf("DOCKER_CONFIG=%s", dockerConfigDir)}) + for index, image := range images { if image == "" { return errors.New("syft: image name must not be empty") } // TrimPrefix needed as syft needs containerRegistry name only - err = execRunner.RunExecutable(syftFile, "scan", fmt.Sprintf("registry:%s/%s", strings.TrimPrefix(registryURL, "https://"), image), "-o", fmt.Sprintf("cyclonedx-xml=bom-docker-%v.xml", index), "-q") + args := []string{"scan", fmt.Sprintf("registry:%s/%s", strings.TrimPrefix(registryURL, "https://"), image), "-o", fmt.Sprintf("cyclonedx-xml=bom-docker-%v.xml", index), "-q"} + args = append(args, s.additionalArgs...) + err := execRunner.RunExecutable(s.syftFile, args...) if err != nil { return fmt.Errorf("failed to generate SBOM: %w", err) } diff --git a/pkg/syft/syft_test.go b/pkg/syft/syft_test.go index c651c1d7e5..93ad0e7888 100644 --- a/pkg/syft/syft_test.go +++ b/pkg/syft/syft_test.go @@ -65,7 +65,7 @@ func TestGenerateSBOM(t *testing.T) { t.Run("error case: no registry", func(t *testing.T) { err := syft.GenerateSBOM("http://test-syft-gh-release.com/syft.tar.gz", "", &execMock, &fileMock, client, "", []string{"image:latest"}) assert.Error(t, err) - assert.Equal(t, "syft: regisitry url must not be empty", err.Error()) + assert.Equal(t, "syft: registry url must not be empty", err.Error()) }) t.Run("error case: no images provided", func(t *testing.T) {