diff --git a/pkg/regsync/cosign.go b/pkg/regsync/cosign.go new file mode 100644 index 00000000..72e8c2ba --- /dev/null +++ b/pkg/regsync/cosign.go @@ -0,0 +1,312 @@ +package regsync + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "os/exec" + "strings" + + "github.com/rancher/charts-build-scripts/pkg/path" + slsactl "github.com/rancherlabs/slsactl/pkg/verify" + + "github.com/sourcegraph/go-diff/diff" +) + +type insertedDiffs struct { + sourceImage string + insertions []insertion +} + +type insertion struct { + tag string + line int32 +} + +// checkNewImageTags will get the git diff for regsync.yaml and parse the new available image tags. +func checkNewImageTags() (map[string][]string, error) { + rawDiff, err := getRawDiff(path.RegsyncYamlFile) + if err != nil { + return nil, err + } + + insertedDiffsToParse, err := parseGitDiff(rawDiff) + if err != nil { + return nil, err + } + + parsedRegsync, err := mapFileLineByValue(path.RegsyncYamlFile) + if err != nil { + return nil, err + } + + return parseSourceImage(insertedDiffsToParse, parsedRegsync) +} + +// getRawDiff returns the raw git diff output for a specific file +func getRawDiff(filePath string) (string, error) { + // Run the git diff command for the specific file + cmd := exec.Command("git", "diff", filePath) + + var stdout bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + // Execute the command + err := cmd.Run() + if err != nil { + return "", fmt.Errorf("failed to execute git diff: %v, %s", err, stderr.String()) + } + + return stdout.String(), nil +} + +func parseGitDiff(rawDiff string) ([]insertedDiffs, error) { + // Parse the raw diff into structured format + file, err := diff.ParseFileDiff([]byte(rawDiff)) + if err != nil { + return nil, err + } + + idfs := make([]insertedDiffs, len(file.Hunks)) + + // Iterate through file diffs + for i, hunk := range file.Hunks { + currentLine := hunk.NewStartLine // Starting line in the new file + idfs[i] = insertedDiffs{ + insertions: make([]insertion, 0), + } + + // Split the hunk body into lines + lines := bytes.Split(hunk.Body, []byte("\n")) + for _, line := range lines { + if len(line) > 0 && line[0] == '+' { + if bytes.Contains(line, []byte("+++")) || + bytes.Contains(line, []byte("source")) || + bytes.Contains(line, []byte("target")) || + bytes.Contains(line, []byte("type")) || + bytes.Contains(line, []byte("tags")) || + bytes.Contains(line, []byte("allow")) || + bytes.Contains(line, []byte("sync")) || + bytes.Contains(line, []byte("default")) || + bytes.Contains(line, []byte("media")) { + currentLine++ + continue + } + // Output the added line and its calculated line number + idfs[i].insertions = append(idfs[i].insertions, insertion{ + tag: parseTag(string(line[1:])), + line: currentLine, + }) + currentLine++ + continue + } + // Skip any line that was not inserted + currentLine++ + } + } + return idfs, nil +} + +// parseTag will parse the tag from the raw line value. +func parseTag(tag string) string { + parsedTag := strings.TrimSpace(tag) + parsedTag = strings.TrimPrefix(parsedTag, "- ") + return parsedTag +} + +// mapFileLineByValue reads a file and maps line numbers to their content. +func mapFileLineByValue(filePath string) (map[int]string, error) { + // Open the file + file, err := os.Open(filePath) + if err != nil { + return nil, fmt.Errorf("failed to open file: %v", err) + } + defer file.Close() + + // Map to store line numbers and their content + lineToContent := make(map[int]string) + + // Read the file line by line + scanner := bufio.NewScanner(file) + lineNumber := 1 + for scanner.Scan() { + lineToContent[lineNumber] = scanner.Text() + lineNumber++ + } + + // Check for errors during scanning + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("failed to read file: %v", err) + } + + return lineToContent, nil +} + +// parseSourceImage will parse the source image and its tags from the regsync.yaml. +func parseSourceImage(insertedDiffs []insertedDiffs, parsedRegsync map[int]string) (map[string][]string, error) { + + var currentSourceImage string + var imageTags map[string][]string = make(map[string][]string) + + for _, idf := range insertedDiffs { + for i, insertion := range idf.insertions { + // iterate only the very first time to get the source image + if i == 0 { + sourceImage, err := findSourceImage(parsedRegsync, int(insertion.line)) + if err != nil { + return imageTags, err + } + parsedSource := strings.TrimPrefix(sourceImage, "- source: docker.io/") + currentSourceImage = parsedSource + imageTags[parsedSource] = []string{} + } + imageTags[currentSourceImage] = append(imageTags[currentSourceImage], insertion.tag) + } + } + + return imageTags, nil +} + +// findSourceImage will find the source image from the regsync.yaml above the line number passed. +func findSourceImage(parsedRegsync map[int]string, maxLine int) (string, error) { + for i := maxLine; i > 0; i-- { + lineTxt := parsedRegsync[i] + if strings.Contains(lineTxt, "source") { + return parsedRegsync[i], nil + } + } + return "", fmt.Errorf("source image not found around line %d", maxLine) +} + +// checkCosignedImages checks if the images are cosigned or not. +func checkCosignedImages(imageTags map[string][]string) (map[string][]string, error) { + // prepare the signed-images.txt file for later manual inspection + if err := prepareSignedImagesFile(); err != nil { + return nil, err + } + + baseImageRepository := "registry.suse.com/" + cosignedImages := make(map[string][]string) + + for image, tags := range imageTags { + for _, tag := range tags { + imageTag := baseImageRepository + image + ":" + tag + + // Close the stdout and stderr so there are no leaks + oldStdout, oldStderr, r, w := closeStdOut() + // Verify the image, if it's cosigned + if err := slsactl.Verify(imageTag); err != nil { + reopenStdOut(oldStdout, oldStderr, r, w) + continue + } + reopenStdOut(oldStdout, oldStderr, r, w) + + // add it to the cosignedImages map + cosignedImages[image] = append(cosignedImages[image], tag) + + // log and write the cosigned images to a file + fmt.Printf("cosigned - %s\n", imageTag) + if err := writeCosignedImages(imageTag); err != nil { + return nil, err + } + } + } + + return cosignedImages, nil +} + +// prepareSignedImagesFile checks if signed-images.txt exists. +// If it exists, it erases all its content. If it does not exist, it creates it. +func prepareSignedImagesFile() error { + // Check if the file exists + _, err := os.Stat(path.SignedImagesFile) + if err != nil { + if os.IsNotExist(err) { + // Create the file if it does not exist + file, err := os.Create(path.SignedImagesFile) + if err != nil { + return err + } + + return file.Close() + } + // some other error occurred while checking the file + return err + } + + return os.Truncate(path.SignedImagesFile, 0) +} + +// writeCosignedImages writes the imageTag to a text file called signed-images.txt. +func writeCosignedImages(imageTag string) error { + // Open the file in append mode, create it if it doesn't exist + file, err := os.OpenFile(path.SignedImagesFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer file.Close() + + // Write the imageTag to the file + if _, err := file.WriteString(imageTag + "\n"); err != nil { + return err + } + + return nil +} + +// closeStdOut closes the stdout and stderr and returns the old ones. +func closeStdOut() (*os.File, *os.File, *os.File, *os.File) { + // Save the current stdout and stderr + oldStdout := os.Stdout + oldStderr := os.Stderr + // Create a pipe to discard the output + r, w, _ := os.Pipe() + os.Stdout = w + os.Stderr = w + return oldStdout, oldStderr, r, w +} + +// reopenStdOut reopens the stdout and stderr and reads the discarded output. +func reopenStdOut(oldStdout, oldStderr, r, w *os.File) { + w.Close() + os.Stdout = oldStdout + os.Stderr = oldStderr + io.ReadAll(r) // Read the discarded output to prevent pipe errors +} + +// removeCosignedImages removes the cosigned images from the imageTagMap. +func removeCosignedImages(imageTagMap, cosignedImages map[string][]string) { + for cosignedImage, cosignedTags := range cosignedImages { + if _, exist := imageTagMap[cosignedImage]; exist { + currentTags := imageTagMap[cosignedImage] + notCosignedTags := excludeCosignedTags(currentTags, cosignedTags) + imageTagMap[cosignedImage] = notCosignedTags + } + } +} + +// excludeCosignedTags removes the cosigned tags from the tags slice. +func excludeCosignedTags(tags, cosignedTags []string) []string { + var isCosigned bool = false + var notCosignedTags []string + for _, tag := range tags { + for _, cTag := range cosignedTags { + if tag == cTag { + isCosigned = true + break + } + } + + if !isCosigned { + notCosignedTags = append(notCosignedTags, tag) + } else { + isCosigned = false + continue + } + } + return notCosignedTags +} diff --git a/pkg/regsync/cosign_test.go b/pkg/regsync/cosign_test.go new file mode 100644 index 00000000..037daf41 --- /dev/null +++ b/pkg/regsync/cosign_test.go @@ -0,0 +1,536 @@ +package regsync + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + diffExample1 = `diff --git a/regsync.yaml b/regsync.yaml +index c584669a3..ab15c1bc5 100644 +--- a/regsync.yaml ++++ b/regsync.yaml +@@ -76,6 +76,26 @@ sync: + - v5.0.1 + - v5.0.2 + - v6.0.0 ++- source: docker.io/rancher/cis-operator ++ target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/cis-operator' ++ type: repository ++ tags: ++ allow: ++ - v1.1.0 ++ - v1.2.0 ++ - v1.3.0 + - source: docker.io/rancher/eks-operator + target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/eks-operator' + type: repository +` + + diffExample2 = `diff --git a/regsync.yaml b/regsync.yaml +index c584669a3..ee5f53416 100644 +--- a/regsync.yaml ++++ b/regsync.yaml +@@ -76,6 +76,28 @@ sync: + - v5.0.1 + - v5.0.2 + - v6.0.0 ++- source: docker.io/rancher/cis-operator ++ target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/cis-operator' ++ type: repository ++ tags: ++ allow: ++ - v1.1.0 ++ - v1.2.0 ++ - v1.3.0 ++ - v1.3.1 ++ - v1.3.3 + - source: docker.io/rancher/eks-operator + target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/eks-operator' + type: repository +@@ -1426,6 +1448,7 @@ sync: + - v0.4.0 + - v0.5.0 + - v0.5.1 ++ - v0.5.2 + - source: docker.io/rancher/shell + target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/shell' + type: repository +` + + badDiffExample = `@@ -76,6 +76,28 @@ sync: + - v5.0.1 + - v5.0.2 + - v6.0.0 ++- source: docker.io/rancher/cis-operator ++ target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/cis-operator' ++ type: repository ++ tags: ++ allow: ++ - v1.1.0 ++ - v1.2.0 ++ - v1.3.0 ++ - v1.3.1 ++ - v1.3.3 + - source: docker.io/rancher/eks-operator + target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/eks-operator' + type: repository +@@ -1426,6 +1448,7 @@ sync: + - v0.4.0 + - v0.5.0 + - v0.5.1 ++ - v0.5.2 + - source: docker.io/rancher/shell + target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/shell' + type: repository +` +) + +func assertError(t *testing.T, err, expectedErr error) { + if expectedErr != nil { + assert.EqualError(t, err, expectedErr.Error()) + } else { + assert.NoError(t, err) + } +} + +func Test_parseGitDiff(t *testing.T) { + type input struct { + diff string + } + type expected struct { + result []insertedDiffs + err error + } + type test struct { + name string + input input + expected expected + } + + tests := []test{ + { + name: "#1", + input: input{ + diff: diffExample1, + }, + expected: expected{ + result: []insertedDiffs{ + insertedDiffs{ + insertions: []insertion{ + insertion{ + tag: "v1.1.0", + line: 84, + }, + insertion{ + tag: "v1.2.0", + line: 85, + }, + insertion{ + tag: "v1.3.0", + line: 86, + }, + }, + }, + }, + err: nil, + }, + }, + { + name: "#2", + input: input{ + diff: diffExample2, + }, + expected: expected{ + result: []insertedDiffs{ + insertedDiffs{ + insertions: []insertion{ + insertion{ + tag: "v1.1.0", + line: 84, + }, + insertion{ + tag: "v1.2.0", + line: 85, + }, + insertion{ + tag: "v1.3.0", + line: 86, + }, + insertion{ + tag: "v1.3.1", + line: 87, + }, + insertion{ + tag: "v1.3.3", + line: 88, + }, + }, + }, + insertedDiffs{ + insertions: []insertion{ + insertion{ + tag: "v0.5.2", + line: 1451, + }, + }, + }, + }, + err: nil, + }, + }, + { + name: "#3", + input: input{ + diff: badDiffExample, + }, + expected: expected{ + result: nil, + err: errors.New("line 25, char 581: expected file header while reading extended headers, got EOF"), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + insertionsToParse, err := parseGitDiff(tt.input.diff) + require.Equal(t, tt.expected.result, insertionsToParse) + assertError(t, err, tt.expected.err) + }) + } + +} + +func Test_parseTag(t *testing.T) { + type input struct { + line string + } + type expected struct { + result string + } + type test struct { + name string + input input + expected expected + } + + tests := []test{ + { + name: "#1", + input: input{ + line: "- v1.0.0", + }, + expected: expected{ + result: "v1.0.0", + }, + }, + { + name: "#2", + input: input{ + line: "- v1.0.1", + }, + expected: expected{ + result: "v1.0.1", + }, + }, + { + name: "#3", + input: input{ + line: " - v1.1.0", + }, + expected: expected{ + result: "v1.1.0", + }, + }, + { + name: "#4", + input: input{ + line: " - 1.1.0", + }, + expected: expected{ + result: "1.1.0", + }, + }, + { + name: "#5", + input: input{ + line: " - 1.1.0-rc1.0.0", + }, + expected: expected{ + result: "1.1.0-rc1.0.0", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := parseTag(tt.input.line) + require.Equal(t, tt.expected.result, result) + }) + } +} + +func Test_parseSourceImage(t *testing.T) { + type input struct { + insertedDiffs []insertedDiffs + parsedRegsync map[int]string + } + type expected struct { + result map[string][]string + err error + } + type test struct { + name string + input input + expected expected + } + + tests := []test{ + { + name: "#1", + input: input{ + insertedDiffs: []insertedDiffs{ + { + insertions: []insertion{ + { + tag: "1.0.0", + line: 100, + }, + { + tag: "1.0.1", + line: 101, + }, + { + tag: "1.1.0", + line: 102, + }, + }, + }, + }, + parsedRegsync: map[int]string{ + 95: "- source: docker.io/rancher/cis-operator", + 96: " target: '{{ env REGISTRY_ENDPOINT }}/rancher/cis-operator'", + 97: " type: repository", + 98: " tags:", + 99: " allow:", + 100: " - 1.0.0", + 101: " - 1.0.1", + 102: " - 1.1.0", + }, + }, + expected: expected{ + result: map[string][]string{ + "rancher/cis-operator": []string{"1.0.0", "1.0.1", "1.1.0"}, + }, + err: nil, + }, + }, + { + name: "#2", + input: input{ + insertedDiffs: []insertedDiffs{ + { + insertions: []insertion{ + { + tag: "1.0.0", + line: 100, + }, + { + tag: "1.0.1", + line: 101, + }, + { + tag: "1.1.0", + line: 102, + }, + }, + }, + { + insertions: []insertion{ + { + tag: "0.0.1", + line: 100000000, + }, + }, + }, + }, + parsedRegsync: map[int]string{ + 95: "- source: docker.io/rancher/cis-operator", + 96: " target: '{{ env REGISTRY_ENDPOINT }}/rancher/cis-operator'", + 97: " type: repository", + 98: " tags:", + 99: " allow:", + 100: " - 1.0.0", + 101: " - 1.0.1", + 102: " - 1.1.0", + + 99999995: "- source: docker.io/rancher/security-scan", + 99999996: " target: '{{ env REGISTRY_ENDPOINT }}/rancher/sec-scan'", + 99999997: " type: repository", + 99999998: " tags:", + 99999999: " allow:", + 100000000: " - 0.0.1", + }, + }, + expected: expected{ + result: map[string][]string{ + "rancher/cis-operator": []string{"1.0.0", "1.0.1", "1.1.0"}, + "rancher/security-scan": []string{"0.0.1"}, + }, + err: nil, + }, + }, + { + name: "#3", + input: input{ + insertedDiffs: []insertedDiffs{ + { + insertions: []insertion{ + { + tag: "1.0.0", + line: 4, + }, + }, + }, + }, + parsedRegsync: map[int]string{ + 1: " type: repository", + 2: " tags:", + 3: " allow:", + 4: " - 1.0.0", + }, + }, + expected: expected{ + result: map[string][]string{}, + err: fmt.Errorf("source image not found around line %d", 4), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := parseSourceImage(tt.input.insertedDiffs, tt.input.parsedRegsync) + require.Equal(t, tt.expected.result, result) + assertError(t, err, tt.expected.err) + }) + } +} + +func Test_removeCosignedImages(t *testing.T) { + type input struct { + imageTagMap map[string][]string + cosignedImages map[string][]string + } + type expected struct { + result map[string][]string + } + type test struct { + name string + input input + expected expected + } + + tests := []test{ + { + name: "#1", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/security-scan": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + cosignedImages: map[string][]string{ + "rancher/cis-operator": {"v1.0.1", "v1.1.0"}, + "rancher/security-scan": {"v1.1.0"}, + }, + }, + expected: expected{ + result: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + "rancher/security-scan": {"v1.0.0", "v1.0.1"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + }, + }, + { + name: "#2", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/security-scan": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + cosignedImages: map[string][]string{ + "rancher/cis-operator": {"v1.1.0"}, + "rancher/security-scan": {"v1.1.0"}, + }, + }, + expected: expected{ + result: map[string][]string{ + "rancher/cis-operator": {"v1.0.0", "v1.0.1"}, + "rancher/security-scan": {"v1.0.0", "v1.0.1"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + }, + }, + { + name: "#3", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + "rancher/security-scan": {"v1.0.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + cosignedImages: map[string][]string{ + "rancher/security-scan": {}, + }, + }, + expected: expected{ + result: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + "rancher/security-scan": {"v1.0.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + }, + }, + { + name: "#4", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + "rancher/security-scan": {"v1.0.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + cosignedImages: map[string][]string{ + "rancher/security-scan": {"2.0.0"}, + }, + }, + expected: expected{ + result: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + "rancher/security-scan": {"v1.0.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + removeCosignedImages(tt.input.imageTagMap, tt.input.cosignedImages) + require.Equal(t, tt.expected.result, tt.input.imageTagMap) + }) + } +} diff --git a/pkg/regsync/generateconfig_test.go b/pkg/regsync/generateconfig_test.go deleted file mode 100644 index 49bf5c2c..00000000 --- a/pkg/regsync/generateconfig_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package regsync - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_SlsaYaml(t *testing.T) { - - type input struct { - imageTagMap map[string][]string - readSlsaYamlMock func() ([]string, error) - } - - type expected struct { - err error - imageTagMap map[string][]string - } - - type test struct { - name string - input input - expected expected - } - - tests := []test{ - { - name: "#1", - input: input{ - imageTagMap: map[string][]string{ - "image1": {"tag1"}, - "image2": {"tag2"}, - "image3": {"tag3"}, - }, - readSlsaYamlMock: func() ([]string, error) { - return []string{"image1"}, nil - }, - }, - expected: expected{ - err: nil, - imageTagMap: map[string][]string{ - "image2": {"tag2"}, - "image3": {"tag3"}, - }, - }, - }, - { - name: "#2", - input: input{ - imageTagMap: map[string][]string{ - "image1": {"tag1", "tag2"}, - "image2": {"tag2"}, - "image3": {"tag3", "tag4"}, - }, - readSlsaYamlMock: func() ([]string, error) { - return []string{"image1", "image2"}, nil - }, - }, - expected: expected{ - err: nil, - imageTagMap: map[string][]string{ - "image3": {"tag3", "tag4"}, - }, - }, - }, - { - name: "#3", - input: input{ - imageTagMap: map[string][]string{ - "image1": {"tag1", "tag2"}, - }, - readSlsaYamlMock: func() ([]string, error) { - return []string{"image1", "image2"}, nil - }, - }, - expected: expected{ - err: nil, - imageTagMap: map[string][]string{}, - }, - }, - { - name: "#4", - input: input{ - imageTagMap: map[string][]string{}, - readSlsaYamlMock: func() ([]string, error) { - return []string{"image1", "image2"}, nil - }, - }, - expected: expected{ - err: nil, - imageTagMap: map[string][]string{}, - }, - }, - { - name: "#5", - input: input{ - imageTagMap: map[string][]string{}, - readSlsaYamlMock: func() ([]string, error) { - return []string{}, nil - }, - }, - expected: expected{ - err: nil, - imageTagMap: map[string][]string{}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := removeSlsaImages(tt.input.imageTagMap, tt.input.readSlsaYamlMock) - require.NoError(t, err) - require.Equal(t, tt.expected.imageTagMap, tt.input.imageTagMap) - - }) - } -}