From 3941bccf1cb19db50627c27176be4f92971044ea Mon Sep 17 00:00:00 2001 From: Miaha Cybersec Date: Wed, 7 Aug 2024 09:55:52 -0600 Subject: [PATCH] Refactor TrivyOpts to separate BuildKit client options, add documentation Extracted BuildKit client configurations from TrivyOpts into a new BkClient struct. Updated related functions and tests to accommodate this change, improving modularity and readability. Signed-off-by: Miaha Cybersec --- pkg/patch/patch.go | 29 ++++++++++++++++++++++------- pkg/patch/patch_test.go | 21 +++++++++++++++------ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/pkg/patch/patch.go b/pkg/patch/patch.go index 6f3367e1..7b684380 100644 --- a/pkg/patch/patch.go +++ b/pkg/patch/patch.go @@ -43,8 +43,6 @@ const ( ) type TrivyOpts struct { - BkClient *client.Client - SolveOpt *client.SolveOpt Image string Ch chan error ReportFile string @@ -57,6 +55,11 @@ type TrivyOpts struct { Format string } +type BkClient struct { + BkClient *client.Client + SolveOpt *client.SolveOpt +} + type BuildStatus struct { BuildChannel chan *client.SolveStatus } @@ -97,7 +100,15 @@ func removeIfNotDebug(workingFolder string) { } } -// patchWithContext patches the user-supplied image, image. +// patchWithContext patches the user-supplied image, image +// reportFile is a vulnerability scan passed in by the user +// userSuppliedPatchTag is a tag set by the user to use for the patched image tag +// workingFolder is the folder used by copa, defaults to system temp folder +// scanner used to generate reportFile, defaults to Trivy +// format is the output format, defaults to openvex +// output is the desired output filepath +// ignoreError defines whether Copa should ignore errors +// bkOpts contains buildkitd options for addresses, CA certs, client certs, and client keys. func patchWithContext(ctx context.Context, ch chan error, image, reportFile, userSuppliedPatchTag, workingFolder, scanner, format, output string, ignoreError bool, bkOpts buildkit.Opts) error { dockerNormalizedImageName, err := reference.ParseNormalizedNamed(image) if err != nil { @@ -105,7 +116,7 @@ func patchWithContext(ctx context.Context, ch chan error, image, reportFile, use } if reference.IsNameOnly(dockerNormalizedImageName) { - log.Warnf("Image name has no tag or digest, using latest as tag") + log.Warnf("Image name %s has no tag or digest, defaulting to %s:latest", image, dockerNormalizedImageName) dockerNormalizedImageName = reference.TagNameOnly(dockerNormalizedImageName) } @@ -183,10 +194,13 @@ func patchWithContext(ctx context.Context, ch chan error, image, reportFile, use err = buildkitBuild( BuildContext{ctx}, &TrivyOpts{ - bkClient, &solveOpt, image, ch, + image, ch, reportFile, workingFolder, updates, ignoreError, output, dockerNormalizedImageName, patchedImageName, format, }, + BkClient{ + bkClient, &solveOpt, + }, BuildStatus{buildChannel}) return err }) @@ -217,8 +231,9 @@ func patchWithContext(ctx context.Context, ch chan error, image, reportFile, use return eg.Wait() } -func buildkitBuild(buildContext BuildContext, trivyOpts *TrivyOpts, buildStatus BuildStatus) error { - _, err := trivyOpts.BkClient.Build(buildContext.Ctx, *trivyOpts.SolveOpt, copaProduct, func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) { +// buildkitBuild submits a build request to BuildKit with the given information. +func buildkitBuild(buildContext BuildContext, trivyOpts *TrivyOpts, bkClient BkClient, buildStatus BuildStatus) error { + _, err := bkClient.BkClient.Build(buildContext.Ctx, *bkClient.SolveOpt, copaProduct, func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) { bkConfig, err := buildkit.InitializeBuildkitConfig(ctx, c, trivyOpts.DockerNormalizedImageName.String()) if err != nil { return handleError(trivyOpts.Ch, err) diff --git a/pkg/patch/patch_test.go b/pkg/patch/patch_test.go index 257dcb3a..e75afbca 100644 --- a/pkg/patch/patch_test.go +++ b/pkg/patch/patch_test.go @@ -3,6 +3,7 @@ package patch import ( "context" "errors" + "fmt" "io" "os" "os/exec" @@ -299,6 +300,8 @@ func TestGetOSVersion(t *testing.T) { } } +// Test generating a patched tag for an image +// If userSuppliedPatchTag is a blank string, the function defaults to defaultPatchedTagSuffix. func TestGeneratePatchedTag(t *testing.T) { testCases := []struct { name string @@ -312,17 +315,23 @@ func TestGeneratePatchedTag(t *testing.T) { userSuppliedPatchTag: "", expectedPatchedTag: defaultPatchedTagSuffix, }, + { + name: "NoTag_UserSupplied", + dockerImageName: "docker.io/library/alpine", + userSuppliedPatchTag: "1234", + expectedPatchedTag: "1234", + }, { name: "WithTag_NoUserSupplied", dockerImageName: "docker.io/redhat/ubi9:latest", userSuppliedPatchTag: "", - expectedPatchedTag: "latest-patched", + expectedPatchedTag: fmt.Sprintf("latest-%s", defaultPatchedTagSuffix), }, { name: "WithTag_UserSupplied", dockerImageName: "docker.io/librari/ubuntu:jammy-20231004", - userSuppliedPatchTag: "20231004-patched", - expectedPatchedTag: "20231004-patched", + userSuppliedPatchTag: "20231004-custom-tag", + expectedPatchedTag: "20231004-custom-tag", }, } @@ -337,9 +346,8 @@ func TestGeneratePatchedTag(t *testing.T) { } } +// This test simulates the vulnerable packages Trivy supplies to Copa. func TestUpdateManifest(t *testing.T) { - errPkgs := []string{"package1", "package2", "package3"} - updates := &unversioned.UpdateManifest{ Metadata: unversioned.Metadata{ OS: unversioned.OS{ @@ -357,6 +365,7 @@ func TestUpdateManifest(t *testing.T) { }, } + // errPkgs in this struct is used to define which packages would throw an error during the update process testCases := []struct { name string updates *unversioned.UpdateManifest @@ -387,7 +396,7 @@ func TestUpdateManifest(t *testing.T) { { name: "AllErrorPackages", updates: updates, - errPkgs: errPkgs, + errPkgs: []string{"package1", "package2", "package3"}, expected: &unversioned.UpdateManifest{ Metadata: unversioned.Metadata{ OS: unversioned.OS{