From ccc3bdaf3c05075677f4968b4445562a6e6d07bc Mon Sep 17 00:00:00 2001 From: greensd4 <33864348+greensd4@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:58:20 +0200 Subject: [PATCH] New output version (#8) * downgrade go version * change output format to align with new engine format * change prop name --- cmd/main.go | 4 +- go.mod | 2 +- internal/extractors/dockerComposeExtractor.go | 10 +++- internal/extractors/dockerfileExtractor.go | 10 +++- internal/extractors/helmExtractor.go | 10 +++- internal/files/imagesUtils.go | 21 ++++--- internal/syftUtils/structs.go | 26 +++++---- internal/syftUtils/syftExtractor.go | 11 +--- internal/syftUtils/utils.go | 58 ++++++++++--------- internal/types/types.go | 18 +++++- pkg/containerResolver/containerScanner.go | 10 +++- 11 files changed, 109 insertions(+), 71 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 34c84c04..c97a89e8 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -5,8 +5,8 @@ import ( "log" ) -const defaultImage = "debian:12" -const defaultImage2 = "nginx:latest" +const defaultImage = "rabbitmq:3" +const defaultImage2 = "golang:1.21.5-alpine3.18" func main() { diff --git a/go.mod b/go.mod index 03192cb7..3b67714f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/CheckmarxDev/containers-resolver -go 1.22.0 +go 1.21.8 require ( github.com/anchore/stereoscope v0.0.2-0.20240208195325-681f6715b0e3 diff --git a/internal/extractors/dockerComposeExtractor.go b/internal/extractors/dockerComposeExtractor.go index 882d321b..80e66e2b 100644 --- a/internal/extractors/dockerComposeExtractor.go +++ b/internal/extractors/dockerComposeExtractor.go @@ -54,9 +54,13 @@ func extractImagesFromDockerComposeFile(l *logger.Logger, filePath types.FilePat fullImageName := fmt.Sprintf("%s:%s", imageName, tag) imageNames = append(imageNames, types.ImageModel{ - Name: fullImageName, - Origin: types.DockerComposeFileOrigin, - Path: filePath.RelativePath, + Name: fullImageName, + ImageLocations: []types.ImageLocation{ + { + Origin: types.DockerComposeFileOrigin, + Path: filePath.RelativePath, + }, + }, }) } } diff --git a/internal/extractors/dockerfileExtractor.go b/internal/extractors/dockerfileExtractor.go index 7300e898..c09d53a8 100644 --- a/internal/extractors/dockerfileExtractor.go +++ b/internal/extractors/dockerfileExtractor.go @@ -55,9 +55,13 @@ func extractImagesFromDockerfile(l *logger.Logger, filePath types.FilePath) ([]t fullImageName := fmt.Sprintf("%s:%s", imageName, tag) imageNames = append(imageNames, types.ImageModel{ - Name: fullImageName, - Origin: types.DockerFileOrigin, - Path: filePath.RelativePath, + Name: fullImageName, + ImageLocations: []types.ImageLocation{ + { + Origin: types.DockerFileOrigin, + Path: filePath.RelativePath, + }, + }, }) } } diff --git a/internal/extractors/helmExtractor.go b/internal/extractors/helmExtractor.go index e8ccea73..d64c39ed 100644 --- a/internal/extractors/helmExtractor.go +++ b/internal/extractors/helmExtractor.go @@ -94,9 +94,13 @@ func extractImageInfo(yamlString string) ([]types.ImageModel, error) { n, _ := extractImageName(microservice) v := types.ImageModel{ - Name: n, - Origin: types.HelmFileOrigin, - Path: s, + Name: n, + ImageLocations: []types.ImageLocation{ + { + Origin: types.HelmFileOrigin, + Path: s, + }, + }, } imageInfoList = append(imageInfoList, v) diff --git a/internal/files/imagesUtils.go b/internal/files/imagesUtils.go index e3151324..ba68cdef 100644 --- a/internal/files/imagesUtils.go +++ b/internal/files/imagesUtils.go @@ -14,18 +14,21 @@ func mergeImages(images, imagesFromDockerFiles, imagesFromDockerComposeFiles, he if len(helmImages) > 0 { images = append(images, helmImages...) } - return removeDuplicates(images) + return mergeDuplicates(images) } -func removeDuplicates(slice []types.ImageModel) []types.ImageModel { - seen := make(map[types.ImageModel]bool) - var result []types.ImageModel +func mergeDuplicates(imageModels []types.ImageModel) []types.ImageModel { + aggregated := make(map[string][]types.ImageLocation) - for _, val := range slice { - if _, ok := seen[val]; !ok { - seen[val] = true - result = append(result, val) - } + for _, img := range imageModels { + aggregated[img.Name] = append(aggregated[img.Name], img.ImageLocations...) } + + // Create the final result by constructing ImageModel objects with aggregated ImageLocations + var result []types.ImageModel + for name, locations := range aggregated { + result = append(result, types.ImageModel{Name: name, ImageLocations: locations}) + } + return result } diff --git a/internal/syftUtils/structs.go b/internal/syftUtils/structs.go index 988cd9f4..e716acee 100644 --- a/internal/syftUtils/structs.go +++ b/internal/syftUtils/structs.go @@ -15,25 +15,27 @@ type RootfsConfig struct { } type ContainerResolution struct { - ContainerImages []ContainerImage + ContainerImage ContainerImage ContainerPackages []ContainerPackage } type ContainerImage struct { - ImageName string - ImageTag string - ImagePath string - Distribution string - ImageHash string - ImageId string - ImageOrigin string - Layers []string - History []Layer + ImageName string + ImageTag string + Distribution string + ImageHash string + ImageId string + ImageLocations []ImageLocation + Layers []string + History []Layer +} + +type ImageLocation struct { + Origin string + Path string } type ContainerPackage struct { - ImageId string - ImageHash string Name string Version string Distribution string diff --git a/internal/syftUtils/syftExtractor.go b/internal/syftUtils/syftExtractor.go index 18243e5d..8933fbac 100644 --- a/internal/syftUtils/syftExtractor.go +++ b/internal/syftUtils/syftExtractor.go @@ -9,12 +9,9 @@ type SyftExtractor struct { *logger.Logger } -func (se *SyftExtractor) AnalyzeImages(images []types.ImageModel) (*ContainerResolution, error) { +func (se *SyftExtractor) AnalyzeImages(images []types.ImageModel) ([]*ContainerResolution, error) { - containerResolution := &ContainerResolution{ - ContainerImages: []ContainerImage{}, - ContainerPackages: []ContainerPackage{}, - } + var containerResolution []*ContainerResolution for _, imageModel := range images { se.Debug("going to analyze image using syft. image: %s", imageModel.Name) @@ -24,9 +21,7 @@ func (se *SyftExtractor) AnalyzeImages(images []types.ImageModel) (*ContainerRes se.Error("Could not analyze image: %s err: %+v", imageModel.Name, err) continue } - - containerResolution.ContainerImages = append(containerResolution.ContainerImages, tmpResolution.ContainerImages...) - containerResolution.ContainerPackages = append(containerResolution.ContainerPackages, tmpResolution.ContainerPackages...) + containerResolution = append(containerResolution, tmpResolution) se.Info("successfully analyzed image: %s", imageModel.Name) } return containerResolution, nil diff --git a/internal/syftUtils/utils.go b/internal/syftUtils/utils.go index 6a6c6e00..773ebbc5 100644 --- a/internal/syftUtils/utils.go +++ b/internal/syftUtils/utils.go @@ -20,14 +20,14 @@ import ( func analyzeImage(l *logger.Logger, imageModel types.ImageModel) (*ContainerResolution, error) { - l.Debug("image is %s, origin: %s, file path: %s", imageModel.Name, imageModel.Origin, imageModel.Path) + l.Debug("image is %s, found in file paths: %s", imageModel.Name, imageModel.GetImageLocationsPathsString()) imageSource, s, err := analyzeImageUsingSyft(l, imageModel.Name) if err != nil { return nil, err } - result := transformSBOMToContainerResolution(l, *s, imageSource, imageModel.Name, imageModel.Path, imageModel.Origin) + result := transformSBOMToContainerResolution(l, *s, imageSource, imageModel) return &result, nil } @@ -68,12 +68,12 @@ func getSBOM(src source.Source) (sbom.SBOM, error) { return *s, nil } -func transformSBOMToContainerResolution(l *logger.Logger, s sbom.SBOM, imageSource *source.StereoscopeImageSource, imageId, imagePath, imageOrigin string) ContainerResolution { +func transformSBOMToContainerResolution(l *logger.Logger, s sbom.SBOM, imageSource *source.StereoscopeImageSource, imageModel types.ImageModel) ContainerResolution { - imageNameAndTag := strings.Split(imageId, ":") + imageNameAndTag := strings.Split(imageModel.Name, ":") - result := ContainerResolution{ - ContainerImages: []ContainerImage{}, + imageResult := ContainerResolution{ + ContainerImage: ContainerImage{}, ContainerPackages: []ContainerPackage{}, } var sourceMetadata source.StereoscopeImageSourceMetadata @@ -81,38 +81,35 @@ func transformSBOMToContainerResolution(l *logger.Logger, s sbom.SBOM, imageSour if sourceMetadata, ok = s.Source.Metadata.(source.StereoscopeImageSourceMetadata); !ok { l.Warn("Value is not StereoscopeImageSourceMetadata - can not analyze") - return result + return imageResult } distro := getDistro(s.Artifacts.LinuxDistribution) - extractImage(distro, imageSource.ID(), imageId, imagePath, imageOrigin, sourceMetadata, imageNameAndTag, &result) - extractImagePackages(s.Artifacts.Packages, imageId, imageSource.ID(), distro, &result) + extractImage(distro, imageSource.ID(), imageModel, sourceMetadata, imageNameAndTag, &imageResult) + extractImagePackages(s.Artifacts.Packages, distro, &imageResult) - return result + return imageResult } -func extractImage(distro string, imageHash artifact.ID, imageId, imagePath, imageOrigin string, sourceMetadata source.StereoscopeImageSourceMetadata, imageNameAndTag []string, result *ContainerResolution) { +func extractImage(distro string, imageHash artifact.ID, imageModel types.ImageModel, sourceMetadata source.StereoscopeImageSourceMetadata, imageNameAndTag []string, result *ContainerResolution) { history := extractHistory(sourceMetadata) layerIds := extractLayerIds(history) - images := ContainerImage{ - ImageName: imageNameAndTag[0], - ImageTag: imageNameAndTag[1], - ImagePath: imagePath, - Distribution: distro, - ImageHash: string(imageHash), - ImageId: imageId, - ImageOrigin: imageOrigin, - Layers: layerIds, - History: history, + result.ContainerImage = ContainerImage{ + ImageName: imageNameAndTag[0], + ImageTag: imageNameAndTag[1], + Distribution: distro, + ImageHash: string(imageHash), + ImageId: imageModel.Name, + Layers: layerIds, + History: history, + ImageLocations: getImageLocations(imageModel.ImageLocations), } - - result.ContainerImages = append(result.ContainerImages, images) } -func extractImagePackages(packages *pkg.Collection, imageId string, imageHash artifact.ID, distro string, result *ContainerResolution) { +func extractImagePackages(packages *pkg.Collection, distro string, result *ContainerResolution) { var containerPackages []ContainerPackage @@ -121,8 +118,6 @@ func extractImagePackages(packages *pkg.Collection, imageId string, imageHash ar sourceName, sourceVersion := getPackageRelationships(containerPackage) containerPackages = append(containerPackages, ContainerPackage{ - ImageId: imageId, - ImageHash: string(imageHash), Name: containerPackage.Name, Version: containerPackage.Version, Distribution: distro, @@ -276,3 +271,14 @@ func trimPatchVersion(versionID string) string { } return versionID } + +func getImageLocations(imageLocations []types.ImageLocation) []ImageLocation { + var slice []ImageLocation + for _, location := range imageLocations { + slice = append(slice, ImageLocation{ + Origin: location.Origin, + Path: location.Path, + }) + } + return slice +} diff --git a/internal/types/types.go b/internal/types/types.go index 00c529c4..b27226cc 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -1,5 +1,9 @@ package types +import ( + "strings" +) + type FileImages struct { Dockerfile []FilePath DockerCompose []FilePath @@ -12,7 +16,19 @@ type FilePath struct { } type ImageModel struct { - Name string + Name string + ImageLocations []ImageLocation +} + +func (imgModel ImageModel) GetImageLocationsPathsString() string { + var paths []string + for _, location := range imgModel.ImageLocations { + paths = append(paths, location.Path) + } + return strings.Join(paths, ", ") +} + +type ImageLocation struct { Origin string Path string } diff --git a/pkg/containerResolver/containerScanner.go b/pkg/containerResolver/containerScanner.go index 5d65b488..bc62f327 100644 --- a/pkg/containerResolver/containerScanner.go +++ b/pkg/containerResolver/containerScanner.go @@ -86,9 +86,13 @@ func toImageModels(images []string) []types.ImageModel { for _, image := range images { imageNames = append(imageNames, types.ImageModel{ - Name: image, - Origin: types.UserInput, - Path: types.NoFilePath, + Name: image, + ImageLocations: []types.ImageLocation{ + { + Origin: types.UserInput, + Path: types.NoFilePath, + }, + }, }) }