Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 147 additions & 3 deletions cmd/kanikoExecute.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package cmd

import (
"encoding/json"
"fmt"
"path/filepath"
"slices"
"strings"

"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"

"github.com/SAP/jenkins-library/pkg/build"
"github.com/SAP/jenkins-library/pkg/buildsettings"
"github.com/SAP/jenkins-library/pkg/certutils"
"github.com/SAP/jenkins-library/pkg/command"
Expand All @@ -17,6 +20,9 @@ import (
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/syft"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/SAP/jenkins-library/pkg/versioning"
"github.com/moby/buildkit/util/purl"
purlParser "github.com/package-url/packageurl-go"
)

func kanikoExecute(config kanikoExecuteOptions, telemetryData *telemetry.CustomData, commonPipelineEnvironment *kanikoExecuteCommonPipelineEnvironment) {
Expand Down Expand Up @@ -187,7 +193,17 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus
}
if config.CreateBOM {
// Syft for multi image, generates bom-docker-(1/2/3).xml
return syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags)
err := syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags)
if err != nil {
return err
}
}

if config.CreateBuildArtifactsMetadata {
err := createDockerBuildArtifactMetadata(commonPipelineEnvironment.container.imageNameTags, commonPipelineEnvironment)
if err != nil {
return err
}
}
return nil

Expand Down Expand Up @@ -284,7 +300,17 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus

if config.CreateBOM {
// Syft for multi image, generates bom-docker-(1/2/3).xml
return syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags)
err := syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags)
if err != nil {
return err
}
}

if config.CreateBuildArtifactsMetadata {
err := createDockerBuildArtifactMetadata(commonPipelineEnvironment.container.imageNameTags, commonPipelineEnvironment)
if err != nil {
return err
}
}
return nil

Expand Down Expand Up @@ -363,7 +389,16 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus

if config.CreateBOM {
// Syft for single image, generates bom-docker-0.xml
return syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags)
err := syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags)
if err != nil {
return err
}
}
if config.CreateBuildArtifactsMetadata {
err := createDockerBuildArtifactMetadata(commonPipelineEnvironment.container.imageNameTags, commonPipelineEnvironment)
if err != nil {
return err
}
}

return nil
Expand Down Expand Up @@ -418,6 +453,115 @@ func runKaniko(dockerFilepath string, buildOptions []string, readDigest bool, ex
return nil
}

func createDockerBuildArtifactMetadata(containerImageNameTags []string, commonPipelineEnvironment *kanikoExecuteCommonPipelineEnvironment) error {
buildCoordinates := []versioning.Coordinates{}

// for docker the logic will be slighlty different since we need to co-relate the sbom generated to the actual built docker images
pattern := "bom*.xml"

files, err := filepath.Glob(pattern)
if err != nil || len(files) == 0 {
log.Entry().Warnf("no sbom files for build not creating build artifact metadata")
return nil
}

for _, file := range files {
parentComponentName := piperutils.GetName(file)
parentComponentVersion := piperutils.GetVersion(file)

// syft sbom do not contain purl for the parent component
// this is problem since the way we tie back promoted artifact to build
// is only via the sbom parent componen , untill the time https://github.com/anchore/syft/issues/1408

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be tracked by DMS or sentinel to make the change when the issue is fixed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently there is no plan from the syft team itself for the fix so its hard to commit, but whenever the sbom topic is handelled centrally will hand over

// is fixed we are generating a purl and inserting it into the sbom
constructedPurl, err := purl.RefToPURL("docker", fmt.Sprintf("%s:%s", parentComponentName, parentComponentVersion), nil)
if err != nil {
log.Entry().Warnf("unable to create purl from reference")
return nil
}

// this purl contains the docker registry and we remove that from the final purl
// and recreate the purl without the registry, and we dont want to expose that
registry, name, version, err := parsePurl(constructedPurl)
if err != nil {
log.Entry().Warnf("unable to parse purl creating build artifact metadata")
return nil
}

constructedPurl, err = purl.RefToPURL("docker", fmt.Sprintf("%s:%s", name, version), nil)
if err != nil {
log.Entry().Warnf("unable to create purl from reference")
return nil
}

log.Entry().Debugf("purl is %s", constructedPurl)
imageNameTag := findImageNameTagInPurl(containerImageNameTags, fmt.Sprintf("%s:%s", name, version))
var coordinate versioning.Coordinates
if imageNameTag != "" {
coordinate.ArtifactID = name
coordinate.BuildPath = filepath.Dir(file)
coordinate.Version = version
coordinate.GroupID = ""
coordinate.PURL = constructedPurl
coordinate.URL = registry
} else {
log.Entry().Warnf("unable to find imageNameTag in purl, not creating build artifact metadata for :%s", file)
return nil
}

err = piperutils.UpdatePurl(file, constructedPurl)
if err != nil {
log.Entry().Warnf("unable to update purl in sbom file, hence not creating build artifact metadata for :%s due to err %v", file, err)
return nil

}
buildCoordinates = append(buildCoordinates, coordinate)
}

if len(buildCoordinates) > 0 {
var buildArtifacts build.BuildArtifacts
buildArtifacts.Coordinates = buildCoordinates
jsonResult, _ := json.Marshal(buildArtifacts)
commonPipelineEnvironment.custom.dockerBuildArtifacts = string(jsonResult)
}

return nil
}

func parsePurl(purlStr string) (registry, name, version string, err error) {
p, err := purlParser.FromString(purlStr)
if err != nil {
return "", "", "", err
}

// Split namespace to extract registry
// E.g., namespace = "ghcr.io/my-org"
namespace := p.Namespace
if namespace == "" {
registry = "docker.io"
} else {
nsParts := strings.Split(namespace, "/")
if strings.Contains(nsParts[0], ".") {
registry = nsParts[0]
} else {
registry = "docker.io"
}
}

name = p.Name
version = p.Version
return
}

func findImageNameTagInPurl(containerImageNameTags []string, purlReference string) string {
for _, entry := range containerImageNameTags {
if entry == purlReference {
log.Entry().Debugf("found image name tag %s in purlReference %s", entry, purlReference)
return entry
}
}
return ""
}

type multipleImageConf struct {
ContextSubPath string `json:"contextSubPath,omitempty"`
DockerfilePath string `json:"dockerfilePath,omitempty"`
Expand Down
16 changes: 15 additions & 1 deletion cmd/kanikoExecute_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 31 additions & 1 deletion cmd/kanikoExecute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"testing"
Expand Down Expand Up @@ -39,6 +40,7 @@ func (c *kanikoMockClient) SendRequest(method, url string, body io.Reader, heade
}
return &http.Response{StatusCode: c.httpStatusCode, Body: io.NopCloser(bytes.NewReader([]byte(c.responseBody)))}, nil
}

func (c *kanikoMockClient) DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error {
if len(c.errorMessage) > 0 {
return fmt.Errorf("%s", c.errorMessage)
Expand All @@ -47,7 +49,6 @@ func (c *kanikoMockClient) DownloadFile(url, filename string, header http.Header
}

func TestRunKanikoExecute(t *testing.T) {

// required due to config resolution during build settings retrieval
// ToDo: proper mocking
openFileBak := configOptions.OpenFile
Expand Down Expand Up @@ -863,3 +864,32 @@ func TestRunKanikoExecute(t *testing.T) {
assert.Contains(t, fmt.Sprint(err), "multipleImages: empty contextSubPath")
})
}

func TestKanikoBuildArtifactMetadata(t *testing.T) {
validBom := `<bom>
<metadata>
<component>
<name>376101288081-20250807-153404266-296.staging.repositories.cloud.sap/dummyImage</name>
<version>1.0.0</version>
</component>
<properties>
<property name="name1" value="value1" />
<property name="name2" value="value2" />
</properties>
</metadata>
</bom>`
bomFile, err := os.Create("bom-docker-0.xml")
// Ensure file is closed and deleted after function finishes
defer bomFile.Close()
defer os.Remove("bom-docker-0.xml") // Delete the file when the function exits

// Write something to the file
_, err = bomFile.WriteString(validBom)

imageNameTags := []string{"dummyImage:1.0.0"}
cpe := kanikoExecuteCommonPipelineEnvironment{}

err = createDockerBuildArtifactMetadata(imageNameTags, &cpe)

assert.NoError(t, err)
}
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ require (
github.com/magicsong/sonargo v0.0.1
github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5
github.com/mitchellh/mapstructure v1.5.0
github.com/moby/buildkit v0.13.1
github.com/motemen/go-nuts v0.0.0-20220604134737-2658d0104f31
github.com/package-url/packageurl-go v0.1.1
github.com/piper-validation/fortify-client-go v0.0.0-20220126145513-7b3e9a72af01
Expand Down Expand Up @@ -80,6 +81,7 @@ require (
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Microsoft/hcsshim v0.11.7 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/apex/log v1.9.0 // indirect
Expand Down Expand Up @@ -115,7 +117,6 @@ require (
github.com/imdario/mergo v1.0.1 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/moby/buildkit v0.12.5 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
Expand Down Expand Up @@ -189,7 +190,7 @@ require (
github.com/buildpacks/imgutil v0.0.0-20230919143643-4ec9360d5f02 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/containerd v1.7.24 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
Expand Down
17 changes: 9 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -244,16 +244,16 @@ github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaD
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA=
github.com/containerd/containerd v1.7.24/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4=
github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
Expand Down Expand Up @@ -752,8 +752,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/buildkit v0.12.5 h1:RNHH1l3HDhYyZafr5EgstEu8aGNCwyfvMtrQDtjH9T0=
github.com/moby/buildkit v0.12.5/go.mod h1:YGwjA2loqyiYfZeEo8FtI7z4x5XponAaIWsWcSjWwso=
github.com/moby/buildkit v0.13.1 h1:L8afOFhPq2RPJJSr/VyzbufwID7jquZVB7oFHbPRcPE=
github.com/moby/buildkit v0.13.1/go.mod h1:aNmNQKLBFYAOFuzQjR3VA27/FijlvtBD1pjNwTSN37k=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
Expand All @@ -762,8 +762,8 @@ github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkV
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g=
github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
Expand Down Expand Up @@ -1020,6 +1020,7 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRND
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 h1:ZtfnDL+tUrs1F0Pzfwbg2d59Gru9NCH3bgSHBM6LDwU=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps=
Expand Down
Loading
Loading