From ee481e812ac0d28a26b5886cd7d56da94e69ce94 Mon Sep 17 00:00:00 2001 From: Anubhav Gupta Date: Sat, 12 Aug 2023 13:22:09 +0530 Subject: [PATCH 1/4] feat: add ignore errors flags Signed-off-by: Anubhav Gupta --- pkg/patch/cmd.go | 5 ++++- pkg/patch/patch.go | 8 ++++---- pkg/pkgmgr/apk.go | 12 ++++++++---- pkg/pkgmgr/apk_test.go | 2 +- pkg/pkgmgr/dpkg.go | 12 ++++++++---- pkg/pkgmgr/dpkg_test.go | 28 ++++++++++++++++++++++------ pkg/pkgmgr/pkgmgr.go | 6 +++--- pkg/pkgmgr/rpm.go | 12 ++++++++---- pkg/pkgmgr/rpm_test.go | 2 +- 9 files changed, 59 insertions(+), 28 deletions(-) diff --git a/pkg/patch/cmd.go b/pkg/patch/cmd.go index 63af4118..94fda3fe 100644 --- a/pkg/patch/cmd.go +++ b/pkg/patch/cmd.go @@ -23,6 +23,7 @@ type patchArgs struct { workingFolder string buildkitAddr string timeout time.Duration + ignoreError bool } func NewPatchCmd() *cobra.Command { @@ -38,7 +39,8 @@ func NewPatchCmd() *cobra.Command { ua.appImage, ua.reportFile, ua.patchedTag, - ua.workingFolder) + ua.workingFolder, + ua.ignoreError) }, } flags := patchCmd.Flags() @@ -48,6 +50,7 @@ func NewPatchCmd() *cobra.Command { flags.StringVarP(&ua.workingFolder, "working-folder", "w", "", "Working folder, defaults to system temp folder") flags.StringVarP(&ua.buildkitAddr, "addr", "a", defaultBuildkitAddr, "Address of buildkitd service, defaults to local buildkitd.sock") flags.DurationVar(&ua.timeout, "timeout", 5*time.Minute, "Timeout for the operation, defaults to '5m'") + flags.BoolVar(&ua.ignoreError, "ignore-errors", false, "Ignore errors and continue patching") if err := patchCmd.MarkFlagRequired("image"); err != nil { panic(err) diff --git a/pkg/patch/patch.go b/pkg/patch/patch.go index 91e9df40..d66f0e9c 100644 --- a/pkg/patch/patch.go +++ b/pkg/patch/patch.go @@ -26,13 +26,13 @@ const ( ) // Patch command applies package updates to an OCI image given a vulnerability report. -func Patch(ctx context.Context, timeout time.Duration, buildkitAddr, image, reportFile, patchedTag, workingFolder string) error { +func Patch(ctx context.Context, timeout time.Duration, buildkitAddr, image, reportFile, patchedTag, workingFolder string, ignoreError bool) error { timeoutCtx, cancel := context.WithTimeout(ctx, timeout) defer cancel() ch := make(chan error) go func() { - ch <- patchWithContext(timeoutCtx, buildkitAddr, image, reportFile, patchedTag, workingFolder) + ch <- patchWithContext(timeoutCtx, buildkitAddr, image, reportFile, patchedTag, workingFolder, ignoreError) }() select { @@ -57,7 +57,7 @@ func removeIfNotDebug(workingFolder string) { } } -func patchWithContext(ctx context.Context, buildkitAddr, image, reportFile, patchedTag, workingFolder string) error { +func patchWithContext(ctx context.Context, buildkitAddr, image, reportFile, patchedTag, workingFolder string, ignoreError bool) error { imageName, err := ref.ParseNamed(image) if err != nil { return err @@ -124,7 +124,7 @@ func patchWithContext(ctx context.Context, buildkitAddr, image, reportFile, patc // Export the patched image state to Docker // TODO: Add support for other output modes as buildctl does. - patchedImageState, err := pkgmgr.InstallUpdates(ctx, updates) + patchedImageState, err := pkgmgr.InstallUpdates(ctx, updates, ignoreError) if err != nil { return err } diff --git a/pkg/pkgmgr/apk.go b/pkg/pkgmgr/apk.go index ff50b4ec..a757828d 100644 --- a/pkg/pkgmgr/apk.go +++ b/pkg/pkgmgr/apk.go @@ -56,7 +56,7 @@ func apkReadResultsManifest(path string) ([]string, error) { return lines, nil } -func validateAPKPackageVersions(updates types.UpdatePackages, cmp VersionComparer, resultsPath string) error { +func validateAPKPackageVersions(updates types.UpdatePackages, cmp VersionComparer, resultsPath string, ignoreErrors bool) error { lines, err := apkReadResultsManifest(resultsPath) if err != nil { return err @@ -115,13 +115,17 @@ func validateAPKPackageVersions(updates types.UpdatePackages, cmp VersionCompare log.Infof("Validated package %s version %s meets requested version %s", update.Name, version, update.Version) } + if ignoreErrors { + return nil + } + return allErrors.ErrorOrNil() } -func (am *apkManager) InstallUpdates(ctx context.Context, manifest *types.UpdateManifest) (*llb.State, error) { +func (am *apkManager) InstallUpdates(ctx context.Context, manifest *types.UpdateManifest, ignoreErrors bool) (*llb.State, error) { // Resolve set of unique packages to update apkComparer := VersionComparer{isValidAPKVersion, isLessThanAPKVersion} - updates, err := GetUniqueLatestUpdates(manifest.Updates, apkComparer) + updates, err := GetUniqueLatestUpdates(manifest.Updates, apkComparer, ignoreErrors) if err != nil { return nil, err } @@ -138,7 +142,7 @@ func (am *apkManager) InstallUpdates(ctx context.Context, manifest *types.Update // Validate that the deployed packages are of the requested version or better resultManifestPath := filepath.Join(am.workingFolder, resultsPath, resultManifest) - if err := validateAPKPackageVersions(updates, apkComparer, resultManifestPath); err != nil { + if err := validateAPKPackageVersions(updates, apkComparer, resultManifestPath, ignoreErrors); err != nil { return nil, err } diff --git a/pkg/pkgmgr/apk_test.go b/pkg/pkgmgr/apk_test.go index a0ef06ca..d2157de1 100644 --- a/pkg/pkgmgr/apk_test.go +++ b/pkg/pkgmgr/apk_test.go @@ -136,7 +136,7 @@ func TestValidateAPKPackageVersions(t *testing.T) { // Use t.Run to run each test case as a subtest t.Run(tc.name, func(t *testing.T) { // Run the function to be tested - err := validateAPKPackageVersions(tc.updates, tc.cmp, tc.resultsPath) + err := validateAPKPackageVersions(tc.updates, tc.cmp, tc.resultsPath, false) if tc.expectedErr != nil { if err == nil || errors.Is(err, tc.expectedErr) { t.Errorf("expected error %v, got %v", tc.expectedErr, err) diff --git a/pkg/pkgmgr/dpkg.go b/pkg/pkgmgr/dpkg.go index d0f80818..bf28f26d 100644 --- a/pkg/pkgmgr/dpkg.go +++ b/pkg/pkgmgr/dpkg.go @@ -98,10 +98,10 @@ func getDPKGStatusType(dir string) dpkgStatusType { return out } -func (dm *dpkgManager) InstallUpdates(ctx context.Context, manifest *types.UpdateManifest) (*llb.State, error) { +func (dm *dpkgManager) InstallUpdates(ctx context.Context, manifest *types.UpdateManifest, ignoreErrors bool) (*llb.State, error) { // Validate and extract unique updates listed in input manifest debComparer := VersionComparer{isValidDebianVersion, isLessThanDebianVersion} - updates, err := GetUniqueLatestUpdates(manifest.Updates, debComparer) + updates, err := GetUniqueLatestUpdates(manifest.Updates, debComparer, ignoreErrors) if err != nil { return nil, err } @@ -131,7 +131,7 @@ func (dm *dpkgManager) InstallUpdates(ctx context.Context, manifest *types.Updat // Validate that the deployed packages are of the requested version or better resultManifestPath := filepath.Join(dm.workingFolder, resultsPath, resultManifest) - if err := validateDebianPackageVersions(updates, debComparer, resultManifestPath); err != nil { + if err := validateDebianPackageVersions(updates, debComparer, resultManifestPath, ignoreErrors); err != nil { return nil, err } @@ -352,7 +352,7 @@ func dpkgParseResultsManifest(path string) (map[string]string, error) { return updateMap, nil } -func validateDebianPackageVersions(updates types.UpdatePackages, cmp VersionComparer, resultsPath string) error { +func validateDebianPackageVersions(updates types.UpdatePackages, cmp VersionComparer, resultsPath string, ignoreErrors bool) error { // Load file into map[string]string for package:version lookup updateMap, err := dpkgParseResultsManifest(resultsPath) if err != nil { @@ -382,5 +382,9 @@ func validateDebianPackageVersions(updates types.UpdatePackages, cmp VersionComp log.Infof("Validated package %s version %s meets requested version %s", update.Name, version, update.Version) } + if ignoreErrors { + return nil + } + return allErrors.ErrorOrNil() } diff --git a/pkg/pkgmgr/dpkg_test.go b/pkg/pkgmgr/dpkg_test.go index 76ac7165..c97c94d2 100644 --- a/pkg/pkgmgr/dpkg_test.go +++ b/pkg/pkgmgr/dpkg_test.go @@ -232,7 +232,7 @@ func TestValidateDebianPackageVersions(t *testing.T) { dpkgComparer := VersionComparer{isValidDebianVersion, isLessThanDebianVersion} t.Run("no updates", func(t *testing.T) { - err := validateDebianPackageVersions(nil, dpkgComparer, "testdata/dpkg_valid.txt") + err := validateDebianPackageVersions(nil, dpkgComparer, "testdata/dpkg_valid.txt", false) assert.NoError(t, err) }) @@ -240,7 +240,7 @@ func TestValidateDebianPackageVersions(t *testing.T) { updates := []types.UpdatePackage{ {Name: "not_installed", Version: "1.0"}, } - err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_valid.txt") + err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_valid.txt", false) assert.NoError(t, err) }) @@ -248,25 +248,41 @@ func TestValidateDebianPackageVersions(t *testing.T) { updates := []types.UpdatePackage{ {Name: "base-files", Version: "1.0.0"}, } - err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_invalid.txt") + err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_invalid.txt", false) assert.Error(t, err) assert.Contains(t, err.Error(), "invalid version") }) + t.Run("invalid version: ignore errors", func(t *testing.T) { + updates := []types.UpdatePackage{ + {Name: "base-files", Version: "1.0.0"}, + } + err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_invalid.txt", true) + assert.NoError(t, err) + }) + t.Run("version lower than requested", func(t *testing.T) { updates := []types.UpdatePackage{ {Name: "apt", Version: "2.0"}, } - err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_valid.txt") + err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_valid.txt", false) assert.Error(t, err) assert.Contains(t, err.Error(), "downloaded package") }) + t.Run("version lower than requested: ignore errors", func(t *testing.T) { + updates := []types.UpdatePackage{ + {Name: "apt", Version: "2.0"}, + } + err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_valid.txt", true) + assert.NoError(t, err) + }) + t.Run("version equal to requested", func(t *testing.T) { updates := []types.UpdatePackage{ {Name: "apt", Version: "1.8.2.3"}, } - err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_valid.txt") + err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_valid.txt", false) assert.NoError(t, err) }) @@ -274,7 +290,7 @@ func TestValidateDebianPackageVersions(t *testing.T) { updates := []types.UpdatePackage{ {Name: "apt", Version: "0.9"}, } - err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_valid.txt") + err := validateDebianPackageVersions(updates, dpkgComparer, "testdata/dpkg_valid.txt", false) assert.NoError(t, err) }) } diff --git a/pkg/pkgmgr/pkgmgr.go b/pkg/pkgmgr/pkgmgr.go index a17b2d5d..55b7c634 100644 --- a/pkg/pkgmgr/pkgmgr.go +++ b/pkg/pkgmgr/pkgmgr.go @@ -27,7 +27,7 @@ const ( ) type PackageManager interface { - InstallUpdates(context.Context, *types.UpdateManifest) (*llb.State, error) + InstallUpdates(context.Context, *types.UpdateManifest, bool) (*llb.State, error) } func GetPackageManager(osType string, config *buildkit.Config, workingFolder string) (PackageManager, error) { @@ -50,7 +50,7 @@ type VersionComparer struct { LessThan func(string, string) bool } -func GetUniqueLatestUpdates(updates types.UpdatePackages, cmp VersionComparer) (types.UpdatePackages, error) { +func GetUniqueLatestUpdates(updates types.UpdatePackages, cmp VersionComparer, ignoreErrors bool) (types.UpdatePackages, error) { dict := make(map[string]string) var allErrors *multierror.Error for _, u := range updates { @@ -68,7 +68,7 @@ func GetUniqueLatestUpdates(updates types.UpdatePackages, cmp VersionComparer) ( continue } } - if allErrors != nil { + if allErrors != nil && !ignoreErrors { return nil, allErrors.ErrorOrNil() } diff --git a/pkg/pkgmgr/rpm.go b/pkg/pkgmgr/rpm.go index 2da1ec0e..cf033451 100644 --- a/pkg/pkgmgr/rpm.go +++ b/pkg/pkgmgr/rpm.go @@ -159,10 +159,10 @@ func getRPMDBType(dir string) rpmDBType { return out } -func (rm *rpmManager) InstallUpdates(ctx context.Context, manifest *types.UpdateManifest) (*llb.State, error) { +func (rm *rpmManager) InstallUpdates(ctx context.Context, manifest *types.UpdateManifest, ignoreErrors bool) (*llb.State, error) { // Resolve set of unique packages to update rpmComparer := VersionComparer{isValidRPMVersion, isLessThanRPMVersion} - updates, err := GetUniqueLatestUpdates(manifest.Updates, rpmComparer) + updates, err := GetUniqueLatestUpdates(manifest.Updates, rpmComparer, ignoreErrors) if err != nil { return nil, err } @@ -193,7 +193,7 @@ func (rm *rpmManager) InstallUpdates(ctx context.Context, manifest *types.Update // Validate that the deployed packages are of the requested version or better resultManifestPath := filepath.Join(rm.workingFolder, resultsPath, resultManifest) - if err := validateRPMPackageVersions(updates, rpmComparer, resultManifestPath); err != nil { + if err := validateRPMPackageVersions(updates, rpmComparer, resultManifestPath, ignoreErrors); err != nil { return nil, err } @@ -440,7 +440,7 @@ func rpmReadResultsManifest(path string) ([]string, error) { return lines, nil } -func validateRPMPackageVersions(updates types.UpdatePackages, cmp VersionComparer, resultsPath string) error { +func validateRPMPackageVersions(updates types.UpdatePackages, cmp VersionComparer, resultsPath string, ignoreErrors bool) error { lines, err := rpmReadResultsManifest(resultsPath) if err != nil { return err @@ -499,5 +499,9 @@ func validateRPMPackageVersions(updates types.UpdatePackages, cmp VersionCompare log.Infof("Validated package %s version %s meets requested version %s", update.Name, version, update.Version) } + if ignoreErrors { + return nil + } + return allErrors.ErrorOrNil() } diff --git a/pkg/pkgmgr/rpm_test.go b/pkg/pkgmgr/rpm_test.go index 547802e3..c2f23265 100644 --- a/pkg/pkgmgr/rpm_test.go +++ b/pkg/pkgmgr/rpm_test.go @@ -294,7 +294,7 @@ func TestValidateRPMPackageVersions(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - err := validateRPMPackageVersions(tc.updates, tc.cmp, tc.resultsPath) + err := validateRPMPackageVersions(tc.updates, tc.cmp, tc.resultsPath, false) if tc.expectedError != nil { if err == nil || errors.Is(err, tc.expectedError) { t.Errorf("expected error %v, got %v", tc.expectedError, err) From b3a86ecab3b386b0fea5d732f23a59179e9e59aa Mon Sep 17 00:00:00 2001 From: Anubhav Gupta Date: Tue, 15 Aug 2023 19:33:53 +0530 Subject: [PATCH 2/4] test: add tests for --ignore-errors flag Signed-off-by: Anubhav Gupta --- integration/fixtures/test-images.json | 44 ++++++++++++++++------ integration/main_test.go | 2 + integration/patch_test.go | 25 +++++++------ pkg/patch/cmd_test.go | 5 +++ pkg/pkgmgr/apk_test.go | 54 ++++++++++++++++----------- pkg/pkgmgr/rpm_test.go | 20 ++++++++-- 6 files changed, 103 insertions(+), 47 deletions(-) diff --git a/integration/fixtures/test-images.json b/integration/fixtures/test-images.json index f1387c23..419f1d74 100644 --- a/integration/fixtures/test-images.json +++ b/integration/fixtures/test-images.json @@ -4,83 +4,103 @@ "tag": "8.5.0", "digest": "sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5", "distro": "Alpine", - "description": "Valid apk/db, apk present" + "description": "Valid apk/db, apk present", + "ignoreErrors": false }, { "image": "docker.io/library/nginx", "tag": "1.21.6", "digest": "sha256:2bcabc23b45489fb0885d69a06ba1d648aeda973fae7bb981bafbb884165e514", "distro": "Debian", - "description": "Valid dpkg/status, apt present" + "description": "Valid dpkg/status, apt present", + "ignoreErrors": false }, { "image": "registry.k8s.io/kube-proxy", "tag": "v1.23.4", "digest": "sha256:30116c7218264d95623d3918a50da703675755cae866cd4c324586611fcd50ea", "distro": "Debian", - "description": "Valid dpkg/status, apt present, custom network config" + "description": "Valid dpkg/status, apt present, custom network config", + "ignoreErrors": false }, { "image": "registry.k8s.io/kube-proxy", "tag": "v1.27.2", "digest": "sha256:1e4f13f5f5c215813fb9c9c6f56da1c0354363f2a69bd12732658f79d585864f", "distro": "Custom Google Distroless", - "description": "Custom dpkg/status.d with text names, no apt, libssl1.1" + "description": "Custom dpkg/status.d with text names, no apt, libssl1.1", + "ignoreErrors": false }, { "image": "docker.io/fluent/fluent-bit", "tag": "1.8.4", "digest": "sha256:2d80c13c2e7e06aa6a2e54a1825c6adbb3829c8a133ff617a0a61790bd61c53d", "distro": "Google Distroless", - "description": "Custom dpkg/status.d with base64 names, no apt" + "description": "Custom dpkg/status.d with base64 names, no apt", + "ignoreErrors": false }, { "image": "docker.io/openpolicyagent/opa", "tag": "0.46.0", "digest": "sha256:c4b11c9b86eaba41276ae682bb6875332316242010b7523efe30f365ad0c3cb8", "distro": "Google Distroless", - "description": "Custom dpkg/status.d with text names, no apt, libssl1" + "description": "Custom dpkg/status.d with text names, no apt, libssl1", + "ignoreErrors": false }, { "image": "quay.io/calico/cni", "tag": "v3.15.1", "digest": "sha256:a925b445c2688fc9c149b20ea04faabd40610d3304a6efda68e5dada7a41b813", "distro": "Redhat", - "description": "Valid rpm DB, microdnf & rpm present" + "description": "Valid rpm DB, microdnf & rpm present", + "ignoreErrors": false }, { "image": "mcr.microsoft.com/cbl-mariner/base/core", "tag": "1.0.20220218", "digest": "sha256:830120b2cbfb7489c6f3270e1c74f3db0de84a4d33fecfffd427890b94d2f236", "distro": "Mariner", - "description": "Valid rpm DB, no dnf, yum & rpm present" + "description": "Valid rpm DB, no dnf, yum & rpm present", + "ignoreErrors": false }, { "image": "mcr.microsoft.com/cbl-mariner/base/core", "tag": "1.0.20220218-arm64", "digest": "sha256:f97ccb4565f8985c28c6dbc7f13cfea0877652b70545f844eb0b83e4475954d1", "distro": "Mariner", - "description": "Valid rpm DB, no dnf, yum & rpm present, arm64 cross-arch" + "description": "Valid rpm DB, no dnf, yum & rpm present, arm64 cross-arch", + "ignoreErrors": false }, { "image": "mcr.microsoft.com/cbl-mariner/distroless/base", "tag": "2.0.20220527", "digest": "sha256:f550c5428df17b145851ad75983aca6d613ad4b51ca7983b2a83e67d0ac91a5d", "distro": "Mariner Distroless", - "description": "Custom rpmmanifest files, no yum/dnf/microdnf/rpm" + "description": "Custom rpmmanifest files, no yum/dnf/microdnf/rpm", + "ignoreErrors": false }, { "image": "docker.io/library/centos", "tag": "7.6.1810", "digest": "sha256:62d9e1c2daa91166139b51577fe4f4f6b4cc41a3a2c7fc36bd895e2a17a3e4e6", "distro": "CentOS", - "description": "Valid rpm DB, yum present" + "description": "Valid rpm DB, yum present", + "ignoreErrors": false }, { "image": "docker.io/library/amazonlinux", "tag": "2.0.20210326.0", "digest": "sha256:06380711d6a8ac0b6989f7e2a4419e560796791d9c7c843753a719c73552dc30", "distro": "Amazon Linux", - "description": "Valid rpm DB, yum present" + "description": "Valid rpm DB, yum present", + "ignoreErrors": false + }, + { + "image": "mcr.microsoft.com/oss/nginx/nginx", + "tag" : "1.21.6", + "digest": "sha256:bcdc041d100a3cc9fec472a8133ad566166e366241587ff92e8cc8fcf955229d", + "distro": "Debian", + "description": "Valid dpkg/status, apt present", + "ignoreErrors": true } ] diff --git a/integration/main_test.go b/integration/main_test.go index a91b41bc..96577cf2 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -9,11 +9,13 @@ import ( var ( buildkitAddr string copaPath string + ignoreErrors bool ) func TestMain(m *testing.M) { flag.StringVar(&buildkitAddr, "addr", "", "buildkit address to pass through to copa binary") flag.StringVar(&copaPath, "copa", "./copa", "path to copa binary") + flag.BoolVar(&ignoreErrors, "ignore-errors", false, "Ignore errors and continue patching") flag.Parse() if copaPath == "" { diff --git a/integration/patch_test.go b/integration/patch_test.go index d5b61e7f..0dbb3452 100644 --- a/integration/patch_test.go +++ b/integration/patch_test.go @@ -24,11 +24,12 @@ var ( ) type testImage struct { - Image string `json:"image"` - Tag string `json:"tag"` - Distro string `json:"distro"` - Digest digest.Digest `json:"digest"` - Description string `json:"description"` + Image string `json:"image"` + Tag string `json:"tag"` + Distro string `json:"distro"` + Digest digest.Digest `json:"digest"` + Description string `json:"description"` + IgnoreErrors bool `json:"ignoreErrors"` } func TestPatch(t *testing.T) { @@ -57,10 +58,10 @@ func TestPatch(t *testing.T) { withIgnoreFile(ignoreFile). withOutput(output). // Do not set a non-zero exit code because we are expecting vulnerabilities. - scan(t, ref) + scan(t, ref, img.IgnoreErrors) t.Log("patching image") - patch(t, ref, tagPatched, output) + patch(t, ref, tagPatched, output, img.IgnoreErrors) t.Log("scanning patched image") scanner(). @@ -68,12 +69,12 @@ func TestPatch(t *testing.T) { withSkipDBUpdate(). // here we want a non-zero exit code because we are expecting no vulnerabilities. withExitCode(1). - scan(t, patchedRef) + scan(t, patchedRef, img.IgnoreErrors) }) } } -func patch(t *testing.T, ref, patchedTag, scan string) { +func patch(t *testing.T, ref, patchedTag, scan string, ignoreErrors bool) { var addrFl string if buildkitAddr != "" { addrFl = "-a=" + buildkitAddr @@ -88,6 +89,7 @@ func patch(t *testing.T, ref, patchedTag, scan string) { "-r="+scan, "--timeout=20m", addrFl, + "--ignore-errors="+strconv.FormatBool(ignoreErrors), ) out, err := cmd.CombinedOutput() require.NoError(t, err, string(out)) @@ -104,7 +106,7 @@ type scannerCmd struct { exitCode int } -func (s *scannerCmd) scan(t *testing.T, ref string) { +func (s *scannerCmd) scan(t *testing.T, ref string, ignoreErrors bool) { args := []string{ "trivy", "image", @@ -121,7 +123,8 @@ func (s *scannerCmd) scan(t *testing.T, ref string) { if s.ignoreFile != "" { args = append(args, "--ignore-policy="+s.ignoreFile) } - if s.exitCode != 0 { + // If ignoreErrors is false, we expect a non-zero exit code. + if s.exitCode != 0 && !ignoreErrors { args = append(args, "--exit-code="+strconv.Itoa(s.exitCode)) } diff --git a/pkg/patch/cmd_test.go b/pkg/patch/cmd_test.go index d95eef74..0672e81c 100644 --- a/pkg/patch/cmd_test.go +++ b/pkg/patch/cmd_test.go @@ -23,6 +23,11 @@ func TestNewPatchCmd(t *testing.T) { args: []string{"-i", "images/python:3.7-alpine", "-t", "3.7-alpine-patched"}, expected: "required flag(s) \"report\" not set", }, + { + name: "Missing report flag with ignore-errors flag", + args: []string{"-i", "images/python:3.7-alpine", "-t", "3.7-alpine-patched", "--ignore-errors"}, + expected: "required flag(s) \"report\" not set", + }, } // Run test cases diff --git a/pkg/pkgmgr/apk_test.go b/pkg/pkgmgr/apk_test.go index d2157de1..97160d9e 100644 --- a/pkg/pkgmgr/apk_test.go +++ b/pkg/pkgmgr/apk_test.go @@ -103,32 +103,44 @@ func TestValidateAPKPackageVersions(t *testing.T) { // Define some test cases with inputs and expected outputs testCases := []struct { - name string - updates types.UpdatePackages - cmp VersionComparer - resultsPath string - expectedErr error + name string + updates types.UpdatePackages + cmp VersionComparer + resultsPath string + ignoreErrors bool + expectedErr error }{ { - name: "valid updates", - updates: []types.UpdatePackage{{Name: "apk-tools", Version: "2.12.7-r0"}, {Name: "busybox", Version: "1.33.1-r8"}}, - cmp: apkComparer, - resultsPath: "testdata/apk_valid.txt", - expectedErr: nil, + name: "valid updates", + updates: []types.UpdatePackage{{Name: "apk-tools", Version: "2.12.7-r0"}, {Name: "busybox", Version: "1.33.1-r8"}}, + cmp: apkComparer, + resultsPath: "testdata/apk_valid.txt", + ignoreErrors: false, + expectedErr: nil, }, { - name: "invalid version", - updates: []types.UpdatePackage{{Name: "apk-tools", Version: "1.0"}, {Name: "busybox", Version: "2.0"}}, - cmp: apkComparer, - resultsPath: "testdata/apk_invalid.txt", - expectedErr: fmt.Errorf("2 errors occurred:\n\t* invalid version x.y found for package apk-tools\n\t* invalid version a.b.c found for package busybox"), + name: "invalid version", + updates: []types.UpdatePackage{{Name: "apk-tools", Version: "1.0"}, {Name: "busybox", Version: "2.0"}}, + cmp: apkComparer, + resultsPath: "testdata/apk_invalid.txt", + ignoreErrors: false, + expectedErr: fmt.Errorf("2 errors occurred:\n\t* invalid version x.y found for package apk-tools\n\t* invalid version a.b.c found for package busybox"), }, { - name: "expected 2 updates, installed 1", - updates: []types.UpdatePackage{{Name: "apk-tools", Version: "2.12.7-r0"}}, - cmp: apkComparer, - resultsPath: "testdata/apk_valid.txt", - expectedErr: fmt.Errorf("expected 2 updates, installed 1"), + name: "invalid version with ignore errors", + updates: []types.UpdatePackage{{Name: "apk-tools", Version: "1.0"}, {Name: "busybox", Version: "2.0"}}, + cmp: apkComparer, + resultsPath: "testdata/apk_valid.txt", + ignoreErrors: true, + expectedErr: nil, + }, + { + name: "expected 2 updates, installed 1", + updates: []types.UpdatePackage{{Name: "apk-tools", Version: "2.12.7-r0"}}, + cmp: apkComparer, + resultsPath: "testdata/apk_valid.txt", + ignoreErrors: false, + expectedErr: fmt.Errorf("expected 2 updates, installed 1"), }, } @@ -136,7 +148,7 @@ func TestValidateAPKPackageVersions(t *testing.T) { // Use t.Run to run each test case as a subtest t.Run(tc.name, func(t *testing.T) { // Run the function to be tested - err := validateAPKPackageVersions(tc.updates, tc.cmp, tc.resultsPath, false) + err := validateAPKPackageVersions(tc.updates, tc.cmp, tc.resultsPath, tc.ignoreErrors) if tc.expectedErr != nil { if err == nil || errors.Is(err, tc.expectedErr) { t.Errorf("expected error %v, got %v", tc.expectedErr, err) diff --git a/pkg/pkgmgr/rpm_test.go b/pkg/pkgmgr/rpm_test.go index c2f23265..9d816d5e 100644 --- a/pkg/pkgmgr/rpm_test.go +++ b/pkg/pkgmgr/rpm_test.go @@ -260,6 +260,7 @@ func TestValidateRPMPackageVersions(t *testing.T) { updates types.UpdatePackages cmp VersionComparer resultsPath string + ignoreErrors bool expectedError error }{ { @@ -268,8 +269,9 @@ func TestValidateRPMPackageVersions(t *testing.T) { {Name: "openssl", Version: "1.1.1k-21.cm2"}, {Name: "openssl-libs", Version: "1.1.1k-21.cm2"}, }, - cmp: rpmComparer, - resultsPath: "testdata/rpm_valid.txt", + cmp: rpmComparer, + resultsPath: "testdata/rpm_valid.txt", + ignoreErrors: false, }, { name: "downloaded package version lower than required", @@ -279,8 +281,20 @@ func TestValidateRPMPackageVersions(t *testing.T) { }, cmp: rpmComparer, resultsPath: "testdata/rpm_valid.txt", + ignoreErrors: false, expectedError: fmt.Errorf("2 errors occurred:\n\t* downloaded package openssl version 2.1.1k-21.cm2 lower than required 3.1.1k-21.cm2 for update\n\t* downloaded package openssl-libs version 2.1.1k-21.cm2 lower than required 3.1.1k-21.cm2 for update"), // nolint:lll }, + { + name: "downloaded package version lower than required with ignore errors", + updates: types.UpdatePackages{ + {Name: "openssl", Version: "3.1.1k-21.cm2"}, + {Name: "openssl-libs", Version: "3.1.1k-21.cm2"}, + }, + cmp: rpmComparer, + resultsPath: "testdata/rpm_valid.txt", + ignoreErrors: true, + expectedError: nil, + }, { name: "unexpected number of installed packages", updates: types.UpdatePackages{ @@ -294,7 +308,7 @@ func TestValidateRPMPackageVersions(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - err := validateRPMPackageVersions(tc.updates, tc.cmp, tc.resultsPath, false) + err := validateRPMPackageVersions(tc.updates, tc.cmp, tc.resultsPath, tc.ignoreErrors) if tc.expectedError != nil { if err == nil || errors.Is(err, tc.expectedError) { t.Errorf("expected error %v, got %v", tc.expectedError, err) From c5a26936a94099c2adb2d31c99ed181836d9ff94 Mon Sep 17 00:00:00 2001 From: Anubhav Gupta Date: Tue, 22 Aug 2023 02:11:32 +0530 Subject: [PATCH 3/4] add docker.io/grafana/grafana-image-renderer to tests Signed-off-by: Anubhav Gupta --- integration/fixtures/test-images.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/integration/fixtures/test-images.json b/integration/fixtures/test-images.json index 419f1d74..30ae7d19 100644 --- a/integration/fixtures/test-images.json +++ b/integration/fixtures/test-images.json @@ -96,11 +96,11 @@ "ignoreErrors": false }, { - "image": "mcr.microsoft.com/oss/nginx/nginx", - "tag" : "1.21.6", - "digest": "sha256:bcdc041d100a3cc9fec472a8133ad566166e366241587ff92e8cc8fcf955229d", - "distro": "Debian", - "description": "Valid dpkg/status, apt present", + "image": "docker.io/grafana/grafana-image-renderer", + "tag" : "3.4.0", + "digest": "sha256:205a39f5b58f96b9ff81a0b523a60c26c86e88e76575696fcd6debde9de02197", + "distro": "Alpine", + "description": "Valid apk/db, apk present", "ignoreErrors": true } ] From 35827eb2d4cf2cfcb3d7cffeac57b4b8c190083c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serta=C3=A7=20=C3=96zercan?= <852750+sozercan@users.noreply.github.com> Date: Tue, 22 Aug 2023 10:17:48 -0700 Subject: [PATCH 4/4] Update integration/fixtures/test-images.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sertaç Özercan <852750+sozercan@users.noreply.github.com> --- integration/fixtures/test-images.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/fixtures/test-images.json b/integration/fixtures/test-images.json index 30ae7d19..50b61489 100644 --- a/integration/fixtures/test-images.json +++ b/integration/fixtures/test-images.json @@ -100,7 +100,7 @@ "tag" : "3.4.0", "digest": "sha256:205a39f5b58f96b9ff81a0b523a60c26c86e88e76575696fcd6debde9de02197", "distro": "Alpine", - "description": "Valid apk/db, apk present", + "description": "Valid apk/db, apk present, fail to patch libssl/libcryto", "ignoreErrors": true } ]