diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eda0535b6..7075a6845 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,8 @@ jobs: go-version: '1.21.x' cache: true check-latest: true + - name: Install Trivy + run: make install-trivy - name: Build run: make build - name: Test @@ -119,6 +121,7 @@ jobs: # Build and load the Git and Bundle image export GIT_CONTAINER_IMAGE="$(KO_DOCKER_REPO=kind.local ko publish ./cmd/git)" export BUNDLE_CONTAINER_IMAGE="$(KO_DOCKER_REPO=kind.local ko publish ./cmd/bundle)" + export IMAGE_PROCESSING_CONTAINER_IMAGE="$(KO_DOCKER_REPO=kind.local ko publish ./cmd/image-processing)" make test-integration diff --git a/Makefile b/Makefile index 64ad22eee..794ea8cdc 100644 --- a/Makefile +++ b/Makefile @@ -157,6 +157,10 @@ install-counterfeiter: install-spruce: hack/install-spruce.sh +.PHONY: install-trivy +install-trivy: + hack/install-trivy.sh + # Install golangci-lint via: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest .PHONY: sanity-check sanity-check: diff --git a/cmd/image-processing/main.go b/cmd/image-processing/main.go index d7f4b1b84..dd39969ed 100644 --- a/cmd/image-processing/main.go +++ b/cmd/image-processing/main.go @@ -9,16 +9,20 @@ package main import ( "context" + "errors" "fmt" "log" "os" + "path/filepath" "strconv" "strings" "time" "github.com/google/go-containerregistry/pkg/name" containerreg "github.com/google/go-containerregistry/pkg/v1" + buildapi "github.com/shipwright-io/build/pkg/apis/build/v1beta1" "github.com/shipwright-io/build/pkg/image" + "github.com/shipwright-io/build/pkg/reconciler/buildrun/resources" "github.com/spf13/pflag" ) @@ -45,7 +49,10 @@ type settings struct { imageTimestampFile, resultFileImageDigest, resultFileImageSize, + resultFileImageVulnerabilities, secretPath string + vulnerabilitySettings resources.VulnerablilityScanParams + vulnerabilityCountLimit int } var flagValues settings @@ -70,6 +77,9 @@ func initializeFlag() { pflag.StringVar(&flagValues.resultFileImageDigest, "result-file-image-digest", "", "A file to write the image digest to") pflag.StringVar(&flagValues.resultFileImageSize, "result-file-image-size", "", "A file to write the image size to") + pflag.StringVar(&flagValues.resultFileImageVulnerabilities, "result-file-image-vulnerabilities", "", "A file to write the image vulnerabilities to") + pflag.Var(&flagValues.vulnerabilitySettings, "vuln-settings", "Vulnerability settings json string. One can enable the scan by setting {\"enabled\":true} to this option") + pflag.IntVar(&flagValues.vulnerabilityCountLimit, "vuln-count-limit", 50, "vulnerability count limit for the output of vulnerability scan") } func main() { @@ -143,7 +153,7 @@ func runImageProcessing(ctx context.Context) error { } // prepare the registry options - options, _, err := image.GetOptions(ctx, imageName, flagValues.insecure, flagValues.secretPath, "Shipwright Build") + options, auth, err := image.GetOptions(ctx, imageName, flagValues.insecure, flagValues.secretPath, "Shipwright Build") if err != nil { return err } @@ -151,12 +161,13 @@ func runImageProcessing(ctx context.Context) error { // load the image or image index (usually multi-platform image) var img containerreg.Image var imageIndex containerreg.ImageIndex + var isImageFromTar bool if flagValues.push == "" { log.Printf("Loading the image from the registry %q\n", imageName.String()) img, imageIndex, err = image.LoadImageOrImageIndexFromRegistry(imageName, options) } else { log.Printf("Loading the image from the directory %q\n", flagValues.push) - img, imageIndex, err = image.LoadImageOrImageIndexFromDirectory(flagValues.push) + img, imageIndex, isImageFromTar, err = image.LoadImageOrImageIndexFromDirectory(flagValues.push) } if err != nil { log.Printf("Failed to load the image: %v\n", err) @@ -179,6 +190,53 @@ func runImageProcessing(ctx context.Context) error { } } + // check for image vulnerabilities if vulnerability scanning is enabled. + var vulns []buildapi.Vulnerability + + if flagValues.vulnerabilitySettings.Enabled { + var imageString string + var imageInDir bool + if flagValues.push != "" { + imageString = flagValues.push + + // for single image in a tar file + if isImageFromTar { + entries, err := os.ReadDir(flagValues.push) + if err != nil { + return err + } + imageString = filepath.Join(imageString, entries[0].Name()) + } + imageInDir = true + } else { + imageString = imageName.String() + imageInDir = false + } + vulns, err = image.RunVulnerabilityScan(ctx, imageString, flagValues.vulnerabilitySettings.VulnerabilityScanOptions, auth, flagValues.insecure, imageInDir, flagValues.vulnerabilityCountLimit) + if err != nil { + return err + } + + // log all the vulnerabilities + if len(vulns) > 0 { + log.Println("vulnerabilities found in the output image :") + for _, vuln := range vulns { + log.Printf("ID: %s, Severity: %s\n", vuln.ID, vuln.Severity) + } + } + vulnOuput := serializeVulnerabilities(vulns) + if err := os.WriteFile(flagValues.resultFileImageVulnerabilities, vulnOuput, 0640); err != nil { + return err + } + } + + // Don't push the image if fail is set to true for shipwright managed push + if flagValues.push != "" { + if flagValues.vulnerabilitySettings.FailOnFinding && len(vulns) > 0 { + log.Println("vulnerabilities have been found in the output image, exiting with code 22") + return &ExitError{Code: 22, Message: "vulnerabilities found, exiting with code 22", Cause: errors.New("vulnerabilities found in the image")} + } + } // mutate the image timestamp if flagValues.imageTimestamp != "" { sec, err := strconv.ParseInt(flagValues.imageTimestamp, 10, 32) @@ -234,3 +292,11 @@ func splitKeyVals(kvPairs []string) (map[string]string, error) { return m, nil } + +func serializeVulnerabilities(Vulnerabilities []buildapi.Vulnerability) []byte { + var output []string + for _, vuln := range Vulnerabilities { + output = append(output, fmt.Sprintf("%s:%c", vuln.ID, vuln.Severity[0])) + } + return []byte(strings.Join(output, ",")) +} diff --git a/cmd/image-processing/main_test.go b/cmd/image-processing/main_test.go index ba3de1a05..05987bf52 100644 --- a/cmd/image-processing/main_test.go +++ b/cmd/image-processing/main_test.go @@ -11,7 +11,9 @@ import ( "net/http/httptest" "net/url" "os" + "path" "strconv" + "strings" "time" . "github.com/onsi/ginkgo/v2" @@ -24,6 +26,8 @@ import ( containerreg "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/remote" + buildapi "github.com/shipwright-io/build/pkg/apis/build/v1beta1" + "github.com/shipwright-io/build/pkg/reconciler/buildrun/resources" "github.com/spf13/pflag" "k8s.io/apimachinery/pkg/util/rand" ) @@ -399,4 +403,143 @@ var _ = Describe("Image Processing Resource", func() { }) }) }) + + Context("vulnerability scanning", func() { + var directory string + BeforeEach(func() { + cwd, err := os.Getwd() + Expect(err).ToNot(HaveOccurred()) + directory = path.Clean(path.Join(cwd, "../..", "test/data/images/vuln-image-in-oci")) + }) + + It("should run vulnerability scanning if it is enabled and output vulnerabilities equal to the limit defined", func() { + vulnOptions := &buildapi.VulnerabilityScanOptions{ + Enabled: true, + } + withTempRegistry(func(endpoint string) { + tag, err := name.NewTag(fmt.Sprintf("%s/%s:%s", endpoint, "temp-image", rand.String(5))) + Expect(err).ToNot(HaveOccurred()) + vulnSettings := &resources.VulnerablilityScanParams{VulnerabilityScanOptions: *vulnOptions} + withTempFile("vuln-scan-result", func(filename string) { + Expect(run( + "--insecure", + "--image", tag.String(), + "--push", directory, + "--vuln-settings", vulnSettings.String(), + "--result-file-image-vulnerabilities", filename, + "--vuln-count-limit", "10", + )).ToNot(HaveOccurred()) + output := filecontent(filename) + Expect(output).To(ContainSubstring("CVE-2019-8457")) + vulnerabilities := strings.Split(output, ",") + Expect(vulnerabilities).To(HaveLen(10)) + }) + }) + }) + + It("should push the image if vulnerabilities are found and fail is false", func() { + vulnOptions := &buildapi.VulnerabilityScanOptions{ + Enabled: true, + FailOnFinding: false, + } + withTempRegistry(func(endpoint string) { + tag, err := name.NewTag(fmt.Sprintf("%s/%s:%s", endpoint, "temp-image", rand.String(5))) + Expect(err).ToNot(HaveOccurred()) + vulnSettings := &resources.VulnerablilityScanParams{VulnerabilityScanOptions: *vulnOptions} + withTempFile("vuln-scan-result", func(filename string) { + Expect(run( + "--insecure", + "--image", tag.String(), + "--push", directory, + "--vuln-settings", vulnSettings.String(), + "--result-file-image-vulnerabilities", filename, + )).ToNot(HaveOccurred()) + output := filecontent(filename) + Expect(output).To(ContainSubstring("CVE-2019-8457")) + }) + + ref, err := name.ParseReference(tag.String()) + Expect(err).ToNot(HaveOccurred()) + _, err = remote.Get(ref) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + It("should not push the image if vulnerabilities are found and fail is true", func() { + vulnOptions := &buildapi.VulnerabilityScanOptions{ + Enabled: true, + FailOnFinding: true, + } + withTempRegistry(func(endpoint string) { + tag, err := name.NewTag(fmt.Sprintf("%s/%s:%s", endpoint, "temp-image", rand.String(5))) + Expect(err).ToNot(HaveOccurred()) + vulnSettings := &resources.VulnerablilityScanParams{VulnerabilityScanOptions: *vulnOptions} + withTempFile("vuln-scan-result", func(filename string) { + Expect(run( + "--insecure", + "--image", tag.String(), + "--push", directory, + "--vuln-settings", vulnSettings.String(), + "--result-file-image-vulnerabilities", filename, + )).To(HaveOccurred()) + output := filecontent(filename) + Expect(output).To(ContainSubstring("CVE-2019-8457")) + }) + + ref, err := name.ParseReference(tag.String()) + Expect(err).ToNot(HaveOccurred()) + + _, err = remote.Get(ref) + Expect(err).To(HaveOccurred()) + + }) + }) + + It("should run vulnerability scanning on an image that is already pushed by the strategy", func() { + ignoreVulnerabilities := buildapi.IgnoredHigh + vulnOptions := &buildapi.VulnerabilityScanOptions{ + Enabled: true, + FailOnFinding: true, + Ignore: &buildapi.VulnerabilityIgnoreOptions{ + Severity: &ignoreVulnerabilities, + }, + } + + withTempRegistry(func(endpoint string) { + originalImageRef := "ghcr.io/shipwright-io/shipwright-samples/node:12" + srcRef, err := name.ParseReference(originalImageRef) + Expect(err).ToNot(HaveOccurred()) + + // Pull the original image + originalImage, err := remote.Image(srcRef) + Expect(err).ToNot(HaveOccurred()) + + // Tag the image with a new name + tag, err := name.NewTag(fmt.Sprintf("%s/%s:%s", endpoint, "temp-image", rand.String(5))) + Expect(err).ToNot(HaveOccurred()) + + err = remote.Write(tag, originalImage) + Expect(err).ToNot(HaveOccurred()) + + vulnSettings := &resources.VulnerablilityScanParams{VulnerabilityScanOptions: *vulnOptions} + withTempFile("vuln-scan-result", func(filename string) { + Expect(run( + "--insecure", + "--image", tag.String(), + "--vuln-settings", vulnSettings.String(), + "--result-file-image-vulnerabilities", filename, + )).ToNot(HaveOccurred()) + output := filecontent(filename) + Expect(output).To(ContainSubstring("CVE-2019-12900")) + }) + + ref, err := name.ParseReference(tag.String()) + Expect(err).ToNot(HaveOccurred()) + + _, err = remote.Get(ref) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + }) }) diff --git a/deploy/crds/shipwright.io_buildruns.yaml b/deploy/crds/shipwright.io_buildruns.yaml index 7306e6b20..80376a6b7 100644 --- a/deploy/crds/shipwright.io_buildruns.yaml +++ b/deploy/crds/shipwright.io_buildruns.yaml @@ -6607,6 +6607,50 @@ spec: as the epoch seconds - or nil/empty to not set any specific timestamp' type: string + vulnerabilityScan: + description: VulnerabilityScan provides configurations + about running a scan for your generated image + properties: + enabled: + description: Enabled indicates whether to run vulnerability + scan for image + type: boolean + failOnFinding: + description: FailOnFinding indicates whether to fail + the build run if the vulnerability scan results + in vulnerabilities + type: boolean + ignore: + description: Ignore refers to ignore options for vulnerability + scan + properties: + id: + description: ID references the security issues + to be ignored in vulnerability scan + items: + type: string + type: array + severity: + description: 'Severity denotes the severity levels + of security issues to be ignored, valid values + are: - "low": it will exclude low severity vulnerabilities, + displaying only medium, high and critical vulnerabilities + - "medium": it will exclude low and medium severity + vulnerabilities, displaying only high and critical + vulnerabilities - "high": it will exclude low, + medium and high severity vulnerabilities, displaying + only the critical vulnerabilities' + enum: + - low + - medium + - high + type: string + unfixed: + description: Unfixed indicates to ignore vulnerabilities + for which no fix exists + type: boolean + type: object + type: object required: - image type: object @@ -8755,6 +8799,48 @@ spec: defined as the epoch seconds - or nil/empty to not set any specific timestamp' type: string + vulnerabilityScan: + description: VulnerabilityScan provides configurations about running + a scan for your generated image + properties: + enabled: + description: Enabled indicates whether to run vulnerability + scan for image + type: boolean + failOnFinding: + description: FailOnFinding indicates whether to fail the build + run if the vulnerability scan results in vulnerabilities + type: boolean + ignore: + description: Ignore refers to ignore options for vulnerability + scan + properties: + id: + description: ID references the security issues to be ignored + in vulnerability scan + items: + type: string + type: array + severity: + description: 'Severity denotes the severity levels of + security issues to be ignored, valid values are: - "low": + it will exclude low severity vulnerabilities, displaying + only medium, high and critical vulnerabilities - "medium": + it will exclude low and medium severity vulnerabilities, + displaying only high and critical vulnerabilities - + "high": it will exclude low, medium and high severity + vulnerabilities, displaying only the critical vulnerabilities' + enum: + - low + - medium + - high + type: string + unfixed: + description: Unfixed indicates to ignore vulnerabilities + for which no fix exists + type: boolean + type: object + type: object required: - image type: object @@ -10629,6 +10715,49 @@ spec: integer number defined as the epoch seconds - or nil/empty to not set any specific timestamp' type: string + vulnerabilityScan: + description: VulnerabilityScan provides configurations about + running a scan for your generated image + properties: + enabled: + description: Enabled indicates whether to run vulnerability + scan for image + type: boolean + failOnFinding: + description: FailOnFinding indicates whether to fail the + build run if the vulnerability scan results in vulnerabilities + type: boolean + ignore: + description: Ignore refers to ignore options for vulnerability + scan + properties: + id: + description: ID references the security issues to + be ignored in vulnerability scan + items: + type: string + type: array + severity: + description: 'Severity denotes the severity levels + of security issues to be ignored, valid values are: + - "low": it will exclude low severity vulnerabilities, + displaying only medium, high and critical vulnerabilities + - "medium": it will exclude low and medium severity + vulnerabilities, displaying only high and critical + vulnerabilities - "high": it will exclude low, medium + and high severity vulnerabilities, displaying only + the critical vulnerabilities' + enum: + - low + - medium + - high + type: string + unfixed: + description: Unfixed indicates to ignore vulnerabilities + for which no fix exists + type: boolean + type: object + type: object required: - image type: object @@ -12618,6 +12747,21 @@ spec: description: Size holds the compressed size of output image format: int64 type: integer + vulnerabilities: + description: Vulnerabilities holds the list of vulnerabilities + detected in the image + items: + description: Vulnerability defines a vulnerability by its ID + and severity + properties: + id: + type: string + severity: + description: VulnerabilitySeverity is an enum for the possible + values for severity of a vulnerability + type: string + type: object + type: array type: object source: description: Source holds the results emitted from the source step diff --git a/deploy/crds/shipwright.io_builds.yaml b/deploy/crds/shipwright.io_builds.yaml index 9f3dc63e8..72212d30c 100644 --- a/deploy/crds/shipwright.io_builds.yaml +++ b/deploy/crds/shipwright.io_builds.yaml @@ -2351,6 +2351,48 @@ spec: defined as the epoch seconds - or nil/empty to not set any specific timestamp' type: string + vulnerabilityScan: + description: VulnerabilityScan provides configurations about running + a scan for your generated image + properties: + enabled: + description: Enabled indicates whether to run vulnerability + scan for image + type: boolean + failOnFinding: + description: FailOnFinding indicates whether to fail the build + run if the vulnerability scan results in vulnerabilities + type: boolean + ignore: + description: Ignore refers to ignore options for vulnerability + scan + properties: + id: + description: ID references the security issues to be ignored + in vulnerability scan + items: + type: string + type: array + severity: + description: 'Severity denotes the severity levels of + security issues to be ignored, valid values are: - "low": + it will exclude low severity vulnerabilities, displaying + only medium, high and critical vulnerabilities - "medium": + it will exclude low and medium severity vulnerabilities, + displaying only high and critical vulnerabilities - + "high": it will exclude low, medium and high severity + vulnerabilities, displaying only the critical vulnerabilities' + enum: + - low + - medium + - high + type: string + unfixed: + description: Unfixed indicates to ignore vulnerabilities + for which no fix exists + type: boolean + type: object + type: object required: - image type: object diff --git a/docs/build.md b/docs/build.md index 9abd31bb4..987f4178e 100644 --- a/docs/build.md +++ b/docs/build.md @@ -111,6 +111,7 @@ The `Build` definition supports the following fields: - Use string `SourceTimestamp` to set the image timestamp to the source timestamp, i.e. the timestamp of the Git commit that was used. - Use string `BuildTimestamp` to set the image timestamp to the timestamp of the build run. - Use any valid UNIX epoch seconds number as a string to set this as the image timestamp. + - `spec.output.vulnerabilityScan` to enable a security vulnerability scan for your generated image. Further options in vulnerability scanning are defined [here](#defining-the-vulnerabilityscan) - `spec.env` - Specifies additional environment variables that should be passed to the build container. The available variables depend on the tool that is being used by the chosen build strategy. - `spec.retention.atBuildDeletion` - Defines if all related BuildRuns needs to be deleted when deleting the Build. The default is false. - `spec.retention.ttlAfterFailed` - Specifies the duration for which a failed buildrun can exist. @@ -592,6 +593,48 @@ spec: timestamp: SourceTimestamp ``` +### Defining the vulnerabilityScan + +`vulnerabilityScan` provides configurations to run a scan for your generated image. + +- `vulnerabilityScan.enabled` - Specify whether to run vulnerability scan for image. The supported values are true and false. +- `vulnerabilityScan.failOnFinding` - indicates whether to fail the build run if the vulnerability scan results in vulnerabilities. The supported values are true and false. This field is optional and false by default. +- `vulnerabilityScan.ignore.issues` - references the security issues to be ignored in vulnerability scan +- `vulnerabilityScan.ignore.severity` - denotes the severity levels of security issues to be ignored, valid values are: + - `low`: it will exclude low severity vulnerabilities, displaying only medium, high and critical vulnerabilities + - `medium`: it will exclude low and medium severity vulnerabilities, displaying only high and critical vulnerabilities + - `high`: it will exclude low, medium and high severity vulnerabilities, displaying only the critical vulnerabilities +- `vulnerabilityScan.ignore.unfixed` - indicates to ignore vulnerabilities for which no fix exists. The supported types are true and false. + +Example of user specified image vulnerability scanning options: + +```yaml +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: sample-go-build +spec: + source: + type: Git + git: + url: https://github.com/shipwright-io/sample-go + contextDir: source-build + strategy: + name: buildkit + kind: ClusterBuildStrategy + output: + image: some.registry.com/namespace/image:tag + pushSecret: credentials + vulnerabilityScan: + enabled: true + failOnFinding: true + ignore: + issues: + - CVE-2022-12345 + severity: Low + unfixed: true +``` + Annotations added to the output image can be verified by running the command: ```sh diff --git a/docs/buildrun.md b/docs/buildrun.md index 9b420133a..841f81afa 100644 --- a/docs/buildrun.md +++ b/docs/buildrun.md @@ -70,6 +70,7 @@ The `BuildRun` definition supports the following fields: - `spec.output.image` - Refers to a custom location where the generated image would be pushed. The value will overwrite the `output.image` value defined in `Build`. (**Note**: other properties of the output, for example, the credentials, cannot be specified in the buildRun spec. ) - `spec.output.pushSecret` - Reference an existing secret to get access to the container registry. This secret will be added to the service account along with the ones requested by the `Build`. - `spec.output.timestamp` - Overrides the output timestamp configuration of the referenced build to instruct the build to change the output image creation timestamp to the specified value. When omitted, the respective build strategy tool defines the output image timestamp. + - `spec.output.vulnerabilityScan` - Overrides the output vulnerabilityScan configuration of the referenced build to run the vulnerability scan for the generated image. - `spec.env` - Specifies additional environment variables that should be passed to the build container. Overrides any environment variables that are specified in the `Build` resource. The available variables depend on the tool used by the chosen build strategy. **Note**: The `spec.build.name` and `spec.build.spec` are mutually exclusive. Furthermore, the overrides for `timeout`, `paramValues`, `output`, and `env` can only be combined with `spec.build.name`, but **not** with `spec.build.spec`. @@ -418,6 +419,24 @@ status: reason: GitRemotePrivate ``` +### Understanding failed BuildRuns due to VulnerabilitiesFound + +A buildrun can be failed, if the vulnerability scan finds vulnerabilities in the generated image and `failOnFinding` is set to true in the `vulnerabilityScan`. For setting `vulnerabilityScan`, see [here](build.md#defining-the-vulnerabilityscan). + +Example of failed BuildRun due to vulnerabilities present in the image: + +```yaml +# [...] +status: + # [...] + conditions: + - type: Succeeded + lastTransitionTime: "2024-03-12T20:00:38Z" + status: "False" + reason: VulnerabilitiesFound + message: "Vulnerabilities have been found in the output image. For detailed information, check buildrun status or see kubectl --namespace default logs vuln-s6skc-v7wd2-pod --container step-image-processing" +``` + #### Understanding failed git-source step All git-related operations support error reporting via `status.failureDetails`. The following table explains the possible @@ -477,6 +496,26 @@ status: **Note**: The digest and size of the output image are only included if the build strategy provides them. See [System results](buildstrategies.md#system-results). +Another example of a `BuildRun` with surfaced results for vulnerability scanning. + +```yaml +# [...] +status: + buildSpec: + # [...] + status: + output: + digest: sha256:1023103 + size: 12310380 + vulnerabilities: + - id: CVE-2022-12345 + severity: high + - id: CVE-2021-54321 + severity: medium +``` + +**Note**: The vulnerability scan will only run if it is specified in the build or buildrun spec. See [Defining the `vulnerabilityScan`](build.md#defining-the-vulnerabilityscan). + ### Build Snapshot For every BuildRun controller reconciliation, the `buildSpec` in the status of the `BuildRun` is updated if an existing owned `TaskRun` is present. During this update, a `Build` resource snapshot is generated and embedded into the `status.buildSpec` path of the `BuildRun`. A `buildSpec` is just a copy of the original `Build` spec, from where the `BuildRun` executed a particular image build. The snapshot approach allows developers to see the original `Build` configuration. diff --git a/docs/configuration.md b/docs/configuration.md index 419465188..46f6c465c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -36,6 +36,7 @@ The following environment variables are available: | `CLUSTERBUILDSTRATEGY_MAX_CONCURRENT_RECONCILES` | The number of concurrent reconciles by the ClusterBuildStrategy controller. A value of 0 or lower will use the default from the [controller-runtime controller Options]. Default is 0. | | `KUBE_API_BURST` | Burst to use for the Kubernetes API client. See [Config.Burst]. A value of 0 or lower will use the default from client-go, which currently is 10. Default is 0. | | `KUBE_API_QPS` | QPS to use for the Kubernetes API client. See [Config.QPS]. A value of 0 or lower will use the default from client-go, which currently is 5. Default is 0. | +| `VULNERABILITY_COUNT_LIMIT` | holds vulnerability count limit if vulnerability scan is enabled for the output image. If it is defined as 10, then it will output only 10 vulnerabilities sorted by severity in the buildrun status.Output. Default is 50. | [^1]: The `runAsUser` and `runAsGroup` are dynamically overwritten depending on the build strategy that is used. See [Security Contexts](buildstrategies.md#security-contexts) for more information. diff --git a/hack/install-trivy.sh b/hack/install-trivy.sh new file mode 100755 index 000000000..6e839cafc --- /dev/null +++ b/hack/install-trivy.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Copyright The Shipwright Contributors +# +# SPDX-License-Identifier: Apache-2.0 + +# +# Installs "trivy" +# + +set -euo pipefail + +# Find a suitable install location +for CANDIDATE in "$HOME/bin" "/usr/local/bin" "/usr/bin"; do + if [[ -w $CANDIDATE ]] && grep -q "$CANDIDATE" <<<"$PATH"; then + TARGET_DIR="$CANDIDATE" + break + fi +done + +# Bail out in case no suitable location could be found +if [[ -z ${TARGET_DIR:-} ]]; then + echo -e "Unable to determine a writable install location. Make sure that you have write access to either \\033[1m/usr/local/bin\\033[0m or \\033[1m${HOME}/bin\\033[0m and that is in your PATH." + exit 1 +fi + +echo "# Install Trivy" +curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b "$TARGET_DIR" + +echo "# Trivy version" +trivy --version diff --git a/pkg/apis/build/v1beta1/build_types.go b/pkg/apis/build/v1beta1/build_types.go index 39a05e6c9..2293dee0e 100644 --- a/pkg/apis/build/v1beta1/build_types.go +++ b/pkg/apis/build/v1beta1/build_types.go @@ -81,6 +81,20 @@ const ( AllValidationsSucceeded = "all validations succeeded" ) +// IgnoredVulnerabilitySeverity is an enum for the possible values for the ignored severity +type IgnoredVulnerabilitySeverity string + +const ( + // High indicates that high, medium, and low severity vulnerabilities should be ignored + IgnoredHigh IgnoredVulnerabilitySeverity = "high" + + // Medium indicates that medium, and low severity vulnerabilities should be ignored + IgnoredMedium IgnoredVulnerabilitySeverity = "medium" + + // High indicates that low severity vulnerabilities should be ignored + IgnoredLow IgnoredVulnerabilitySeverity = "low" +) + // BuildReasonPtr returns a pointer to the passed BuildReason. func BuildReasonPtr(s BuildReason) *BuildReason { return &s @@ -193,6 +207,49 @@ func (buildSpec *BuildSpec) StrategyName() string { return buildSpec.Strategy.Name } +// VulnerabilityIgnoreOptions refers to ignore options for vulnerability scan +type VulnerabilityIgnoreOptions struct { + + // ID references the security issues to be ignored in vulnerability scan + // + // +optional + ID []string `json:"id,omitempty"` + + // Timestamp references the optional image timestamp to be set, valid values are: + // - "Zero", to set 00:00:00 UTC on 1 January 1970 + // - "SourceTimestamp", to set the source timestamp dereived from the input source + // - "BuildTimestamp", to set the timestamp of the current build itself + // - Parsable integer number defined as the epoch seconds + // - or nil/empty to not set any specific timestamp + + // Severity denotes the severity levels of security issues to be ignored, valid values are: + // - "low": it will exclude low severity vulnerabilities, displaying only medium, high and critical vulnerabilities + // - "medium": it will exclude low and medium severity vulnerabilities, displaying only high and critical vulnerabilities + // - "high": it will exclude low, medium and high severity vulnerabilities, displaying only the critical vulnerabilities + // + // +optional + // +kubebuilder:validation:Enum=low;medium;high + Severity *IgnoredVulnerabilitySeverity `json:"severity,omitempty"` + + // Unfixed indicates to ignore vulnerabilities for which no fix exists + // + // +optional + Unfixed *bool `json:"unfixed,omitempty"` +} + +// VulnerabilityScanOptions provides configurations about running a scan for your generated image +type VulnerabilityScanOptions struct { + + // Enabled indicates whether to run vulnerability scan for image + Enabled bool `json:"enabled,omitempty"` + + // FailOnFinding indicates whether to fail the build run if the vulnerability scan results in vulnerabilities + FailOnFinding bool `json:"failOnFinding,omitempty"` + + // Ignore refers to ignore options for vulnerability scan + Ignore *VulnerabilityIgnoreOptions `json:"ignore,omitempty"` +} + // Image refers to an container image with credentials type Image struct { // Image is the reference of the image. @@ -218,6 +275,11 @@ type Image struct { // +optional Labels map[string]string `json:"labels,omitempty"` + // VulnerabilityScan provides configurations about running a scan for your generated image + // + // +optional + VulnerabilityScan *VulnerabilityScanOptions `json:"vulnerabilityScan,omitempty"` + // Timestamp references the optional image timestamp to be set, valid values are: // - "Zero", to set 00:00:00 UTC on 1 January 1970 // - "SourceTimestamp", to set the source timestamp dereived from the input source diff --git a/pkg/apis/build/v1beta1/buildrun_conversion.go b/pkg/apis/build/v1beta1/buildrun_conversion.go index 7d0c2b6ca..4d7ecd1ef 100644 --- a/pkg/apis/build/v1beta1/buildrun_conversion.go +++ b/pkg/apis/build/v1beta1/buildrun_conversion.go @@ -142,9 +142,17 @@ func (src *BuildRun) ConvertTo(ctx context.Context, obj *unstructured.Unstructur conditions = append(conditions, ct) } + var output *v1alpha1.Output + if src.Status.Output != nil { + output = &v1alpha1.Output{ + Digest: src.Status.Output.Digest, + Size: src.Status.Output.Size, + } + } + alphaBuildRun.Status = v1alpha1.BuildRunStatus{ Sources: sourceStatus, - Output: (*v1alpha1.Output)(src.Status.Output), + Output: output, Conditions: conditions, LatestTaskRunRef: src.Status.TaskRunName, StartTime: src.Status.StartTime, @@ -232,9 +240,17 @@ func (src *BuildRun) ConvertFrom(ctx context.Context, obj *unstructured.Unstruct } } + var output *Output + if alphaBuildRun.Status.Output != nil { + output = &Output{ + Digest: alphaBuildRun.Status.Output.Digest, + Size: alphaBuildRun.Status.Output.Size, + } + } + src.Status = BuildRunStatus{ Source: sourceStatus, - Output: (*Output)(alphaBuildRun.Status.Output), + Output: output, Conditions: conditions, TaskRunName: alphaBuildRun.Status.LatestTaskRunRef, StartTime: alphaBuildRun.Status.StartTime, diff --git a/pkg/apis/build/v1beta1/buildrun_types.go b/pkg/apis/build/v1beta1/buildrun_types.go index d3a2dc361..ef6745c07 100644 --- a/pkg/apis/build/v1beta1/buildrun_types.go +++ b/pkg/apis/build/v1beta1/buildrun_types.go @@ -20,6 +20,26 @@ const ( LabelBuildRunGeneration = BuildRunDomain + "/generation" ) +// VulnerabilitySeverity is an enum for the possible values for severity of a vulnerability +type VulnerabilitySeverity string + +const ( + // Critical indicates a critical severity + Critical VulnerabilitySeverity = "critical" + + // High indicates a high severity + High VulnerabilitySeverity = "high" + + // Medium indicates a medium severity + Medium VulnerabilitySeverity = "medium" + + // Low indicates a low severity + Low VulnerabilitySeverity = "low" + + // Unknown indicates an unknown severity + Unknown VulnerabilitySeverity = "unknown" +) + type ReferencedBuild struct { // Spec refers to an embedded build specification // @@ -103,6 +123,9 @@ const ( // if not already canceled or terminated BuildRunStateCancel = "BuildRunCanceled" + // BuildRunStateVulnerabilitiesFound indicates that unignored vulnerabilities were found in the image that was built + BuildRunStateVulnerabilitiesFound = "VulnerabilitiesFound" + // BuildRunStatePodEvicted indicates that if the pods got evicted // due to some reason. (Probably ran out of ephemeral storage) BuildRunStatePodEvicted = "PodEvicted" @@ -156,6 +179,12 @@ type GitSourceResult struct { BranchName string `json:"branchName,omitempty"` } +// Vulnerability defines a vulnerability by its ID and severity +type Vulnerability struct { + ID string `json:"id,omitempty"` + Severity VulnerabilitySeverity `json:"severity,omitempty"` +} + // Output holds the information about the container image that the BuildRun built type Output struct { // Digest holds the digest of output image @@ -167,6 +196,11 @@ type Output struct { // // +optional Size int64 `json:"size,omitempty"` + + // Vulnerabilities holds the list of vulnerabilities detected in the image + // + // +optional + Vulnerabilities []Vulnerability `json:"vulnerabilities,omitempty"` } // BuildRunStatus defines the observed state of BuildRun diff --git a/pkg/apis/build/v1beta1/zz_generated.deepcopy.go b/pkg/apis/build/v1beta1/zz_generated.deepcopy.go index 9c1ce78f6..44a8d1713 100644 --- a/pkg/apis/build/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/build/v1beta1/zz_generated.deepcopy.go @@ -304,7 +304,7 @@ func (in *BuildRunStatus) DeepCopyInto(out *BuildRunStatus) { if in.Output != nil { in, out := &in.Output, &out.Output *out = new(Output) - **out = **in + (*in).DeepCopyInto(*out) } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions @@ -808,6 +808,11 @@ func (in *Image) DeepCopyInto(out *Image) { (*out)[key] = val } } + if in.VulnerabilityScan != nil { + in, out := &in.VulnerabilityScan, &out.VulnerabilityScan + *out = new(VulnerabilityScanOptions) + (*in).DeepCopyInto(*out) + } if in.Timestamp != nil { in, out := &in.Timestamp, &out.Timestamp *out = new(string) @@ -929,6 +934,11 @@ func (in *OciArtifactSourceResult) DeepCopy() *OciArtifactSourceResult { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Output) DeepCopyInto(out *Output) { *out = *in + if in.Vulnerabilities != nil { + in, out := &in.Vulnerabilities, &out.Vulnerabilities + *out = make([]Vulnerability, len(*in)) + copy(*out, *in) + } return } @@ -1249,6 +1259,74 @@ func (in *TriggerWhen) DeepCopy() *TriggerWhen { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Vulnerability) DeepCopyInto(out *Vulnerability) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Vulnerability. +func (in *Vulnerability) DeepCopy() *Vulnerability { + if in == nil { + return nil + } + out := new(Vulnerability) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VulnerabilityIgnoreOptions) DeepCopyInto(out *VulnerabilityIgnoreOptions) { + *out = *in + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Severity != nil { + in, out := &in.Severity, &out.Severity + *out = new(IgnoredVulnerabilitySeverity) + **out = **in + } + if in.Unfixed != nil { + in, out := &in.Unfixed, &out.Unfixed + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VulnerabilityIgnoreOptions. +func (in *VulnerabilityIgnoreOptions) DeepCopy() *VulnerabilityIgnoreOptions { + if in == nil { + return nil + } + out := new(VulnerabilityIgnoreOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VulnerabilityScanOptions) DeepCopyInto(out *VulnerabilityScanOptions) { + *out = *in + if in.Ignore != nil { + in, out := &in.Ignore, &out.Ignore + *out = new(VulnerabilityIgnoreOptions) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VulnerabilityScanOptions. +func (in *VulnerabilityScanOptions) DeepCopy() *VulnerabilityScanOptions { + if in == nil { + return nil + } + out := new(VulnerabilityScanOptions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WhenGitHub) DeepCopyInto(out *WhenGitHub) { *out = *in diff --git a/pkg/config/config.go b/pkg/config/config.go index e98e265a7..be608c793 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -76,6 +76,9 @@ const ( // environment variable for the Git rewrite setting useGitRewriteRule = "GIT_ENABLE_REWRITE_RULE" + + // environment variable to hold vulnerability count limit + VulnerabilityCountLimitEnvVar = "VULNERABILITY_COUNT_LIMIT" ) var ( @@ -103,6 +106,7 @@ type Config struct { Controllers Controllers KubeAPIOptions KubeAPIOptions GitRewriteRule bool + VulnerabilityCountLimit int } // PrometheusConfig contains the specific configuration for the @@ -158,6 +162,7 @@ func NewDefaultConfig() *Config { RemoteArtifactsContainerImage: remoteArtifactsDefaultImage, TerminationLogPath: terminationLogPathDefault, GitRewriteRule: false, + VulnerabilityCountLimit: 50, GitContainerTemplate: Step{ Image: gitDefaultImage, @@ -339,6 +344,15 @@ func (c *Config) SetConfigFromEnv() error { c.ImageProcessingContainerTemplate.Image = imageProcessingImage } + // set environment variable for vulnerability count limit + if vcStr := os.Getenv(VulnerabilityCountLimitEnvVar); vcStr != "" { + vc, err := strconv.Atoi(vcStr) + if err != nil { + return err + } + c.VulnerabilityCountLimit = vc + } + // Mark that the Git wrapper is suppose to use Git rewrite rule if useGitRewriteRule := os.Getenv(useGitRewriteRule); useGitRewriteRule != "" { c.GitRewriteRule = strings.ToLower(useGitRewriteRule) == "true" diff --git a/pkg/image/load.go b/pkg/image/load.go index 9ee29f245..514d61fd3 100644 --- a/pkg/image/load.go +++ b/pkg/image/load.go @@ -17,18 +17,18 @@ import ( ) // LoadImageOrImageIndexFromDirectory loads an image or an image index from a directory -func LoadImageOrImageIndexFromDirectory(directory string) (containerreg.Image, containerreg.ImageIndex, error) { +func LoadImageOrImageIndexFromDirectory(directory string) (containerreg.Image, containerreg.ImageIndex, bool, error) { // if we have an index.json, then we have an OCI image index fileInfo, err := os.Stat(path.Join(directory, "index.json")) if err == nil && !fileInfo.IsDir() { imageIndex, err := layout.ImageIndexFromPath(directory) if err != nil { - return nil, nil, err + return nil, nil, false, err } indexManifest, err := imageIndex.IndexManifest() if err != nil { - return nil, nil, err + return nil, nil, false, err } // flatten an index with exactly one entry, in particular if we push an index with an index that contains the images, @@ -41,17 +41,17 @@ func LoadImageOrImageIndexFromDirectory(directory string) (containerreg.Image, c case desc.MediaType.IsImage(): image, err := imageIndex.Image(desc.Digest) - return image, nil, err + return image, nil, false, err case desc.MediaType.IsIndex(): imageIndex, err = imageIndex.ImageIndex(desc.Digest) if err != nil { - return nil, nil, err + return nil, nil, false, err } indexManifest, err = imageIndex.IndexManifest() if err != nil { - return nil, nil, err + return nil, nil, false, err } default: @@ -59,21 +59,21 @@ func LoadImageOrImageIndexFromDirectory(directory string) (containerreg.Image, c } } - return nil, imageIndex, nil + return nil, imageIndex, false, nil } entries, err := os.ReadDir(directory) if err != nil { - return nil, nil, err + return nil, nil, false, err } if len(entries) == 1 { // tag nil is correct here, a tag would be needed if the tarball contains several images // which is not desired to be the case here image, err := tarball.ImageFromPath(path.Join(directory, entries[0].Name()), nil) - return image, nil, err + return image, nil, true, err } - return nil, nil, fmt.Errorf("no image was found at %q", directory) + return nil, nil, false, fmt.Errorf("no image was found at %q", directory) } // LoadImageOrImageIndexFromRegistry loads an image or an image index from a registry diff --git a/pkg/image/load_test.go b/pkg/image/load_test.go index dd51d9389..d3294e2d5 100644 --- a/pkg/image/load_test.go +++ b/pkg/image/load_test.go @@ -75,10 +75,11 @@ var _ = Describe("For a local directory", func() { }) It("LoadImageOrImageIndexFromDirectory returns an error", func() { - img, imageIndex, err := image.LoadImageOrImageIndexFromDirectory(directory) + img, imageIndex, isImageFromTar, err := image.LoadImageOrImageIndexFromDirectory(directory) Expect(err).To(HaveOccurred()) Expect(imageIndex).To(BeNil()) Expect(img).To(BeNil()) + Expect(isImageFromTar).To(BeFalse()) }) }) @@ -89,10 +90,11 @@ var _ = Describe("For a local directory", func() { directory := path.Clean(path.Join(cwd, "../..", "test/data/images/multi-platform-image-in-oci")) It("LoadImageOrImageIndexFromDirectory returns an ImageIndex", func() { - img, imageIndex, err := image.LoadImageOrImageIndexFromDirectory(directory) + img, imageIndex, isImageFromTar, err := image.LoadImageOrImageIndexFromDirectory(directory) Expect(err).ToNot(HaveOccurred()) Expect(imageIndex).ToNot(BeNil()) Expect(img).To(BeNil()) + Expect(isImageFromTar).To(BeFalse()) }) }) @@ -103,10 +105,11 @@ var _ = Describe("For a local directory", func() { directory := path.Clean(path.Join(cwd, "../..", "test/data/images/single-image")) It("LoadImageOrImageIndexFromDirectory returns an Image", func() { - img, imageIndex, err := image.LoadImageOrImageIndexFromDirectory(directory) + img, imageIndex, isImageFromTar, err := image.LoadImageOrImageIndexFromDirectory(directory) Expect(err).ToNot(HaveOccurred()) Expect(imageIndex).To(BeNil()) Expect(img).ToNot(BeNil()) + Expect(isImageFromTar).To(BeTrue()) }) }) @@ -117,10 +120,11 @@ var _ = Describe("For a local directory", func() { directory := path.Clean(path.Join(cwd, "../..", "test/data/images/single-image-in-oci")) It("LoadImageOrImageIndexFromDirectory returns an Image", func() { - img, imageIndex, err := image.LoadImageOrImageIndexFromDirectory(directory) + img, imageIndex, isImageFromTar, err := image.LoadImageOrImageIndexFromDirectory(directory) Expect(err).ToNot(HaveOccurred()) Expect(imageIndex).To(BeNil()) Expect(img).ToNot(BeNil()) + Expect(isImageFromTar).To(BeFalse()) }) }) }) diff --git a/pkg/image/vulnerability_scan.go b/pkg/image/vulnerability_scan.go new file mode 100644 index 000000000..1eaf1beaa --- /dev/null +++ b/pkg/image/vulnerability_scan.go @@ -0,0 +1,145 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 +package image + +import ( + "context" + "encoding/json" + "fmt" + "log" + "os/exec" + "sort" + "strings" + + "github.com/google/go-containerregistry/pkg/authn" + buildapi "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +type TrivyVulnerability struct { + VulnerabilityID string `json:"vulnerabilityID,omitempty"` + Severity string `json:"severity,omitempty"` +} + +type TrivyResult struct { + Results []struct { + Vulnerabilities []TrivyVulnerability `json:"vulnerabilities"` + } `json:"Results"` +} + +func RunVulnerabilityScan(ctx context.Context, imagePath string, settings buildapi.VulnerabilityScanOptions, auth *authn.AuthConfig, insecure bool, imageInDir bool, vulnCountLimit int) ([]buildapi.Vulnerability, error) { + + trivyArgs := []string{"image", "--quiet", "--format", "json"} + if imageInDir { + trivyArgs = append(trivyArgs, "--input", imagePath) + } else { + trivyArgs = append(trivyArgs, imagePath) + authParams := getAuthStringForTrivyScan(auth) + trivyArgs = append(trivyArgs, authParams...) + + if insecure { + trivyArgs = append(trivyArgs, "--insecure") + } + } + if settings.Ignore != nil { + if settings.Ignore.Severity != nil { + severity := getSeverityStringForTrivyScan(*settings.Ignore.Severity) + trivyArgs = append(trivyArgs, "--severity", severity) + } + if settings.Ignore.Unfixed != nil && *settings.Ignore.Unfixed { + trivyArgs = append(trivyArgs, "--ignore-unfixed") + } + } + + cmd := exec.CommandContext(ctx, "trivy", trivyArgs...) + + cmd.Stdin = nil + + result, err := cmd.CombinedOutput() + if err != nil { + log.Printf("failed to run trivy:\n%s", string(result)) + return nil, fmt.Errorf("failed to run trivy: %w", err) + } + + var trivyResult TrivyResult + if err := json.Unmarshal(result, &trivyResult); err != nil { + return nil, err + } + + // Get the vulnerabilities from the trivy scan + vulnerabilities := parseTrivyResult(trivyResult, settings.Ignore) + + // Sort the vulnerabilities by severity + severityOrder := map[buildapi.VulnerabilitySeverity]int{ + buildapi.Critical: 0, + buildapi.High: 1, + buildapi.Medium: 2, + buildapi.Low: 3, + buildapi.Unknown: 4, + } + sort.Slice(vulnerabilities, func(i, j int) bool { + return severityOrder[vulnerabilities[i].Severity] < severityOrder[vulnerabilities[j].Severity] + }) + + if len(vulnerabilities) > vulnCountLimit { + vulnerabilities = vulnerabilities[:vulnCountLimit] + } + + return vulnerabilities, nil +} + +func getSeverityStringForTrivyScan(ignoreSeverity buildapi.IgnoredVulnerabilitySeverity) string { + switch ignoreSeverity { + case buildapi.IgnoredLow: + return "MEDIUM,HIGH,CRITICAL" + case buildapi.IgnoredMedium: + return "HIGH,CRITICAL" + case buildapi.IgnoredHigh: + return "CRITICAL" + default: + return "LOW,MEDIUM,HIGH,CRITICAL" + } +} + +func parseTrivyResult(trivyResult TrivyResult, ignoreOptions *buildapi.VulnerabilityIgnoreOptions) []buildapi.Vulnerability { + var totalVulnerabilities []TrivyVulnerability + for _, result := range trivyResult.Results { + totalVulnerabilities = append(totalVulnerabilities, result.Vulnerabilities...) + } + + var vulnerabilities []buildapi.Vulnerability + + // create a map for ignored vulnerabilities + ignoreMap := make(map[string]bool) + if ignoreOptions != nil { + for _, vuln := range ignoreOptions.ID { + ignoreMap[vuln] = true + } + } + for _, vuln := range totalVulnerabilities { + if !ignoreMap[vuln.VulnerabilityID] { + vulnerability := buildapi.Vulnerability{ + ID: vuln.VulnerabilityID, + Severity: buildapi.VulnerabilitySeverity(strings.ToLower(vuln.Severity)), + } + vulnerabilities = append(vulnerabilities, vulnerability) + } + } + return vulnerabilities +} + +func getAuthStringForTrivyScan(auth *authn.AuthConfig) []string { + var authParams []string + if auth != nil { + if auth.Username != "" { + authParams = append(authParams, "--username", auth.Username) + } + if auth.Password != "" { + authParams = append(authParams, "--password", auth.Password) + } + if auth.RegistryToken != "" { + authParams = append(authParams, "--registry-token", auth.RegistryToken) + } + } + return authParams +} diff --git a/pkg/image/vulnerability_scan_test.go b/pkg/image/vulnerability_scan_test.go new file mode 100644 index 000000000..d6e02305c --- /dev/null +++ b/pkg/image/vulnerability_scan_test.go @@ -0,0 +1,128 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package image_test + +import ( + "context" + "os" + "path" + + buildapi "github.com/shipwright-io/build/pkg/apis/build/v1beta1" + "github.com/shipwright-io/build/pkg/image" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/types" +) + +var ( + vulnOptions buildapi.VulnerabilityScanOptions + directory string + vulnerableImage string +) + +var _ = Describe("VulnerabilityScanning", func() { + + Context("For a single image in registry", func() { + BeforeEach(func() { + vulnOptions = buildapi.VulnerabilityScanOptions{ + Enabled: true, + } + vulnerableImage = "ghcr.io/shipwright-io/shipwright-samples/node:12" + }) + + It("runs the image vulnerability scan", func() { + vulns, err := image.RunVulnerabilityScan(context.TODO(), vulnerableImage, vulnOptions, nil, false, false, 20) + Expect(err).ToNot(HaveOccurred()) + Expect(vulns).ToNot(BeEmpty()) + }) + }) + + Context("For an image index in a directory", func() { + BeforeEach(func() { + vulnOptions = buildapi.VulnerabilityScanOptions{ + Enabled: true, + } + cwd, err := os.Getwd() + Expect(err).ToNot(HaveOccurred()) + directory = path.Clean(path.Join(cwd, "../..", "test/data/images/vuln-image-in-oci")) + }) + + It("runs the image vulnerability scan", func() { + vulns, err := image.RunVulnerabilityScan(context.TODO(), directory, vulnOptions, nil, false, true, 20) + Expect(err).ToNot(HaveOccurred()) + Expect(vulns).ToNot(BeEmpty()) + }) + }) + + Context("For a single image in a directory", func() { + BeforeEach(func() { + cwd, err := os.Getwd() + Expect(err).ToNot(HaveOccurred()) + directory = path.Clean(path.Join(cwd, "../..", "test/data/images/vuln-single-image/vuln-image.tar")) + }) + + It("runs the image vulnerability scan", func() { + vulns, err := image.RunVulnerabilityScan(context.TODO(), directory, buildapi.VulnerabilityScanOptions{}, nil, false, true, 20) + Expect(err).ToNot(HaveOccurred()) + Expect(vulns).ToNot(BeEmpty()) + }) + + It("should ignore the severity defined in ignore options", func() { + ignoreSeverity := buildapi.IgnoredLow + vulnOptions := buildapi.VulnerabilityScanOptions{ + Enabled: true, + Ignore: &buildapi.VulnerabilityIgnoreOptions{ + Severity: &ignoreSeverity, + }, + } + vulns, err := image.RunVulnerabilityScan(context.TODO(), directory, vulnOptions, nil, false, true, 20) + Expect(err).ToNot(HaveOccurred()) + Expect(vulns).ToNot(BeEmpty()) + Expect(vulns).ToNot(containsSeverity("LOW")) + }) + + It("should ignore the vulnerabilities defined in ignore options", func() { + unfixed := true + vulnOptions := buildapi.VulnerabilityScanOptions{ + Enabled: true, + Ignore: &buildapi.VulnerabilityIgnoreOptions{ + ID: []string{ + "CVE-2018-20843", + }, + Unfixed: &unfixed, + }, + } + + vulns, err := image.RunVulnerabilityScan(context.TODO(), directory, vulnOptions, nil, false, true, 20) + Expect(err).ToNot(HaveOccurred()) + Expect(vulns).ToNot(BeEmpty()) + Expect(vulns).ToNot(containsVulnerability(vulnOptions.Ignore.ID[0])) + }) + }) + +}) + +func containsVulnerability(vulnerability string) types.GomegaMatcher { + return WithTransform(func(vulns []buildapi.Vulnerability) bool { + for _, vuln := range vulns { + if vuln.ID == vulnerability { + return true + } + } + return false + }, BeTrue()) +} + +func containsSeverity(severity buildapi.VulnerabilitySeverity) types.GomegaMatcher { + return WithTransform(func(vulns []buildapi.Vulnerability) bool { + for _, vuln := range vulns { + if vuln.Severity == severity { + return true + } + } + return false + }, BeTrue()) +} diff --git a/pkg/reconciler/buildrun/resources/conditions.go b/pkg/reconciler/buildrun/resources/conditions.go index e48b30a43..70e2e14a9 100644 --- a/pkg/reconciler/buildrun/resources/conditions.go +++ b/pkg/reconciler/buildrun/resources/conditions.go @@ -110,21 +110,30 @@ func UpdateBuildRunUsingTaskRunCondition(ctx context.Context, client client.Clie } else if failedContainer != nil { buildRun.Status.FailureDetails.Location.Container = failedContainer.Name - if failedContainerStatus != nil && failedContainerStatus.State.Terminated != nil && failedContainerStatus.State.Terminated.Reason == "OOMKilled" { - reason = buildv1beta1.BuildRunStateStepOutOfMemory - message = fmt.Sprintf("buildrun step %s failed due to out-of-memory, for detailed information: kubectl --namespace %s logs %s --container=%s", - failedContainer.Name, - pod.Namespace, - pod.Name, - failedContainer.Name, - ) - } else { - message = fmt.Sprintf("buildrun step %s failed, for detailed information: kubectl --namespace %s logs %s --container=%s", - failedContainer.Name, - pod.Namespace, - pod.Name, - failedContainer.Name, - ) + message = fmt.Sprintf("buildrun step %s failed, for detailed information: kubectl --namespace %s logs %s --container=%s", + failedContainer.Name, + pod.Namespace, + pod.Name, + failedContainer.Name, + ) + + if failedContainerStatus != nil && failedContainerStatus.State.Terminated != nil { + if failedContainerStatus.State.Terminated.Reason == "OOMKilled" { + reason = buildv1beta1.BuildRunStateStepOutOfMemory + message = fmt.Sprintf("buildrun step %s failed due to out-of-memory, for detailed information: kubectl --namespace %s logs %s --container=%s", + failedContainer.Name, + pod.Namespace, + pod.Name, + failedContainer.Name, + ) + } else if failedContainer.Name == "step-image-processing" && failedContainerStatus.State.Terminated.ExitCode == 22 { + reason = buildv1beta1.BuildRunStateVulnerabilitiesFound + message = fmt.Sprintf("Vulnerabilities have been found in the image which can be seen in the buildrun status. For detailed information,see kubectl --namespace %s logs %s --container=%s", + pod.Namespace, + pod.Name, + failedContainer.Name, + ) + } } } else { message = fmt.Sprintf("buildrun failed due to an unexpected error in pod %s: for detailed information: kubectl --namespace %s logs %s --all-containers", diff --git a/pkg/reconciler/buildrun/resources/conditions_test.go b/pkg/reconciler/buildrun/resources/conditions_test.go index 5662eb6ab..664c5c7f9 100644 --- a/pkg/reconciler/buildrun/resources/conditions_test.go +++ b/pkg/reconciler/buildrun/resources/conditions_test.go @@ -228,6 +228,69 @@ var _ = Describe("Conditions", func() { )).To(BeNil()) }) + It("updates BuildRun condition when TaskRun fails in vulnerability scan in image-processing step", func() { + // Generate a pod with name step-image-processing and exitCode 22 + failedTaskRunEvictedPod := corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "evilpod", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "step-image-processing", + }, + }, + }, + Status: corev1.PodStatus{ + Reason: "VulnerabilityScanFailed", + ContainerStatuses: []corev1.ContainerStatus{ + { + Name: "step-image-processing", + State: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 22, + }, + }, + }, + }, + }, + } + + // stub a GET API call with to pass the created pod + getClientStub := func(_ context.Context, nn types.NamespacedName, object crc.Object, _ ...crc.GetOption) error { + switch object := object.(type) { + case *corev1.Pod: + failedTaskRunEvictedPod.DeepCopyInto(object) + return nil + } + return k8serrors.NewNotFound(schema.GroupResource{}, nn.Name) + } + + // fake the calls with the above stub + client.GetCalls(getClientStub) + + // Now we need to create a fake failed taskrun so that it hits the code + fakeTRCondition := &apis.Condition{ + Type: apis.ConditionSucceeded, + Reason: "Failed", + Message: "not relevant", + } + + // We call the function with all the info + Expect(resources.UpdateBuildRunUsingTaskRunCondition( + context.TODO(), + client, + br, + tr, + fakeTRCondition, + )).To(BeNil()) + + // Finally, check the output of the buildRun + Expect(br.Status.GetCondition( + build.Succeeded).Reason, + ).To(Equal(build.BuildRunStateVulnerabilitiesFound)) + }) + It("updates BuildRun condition when TaskRun fails and pod is evicted", func() { // Generate a pod with the status to be evicted failedTaskRunEvictedPod := corev1.Pod{ diff --git a/pkg/reconciler/buildrun/resources/image_processing.go b/pkg/reconciler/buildrun/resources/image_processing.go index 28058e58d..0e3fda8e2 100644 --- a/pkg/reconciler/buildrun/resources/image_processing.go +++ b/pkg/reconciler/buildrun/resources/image_processing.go @@ -5,6 +5,7 @@ package resources import ( + "encoding/json" "fmt" "strconv" "time" @@ -14,6 +15,7 @@ import ( build "github.com/shipwright-io/build/pkg/apis/build/v1beta1" "github.com/shipwright-io/build/pkg/config" "github.com/shipwright-io/build/pkg/reconciler/buildrun/resources/sources" + "github.com/spf13/pflag" pipelineapi "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" ) @@ -23,6 +25,28 @@ const ( paramOutputDirectory = "output-directory" ) +type VulnerablilityScanParams struct { + build.VulnerabilityScanOptions +} + +var _ pflag.Value = &VulnerablilityScanParams{} + +func (v *VulnerablilityScanParams) Set(s string) error { + return json.Unmarshal([]byte(s), v) +} + +func (v *VulnerablilityScanParams) String() string { + data, err := json.Marshal(*v) + if err != nil { + panic(err.Error()) + } + return string(data) +} + +func (v *VulnerablilityScanParams) Type() string { + return "vulnerability-scan-params" +} + // SetupImageProcessing appends the image-processing step to a TaskRun if desired func SetupImageProcessing(taskRun *pipelineapi.TaskRun, cfg *config.Config, creationTimestamp time.Time, buildOutput, buildRunOutput build.Image) error { stepArgs := []string{} @@ -85,6 +109,18 @@ func SetupImageProcessing(taskRun *pipelineapi.TaskRun, cfg *config.Config, crea stepArgs = append(stepArgs, convertMutateArgs("--label", labels)...) } + vulnerabilitySettings := GetVulnerabilityScanOptions(buildOutput, buildRunOutput) + + // check if we need to add vulnerability scan arguments + if vulnerabilitySettings != nil && vulnerabilitySettings.Enabled { + vulnerablilityScanParams := &VulnerablilityScanParams{*vulnerabilitySettings} + stepArgs = append(stepArgs, "--vuln-settings", vulnerablilityScanParams.String()) + + if cfg.VulnerabilityCountLimit > 0 { + stepArgs = append(stepArgs, "--vuln-count-limit", strconv.Itoa(cfg.VulnerabilityCountLimit)) + } + } + // check if we need to set image timestamp if imageTimestamp := getImageTimestamp(buildOutput, buildRunOutput); imageTimestamp != nil { switch *imageTimestamp { @@ -122,6 +158,7 @@ func SetupImageProcessing(taskRun *pipelineapi.TaskRun, cfg *config.Config, crea // add the result arguments stepArgs = append(stepArgs, "--result-file-image-digest", fmt.Sprintf("$(results.%s-%s.path)", prefixParamsResultsVolumes, imageDigestResult)) stepArgs = append(stepArgs, "--result-file-image-size", fmt.Sprintf("$(results.%s-%s.path)", prefixParamsResultsVolumes, imageSizeResult)) + stepArgs = append(stepArgs, "--result-file-image-vulnerabilities", fmt.Sprintf("$(results.%s-%s.path)", prefixParamsResultsVolumes, imageVulnerabilities)) // add the push step @@ -194,6 +231,17 @@ func mergeMaps(first map[string]string, second map[string]string) map[string]str return first } +func GetVulnerabilityScanOptions(buildOutput, buildRunOutput build.Image) *build.VulnerabilityScanOptions { + switch { + case buildRunOutput.VulnerabilityScan != nil: + return buildRunOutput.VulnerabilityScan + case buildOutput.VulnerabilityScan != nil: + return buildOutput.VulnerabilityScan + default: + return nil + } +} + func getImageTimestamp(buildOutput, buildRunOutput build.Image) *string { switch { case buildRunOutput.Timestamp != nil: diff --git a/pkg/reconciler/buildrun/resources/image_processing_test.go b/pkg/reconciler/buildrun/resources/image_processing_test.go index fb9a6bc9f..21b8a4c9f 100644 --- a/pkg/reconciler/buildrun/resources/image_processing_test.go +++ b/pkg/reconciler/buildrun/resources/image_processing_test.go @@ -89,10 +89,48 @@ var _ = Describe("Image Processing overrides", func() { "$(results.shp-image-digest.path)", "--result-file-image-size", "$(results.shp-image-size.path)", + "--result-file-image-vulnerabilities", + "$(results.shp-image-vulnerabilities.path)", })) Expect(processedTaskRun.Spec.TaskSpec.Steps[1].VolumeMounts).ToNot(utils.ContainNamedElement("shp-output-directory")) }) }) + + Context("for a build with a vulnerability scan options in the output", func() { + BeforeEach(func() { + processedTaskRun = taskRun.DeepCopy() + resources.SetupImageProcessing(processedTaskRun, config, refTimestamp, buildv1beta1.Image{ + Image: "some-registry/some-namespace/some-image", + VulnerabilityScan: &buildv1beta1.VulnerabilityScanOptions{ + Enabled: true, + }, + }, buildv1beta1.Image{}) + }) + + It("adds the image-processing step", func() { + Expect(processedTaskRun.Spec.TaskSpec.Steps).To(HaveLen(2)) + Expect(processedTaskRun.Spec.TaskSpec.Steps[1].Name).To(Equal("image-processing")) + Expect(processedTaskRun.Spec.TaskSpec.Steps[1].Image).To(Equal(config.ImageProcessingContainerTemplate.Image)) + Expect(processedTaskRun.Spec.TaskSpec.Steps[1].Command).To(Equal(config.ImageProcessingContainerTemplate.Command)) + Expect(processedTaskRun.Spec.TaskSpec.Steps[1].Args).To(Equal([]string{ + "--vuln-settings", + "{\"enabled\":true}", + "--vuln-count-limit", + "50", + "--image", + "$(params.shp-output-image)", + "--insecure=$(params.shp-output-insecure)", + "--result-file-image-digest", + "$(results.shp-image-digest.path)", + "--result-file-image-size", + "$(results.shp-image-size.path)", + "--result-file-image-vulnerabilities", + "$(results.shp-image-vulnerabilities.path)", + })) + Expect(processedTaskRun.Spec.TaskSpec.Steps[1].VolumeMounts).ToNot(utils.ContainNamedElement("shp-output-directory")) + }) + }) + }) Context("for a TaskRun that references the output directory", func() { @@ -163,6 +201,8 @@ var _ = Describe("Image Processing overrides", func() { "$(results.shp-image-digest.path)", "--result-file-image-size", "$(results.shp-image-size.path)", + "--result-file-image-vulnerabilities", + "$(results.shp-image-vulnerabilities.path)", })) Expect(processedTaskRun.Spec.TaskSpec.Steps[1].VolumeMounts).To(utils.ContainNamedElement("shp-output-directory")) }) @@ -204,6 +244,8 @@ var _ = Describe("Image Processing overrides", func() { "$(results.shp-image-digest.path)", "--result-file-image-size", "$(results.shp-image-size.path)", + "--result-file-image-vulnerabilities", + "$(results.shp-image-vulnerabilities.path)", })) Expect(processedTaskRun.Spec.TaskSpec.Steps[1].VolumeMounts).To(utils.ContainNamedElement("shp-output-directory")) }) @@ -254,6 +296,8 @@ var _ = Describe("Image Processing overrides", func() { "$(results.shp-image-digest.path)", "--result-file-image-size", "$(results.shp-image-size.path)", + "--result-file-image-vulnerabilities", + "$(results.shp-image-vulnerabilities.path)", "--secret-path", "/workspace/shp-push-secret", })) diff --git a/pkg/reconciler/buildrun/resources/results.go b/pkg/reconciler/buildrun/resources/results.go index d6d60dae9..3b005b0e2 100644 --- a/pkg/reconciler/buildrun/resources/results.go +++ b/pkg/reconciler/buildrun/resources/results.go @@ -8,6 +8,7 @@ import ( "context" "fmt" "strconv" + "strings" build "github.com/shipwright-io/build/pkg/apis/build/v1beta1" "github.com/shipwright-io/build/pkg/ctxlog" @@ -17,8 +18,9 @@ import ( ) const ( - imageDigestResult = "image-digest" - imageSizeResult = "image-size" + imageDigestResult = "image-digest" + imageSizeResult = "image-size" + imageVulnerabilities = "image-vulnerabilities" ) // UpdateBuildRunUsingTaskResults surface the task results @@ -52,6 +54,8 @@ func updateBuildRunStatusWithOutputResult(ctx context.Context, buildRun *build.B } else { buildRun.Status.Output.Size = size } + case generateOutputResultName(imageVulnerabilities): + buildRun.Status.Output.Vulnerabilities = getImageVulnerabilitiesResult(result) } } } @@ -70,5 +74,38 @@ func getTaskSpecResults() []pipelineapi.TaskResult { Name: fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, imageSizeResult), Description: "The compressed size of the image", }, + { + Name: fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, imageVulnerabilities), + Description: "List of vulnerabilities", + }, + } +} + +func getImageVulnerabilitiesResult(result pipelineapi.TaskRunResult) []build.Vulnerability { + vulnerabilities := strings.Split(result.Value.StringVal, ",") + var vulns []build.Vulnerability + for _, vulnerability := range vulnerabilities { + vuln := strings.Split(vulnerability, ":") + severity := getSeverity(vuln[1]) + vulns = append(vulns, build.Vulnerability{ + ID: vuln[0], + Severity: severity, + }) + } + return vulns +} + +func getSeverity(sev string) build.VulnerabilitySeverity { + switch sev { + case "L": + return build.Low + case "M": + return build.Medium + case "H": + return build.High + case "C": + return build.Critical + default: + return build.Unknown } } diff --git a/pkg/reconciler/buildrun/resources/results_test.go b/pkg/reconciler/buildrun/resources/results_test.go index cb585aa55..57105a45b 100644 --- a/pkg/reconciler/buildrun/resources/results_test.go +++ b/pkg/reconciler/buildrun/resources/results_test.go @@ -108,7 +108,7 @@ var _ = Describe("TaskRun results to BuildRun", func() { It("should surface the TaskRun results emitting from output step", func() { imageDigest := "sha256:fe1b73cd25ac3f11dec752755e2" - + vulns := `CVE-2019-12900:C,CVE-2019-8457:H` tr.Status.Results = append(tr.Status.Results, pipelineapi.TaskRunResult{ Name: "shp-image-digest", @@ -123,12 +123,22 @@ var _ = Describe("TaskRun results to BuildRun", func() { Type: pipelineapi.ParamTypeString, StringVal: "230", }, + }, + pipelineapi.TaskRunResult{ + Name: "shp-image-vulnerabilities", + Value: pipelineapi.ParamValue{ + Type: pipelineapi.ParamTypeString, + StringVal: vulns, + }, }) resources.UpdateBuildRunUsingTaskResults(ctx, br, tr.Status.Results, taskRunRequest) Expect(br.Status.Output.Digest).To(Equal(imageDigest)) Expect(br.Status.Output.Size).To(Equal(int64(230))) + Expect(br.Status.Output.Vulnerabilities).To(HaveLen(2)) + Expect(br.Status.Output.Vulnerabilities[0].ID).To(Equal("CVE-2019-12900")) + Expect(br.Status.Output.Vulnerabilities[0].Severity).To(Equal(build.Critical)) }) It("should surface the TaskRun results emitting from source and output step", func() { diff --git a/pkg/reconciler/buildrun/resources/taskrun_test.go b/pkg/reconciler/buildrun/resources/taskrun_test.go index aa2703946..14436384d 100644 --- a/pkg/reconciler/buildrun/resources/taskrun_test.go +++ b/pkg/reconciler/buildrun/resources/taskrun_test.go @@ -154,6 +154,8 @@ var _ = Describe("GenerateTaskrun", func() { "$(results.shp-image-digest.path)", "--result-file-image-size", "$(results.shp-image-size.path)", + "--result-file-image-vulnerabilities", + "$(results.shp-image-vulnerabilities.path)", })) }) @@ -184,6 +186,8 @@ var _ = Describe("GenerateTaskrun", func() { "$(results.shp-image-digest.path)", "--result-file-image-size", "$(results.shp-image-size.path)", + "--result-file-image-vulnerabilities", + "$(results.shp-image-vulnerabilities.path)", } Expect(got.Steps[3].Args).To(HaveLen(len(expected))) diff --git a/samples/v1beta1/build/build_buildah_vulnerability_scanning_cr.yaml b/samples/v1beta1/build/build_buildah_vulnerability_scanning_cr.yaml new file mode 100644 index 000000000..11880a88f --- /dev/null +++ b/samples/v1beta1/build/build_buildah_vulnerability_scanning_cr.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah-golang-build +spec: + source: + type: Git + git: + url: https://github.com/shipwright-io/sample-go + contextDir: docker-build + strategy: + name: buildah-shipwright-managed-push + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app + vulnerabilityScan: + enabled: true + failOnFinding: false # if set to true, then the image won't be pushed to the registry diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/036a434e772e404a8b13272e1b8f711036bb22a33f4060f6710cc75c102a3162 b/test/data/images/vuln-image-in-oci/blobs/sha256/036a434e772e404a8b13272e1b8f711036bb22a33f4060f6710cc75c102a3162 new file mode 100644 index 000000000..d385ee4ea Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/036a434e772e404a8b13272e1b8f711036bb22a33f4060f6710cc75c102a3162 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/0eaf06035b3f7b971bf69f2792f93a091f1df5f8a28a2b09ee38f0a9ee156163 b/test/data/images/vuln-image-in-oci/blobs/sha256/0eaf06035b3f7b971bf69f2792f93a091f1df5f8a28a2b09ee38f0a9ee156163 new file mode 100644 index 000000000..afc8bd106 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/0eaf06035b3f7b971bf69f2792f93a091f1df5f8a28a2b09ee38f0a9ee156163 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/1035e489ecce623de89a5a7a5f3a0c2db28ea05cf2591088b13d25c211c9acb8 b/test/data/images/vuln-image-in-oci/blobs/sha256/1035e489ecce623de89a5a7a5f3a0c2db28ea05cf2591088b13d25c211c9acb8 new file mode 100644 index 000000000..1a8831566 --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/1035e489ecce623de89a5a7a5f3a0c2db28ea05cf2591088b13d25c211c9acb8 @@ -0,0 +1 @@ +{"architecture":"arm","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["python3"],"ArgsEscaped":true,"Image":"sha256:0cfac74f4d2ae98309879c2523906781c47bc3c99bbd6a9690339973766fd7e8","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"9d57a4a5c9e76346dba2ea3b3462b05cfb9af62e80ff8dd8e88f57fef51a7130","container_config":{"Hostname":"9d57a4a5c9e7","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"python3\"]"],"ArgsEscaped":true,"Image":"sha256:0cfac74f4d2ae98309879c2523906781c47bc3c99bbd6a9690339973766fd7e8","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2019-03-20T12:52:59.400651896Z","docker_version":"18.06.1-ce","history":[{"created":"2019-03-08T03:35:52.222531099Z","created_by":"/bin/sh -c #(nop) ADD file:e9ae09c8f3973b62bf7e10dd8b9251e50f5479c0d8d1644f632f3886e02c7323 in / "},{"created":"2019-03-08T03:35:52.612420119Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true},{"created":"2019-03-12T12:31:28.616458864Z","created_by":"/bin/sh -c #(nop) ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","empty_layer":true},{"created":"2019-03-12T12:31:28.988536195Z","created_by":"/bin/sh -c #(nop) ENV LANG=C.UTF-8","empty_layer":true},{"created":"2019-03-12T12:31:30.843105341Z","created_by":"/bin/sh -c apk add --no-cache ca-certificates"},{"created":"2019-03-12T12:43:08.637290044Z","created_by":"/bin/sh -c #(nop) ENV GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","empty_layer":true},{"created":"2019-03-20T12:49:29.220738697Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_VERSION=3.4.10","empty_layer":true},{"created":"2019-03-20T12:52:47.917991665Z","created_by":"/bin/sh -c set -ex \t\u0026\u0026 apk add --no-cache --virtual .fetch-deps \t\tgnupg \t\ttar \t\txz \t\t\u0026\u0026 wget -O python.tar.xz \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz\" \t\u0026\u0026 wget -O python.tar.xz.asc \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc\" \t\u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \t\u0026\u0026 gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys \"$GPG_KEY\" \t\u0026\u0026 gpg --batch --verify python.tar.xz.asc python.tar.xz \t\u0026\u0026 { command -v gpgconf \u003e /dev/null \u0026\u0026 gpgconf --kill all || :; } \t\u0026\u0026 rm -rf \"$GNUPGHOME\" python.tar.xz.asc \t\u0026\u0026 mkdir -p /usr/src/python \t\u0026\u0026 tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \t\u0026\u0026 rm python.tar.xz \t\t\u0026\u0026 apk add --no-cache --virtual .build-deps \t\tbzip2-dev \t\tcoreutils \t\tdpkg-dev dpkg \t\texpat-dev \t\tfindutils \t\tgcc \t\tgdbm-dev \t\tlibc-dev \t\tlibffi-dev \t\tlinux-headers \t\tmake \t\tncurses-dev \t\tlibressl-dev \t\tpax-utils \t\treadline-dev \t\tsqlite-dev \t\ttcl-dev \t\ttk \t\ttk-dev \t\txz-dev \t\tzlib-dev \t\u0026\u0026 apk del .fetch-deps \t\t\u0026\u0026 cd /usr/src/python \t\u0026\u0026 gnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\" \t\u0026\u0026 ./configure \t\t--build=\"$gnuArch\" \t\t--enable-loadable-sqlite-extensions \t\t--enable-shared \t\t--with-system-expat \t\t--with-system-ffi \t\t--without-ensurepip \t\u0026\u0026 make -j \"$(nproc)\" \t\tEXTRA_CFLAGS=\"-DTHREAD_STACK_SIZE=0x100000\" \t\u0026\u0026 make install \t\t\u0026\u0026 find /usr/local -type f -executable -not \\( -name '*tkinter*' \\) -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \t\t| tr ',' '\\n' \t\t| sort -u \t\t| awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' \t\t| xargs -rt apk add --no-cache --virtual .python-rundeps \t\u0026\u0026 apk del .build-deps \t\t\u0026\u0026 find /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' + \t\u0026\u0026 rm -rf /usr/src/python \t\t\u0026\u0026 python3 --version"},{"created":"2019-03-20T12:52:49.485141454Z","created_by":"/bin/sh -c cd /usr/local/bin \t\u0026\u0026 ln -s idle3 idle \t\u0026\u0026 ln -s pydoc3 pydoc \t\u0026\u0026 ln -s python3 python \t\u0026\u0026 ln -s python3-config python-config"},{"created":"2019-03-20T12:52:49.809008671Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_PIP_VERSION=19.0.3","empty_layer":true},{"created":"2019-03-20T12:52:58.960750097Z","created_by":"/bin/sh -c set -ex; \t\twget -O get-pip.py 'https://bootstrap.pypa.io/get-pip.py'; \t\tpython get-pip.py \t\t--disable-pip-version-check \t\t--no-cache-dir \t\t\"pip==$PYTHON_PIP_VERSION\" \t; \tpip --version; \t\tfind /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' +; \trm -f get-pip.py"},{"created":"2019-03-20T12:52:59.400651896Z","created_by":"/bin/sh -c #(nop) CMD [\"python3\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:21705165756ddfdc31d10bff59efb9893ded3cd793d06f2432c2dc819aa1cd72","sha256:7b455482516cdbc9016824bb1af350e72a53334e1842f7d474cc52e05d73c9b0","sha256:f41ac40c0eaf9bf5e18a824092c3e6deec253b939016162e5ed8f355e3d7ee7c","sha256:12e11f92b4cbfeb0be7075b11e5a8c717a04626fb362775e5e277fd520097aad","sha256:59ebbf890f78d8e84da7ee3bfe4ecb770a1491485ad1143f6328a36b97d3719b"]}} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/1568398b83b63ab9962703d443dab80693907072ab0e0121ac4613c9cdf0dc07 b/test/data/images/vuln-image-in-oci/blobs/sha256/1568398b83b63ab9962703d443dab80693907072ab0e0121ac4613c9cdf0dc07 new file mode 100644 index 000000000..a7080adbd --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/1568398b83b63ab9962703d443dab80693907072ab0e0121ac4613c9cdf0dc07 @@ -0,0 +1,36 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 6080, + "digest": "sha256:378116235ac3435e97f63bf688667454eaa89e27ab4ee1d62ec848962c233459" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 2540673, + "digest": "sha256:d33b53ae0340b7490b486edd65310a4333d7e0057854aadd12a5006faf7fa576" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 302105, + "digest": "sha256:e6f4a99552d2a12a434e2b1b6dce8499a72e10e66c295ee8b0951104d3960cd5" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 21092573, + "digest": "sha256:d113ef8b10c5a18eb09e6521fe4a2afdc5f540f560d88a38bf556046ceb24c0c" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 232, + "digest": "sha256:6e272e78cc284bee0414f3f81804ab480af8a5eb5f6818ec3531cec95b5a2986" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 1812877, + "digest": "sha256:f54eba5576d4d8d712791eac30f1c1fd7ee79de40b28c7b2445301b94d1ae813" + } + ] +} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/378116235ac3435e97f63bf688667454eaa89e27ab4ee1d62ec848962c233459 b/test/data/images/vuln-image-in-oci/blobs/sha256/378116235ac3435e97f63bf688667454eaa89e27ab4ee1d62ec848962c233459 new file mode 100644 index 000000000..dd4f1dce5 --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/378116235ac3435e97f63bf688667454eaa89e27ab4ee1d62ec848962c233459 @@ -0,0 +1 @@ +{"architecture":"arm","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["python3"],"ArgsEscaped":true,"Image":"sha256:aaf2b4260bf8828aae8de15121bd8917260c893bf14499959f7844a4d8601bba","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"fcdb1e1f10871fc90d046ad068a4ad77da1aff62e44cf7b44de2da1b5ee9b1c5","container_config":{"Hostname":"fcdb1e1f1087","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"python3\"]"],"ArgsEscaped":true,"Image":"sha256:aaf2b4260bf8828aae8de15121bd8917260c893bf14499959f7844a4d8601bba","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2019-03-20T08:07:52.32872853Z","docker_version":"18.06.1-ce","history":[{"created":"2019-03-08T03:36:05.778842336Z","created_by":"/bin/sh -c #(nop) ADD file:2bf8ccaacb388ca6271670ed8a86b8bc4450823156917d29d9d9ec0a07fd64a7 in / "},{"created":"2019-03-08T03:36:06.13674873Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true},{"created":"2019-03-08T04:31:21.416781718Z","created_by":"/bin/sh -c #(nop) ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","empty_layer":true},{"created":"2019-03-08T04:31:21.732646932Z","created_by":"/bin/sh -c #(nop) ENV LANG=C.UTF-8","empty_layer":true},{"created":"2019-03-08T04:31:23.390698682Z","created_by":"/bin/sh -c apk add --no-cache ca-certificates"},{"created":"2019-03-08T04:49:26.036937812Z","created_by":"/bin/sh -c #(nop) ENV GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","empty_layer":true},{"created":"2019-03-20T08:04:18.348735257Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_VERSION=3.4.10","empty_layer":true},{"created":"2019-03-20T08:07:40.40972864Z","created_by":"/bin/sh -c set -ex \t\u0026\u0026 apk add --no-cache --virtual .fetch-deps \t\tgnupg \t\ttar \t\txz \t\t\u0026\u0026 wget -O python.tar.xz \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz\" \t\u0026\u0026 wget -O python.tar.xz.asc \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc\" \t\u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \t\u0026\u0026 gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys \"$GPG_KEY\" \t\u0026\u0026 gpg --batch --verify python.tar.xz.asc python.tar.xz \t\u0026\u0026 { command -v gpgconf \u003e /dev/null \u0026\u0026 gpgconf --kill all || :; } \t\u0026\u0026 rm -rf \"$GNUPGHOME\" python.tar.xz.asc \t\u0026\u0026 mkdir -p /usr/src/python \t\u0026\u0026 tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \t\u0026\u0026 rm python.tar.xz \t\t\u0026\u0026 apk add --no-cache --virtual .build-deps \t\tbzip2-dev \t\tcoreutils \t\tdpkg-dev dpkg \t\texpat-dev \t\tfindutils \t\tgcc \t\tgdbm-dev \t\tlibc-dev \t\tlibffi-dev \t\tlinux-headers \t\tmake \t\tncurses-dev \t\tlibressl-dev \t\tpax-utils \t\treadline-dev \t\tsqlite-dev \t\ttcl-dev \t\ttk \t\ttk-dev \t\txz-dev \t\tzlib-dev \t\u0026\u0026 apk del .fetch-deps \t\t\u0026\u0026 cd /usr/src/python \t\u0026\u0026 gnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\" \t\u0026\u0026 ./configure \t\t--build=\"$gnuArch\" \t\t--enable-loadable-sqlite-extensions \t\t--enable-shared \t\t--with-system-expat \t\t--with-system-ffi \t\t--without-ensurepip \t\u0026\u0026 make -j \"$(nproc)\" \t\tEXTRA_CFLAGS=\"-DTHREAD_STACK_SIZE=0x100000\" \t\u0026\u0026 make install \t\t\u0026\u0026 find /usr/local -type f -executable -not \\( -name '*tkinter*' \\) -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \t\t| tr ',' '\\n' \t\t| sort -u \t\t| awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' \t\t| xargs -rt apk add --no-cache --virtual .python-rundeps \t\u0026\u0026 apk del .build-deps \t\t\u0026\u0026 find /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' + \t\u0026\u0026 rm -rf /usr/src/python \t\t\u0026\u0026 python3 --version"},{"created":"2019-03-20T08:07:42.0689915Z","created_by":"/bin/sh -c cd /usr/local/bin \t\u0026\u0026 ln -s idle3 idle \t\u0026\u0026 ln -s pydoc3 pydoc \t\u0026\u0026 ln -s python3 python \t\u0026\u0026 ln -s python3-config python-config"},{"created":"2019-03-20T08:07:42.429043827Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_PIP_VERSION=19.0.3","empty_layer":true},{"created":"2019-03-20T08:07:51.931716176Z","created_by":"/bin/sh -c set -ex; \t\twget -O get-pip.py 'https://bootstrap.pypa.io/get-pip.py'; \t\tpython get-pip.py \t\t--disable-pip-version-check \t\t--no-cache-dir \t\t\"pip==$PYTHON_PIP_VERSION\" \t; \tpip --version; \t\tfind /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' +; \trm -f get-pip.py"},{"created":"2019-03-20T08:07:52.32872853Z","created_by":"/bin/sh -c #(nop) CMD [\"python3\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:91b223746bb0547e3dc02e8578c6279f49dbb83e692dc69cdef913c35c4474f4","sha256:91e132420527ca0c02c4e20c0d59693e68f3120b150f20845cba1c3a1b07ce9a","sha256:f525936bdf0301416b4af459b8c0c099d95ad2f879206a87a80745b9fe04d4d1","sha256:42c7fcb4e305038c5ce435a24a1adbb46b32f57014ed9c3141deec6a7a9a6f1a","sha256:3ed98eff164062638a84af7b61a5e0c10fdfb77f24221dca62873a359647d4ff"]}} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/3b00a3925ee4b356facd24aea8ece58982a66577023cb3596ce3a321aef976f9 b/test/data/images/vuln-image-in-oci/blobs/sha256/3b00a3925ee4b356facd24aea8ece58982a66577023cb3596ce3a321aef976f9 new file mode 100644 index 000000000..e83056abb Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/3b00a3925ee4b356facd24aea8ece58982a66577023cb3596ce3a321aef976f9 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/3cdfd0b186f5cc8c75073c2b29506b067fe96284f23922e0f19c90eff3011865 b/test/data/images/vuln-image-in-oci/blobs/sha256/3cdfd0b186f5cc8c75073c2b29506b067fe96284f23922e0f19c90eff3011865 new file mode 100644 index 000000000..aad2340c0 --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/3cdfd0b186f5cc8c75073c2b29506b067fe96284f23922e0f19c90eff3011865 @@ -0,0 +1,36 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 6086, + "digest": "sha256:99a53e6ad17ef68837e44123482b2c52f0bccdb1db0dde032d2e91eb060c0c72" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 2687939, + "digest": "sha256:3b00a3925ee4b356facd24aea8ece58982a66577023cb3596ce3a321aef976f9" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 302312, + "digest": "sha256:a3cd3e3d08dd7b819f6ec2fec885bf1abbe92799bab1485623f8fd42e2ba2a35" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 22281933, + "digest": "sha256:bed9db30154fbe7821c4aef53b1ac83d89dfb2066b5e486d0cb5760cd2595df5" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 231, + "digest": "sha256:d837e8e30360686a17ea015a8d6787b7c35599d3d53d73b4fb88f95fede90980" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 1812617, + "digest": "sha256:f307ca8b43fdab6ef1281e027a6cc217da276e8ef99e39c9c7f0f4d09a2b93a9" + } + ] +} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/4411c39d8bb1c9d575e7c3d68ccd77c6eb2679eb0a53a101c6a2427512a56f1a b/test/data/images/vuln-image-in-oci/blobs/sha256/4411c39d8bb1c9d575e7c3d68ccd77c6eb2679eb0a53a101c6a2427512a56f1a new file mode 100644 index 000000000..d1029384a Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/4411c39d8bb1c9d575e7c3d68ccd77c6eb2679eb0a53a101c6a2427512a56f1a differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/4a8494c47795a2433a510d10be9885d3ce3aa277db1aa2f2068d80ce20de4e09 b/test/data/images/vuln-image-in-oci/blobs/sha256/4a8494c47795a2433a510d10be9885d3ce3aa277db1aa2f2068d80ce20de4e09 new file mode 100644 index 000000000..e6e2419eb Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/4a8494c47795a2433a510d10be9885d3ce3aa277db1aa2f2068d80ce20de4e09 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/57e7cff0ea1bce1c6698eba0d76064389a8d282bc042a417d6583a45b7b329ab b/test/data/images/vuln-image-in-oci/blobs/sha256/57e7cff0ea1bce1c6698eba0d76064389a8d282bc042a417d6583a45b7b329ab new file mode 100644 index 000000000..e98a06597 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/57e7cff0ea1bce1c6698eba0d76064389a8d282bc042a417d6583a45b7b329ab differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/5d2da0878a6ef4d2bf07e36f53bb33fc920d883f5afae802949629383aaec53a b/test/data/images/vuln-image-in-oci/blobs/sha256/5d2da0878a6ef4d2bf07e36f53bb33fc920d883f5afae802949629383aaec53a new file mode 100644 index 000000000..3fd379d22 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/5d2da0878a6ef4d2bf07e36f53bb33fc920d883f5afae802949629383aaec53a differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/611bce2a4fa9a0e18025e088381d26f1dda2e1c02160c4d24a2720f9dbca9eaf b/test/data/images/vuln-image-in-oci/blobs/sha256/611bce2a4fa9a0e18025e088381d26f1dda2e1c02160c4d24a2720f9dbca9eaf new file mode 100644 index 000000000..f3ea3ddc1 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/611bce2a4fa9a0e18025e088381d26f1dda2e1c02160c4d24a2720f9dbca9eaf differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/6634032050259cddf4a3cd57763eace12fec211f9c65e5a205f179e355ba7685 b/test/data/images/vuln-image-in-oci/blobs/sha256/6634032050259cddf4a3cd57763eace12fec211f9c65e5a205f179e355ba7685 new file mode 100644 index 000000000..22bb4ae79 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/6634032050259cddf4a3cd57763eace12fec211f9c65e5a205f179e355ba7685 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/6a68826471381b09574cbffb43f630258f7734e6cc65adaa3b1dc0b5e5970f68 b/test/data/images/vuln-image-in-oci/blobs/sha256/6a68826471381b09574cbffb43f630258f7734e6cc65adaa3b1dc0b5e5970f68 new file mode 100644 index 000000000..13ddcd17d Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/6a68826471381b09574cbffb43f630258f7734e6cc65adaa3b1dc0b5e5970f68 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/6d13cb5356f63b849680a8e0c6bc80b0a5206366aa73c8153fa995f930475427 b/test/data/images/vuln-image-in-oci/blobs/sha256/6d13cb5356f63b849680a8e0c6bc80b0a5206366aa73c8153fa995f930475427 new file mode 100644 index 000000000..6d4f762ad Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/6d13cb5356f63b849680a8e0c6bc80b0a5206366aa73c8153fa995f930475427 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/6e272e78cc284bee0414f3f81804ab480af8a5eb5f6818ec3531cec95b5a2986 b/test/data/images/vuln-image-in-oci/blobs/sha256/6e272e78cc284bee0414f3f81804ab480af8a5eb5f6818ec3531cec95b5a2986 new file mode 100644 index 000000000..aababda0f Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/6e272e78cc284bee0414f3f81804ab480af8a5eb5f6818ec3531cec95b5a2986 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/8e402f1a9c577ded051c1ef10e9fe4492890459522089959988a4852dee8ab2c b/test/data/images/vuln-image-in-oci/blobs/sha256/8e402f1a9c577ded051c1ef10e9fe4492890459522089959988a4852dee8ab2c new file mode 100644 index 000000000..f3700a897 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/8e402f1a9c577ded051c1ef10e9fe4492890459522089959988a4852dee8ab2c differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/99a53e6ad17ef68837e44123482b2c52f0bccdb1db0dde032d2e91eb060c0c72 b/test/data/images/vuln-image-in-oci/blobs/sha256/99a53e6ad17ef68837e44123482b2c52f0bccdb1db0dde032d2e91eb060c0c72 new file mode 100644 index 000000000..546d0d407 --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/99a53e6ad17ef68837e44123482b2c52f0bccdb1db0dde032d2e91eb060c0c72 @@ -0,0 +1 @@ +{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["python3"],"ArgsEscaped":true,"Image":"sha256:5985eea7303e698be6f21e178ada1c432cb520c39200df8abf105bb83abeda38","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"091964670fa3136378f186b1b9e5cf3817c5eff4664def4ea4fdaff16db718f4","container_config":{"Hostname":"091964670fa3","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"python3\"]"],"ArgsEscaped":true,"Image":"sha256:5985eea7303e698be6f21e178ada1c432cb520c39200df8abf105bb83abeda38","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2019-03-20T10:01:34.999307302Z","docker_version":"18.06.1-ce","history":[{"created":"2019-03-08T03:37:01.50193332Z","created_by":"/bin/sh -c #(nop) ADD file:2e80d7b240ac8c544a868180a2a08b2533c450061e0ec276ceacaff7b87a380c in / "},{"created":"2019-03-08T03:37:02.050813908Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true},{"created":"2019-03-08T05:08:09.702579595Z","created_by":"/bin/sh -c #(nop) ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","empty_layer":true},{"created":"2019-03-08T10:00:51.709269545Z","created_by":"/bin/sh -c #(nop) ENV LANG=C.UTF-8","empty_layer":true},{"created":"2019-03-08T10:00:56.559816451Z","created_by":"/bin/sh -c apk add --no-cache ca-certificates"},{"created":"2019-03-08T10:50:00.896586854Z","created_by":"/bin/sh -c #(nop) ENV GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","empty_layer":true},{"created":"2019-03-20T09:54:41.78747646Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_VERSION=3.4.10","empty_layer":true},{"created":"2019-03-20T10:01:13.418317588Z","created_by":"/bin/sh -c set -ex \t\u0026\u0026 apk add --no-cache --virtual .fetch-deps \t\tgnupg \t\ttar \t\txz \t\t\u0026\u0026 wget -O python.tar.xz \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz\" \t\u0026\u0026 wget -O python.tar.xz.asc \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc\" \t\u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \t\u0026\u0026 gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys \"$GPG_KEY\" \t\u0026\u0026 gpg --batch --verify python.tar.xz.asc python.tar.xz \t\u0026\u0026 { command -v gpgconf \u003e /dev/null \u0026\u0026 gpgconf --kill all || :; } \t\u0026\u0026 rm -rf \"$GNUPGHOME\" python.tar.xz.asc \t\u0026\u0026 mkdir -p /usr/src/python \t\u0026\u0026 tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \t\u0026\u0026 rm python.tar.xz \t\t\u0026\u0026 apk add --no-cache --virtual .build-deps \t\tbzip2-dev \t\tcoreutils \t\tdpkg-dev dpkg \t\texpat-dev \t\tfindutils \t\tgcc \t\tgdbm-dev \t\tlibc-dev \t\tlibffi-dev \t\tlinux-headers \t\tmake \t\tncurses-dev \t\tlibressl-dev \t\tpax-utils \t\treadline-dev \t\tsqlite-dev \t\ttcl-dev \t\ttk \t\ttk-dev \t\txz-dev \t\tzlib-dev \t\u0026\u0026 apk del .fetch-deps \t\t\u0026\u0026 cd /usr/src/python \t\u0026\u0026 gnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\" \t\u0026\u0026 ./configure \t\t--build=\"$gnuArch\" \t\t--enable-loadable-sqlite-extensions \t\t--enable-shared \t\t--with-system-expat \t\t--with-system-ffi \t\t--without-ensurepip \t\u0026\u0026 make -j \"$(nproc)\" \t\tEXTRA_CFLAGS=\"-DTHREAD_STACK_SIZE=0x100000\" \t\u0026\u0026 make install \t\t\u0026\u0026 find /usr/local -type f -executable -not \\( -name '*tkinter*' \\) -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \t\t| tr ',' '\\n' \t\t| sort -u \t\t| awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' \t\t| xargs -rt apk add --no-cache --virtual .python-rundeps \t\u0026\u0026 apk del .build-deps \t\t\u0026\u0026 find /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' + \t\u0026\u0026 rm -rf /usr/src/python \t\t\u0026\u0026 python3 --version"},{"created":"2019-03-20T10:01:16.073654148Z","created_by":"/bin/sh -c cd /usr/local/bin \t\u0026\u0026 ln -s idle3 idle \t\u0026\u0026 ln -s pydoc3 pydoc \t\u0026\u0026 ln -s python3 python \t\u0026\u0026 ln -s python3-config python-config"},{"created":"2019-03-20T10:01:16.699393758Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_PIP_VERSION=19.0.3","empty_layer":true},{"created":"2019-03-20T10:01:34.158289571Z","created_by":"/bin/sh -c set -ex; \t\twget -O get-pip.py 'https://bootstrap.pypa.io/get-pip.py'; \t\tpython get-pip.py \t\t--disable-pip-version-check \t\t--no-cache-dir \t\t\"pip==$PYTHON_PIP_VERSION\" \t; \tpip --version; \t\tfind /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' +; \trm -f get-pip.py"},{"created":"2019-03-20T10:01:34.999307302Z","created_by":"/bin/sh -c #(nop) CMD [\"python3\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:b83703e07573d5eac589806f0afe1648f84ec111b9169ff9db1884730b5603e3","sha256:ac2db0ac12b5b0957f35c394aa8bb5a1bdc82def752c959d24a343a9a75b5a20","sha256:91d422df6e4796fe8c48844f0eec5ecf537d63685874191e4e927cd3c90ded12","sha256:02ac95b0c389eb39b48f8ffe7b50bfc40ceec67c3706827524b5cb7229e09487","sha256:50b409522ef7d06a0a7865ce9f162abff86800ac0f0b556a02c2f7a42bbe888c"]}} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/a3cd3e3d08dd7b819f6ec2fec885bf1abbe92799bab1485623f8fd42e2ba2a35 b/test/data/images/vuln-image-in-oci/blobs/sha256/a3cd3e3d08dd7b819f6ec2fec885bf1abbe92799bab1485623f8fd42e2ba2a35 new file mode 100644 index 000000000..a41c7f471 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/a3cd3e3d08dd7b819f6ec2fec885bf1abbe92799bab1485623f8fd42e2ba2a35 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/a3fce9aefe52bd5599ab9e43c36f3122b6ba0c074226efa4ed2415ad4b441372 b/test/data/images/vuln-image-in-oci/blobs/sha256/a3fce9aefe52bd5599ab9e43c36f3122b6ba0c074226efa4ed2415ad4b441372 new file mode 100644 index 000000000..6f974d92b --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/a3fce9aefe52bd5599ab9e43c36f3122b6ba0c074226efa4ed2415ad4b441372 @@ -0,0 +1,36 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 6084, + "digest": "sha256:c038d137e375a579cba6e962c6a063b16f9f5fbd845e220222ad82696962459b" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 2749355, + "digest": "sha256:a4c1e43a7431a562d92c5eb6e09e96b4731a474af51cad1c46e2d305ede29005" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 302435, + "digest": "sha256:bd664a9c3aab654be09d9409e5da4d288cd0055e7a520c6182bd6aabdd06c897" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 21437839, + "digest": "sha256:adc5dc2e39b1feae6a1679a679ed10f75a85ac55d9e59a9ad720b017b4dc3b71" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 230, + "digest": "sha256:6634032050259cddf4a3cd57763eace12fec211f9c65e5a205f179e355ba7685" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 1812621, + "digest": "sha256:f2563b8febbcac7bb8b811e535612d222ae160e2cd798109e20798a4f88fe3a2" + } + ] +} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/a4b05d114c58fabadf6b3af4b4aeb0156a2fda07322e1eb73a25d0a5ec192f9f b/test/data/images/vuln-image-in-oci/blobs/sha256/a4b05d114c58fabadf6b3af4b4aeb0156a2fda07322e1eb73a25d0a5ec192f9f new file mode 100644 index 000000000..c99cab73b Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/a4b05d114c58fabadf6b3af4b4aeb0156a2fda07322e1eb73a25d0a5ec192f9f differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/a4c1e43a7431a562d92c5eb6e09e96b4731a474af51cad1c46e2d305ede29005 b/test/data/images/vuln-image-in-oci/blobs/sha256/a4c1e43a7431a562d92c5eb6e09e96b4731a474af51cad1c46e2d305ede29005 new file mode 100644 index 000000000..29c91d7fb Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/a4c1e43a7431a562d92c5eb6e09e96b4731a474af51cad1c46e2d305ede29005 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/aafecf9bbbfd514858f0d93fa0f65c2d59c8a1c46ec4b55963e42619f00a0a61 b/test/data/images/vuln-image-in-oci/blobs/sha256/aafecf9bbbfd514858f0d93fa0f65c2d59c8a1c46ec4b55963e42619f00a0a61 new file mode 100644 index 000000000..a3ec9dad5 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/aafecf9bbbfd514858f0d93fa0f65c2d59c8a1c46ec4b55963e42619f00a0a61 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/adc5dc2e39b1feae6a1679a679ed10f75a85ac55d9e59a9ad720b017b4dc3b71 b/test/data/images/vuln-image-in-oci/blobs/sha256/adc5dc2e39b1feae6a1679a679ed10f75a85ac55d9e59a9ad720b017b4dc3b71 new file mode 100644 index 000000000..e3d0bb387 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/adc5dc2e39b1feae6a1679a679ed10f75a85ac55d9e59a9ad720b017b4dc3b71 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/af12801be66794493df79babda2a36c83a4c36e9944ce566adf30db40286d7de b/test/data/images/vuln-image-in-oci/blobs/sha256/af12801be66794493df79babda2a36c83a4c36e9944ce566adf30db40286d7de new file mode 100644 index 000000000..911ade4ed --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/af12801be66794493df79babda2a36c83a4c36e9944ce566adf30db40286d7de @@ -0,0 +1,36 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 6087, + "digest": "sha256:b8fba8165a95e9215e4cc15f54bc83fe18d499edad885787289cd6e55f9c8032" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 2778638, + "digest": "sha256:611bce2a4fa9a0e18025e088381d26f1dda2e1c02160c4d24a2720f9dbca9eaf" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 304533, + "digest": "sha256:c4deb1aa89fc6837dd79db6b739b5ea154b6bdf57662254bc76c87220a0526e4" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 23250302, + "digest": "sha256:5d2da0878a6ef4d2bf07e36f53bb33fc920d883f5afae802949629383aaec53a" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 229, + "digest": "sha256:a4b05d114c58fabadf6b3af4b4aeb0156a2fda07322e1eb73a25d0a5ec192f9f" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 1812920, + "digest": "sha256:4411c39d8bb1c9d575e7c3d68ccd77c6eb2679eb0a53a101c6a2427512a56f1a" + } + ] +} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/afa372851354c70082549353d04ec1147e806cec361b7ca10df4638ae1524aea b/test/data/images/vuln-image-in-oci/blobs/sha256/afa372851354c70082549353d04ec1147e806cec361b7ca10df4638ae1524aea new file mode 100644 index 000000000..44fd4e0d4 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/afa372851354c70082549353d04ec1147e806cec361b7ca10df4638ae1524aea differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/b8fba8165a95e9215e4cc15f54bc83fe18d499edad885787289cd6e55f9c8032 b/test/data/images/vuln-image-in-oci/blobs/sha256/b8fba8165a95e9215e4cc15f54bc83fe18d499edad885787289cd6e55f9c8032 new file mode 100644 index 000000000..2ea3ea15c --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/b8fba8165a95e9215e4cc15f54bc83fe18d499edad885787289cd6e55f9c8032 @@ -0,0 +1 @@ +{"architecture":"ppc64le","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["python3"],"ArgsEscaped":true,"Image":"sha256:00f1ea9c80fdfa8bc2fcd70a6ca03716fca0eec742af1962f26a49d38b96824a","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"360a325695d83479690f9fcc644bbe955c987cdfc2b4b4090a97163a4144e885","container_config":{"Hostname":"360a325695d8","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"python3\"]"],"ArgsEscaped":true,"Image":"sha256:00f1ea9c80fdfa8bc2fcd70a6ca03716fca0eec742af1962f26a49d38b96824a","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2019-03-20T09:31:20.14881329Z","docker_version":"18.06.1-ce","history":[{"created":"2019-03-08T03:37:33.455077773Z","created_by":"/bin/sh -c #(nop) ADD file:6dfaec9befa64397af571d1da2e766c694148f7a025b9411404a924d3de64bd3 in / "},{"created":"2019-03-08T03:37:36.455241563Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true},{"created":"2019-03-08T06:51:44.701802084Z","created_by":"/bin/sh -c #(nop) ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","empty_layer":true},{"created":"2019-03-08T06:51:46.96709653Z","created_by":"/bin/sh -c #(nop) ENV LANG=C.UTF-8","empty_layer":true},{"created":"2019-03-08T06:51:53.446641003Z","created_by":"/bin/sh -c apk add --no-cache ca-certificates"},{"created":"2019-03-08T07:09:54.005285875Z","created_by":"/bin/sh -c #(nop) ENV GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","empty_layer":true},{"created":"2019-03-20T09:28:00.102621117Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_VERSION=3.4.10","empty_layer":true},{"created":"2019-03-20T09:30:53.201915494Z","created_by":"/bin/sh -c set -ex \t\u0026\u0026 apk add --no-cache --virtual .fetch-deps \t\tgnupg \t\ttar \t\txz \t\t\u0026\u0026 wget -O python.tar.xz \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz\" \t\u0026\u0026 wget -O python.tar.xz.asc \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc\" \t\u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \t\u0026\u0026 gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys \"$GPG_KEY\" \t\u0026\u0026 gpg --batch --verify python.tar.xz.asc python.tar.xz \t\u0026\u0026 { command -v gpgconf \u003e /dev/null \u0026\u0026 gpgconf --kill all || :; } \t\u0026\u0026 rm -rf \"$GNUPGHOME\" python.tar.xz.asc \t\u0026\u0026 mkdir -p /usr/src/python \t\u0026\u0026 tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \t\u0026\u0026 rm python.tar.xz \t\t\u0026\u0026 apk add --no-cache --virtual .build-deps \t\tbzip2-dev \t\tcoreutils \t\tdpkg-dev dpkg \t\texpat-dev \t\tfindutils \t\tgcc \t\tgdbm-dev \t\tlibc-dev \t\tlibffi-dev \t\tlinux-headers \t\tmake \t\tncurses-dev \t\tlibressl-dev \t\tpax-utils \t\treadline-dev \t\tsqlite-dev \t\ttcl-dev \t\ttk \t\ttk-dev \t\txz-dev \t\tzlib-dev \t\u0026\u0026 apk del .fetch-deps \t\t\u0026\u0026 cd /usr/src/python \t\u0026\u0026 gnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\" \t\u0026\u0026 ./configure \t\t--build=\"$gnuArch\" \t\t--enable-loadable-sqlite-extensions \t\t--enable-shared \t\t--with-system-expat \t\t--with-system-ffi \t\t--without-ensurepip \t\u0026\u0026 make -j \"$(nproc)\" \t\tEXTRA_CFLAGS=\"-DTHREAD_STACK_SIZE=0x100000\" \t\u0026\u0026 make install \t\t\u0026\u0026 find /usr/local -type f -executable -not \\( -name '*tkinter*' \\) -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \t\t| tr ',' '\\n' \t\t| sort -u \t\t| awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' \t\t| xargs -rt apk add --no-cache --virtual .python-rundeps \t\u0026\u0026 apk del .build-deps \t\t\u0026\u0026 find /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' + \t\u0026\u0026 rm -rf /usr/src/python \t\t\u0026\u0026 python3 --version"},{"created":"2019-03-20T09:31:00.427898933Z","created_by":"/bin/sh -c cd /usr/local/bin \t\u0026\u0026 ln -s idle3 idle \t\u0026\u0026 ln -s pydoc3 pydoc \t\u0026\u0026 ln -s python3 python \t\u0026\u0026 ln -s python3-config python-config"},{"created":"2019-03-20T09:31:02.952398009Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_PIP_VERSION=19.0.3","empty_layer":true},{"created":"2019-03-20T09:31:16.847975484Z","created_by":"/bin/sh -c set -ex; \t\twget -O get-pip.py 'https://bootstrap.pypa.io/get-pip.py'; \t\tpython get-pip.py \t\t--disable-pip-version-check \t\t--no-cache-dir \t\t\"pip==$PYTHON_PIP_VERSION\" \t; \tpip --version; \t\tfind /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' +; \trm -f get-pip.py"},{"created":"2019-03-20T09:31:20.14881329Z","created_by":"/bin/sh -c #(nop) CMD [\"python3\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:d717c319100b8ea176cc8c03bcd31841517e5b11ff26a2cc55033c955c0b4205","sha256:f97150bed18a3875aaaa16ebe99a0241d6e2d1093c6a999a7cc3a28143c1d5fb","sha256:19f389ae21da3ad1e7c1359365b5035ccb737cc2fbe12e299d953610df8dca30","sha256:a28bc96be5ffbaa7a15a692b467c41e15ca80bbc4972d3e05659d9fa0edb6ab7","sha256:14501edc03e68fbe4169823a8ff192e4e95d7e6df937231635086583afa8a031"]}} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/bc2e7e266629e9571a64d9d9050a49b38904b28302056c90222072f84687ba76 b/test/data/images/vuln-image-in-oci/blobs/sha256/bc2e7e266629e9571a64d9d9050a49b38904b28302056c90222072f84687ba76 new file mode 100644 index 000000000..29235cf31 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/bc2e7e266629e9571a64d9d9050a49b38904b28302056c90222072f84687ba76 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/bd664a9c3aab654be09d9409e5da4d288cd0055e7a520c6182bd6aabdd06c897 b/test/data/images/vuln-image-in-oci/blobs/sha256/bd664a9c3aab654be09d9409e5da4d288cd0055e7a520c6182bd6aabdd06c897 new file mode 100644 index 000000000..11ff8f67a Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/bd664a9c3aab654be09d9409e5da4d288cd0055e7a520c6182bd6aabdd06c897 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/bed9db30154fbe7821c4aef53b1ac83d89dfb2066b5e486d0cb5760cd2595df5 b/test/data/images/vuln-image-in-oci/blobs/sha256/bed9db30154fbe7821c4aef53b1ac83d89dfb2066b5e486d0cb5760cd2595df5 new file mode 100644 index 000000000..06075d114 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/bed9db30154fbe7821c4aef53b1ac83d89dfb2066b5e486d0cb5760cd2595df5 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/c038d137e375a579cba6e962c6a063b16f9f5fbd845e220222ad82696962459b b/test/data/images/vuln-image-in-oci/blobs/sha256/c038d137e375a579cba6e962c6a063b16f9f5fbd845e220222ad82696962459b new file mode 100644 index 000000000..b1fe8261a --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/c038d137e375a579cba6e962c6a063b16f9f5fbd845e220222ad82696962459b @@ -0,0 +1 @@ +{"architecture":"386","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["python3"],"ArgsEscaped":true,"Image":"sha256:bcb3f8bf87048aeeb8a8126b5940c03b1e932c862636ddfb0d89fe321d1f45cf","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"f26e79d49fd3018eba71ec262786b04a0127350f5c9def46a5e2f3505afe59ad","container_config":{"Hostname":"f26e79d49fd3","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"python3\"]"],"ArgsEscaped":true,"Image":"sha256:bcb3f8bf87048aeeb8a8126b5940c03b1e932c862636ddfb0d89fe321d1f45cf","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2019-03-20T12:40:18.466988391Z","docker_version":"18.06.1-ce","history":[{"created":"2019-03-08T03:35:50.062000621Z","created_by":"/bin/sh -c #(nop) ADD file:98a0f92574b6a8592a131ccb07c4f516694e053d610a1701fde2d5e7c65c2175 in / "},{"created":"2019-03-08T03:35:50.32277691Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true},{"created":"2019-03-08T10:32:41.466398576Z","created_by":"/bin/sh -c #(nop) ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","empty_layer":true},{"created":"2019-03-08T10:32:41.642548566Z","created_by":"/bin/sh -c #(nop) ENV LANG=C.UTF-8","empty_layer":true},{"created":"2019-03-08T10:32:42.644256901Z","created_by":"/bin/sh -c apk add --no-cache ca-certificates"},{"created":"2019-03-08T10:47:16.64818578Z","created_by":"/bin/sh -c #(nop) ENV GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","empty_layer":true},{"created":"2019-03-20T12:37:43.210736786Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_VERSION=3.4.10","empty_layer":true},{"created":"2019-03-20T12:40:11.327880914Z","created_by":"/bin/sh -c set -ex \t\u0026\u0026 apk add --no-cache --virtual .fetch-deps \t\tgnupg \t\ttar \t\txz \t\t\u0026\u0026 wget -O python.tar.xz \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz\" \t\u0026\u0026 wget -O python.tar.xz.asc \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc\" \t\u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \t\u0026\u0026 gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys \"$GPG_KEY\" \t\u0026\u0026 gpg --batch --verify python.tar.xz.asc python.tar.xz \t\u0026\u0026 { command -v gpgconf \u003e /dev/null \u0026\u0026 gpgconf --kill all || :; } \t\u0026\u0026 rm -rf \"$GNUPGHOME\" python.tar.xz.asc \t\u0026\u0026 mkdir -p /usr/src/python \t\u0026\u0026 tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \t\u0026\u0026 rm python.tar.xz \t\t\u0026\u0026 apk add --no-cache --virtual .build-deps \t\tbzip2-dev \t\tcoreutils \t\tdpkg-dev dpkg \t\texpat-dev \t\tfindutils \t\tgcc \t\tgdbm-dev \t\tlibc-dev \t\tlibffi-dev \t\tlinux-headers \t\tmake \t\tncurses-dev \t\tlibressl-dev \t\tpax-utils \t\treadline-dev \t\tsqlite-dev \t\ttcl-dev \t\ttk \t\ttk-dev \t\txz-dev \t\tzlib-dev \t\u0026\u0026 apk del .fetch-deps \t\t\u0026\u0026 cd /usr/src/python \t\u0026\u0026 gnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\" \t\u0026\u0026 ./configure \t\t--build=\"$gnuArch\" \t\t--enable-loadable-sqlite-extensions \t\t--enable-shared \t\t--with-system-expat \t\t--with-system-ffi \t\t--without-ensurepip \t\u0026\u0026 make -j \"$(nproc)\" \t\tEXTRA_CFLAGS=\"-DTHREAD_STACK_SIZE=0x100000\" \t\u0026\u0026 make install \t\t\u0026\u0026 find /usr/local -type f -executable -not \\( -name '*tkinter*' \\) -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \t\t| tr ',' '\\n' \t\t| sort -u \t\t| awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' \t\t| xargs -rt apk add --no-cache --virtual .python-rundeps \t\u0026\u0026 apk del .build-deps \t\t\u0026\u0026 find /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' + \t\u0026\u0026 rm -rf /usr/src/python \t\t\u0026\u0026 python3 --version"},{"created":"2019-03-20T12:40:12.106697465Z","created_by":"/bin/sh -c cd /usr/local/bin \t\u0026\u0026 ln -s idle3 idle \t\u0026\u0026 ln -s pydoc3 pydoc \t\u0026\u0026 ln -s python3 python \t\u0026\u0026 ln -s python3-config python-config"},{"created":"2019-03-20T12:40:12.291142178Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_PIP_VERSION=19.0.3","empty_layer":true},{"created":"2019-03-20T12:40:18.286893813Z","created_by":"/bin/sh -c set -ex; \t\twget -O get-pip.py 'https://bootstrap.pypa.io/get-pip.py'; \t\tpython get-pip.py \t\t--disable-pip-version-check \t\t--no-cache-dir \t\t\"pip==$PYTHON_PIP_VERSION\" \t; \tpip --version; \t\tfind /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' +; \trm -f get-pip.py"},{"created":"2019-03-20T12:40:18.466988391Z","created_by":"/bin/sh -c #(nop) CMD [\"python3\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:a01f8361416bc0dfc93c51903a7d7eccdb7c890bc50e152cf84637f6cbbacede","sha256:f776f0e770885d3b94adc3408ba0cd6a7ddd549286a7a3a1d769cd14e60404f6","sha256:a2553a1705e6ef99837a5a875feb8f02cf84dff11d8d822fd8a761ec058f65ae","sha256:934b11b4e6e5a858e729e3ee8a525fe42cc5a295a8ce015c8623fd26e5ae19b6","sha256:63f59d5e122f6c7d59896086467c6e47ad4efbbcc0429bb21858ef568b52ca68"]}} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/c06adcf62f6ef21ae5c586552532b04b693f9ab6df377d7ea066fd682c470864 b/test/data/images/vuln-image-in-oci/blobs/sha256/c06adcf62f6ef21ae5c586552532b04b693f9ab6df377d7ea066fd682c470864 new file mode 100644 index 000000000..d700a4395 --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/c06adcf62f6ef21ae5c586552532b04b693f9ab6df377d7ea066fd682c470864 @@ -0,0 +1 @@ +{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["python3"],"ArgsEscaped":true,"Image":"sha256:697212bb9c9566014ab21f1349d5d310a599c4b49667672c25c18cbd17b29b6a","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"6501c23ff7d9b6eabdd244b01cf953d0b811ba60bf813becd37811f70fd6e14b","container_config":{"Hostname":"6501c23ff7d9","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"python3\"]"],"ArgsEscaped":true,"Image":"sha256:697212bb9c9566014ab21f1349d5d310a599c4b49667672c25c18cbd17b29b6a","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2019-03-20T01:08:45.554576322Z","docker_version":"18.06.1-ce","history":[{"created":"2019-03-07T22:19:40.113750514Z","created_by":"/bin/sh -c #(nop) ADD file:88875982b0512a9d0ba001bfea19497ae9a9442c257b19c61bffc56e7201b0c3 in / "},{"created":"2019-03-07T22:19:40.247110971Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true},{"created":"2019-03-07T22:35:55.42665282Z","created_by":"/bin/sh -c #(nop) ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","empty_layer":true},{"created":"2019-03-07T22:35:55.582534558Z","created_by":"/bin/sh -c #(nop) ENV LANG=C.UTF-8","empty_layer":true},{"created":"2019-03-07T22:35:56.502707999Z","created_by":"/bin/sh -c apk add --no-cache ca-certificates"},{"created":"2019-03-07T22:53:53.279483257Z","created_by":"/bin/sh -c #(nop) ENV GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","empty_layer":true},{"created":"2019-03-20T01:06:28.158996506Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_VERSION=3.4.10","empty_layer":true},{"created":"2019-03-20T01:08:39.371438649Z","created_by":"/bin/sh -c set -ex \t\u0026\u0026 apk add --no-cache --virtual .fetch-deps \t\tgnupg \t\ttar \t\txz \t\t\u0026\u0026 wget -O python.tar.xz \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz\" \t\u0026\u0026 wget -O python.tar.xz.asc \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc\" \t\u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \t\u0026\u0026 gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys \"$GPG_KEY\" \t\u0026\u0026 gpg --batch --verify python.tar.xz.asc python.tar.xz \t\u0026\u0026 { command -v gpgconf \u003e /dev/null \u0026\u0026 gpgconf --kill all || :; } \t\u0026\u0026 rm -rf \"$GNUPGHOME\" python.tar.xz.asc \t\u0026\u0026 mkdir -p /usr/src/python \t\u0026\u0026 tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \t\u0026\u0026 rm python.tar.xz \t\t\u0026\u0026 apk add --no-cache --virtual .build-deps \t\tbzip2-dev \t\tcoreutils \t\tdpkg-dev dpkg \t\texpat-dev \t\tfindutils \t\tgcc \t\tgdbm-dev \t\tlibc-dev \t\tlibffi-dev \t\tlinux-headers \t\tmake \t\tncurses-dev \t\tlibressl-dev \t\tpax-utils \t\treadline-dev \t\tsqlite-dev \t\ttcl-dev \t\ttk \t\ttk-dev \t\txz-dev \t\tzlib-dev \t\u0026\u0026 apk del .fetch-deps \t\t\u0026\u0026 cd /usr/src/python \t\u0026\u0026 gnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\" \t\u0026\u0026 ./configure \t\t--build=\"$gnuArch\" \t\t--enable-loadable-sqlite-extensions \t\t--enable-shared \t\t--with-system-expat \t\t--with-system-ffi \t\t--without-ensurepip \t\u0026\u0026 make -j \"$(nproc)\" \t\tEXTRA_CFLAGS=\"-DTHREAD_STACK_SIZE=0x100000\" \t\u0026\u0026 make install \t\t\u0026\u0026 find /usr/local -type f -executable -not \\( -name '*tkinter*' \\) -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \t\t| tr ',' '\\n' \t\t| sort -u \t\t| awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' \t\t| xargs -rt apk add --no-cache --virtual .python-rundeps \t\u0026\u0026 apk del .build-deps \t\t\u0026\u0026 find /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' + \t\u0026\u0026 rm -rf /usr/src/python \t\t\u0026\u0026 python3 --version"},{"created":"2019-03-20T01:08:40.124325287Z","created_by":"/bin/sh -c cd /usr/local/bin \t\u0026\u0026 ln -s idle3 idle \t\u0026\u0026 ln -s pydoc3 pydoc \t\u0026\u0026 ln -s python3 python \t\u0026\u0026 ln -s python3-config python-config"},{"created":"2019-03-20T01:08:40.278839566Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_PIP_VERSION=19.0.3","empty_layer":true},{"created":"2019-03-20T01:08:45.36541868Z","created_by":"/bin/sh -c set -ex; \t\twget -O get-pip.py 'https://bootstrap.pypa.io/get-pip.py'; \t\tpython get-pip.py \t\t--disable-pip-version-check \t\t--no-cache-dir \t\t\"pip==$PYTHON_PIP_VERSION\" \t; \tpip --version; \t\tfind /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' +; \trm -f get-pip.py"},{"created":"2019-03-20T01:08:45.554576322Z","created_by":"/bin/sh -c #(nop) CMD [\"python3\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:bcf2f368fe234217249e00ad9d762d8f1a3156d60c442ed92079fa5b120634a1","sha256:aabe8fddede54277f929724919213cc5df2ab4e4175a5ce45ff4e00909a4b757","sha256:fbe16fc07f0d81390525c348fbd720725dcae6498bd5e902ce5d37f2b7eed743","sha256:58026b9b6bf1a7dbc0872462e9ea675cad54a45bc7682bd3631dd4f3c16b1332","sha256:62de8bcc470aef81ddbec19b7f5aeed24d7b7ec1bff09422f7e0da3a4842d346"]}} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/c210b660e2ea553a7afa23b41a6ed112f85dbce25cbcb567c75dfe05342a4c4b b/test/data/images/vuln-image-in-oci/blobs/sha256/c210b660e2ea553a7afa23b41a6ed112f85dbce25cbcb567c75dfe05342a4c4b new file mode 100755 index 000000000..ac11ef04f --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/c210b660e2ea553a7afa23b41a6ed112f85dbce25cbcb567c75dfe05342a4c4b @@ -0,0 +1,72 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", + "manifests": [ + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "size": 1368, + "digest": "sha256:f031db30449b815a6ef2abcc8a9241a68f55c63035170b85dca3b1db2891e6fa", + "platform": { + "architecture": "amd64", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "size": 1368, + "digest": "sha256:1568398b83b63ab9962703d443dab80693907072ab0e0121ac4613c9cdf0dc07", + "platform": { + "architecture": "arm", + "os": "linux", + "variant": "v6" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "size": 1368, + "digest": "sha256:f4f39efdcf56dbf787903372c7ec1c2b7dd9b4c01583dfb29493bbf31341f48f", + "platform": { + "architecture": "arm", + "os": "linux", + "variant": "v7" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "size": 1368, + "digest": "sha256:3cdfd0b186f5cc8c75073c2b29506b067fe96284f23922e0f19c90eff3011865", + "platform": { + "architecture": "arm64", + "os": "linux", + "variant": "v8" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "size": 1368, + "digest": "sha256:a3fce9aefe52bd5599ab9e43c36f3122b6ba0c074226efa4ed2415ad4b441372", + "platform": { + "architecture": "386", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "size": 1368, + "digest": "sha256:af12801be66794493df79babda2a36c83a4c36e9944ce566adf30db40286d7de", + "platform": { + "architecture": "ppc64le", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "size": 1368, + "digest": "sha256:c2f1d4a3a75e7960d968ee489ecaf910556e6a90dab15a48c5e17add27ec4899", + "platform": { + "architecture": "s390x", + "os": "linux" + } + } + ] +} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/c2f1d4a3a75e7960d968ee489ecaf910556e6a90dab15a48c5e17add27ec4899 b/test/data/images/vuln-image-in-oci/blobs/sha256/c2f1d4a3a75e7960d968ee489ecaf910556e6a90dab15a48c5e17add27ec4899 new file mode 100644 index 000000000..8adcea09d --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/c2f1d4a3a75e7960d968ee489ecaf910556e6a90dab15a48c5e17add27ec4899 @@ -0,0 +1,36 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 6087, + "digest": "sha256:c565211317b649cccc67ae72e211890ee3561e6b8114cca4d16a546683836e67" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 2541305, + "digest": "sha256:d981dc59d98dcf79c1920d433d8d56f9892f229da716de64280980f317a80ef1" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 302394, + "digest": "sha256:4a8494c47795a2433a510d10be9885d3ce3aa277db1aa2f2068d80ce20de4e09" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 22754023, + "digest": "sha256:036a434e772e404a8b13272e1b8f711036bb22a33f4060f6710cc75c102a3162" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 233, + "digest": "sha256:afa372851354c70082549353d04ec1147e806cec361b7ca10df4638ae1524aea" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 1812575, + "digest": "sha256:ec8bcca4524c4d66a4d1a7b810fa84707fce8f0b3d9cf46ae84e7849704625a1" + } + ] +} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/c4deb1aa89fc6837dd79db6b739b5ea154b6bdf57662254bc76c87220a0526e4 b/test/data/images/vuln-image-in-oci/blobs/sha256/c4deb1aa89fc6837dd79db6b739b5ea154b6bdf57662254bc76c87220a0526e4 new file mode 100644 index 000000000..5ae7fcc7e Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/c4deb1aa89fc6837dd79db6b739b5ea154b6bdf57662254bc76c87220a0526e4 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/c565211317b649cccc67ae72e211890ee3561e6b8114cca4d16a546683836e67 b/test/data/images/vuln-image-in-oci/blobs/sha256/c565211317b649cccc67ae72e211890ee3561e6b8114cca4d16a546683836e67 new file mode 100644 index 000000000..4f6bf8f1d --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/c565211317b649cccc67ae72e211890ee3561e6b8114cca4d16a546683836e67 @@ -0,0 +1 @@ +{"architecture":"s390x","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["python3"],"ArgsEscaped":true,"Image":"sha256:8ecf7814a8049fcd4372a34df5de75acc1fa2ed27cfc39be3dc3a55df4678565","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"fdd48dfa58e04e35c30fca3a6f8fd3c4e8b4be3dd42c4a6b0fcb36740e307ee5","container_config":{"Hostname":"fdd48dfa58e0","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","PYTHON_VERSION=3.4.10","PYTHON_PIP_VERSION=19.0.3"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"python3\"]"],"ArgsEscaped":true,"Image":"sha256:8ecf7814a8049fcd4372a34df5de75acc1fa2ed27cfc39be3dc3a55df4678565","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2019-03-20T12:58:12.828078783Z","docker_version":"18.06.1-ce","history":[{"created":"2019-03-08T03:35:46.020933297Z","created_by":"/bin/sh -c #(nop) ADD file:3f769e5ff31fbae6ea6b835fa878b05d6c6920ca4313aa63be7d057e29241d87 in / "},{"created":"2019-03-08T03:35:46.197387109Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true},{"created":"2019-03-08T05:31:43.617619881Z","created_by":"/bin/sh -c #(nop) ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","empty_layer":true},{"created":"2019-03-08T05:31:43.800165795Z","created_by":"/bin/sh -c #(nop) ENV LANG=C.UTF-8","empty_layer":true},{"created":"2019-03-08T05:31:44.773708917Z","created_by":"/bin/sh -c apk add --no-cache ca-certificates"},{"created":"2019-03-08T05:44:01.832487517Z","created_by":"/bin/sh -c #(nop) ENV GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D","empty_layer":true},{"created":"2019-03-20T12:55:16.924040765Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_VERSION=3.4.10","empty_layer":true},{"created":"2019-03-20T12:58:03.48354594Z","created_by":"/bin/sh -c set -ex \t\u0026\u0026 apk add --no-cache --virtual .fetch-deps \t\tgnupg \t\ttar \t\txz \t\t\u0026\u0026 wget -O python.tar.xz \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz\" \t\u0026\u0026 wget -O python.tar.xz.asc \"https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc\" \t\u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \t\u0026\u0026 gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys \"$GPG_KEY\" \t\u0026\u0026 gpg --batch --verify python.tar.xz.asc python.tar.xz \t\u0026\u0026 { command -v gpgconf \u003e /dev/null \u0026\u0026 gpgconf --kill all || :; } \t\u0026\u0026 rm -rf \"$GNUPGHOME\" python.tar.xz.asc \t\u0026\u0026 mkdir -p /usr/src/python \t\u0026\u0026 tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \t\u0026\u0026 rm python.tar.xz \t\t\u0026\u0026 apk add --no-cache --virtual .build-deps \t\tbzip2-dev \t\tcoreutils \t\tdpkg-dev dpkg \t\texpat-dev \t\tfindutils \t\tgcc \t\tgdbm-dev \t\tlibc-dev \t\tlibffi-dev \t\tlinux-headers \t\tmake \t\tncurses-dev \t\tlibressl-dev \t\tpax-utils \t\treadline-dev \t\tsqlite-dev \t\ttcl-dev \t\ttk \t\ttk-dev \t\txz-dev \t\tzlib-dev \t\u0026\u0026 apk del .fetch-deps \t\t\u0026\u0026 cd /usr/src/python \t\u0026\u0026 gnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\" \t\u0026\u0026 ./configure \t\t--build=\"$gnuArch\" \t\t--enable-loadable-sqlite-extensions \t\t--enable-shared \t\t--with-system-expat \t\t--with-system-ffi \t\t--without-ensurepip \t\u0026\u0026 make -j \"$(nproc)\" \t\tEXTRA_CFLAGS=\"-DTHREAD_STACK_SIZE=0x100000\" \t\u0026\u0026 make install \t\t\u0026\u0026 find /usr/local -type f -executable -not \\( -name '*tkinter*' \\) -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \t\t| tr ',' '\\n' \t\t| sort -u \t\t| awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' \t\t| xargs -rt apk add --no-cache --virtual .python-rundeps \t\u0026\u0026 apk del .build-deps \t\t\u0026\u0026 find /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' + \t\u0026\u0026 rm -rf /usr/src/python \t\t\u0026\u0026 python3 --version"},{"created":"2019-03-20T12:58:05.008456386Z","created_by":"/bin/sh -c cd /usr/local/bin \t\u0026\u0026 ln -s idle3 idle \t\u0026\u0026 ln -s pydoc3 pydoc \t\u0026\u0026 ln -s python3 python \t\u0026\u0026 ln -s python3-config python-config"},{"created":"2019-03-20T12:58:05.500559943Z","created_by":"/bin/sh -c #(nop) ENV PYTHON_PIP_VERSION=19.0.3","empty_layer":true},{"created":"2019-03-20T12:58:12.485123879Z","created_by":"/bin/sh -c set -ex; \t\twget -O get-pip.py 'https://bootstrap.pypa.io/get-pip.py'; \t\tpython get-pip.py \t\t--disable-pip-version-check \t\t--no-cache-dir \t\t\"pip==$PYTHON_PIP_VERSION\" \t; \tpip --version; \t\tfind /usr/local -depth \t\t\\( \t\t\t\\( -type d -a \\( -name test -o -name tests \\) \\) \t\t\t-o \t\t\t\\( -type f -a \\( -name '*.pyc' -o -name '*.pyo' \\) \\) \t\t\\) -exec rm -rf '{}' +; \trm -f get-pip.py"},{"created":"2019-03-20T12:58:12.828078783Z","created_by":"/bin/sh -c #(nop) CMD [\"python3\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:3e69aafc030dcb348e76de325d1222693104f2cc260a672fd3fe15d72f52af5d","sha256:63329a63a425b2f0462d17613d28f8753654ad7fce4c98a483220fb07901ee81","sha256:4199902917f9935fa7505b58255560ba12a4aab3c37502533d28b2fd66a578d2","sha256:d161b7664c1cb8a9352a35b72f425a78ba0f687a9dd2f0b97171e122f9fd995d","sha256:737db18abc9d20aaeab543602addaa971dbe661ea2aaa54632245a34c836d683"]}} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/cda9ba2397ef5f09cf0e723c11a145dbb42779e78fe441904085928399386f10 b/test/data/images/vuln-image-in-oci/blobs/sha256/cda9ba2397ef5f09cf0e723c11a145dbb42779e78fe441904085928399386f10 new file mode 100644 index 000000000..65621f231 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/cda9ba2397ef5f09cf0e723c11a145dbb42779e78fe441904085928399386f10 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/d113ef8b10c5a18eb09e6521fe4a2afdc5f540f560d88a38bf556046ceb24c0c b/test/data/images/vuln-image-in-oci/blobs/sha256/d113ef8b10c5a18eb09e6521fe4a2afdc5f540f560d88a38bf556046ceb24c0c new file mode 100644 index 000000000..c07cb83b5 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/d113ef8b10c5a18eb09e6521fe4a2afdc5f540f560d88a38bf556046ceb24c0c differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/d33b53ae0340b7490b486edd65310a4333d7e0057854aadd12a5006faf7fa576 b/test/data/images/vuln-image-in-oci/blobs/sha256/d33b53ae0340b7490b486edd65310a4333d7e0057854aadd12a5006faf7fa576 new file mode 100644 index 000000000..60188ed82 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/d33b53ae0340b7490b486edd65310a4333d7e0057854aadd12a5006faf7fa576 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/d837e8e30360686a17ea015a8d6787b7c35599d3d53d73b4fb88f95fede90980 b/test/data/images/vuln-image-in-oci/blobs/sha256/d837e8e30360686a17ea015a8d6787b7c35599d3d53d73b4fb88f95fede90980 new file mode 100644 index 000000000..c8a63fb02 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/d837e8e30360686a17ea015a8d6787b7c35599d3d53d73b4fb88f95fede90980 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/d8d287cbc5740a5d49f37493eecadafc220fee4fd570329023344cad978c4272 b/test/data/images/vuln-image-in-oci/blobs/sha256/d8d287cbc5740a5d49f37493eecadafc220fee4fd570329023344cad978c4272 new file mode 100644 index 000000000..333fc8621 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/d8d287cbc5740a5d49f37493eecadafc220fee4fd570329023344cad978c4272 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/d981dc59d98dcf79c1920d433d8d56f9892f229da716de64280980f317a80ef1 b/test/data/images/vuln-image-in-oci/blobs/sha256/d981dc59d98dcf79c1920d433d8d56f9892f229da716de64280980f317a80ef1 new file mode 100644 index 000000000..a1dd795d4 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/d981dc59d98dcf79c1920d433d8d56f9892f229da716de64280980f317a80ef1 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/e1977129b756c4b8dbc6e4d094384c2743576ccd39b059903dfe1154edd33489 b/test/data/images/vuln-image-in-oci/blobs/sha256/e1977129b756c4b8dbc6e4d094384c2743576ccd39b059903dfe1154edd33489 new file mode 100644 index 000000000..e1d752430 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/e1977129b756c4b8dbc6e4d094384c2743576ccd39b059903dfe1154edd33489 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/e6f4a99552d2a12a434e2b1b6dce8499a72e10e66c295ee8b0951104d3960cd5 b/test/data/images/vuln-image-in-oci/blobs/sha256/e6f4a99552d2a12a434e2b1b6dce8499a72e10e66c295ee8b0951104d3960cd5 new file mode 100644 index 000000000..fa4627d1c Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/e6f4a99552d2a12a434e2b1b6dce8499a72e10e66c295ee8b0951104d3960cd5 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/ec8bcca4524c4d66a4d1a7b810fa84707fce8f0b3d9cf46ae84e7849704625a1 b/test/data/images/vuln-image-in-oci/blobs/sha256/ec8bcca4524c4d66a4d1a7b810fa84707fce8f0b3d9cf46ae84e7849704625a1 new file mode 100644 index 000000000..320f7e233 Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/ec8bcca4524c4d66a4d1a7b810fa84707fce8f0b3d9cf46ae84e7849704625a1 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/f031db30449b815a6ef2abcc8a9241a68f55c63035170b85dca3b1db2891e6fa b/test/data/images/vuln-image-in-oci/blobs/sha256/f031db30449b815a6ef2abcc8a9241a68f55c63035170b85dca3b1db2891e6fa new file mode 100644 index 000000000..938084b51 --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/f031db30449b815a6ef2abcc8a9241a68f55c63035170b85dca3b1db2891e6fa @@ -0,0 +1,36 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 6086, + "digest": "sha256:c06adcf62f6ef21ae5c586552532b04b693f9ab6df377d7ea066fd682c470864" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 2754729, + "digest": "sha256:8e402f1a9c577ded051c1ef10e9fe4492890459522089959988a4852dee8ab2c" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 301882, + "digest": "sha256:cda9ba2397ef5f09cf0e723c11a145dbb42779e78fe441904085928399386f10" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 22361764, + "digest": "sha256:aafecf9bbbfd514858f0d93fa0f65c2d59c8a1c46ec4b55963e42619f00a0a61" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 227, + "digest": "sha256:bc2e7e266629e9571a64d9d9050a49b38904b28302056c90222072f84687ba76" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 1812398, + "digest": "sha256:e1977129b756c4b8dbc6e4d094384c2743576ccd39b059903dfe1154edd33489" + } + ] +} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/f2563b8febbcac7bb8b811e535612d222ae160e2cd798109e20798a4f88fe3a2 b/test/data/images/vuln-image-in-oci/blobs/sha256/f2563b8febbcac7bb8b811e535612d222ae160e2cd798109e20798a4f88fe3a2 new file mode 100644 index 000000000..ba01bd9bd Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/f2563b8febbcac7bb8b811e535612d222ae160e2cd798109e20798a4f88fe3a2 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/f307ca8b43fdab6ef1281e027a6cc217da276e8ef99e39c9c7f0f4d09a2b93a9 b/test/data/images/vuln-image-in-oci/blobs/sha256/f307ca8b43fdab6ef1281e027a6cc217da276e8ef99e39c9c7f0f4d09a2b93a9 new file mode 100644 index 000000000..fd66c8b0c Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/f307ca8b43fdab6ef1281e027a6cc217da276e8ef99e39c9c7f0f4d09a2b93a9 differ diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/f4f39efdcf56dbf787903372c7ec1c2b7dd9b4c01583dfb29493bbf31341f48f b/test/data/images/vuln-image-in-oci/blobs/sha256/f4f39efdcf56dbf787903372c7ec1c2b7dd9b4c01583dfb29493bbf31341f48f new file mode 100644 index 000000000..02db2a42c --- /dev/null +++ b/test/data/images/vuln-image-in-oci/blobs/sha256/f4f39efdcf56dbf787903372c7ec1c2b7dd9b4c01583dfb29493bbf31341f48f @@ -0,0 +1,36 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 6086, + "digest": "sha256:1035e489ecce623de89a5a7a5f3a0c2db28ea05cf2591088b13d25c211c9acb8" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 2348580, + "digest": "sha256:d8d287cbc5740a5d49f37493eecadafc220fee4fd570329023344cad978c4272" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 301025, + "digest": "sha256:0eaf06035b3f7b971bf69f2792f93a091f1df5f8a28a2b09ee38f0a9ee156163" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 20763500, + "digest": "sha256:6a68826471381b09574cbffb43f630258f7734e6cc65adaa3b1dc0b5e5970f68" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 231, + "digest": "sha256:6d13cb5356f63b849680a8e0c6bc80b0a5206366aa73c8153fa995f930475427" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 1812880, + "digest": "sha256:57e7cff0ea1bce1c6698eba0d76064389a8d282bc042a417d6583a45b7b329ab" + } + ] +} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/blobs/sha256/f54eba5576d4d8d712791eac30f1c1fd7ee79de40b28c7b2445301b94d1ae813 b/test/data/images/vuln-image-in-oci/blobs/sha256/f54eba5576d4d8d712791eac30f1c1fd7ee79de40b28c7b2445301b94d1ae813 new file mode 100644 index 000000000..b7f19afef Binary files /dev/null and b/test/data/images/vuln-image-in-oci/blobs/sha256/f54eba5576d4d8d712791eac30f1c1fd7ee79de40b28c7b2445301b94d1ae813 differ diff --git a/test/data/images/vuln-image-in-oci/index.json b/test/data/images/vuln-image-in-oci/index.json new file mode 100755 index 000000000..7873f7678 --- /dev/null +++ b/test/data/images/vuln-image-in-oci/index.json @@ -0,0 +1,11 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.index.v1+json", + "manifests": [ + { + "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", + "size": 2371, + "digest": "sha256:c210b660e2ea553a7afa23b41a6ed112f85dbce25cbcb567c75dfe05342a4c4b" + } + ] +} \ No newline at end of file diff --git a/test/data/images/vuln-image-in-oci/oci-layout b/test/data/images/vuln-image-in-oci/oci-layout new file mode 100755 index 000000000..224a86981 --- /dev/null +++ b/test/data/images/vuln-image-in-oci/oci-layout @@ -0,0 +1,3 @@ +{ + "imageLayoutVersion": "1.0.0" +} \ No newline at end of file diff --git a/test/data/images/vuln-single-image/vuln-image.tar b/test/data/images/vuln-single-image/vuln-image.tar new file mode 100644 index 000000000..02572b3ec Binary files /dev/null and b/test/data/images/vuln-single-image/vuln-image.tar differ diff --git a/test/e2e/v1beta1/common_cbs_test.go b/test/e2e/v1beta1/common_cbs_test.go new file mode 100644 index 000000000..e3f69d54a --- /dev/null +++ b/test/e2e/v1beta1/common_cbs_test.go @@ -0,0 +1,87 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + + buildv1beta1 "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +type clusterBuildStrategyPrototype struct { + clusterBuildStrategy buildv1beta1.ClusterBuildStrategy +} + +func NewClusterBuildStrategyPrototype() *clusterBuildStrategyPrototype { + return &clusterBuildStrategyPrototype{ + clusterBuildStrategy: buildv1beta1.ClusterBuildStrategy{}, + } +} + +func (c *clusterBuildStrategyPrototype) Name(name string) *clusterBuildStrategyPrototype { + c.clusterBuildStrategy.ObjectMeta.Name = name + return c +} + +func (c *clusterBuildStrategyPrototype) BuildStep(buildStep buildv1beta1.Step) *clusterBuildStrategyPrototype { + c.clusterBuildStrategy.Spec.Steps = append(c.clusterBuildStrategy.Spec.Steps, buildStep) + return c +} + +func (c *clusterBuildStrategyPrototype) Parameter(parameter buildv1beta1.Parameter) *clusterBuildStrategyPrototype { + c.clusterBuildStrategy.Spec.Parameters = append(c.clusterBuildStrategy.Spec.Parameters, parameter) + return c +} + +func (c *clusterBuildStrategyPrototype) Volume(volume buildv1beta1.BuildStrategyVolume) *clusterBuildStrategyPrototype { + c.clusterBuildStrategy.Spec.Volumes = append(c.clusterBuildStrategy.Spec.Volumes, volume) + return c +} + +func (c *clusterBuildStrategyPrototype) Create() (cbs *buildv1beta1.ClusterBuildStrategy, err error) { + ctx := context.Background() + + _, err = testBuild. + BuildClientSet. + ShipwrightV1beta1(). + ClusterBuildStrategies(). + Create(ctx, &c.clusterBuildStrategy, metav1.CreateOptions{}) + + if err != nil { + return nil, err + } + + err = wait.PollUntilContextTimeout(ctx, pollCreateInterval, pollCreateTimeout, true, func(context.Context) (done bool, err error) { + cbs, err = testBuild.BuildClientSet.ShipwrightV1beta1().ClusterBuildStrategies().Get(ctx, c.clusterBuildStrategy.Name, metav1.GetOptions{}) + if err != nil { + return false, err + } + + return true, nil + }) + + return +} + +func (c *clusterBuildStrategyPrototype) TestMe(f func(clusterBuildStrategy *buildv1beta1.ClusterBuildStrategy)) { + GinkgoHelper() + cbs, err := c.Create() + Expect(err).ToNot(HaveOccurred()) + + f(cbs) + + Expect(testBuild. + BuildClientSet. + ShipwrightV1beta1(). + ClusterBuildStrategies(). + Delete(context.Background(), cbs.Name, metav1.DeleteOptions{}), + ).To(Succeed()) +} diff --git a/test/e2e/v1beta1/common_suite_test.go b/test/e2e/v1beta1/common_suite_test.go index 699e0c9a4..494a9818b 100644 --- a/test/e2e/v1beta1/common_suite_test.go +++ b/test/e2e/v1beta1/common_suite_test.go @@ -154,6 +154,11 @@ func (b *buildPrototype) OutputImage(image string) *buildPrototype { return b } +func (b *buildPrototype) OutputVulnerabilitySettings(settings buildv1beta1.VulnerabilityScanOptions) *buildPrototype { + b.build.Spec.Output.VulnerabilityScan = &settings + return b +} + func (b *buildPrototype) determineParameterIndex(name string) int { index := -1 for i, paramValue := range b.build.Spec.ParamValues { diff --git a/test/e2e/v1beta1/e2e_vulnerability_scanning_test.go b/test/e2e/v1beta1/e2e_vulnerability_scanning_test.go new file mode 100644 index 000000000..92f34e2c7 --- /dev/null +++ b/test/e2e/v1beta1/e2e_vulnerability_scanning_test.go @@ -0,0 +1,107 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + "fmt" + "os" + "strings" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" + + buildapi "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +var _ = Describe("Vulnerability Scanning", func() { + var testID string + var err error + var buildRun *buildapi.BuildRun + var cbs *clusterBuildStrategyPrototype + var insecure bool + + AfterEach(func() { + testBuild.DeleteBR(buildRun.Name) + }) + + Context("Scanning for vulnerabilities in container images", func() { + var outputImage string + BeforeEach(func() { + testID = generateTestID("vuln") + outputImage = fmt.Sprintf("%s/%s:%s", + os.Getenv(EnvVarImageRepo), + testID, + "latest", + ) + + // Assume we use secure registry unless it is a cluster + // internal registry used as part of the test setup + insecure = strings.Contains(outputImage, "cluster.local") + cbs = NewClusterBuildStrategyPrototype(). + Name("crane-pull-" + testID). + BuildStep(buildapi.Step{ + Name: "crane-pull", + Image: "gcr.io/go-containerregistry/crane:latest", + WorkingDir: "$(params.shp-source-root)", + SecurityContext: &corev1.SecurityContext{ + RunAsUser: pointer.Int64(1000), + RunAsGroup: pointer.Int64(1000), + }, + Env: []corev1.EnvVar{ + {Name: "DOCKER_CONFIG", Value: "/tekton/home/.docker"}, + {Name: "HOME", Value: "/tekton/home"}, + }, + Command: []string{"crane"}, + Args: []string{ + "pull", + "--format=tarball", + "ghcr.io/shipwright-io/shipwright-samples/node:12", + "$(params.shp-output-directory)/image.tar", + }, + }) + }) + + It("should find vulnerabilities in an image", func() { + cbs.TestMe(func(cbs *buildapi.ClusterBuildStrategy) { + unfixed := true + ignoreSeverity := buildapi.IgnoredLow + buildRun, err = NewBuildRunPrototype(). + Namespace(testBuild.Namespace). + Name(testID). + WithBuildSpec(NewBuildPrototype(). + ClusterBuildStrategy(cbs.Name). + Namespace(testBuild.Namespace). + Name(testID). + OutputImage(outputImage). + OutputImageCredentials(os.Getenv(EnvVarImageRepoSecret)). + OutputImageInsecure(insecure). + OutputVulnerabilitySettings(buildapi.VulnerabilityScanOptions{ + Enabled: true, + FailOnFinding: true, + Ignore: &buildapi.VulnerabilityIgnoreOptions{ + ID: []string{"CVE-2021-3807", "CVE-2022-33987"}, + Severity: &ignoreSeverity, + Unfixed: &unfixed, + }, + }). + BuildSpec()). + Create() + Expect(err).ToNot(HaveOccurred()) + validateBuildRunToFail(testBuild, buildRun) + buildRun, err = testBuild.GetBR(buildRun.Name) + Expect(err).To(BeNil()) + Expect(buildRun).ToNot(BeNil()) + Expect(buildRun.Status).ToNot(BeNil()) + Expect(buildRun.Status.Output).ToNot(BeNil()) + Expect(buildRun.Status.Output.Vulnerabilities).ToNot(BeNil()) + Expect(len(buildRun.Status.Output.Vulnerabilities)).ToNot(BeZero()) + + }) + }) + }) +}) diff --git a/test/e2e/v1beta1/validators_test.go b/test/e2e/v1beta1/validators_test.go index ca1221548..3e1c17c73 100644 --- a/test/e2e/v1beta1/validators_test.go +++ b/test/e2e/v1beta1/validators_test.go @@ -216,10 +216,13 @@ func validateBuildRunResultsFromBundleSource(testBuildRun *buildv1beta1.BuildRun func validateBuildRunToFail(testBuild *utils.TestBuild, testBuildRun *buildv1beta1.BuildRun) { trueCondition := corev1.ConditionTrue falseCondition := corev1.ConditionFalse + var err error // Ensure the BuildRun has been created - err := testBuild.CreateBR(testBuildRun) - Expect(err).ToNot(HaveOccurred(), "Failed to create BuildRun") + if _, err := testBuild.GetBR(testBuildRun.Name); err != nil { + Expect(testBuild.CreateBR(testBuildRun)). + ToNot(HaveOccurred(), "Failed to create BuildRun") + } // Ensure a BuildRun eventually moves to a succeeded FALSE status nextStatusLog := time.Now().Add(60 * time.Second) diff --git a/test/integration/build_to_taskruns_test.go b/test/integration/build_to_taskruns_test.go index 579ef65cd..ee3b43ae9 100644 --- a/test/integration/build_to_taskruns_test.go +++ b/test/integration/build_to_taskruns_test.go @@ -176,6 +176,8 @@ var _ = Describe("Integration tests Build and TaskRun", func() { "$(results.shp-image-digest.path)", "--result-file-image-size", "$(results.shp-image-size.path)", + "--result-file-image-vulnerabilities", + "$(results.shp-image-vulnerabilities.path)", })) }) diff --git a/test/integration/buildstrategy_to_taskruns_test.go b/test/integration/buildstrategy_to_taskruns_test.go index aefffa02b..0c2ebbce8 100644 --- a/test/integration/buildstrategy_to_taskruns_test.go +++ b/test/integration/buildstrategy_to_taskruns_test.go @@ -588,6 +588,8 @@ var _ = Describe("Integration tests BuildStrategies and TaskRuns", func() { "$(results.shp-image-digest.path)", "--result-file-image-size", "$(results.shp-image-size.path)", + "--result-file-image-vulnerabilities", + "$(results.shp-image-vulnerabilities.path)", })) }) }) @@ -644,6 +646,8 @@ var _ = Describe("Integration tests BuildStrategies and TaskRuns", func() { "$(results.shp-image-digest.path)", "--result-file-image-size", "$(results.shp-image-size.path)", + "--result-file-image-vulnerabilities", + "$(results.shp-image-vulnerabilities.path)", "--secret-path", "/workspace/shp-push-secret", })) diff --git a/test/integration/vulnerability_scan_test.go b/test/integration/vulnerability_scan_test.go new file mode 100644 index 000000000..6777dd4d6 --- /dev/null +++ b/test/integration/vulnerability_scan_test.go @@ -0,0 +1,159 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package integration_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + + buildapi "github.com/shipwright-io/build/pkg/apis/build/v1beta1" + test "github.com/shipwright-io/build/test/v1beta1_samples" +) + +var _ = Describe("Vulnerability scan tests", func() { + + var ( + cbsObject *buildapi.ClusterBuildStrategy + buildObject *buildapi.Build + buildRunObject *buildapi.BuildRun + buildSample []byte + buildRunSample []byte + ) + + // Load the ClusterBuildStrategies before each test case + BeforeEach(func() { + cbsObject, err = tb.Catalog.LoadCBSWithName(STRATEGY+tb.Namespace, []byte(test.ClusterBuildStrategyForVulnerabilityScanning)) + Expect(err).To(BeNil()) + + err = tb.CreateClusterBuildStrategy(cbsObject) + Expect(err).To(BeNil()) + + }) + + // Delete the ClusterBuildStrategies after each test case + AfterEach(func() { + if buildObject != nil { + _, err = tb.GetBuild(buildObject.Name) + if err == nil { + Expect(tb.DeleteBuild(buildObject.Name)).To(BeNil()) + } + } + + err := tb.DeleteClusterBuildStrategy(cbsObject.Name) + Expect(err).To(BeNil()) + }) + + // Override the Builds and BuildRuns CRDs instances to use + // before an It() statement is executed + JustBeforeEach(func() { + if buildSample != nil { + buildObject, err = tb.Catalog.LoadBuildWithNameAndStrategy(BUILD+tb.Namespace, STRATEGY+tb.Namespace, buildSample) + Expect(err).To(BeNil()) + } + + if buildRunSample != nil { + buildRunObject, err = tb.Catalog.LoadBRWithNameAndRef(BUILDRUN+tb.Namespace, BUILD+tb.Namespace, buildRunSample) + Expect(err).To(BeNil()) + } + }) + + Context("when a build with vulnerability scan options is defined", func() { + + BeforeEach(func() { + buildSample = []byte(test.BuildWithVulnerabilityScanOptions) + buildRunSample = []byte(test.MinimalBuildRun) + }) + + It("should fail the builRun with a Reason", func() { + + Expect(tb.CreateBuild(buildObject)).To(BeNil()) + + buildObject, err = tb.GetBuildTillValidation(buildObject.Name) + Expect(err).To(BeNil()) + + Expect(tb.CreateBR(buildRunObject)).To(BeNil()) + + br, err := tb.GetBRTillCompletion(buildRunObject.Name) + Expect(err).To(BeNil()) + + Expect(br.Status.GetCondition(buildapi.Succeeded).Status).To(Equal(corev1.ConditionFalse)) + Expect(br.Status.GetCondition(buildapi.Succeeded).Reason).To(Equal("VulnerabilitiesFound")) + Expect(br.Status.GetCondition(buildapi.Succeeded).Message).To(ContainSubstring("Vulnerabilities have been found in the image")) + }) + }) + + Context("When a buildrun with vulnerability scan is defined", func() { + BeforeEach(func() { + buildSample = []byte(test.MinimalBuild) + buildRunSample = []byte(test.MinimalBuildRunWithVulnerabilityScan) + }) + + It("should fail the builRun with a Reason", func() { + + Expect(tb.CreateBuild(buildObject)).To(BeNil()) + + buildObject, err = tb.GetBuildTillValidation(buildObject.Name) + Expect(err).To(BeNil()) + + Expect(tb.CreateBR(buildRunObject)).To(BeNil()) + + br, err := tb.GetBRTillCompletion(buildRunObject.Name) + Expect(err).To(BeNil()) + + Expect(br.Status.GetCondition(buildapi.Succeeded).Status).To(Equal(corev1.ConditionFalse)) + Expect(br.Status.GetCondition(buildapi.Succeeded).Reason).To(Equal("VulnerabilitiesFound")) + Expect(br.Status.GetCondition(buildapi.Succeeded).Message).To(ContainSubstring("Vulnerabilities have been found in the image")) + }) + }) + + Context("When both build and buildrun with vulnerability scan is defined", func() { + BeforeEach(func() { + buildSample = []byte(test.BuildWithDisableVulnerabilityScan) + buildRunSample = []byte(test.MinimalBuildRunWithVulnerabilityScan) + }) + + It("should override the vulnerability settings from builRun", func() { + + Expect(tb.CreateBuild(buildObject)).To(BeNil()) + + buildObject, err = tb.GetBuildTillValidation(buildObject.Name) + Expect(err).To(BeNil()) + + Expect(tb.CreateBR(buildRunObject)).To(BeNil()) + + br, err := tb.GetBRTillCompletion(buildRunObject.Name) + Expect(err).To(BeNil()) + + Expect(br.Status.GetCondition(buildapi.Succeeded).Status).To(Equal(corev1.ConditionFalse)) + Expect(br.Status.GetCondition(buildapi.Succeeded).Reason).To(Equal("VulnerabilitiesFound")) + Expect(br.Status.GetCondition(buildapi.Succeeded).Message).To(ContainSubstring("Vulnerabilities have been found in the image")) + }) + }) + + Context("When a standalone buildrun with vulnerability scan is defined", func() { + var standAloneBuildRunSample []byte + var standaloneBuildRunObject *buildapi.BuildRun + + BeforeEach(func() { + standAloneBuildRunSample = []byte(test.OneOffBuildRunWithVulnerabilityScan) + standaloneBuildRunObject, err = tb.Catalog.LoadStandAloneBuildRunWithNameAndStrategy(BUILDRUN+tb.Namespace+"-standalone", cbsObject, standAloneBuildRunSample) + Expect(err).To(BeNil()) + }) + + It("should fail the builRun with a Reason", func() { + + Expect(tb.CreateBR(standaloneBuildRunObject)).To(BeNil()) + + br, err := tb.GetBRTillCompletion(standaloneBuildRunObject.Name) + Expect(err).To(BeNil()) + + Expect(br.Status.GetCondition(buildapi.Succeeded).Status).To(Equal(corev1.ConditionFalse)) + Expect(br.Status.GetCondition(buildapi.Succeeded).Reason).To(Equal("VulnerabilitiesFound")) + Expect(br.Status.GetCondition(buildapi.Succeeded).Message).To(ContainSubstring("Vulnerabilities have been found in the image")) + }) + }) + +}) diff --git a/test/utils/v1beta1/environment.go b/test/utils/v1beta1/environment.go index df541a02f..185876954 100644 --- a/test/utils/v1beta1/environment.go +++ b/test/utils/v1beta1/environment.go @@ -90,7 +90,7 @@ func NewTestBuild() (*TestBuild, error) { return &TestBuild{ // TODO: interval and timeout can be configured via ENV vars Interval: time.Second * 3, - TimeOut: time.Second * 180, + TimeOut: time.Second * 300, KubeConfig: restConfig, Clientset: kubeConfig, Namespace: testNamespace, diff --git a/test/v1beta1_samples/build_samples.go b/test/v1beta1_samples/build_samples.go index 289a33bd5..325eee108 100644 --- a/test/v1beta1_samples/build_samples.go +++ b/test/v1beta1_samples/build_samples.go @@ -493,6 +493,45 @@ spec: image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app ` +// BuildWithVulnerabilityScanOptions defines a Build with vulnerability scan options +const BuildWithVulnerabilityScanOptions = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + strategy: + kind: ClusterBuildStrategy + output: + vulnerabilityScan: + enabled: true + failOnFinding: true + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildWithVulnerabilityScanOptions defines a Build with vulnerability scan options +const BuildWithDisableVulnerabilityScan = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + strategy: + kind: ClusterBuildStrategy + output: + vulnerabilityScan: + enabled: false + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildWithRestrictedParam defines a Build using params that are reserved only +// for shipwright +const MinimalBuild = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + // BuildWithUndefinedParameter defines a param that was not declared under the // strategy parameters const BuildWithUndefinedParam = ` diff --git a/test/v1beta1_samples/buildrun_samples.go b/test/v1beta1_samples/buildrun_samples.go index cb88c40e0..27ba30a0c 100644 --- a/test/v1beta1_samples/buildrun_samples.go +++ b/test/v1beta1_samples/buildrun_samples.go @@ -213,6 +213,39 @@ spec: name: foobar ` +// MinimalBuildRunWithVulnerabilityScan defines a BuildRun with +// an override for the Build Output +const MinimalBuildRunWithVulnerabilityScan = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +spec: + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + vulnerabilityScan: + enabled: true + failOnFinding: true + build: + name: foobar +` + +// MinimalBuildRunWithVulnerabilityScan defines a BuildRun with +// an override for the Build Output +const OneOffBuildRunWithVulnerabilityScan = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +spec: + build: + spec: + strategy: + kind: ClusterBuildStrategy + name: buildah + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + vulnerabilityScan: + enabled: true + failOnFinding: true +` + // MinimalBuildRunRetention defines a minimal BuildRun // with a reference used to test retention fields const MinimalBuildRunRetention = ` diff --git a/test/v1beta1_samples/clusterbuildstrategy_samples.go b/test/v1beta1_samples/clusterbuildstrategy_samples.go index 3b5bfec33..2efae5a8d 100644 --- a/test/v1beta1_samples/clusterbuildstrategy_samples.go +++ b/test/v1beta1_samples/clusterbuildstrategy_samples.go @@ -120,6 +120,41 @@ spec: mountPath: /var/lib/containers/storage ` +// ClusterBuildStrategyForVulnerabilityScanning is a strategy that does nothing and has no dependencies +const ClusterBuildStrategyForVulnerabilityScanning = ` +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: crane-pull +spec: + steps: + - name: crane-pull + image: gcr.io/go-containerregistry/crane:latest + workingDir: $(params.shp-source-root) + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: HOME + value: /tekton/home + command: + - crane + args: + - pull + - "--format=tarball" + - "ghcr.io/shipwright-io/shipwright-samples/node:12" + - "$(params.shp-output-directory)/image.tar" + resources: + limits: + cpu: 250m + memory: 128Mi + requests: + cpu: 250m + memory: 128Mi +` + // ClusterBuildStrategySingleStepKaniko is a cluster build strategy based on // Kaniko, which is very close to the actual Kaniko build strategy example in // the project