1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-14 11:03:09 +02:00
sap-jenkins-library/pkg/syft/syft.go

124 lines
2.8 KiB
Go
Raw Normal View History

package syft
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"net/http"
"path/filepath"
"strings"
"github.com/SAP/jenkins-library/pkg/command"
piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/piperutils"
"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")
}
if len(images) == 0 {
return errors.New("syft: no images provided")
}
execRunner.AppendEnv([]string{"DOCKER_CONFIG", dockerConfigDir})
tmpDir, err := fileUtils.TempDir("", "syft")
if err != nil {
return err
}
syftFile := filepath.Join(tmpDir, "syft")
err = install(syftDownloadURL, syftFile, fileUtils, httpClient)
if err != nil {
return errors.Wrap(err, "failed to install syft")
}
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, "packages", fmt.Sprintf("%s/%s", strings.TrimPrefix(registryURL, "https://"), image), "-o", "cyclonedx-xml", "--file", fmt.Sprintf("bom-docker-%v.xml", index), "-q")
if err != nil {
return fmt.Errorf("failed to generate SBOM: %w", err)
}
}
return nil
}
func install(syftDownloadURL, dest string, fileUtils piperutils.FileUtils, httpClient piperhttp.Sender) error {
response, err := httpClient.SendRequest(http.MethodGet, syftDownloadURL, nil, nil, nil)
if err != nil {
return fmt.Errorf("failed to download syft binary: %w", err)
}
defer response.Body.Close()
err = extractSyft(response.Body, dest, fileUtils)
if err != nil {
return errors.Wrap(err, "failed to extract syft binary")
}
err = fileUtils.Chmod(dest, 0755)
if err != nil {
return err
}
return nil
}
func extractSyft(archive io.Reader, dest string, fileUtils piperutils.FileUtils) error {
zr, err := gzip.NewReader(archive)
if err != nil {
return err
}
defer zr.Close()
tr := tar.NewReader(zr)
fileFound := false
for {
f, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return errors.Wrap(err, "failed to read archive")
}
if filepath.Base(f.Name) == "syft" {
fileFound = true
df, err := fileUtils.Create(dest)
if err != nil {
return errors.Wrapf(err, "failed to create file %q", dest)
}
size, err := io.Copy(df, tr)
if err != nil {
return err
}
err = df.Close()
if err != nil {
return err
}
if size != f.Size {
return fmt.Errorf("only wrote %d bytes to %s; expected %d", size, dest, f.Size)
}
}
}
if !fileFound {
return errors.New("no file with the name 'syft' was found")
}
return nil
}