Skip to content

Commit 80276d5

Browse files
Merge branch 'main' into bug/AST-106740
2 parents adeb1dd + 07d2d00 commit 80276d5

File tree

8 files changed

+90
-37
lines changed

8 files changed

+90
-37
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/checkmarx/ast-cli
33
go 1.24.4
44

55
require (
6-
github.com/Checkmarx/containers-resolver v1.0.15
6+
github.com/Checkmarx/containers-resolver v1.0.19
77
github.com/Checkmarx/containers-types v1.0.9
88
github.com/Checkmarx/gen-ai-prompts v0.0.0-20240807143411-708ceec12b63
99
github.com/Checkmarx/gen-ai-wrapper v1.0.2
@@ -42,7 +42,7 @@ require (
4242
github.com/BobuSumisu/aho-corasick v1.0.3 // indirect
4343
github.com/BurntSushi/toml v1.5.0 // indirect
4444
github.com/Checkmarx/containers-images-extractor v1.0.17
45-
github.com/Checkmarx/containers-syft-packages-extractor v1.0.13 // indirect
45+
github.com/Checkmarx/containers-syft-packages-extractor v1.0.15 // indirect
4646
github.com/CycloneDX/cyclonedx-go v0.9.2 // indirect
4747
github.com/DataDog/zstd v1.5.6 // indirect
4848
github.com/Masterminds/goutils v1.1.1 // indirect

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2
6565
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
6666
github.com/Checkmarx/containers-images-extractor v1.0.17 h1:lzisdh50nR5yzTjTkT9r9dlHHI7aC72XTGjTp35KqHM=
6767
github.com/Checkmarx/containers-images-extractor v1.0.17/go.mod h1:hRXOiq6Vw2QiIuxIqV+6+osMk0vvIpoMdTMLyz9OfE8=
68-
github.com/Checkmarx/containers-resolver v1.0.15 h1:cm4d6vYWi6G9J9vnAw+dWcMsJwEFMo+anCHVaSp0nMQ=
69-
github.com/Checkmarx/containers-resolver v1.0.15/go.mod h1:9mdw8elUHj9NO9+ejjuuuCByfxvx9mG+JTJxDLi9ubM=
70-
github.com/Checkmarx/containers-syft-packages-extractor v1.0.13 h1:9ah0rruMGgRiug/bD/JJDSrDqEqS7sKGVdc5sqbkwk8=
71-
github.com/Checkmarx/containers-syft-packages-extractor v1.0.13/go.mod h1:EFeB4//lO4KMVj9+eMg6z5jnO9F1e1T4jUoIcx0/19M=
68+
github.com/Checkmarx/containers-resolver v1.0.19 h1:OqPJq3dL0vv8BC2Qco6/VTqmg1Jurk32Yf/bW9cZuq8=
69+
github.com/Checkmarx/containers-resolver v1.0.19/go.mod h1:UwT3Z+rf6RZv1voMt1xtEctWguhQrzHk1dhEb0Dl5fY=
70+
github.com/Checkmarx/containers-syft-packages-extractor v1.0.15 h1:yM7Plt86oL47Kijr1fwsrWwuACNTwWgxZSZ/lifXTlk=
71+
github.com/Checkmarx/containers-syft-packages-extractor v1.0.15/go.mod h1:Jr3dQVFslMCJ+8orsF1orFn05cO3mprUy5b43yn0IIM=
7272
github.com/Checkmarx/containers-types v1.0.9 h1:LbHDj9LZ0x3f28wDx398WC19sw0U0EfEewHMLStBwvs=
7373
github.com/Checkmarx/containers-types v1.0.9/go.mod h1:KR0w8XCosq3+6jRCfQrH7i//Nj2u11qaUJM62CREFZA=
7474
github.com/Checkmarx/gen-ai-prompts v0.0.0-20240807143411-708ceec12b63 h1:SCuTcE+CFvgjbIxUNL8rsdB2sAhfuNx85HvxImKta3g=

internal/commands/scan.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,8 @@ func addContainersScan(cmd *cobra.Command, resubmitConfig []wrappers.Config) (ma
11221122
containerMapConfig[resultsMapType] = commonParams.ContainersType
11231123
containerConfig := wrappers.ContainerConfig{}
11241124

1125-
initializeContainersConfigWithResubmitValues(resubmitConfig, &containerConfig)
1125+
containerResolveLocally, _ := cmd.Flags().GetBool(commonParams.ContainerResolveLocallyFlag)
1126+
initializeContainersConfigWithResubmitValues(resubmitConfig, &containerConfig, containerResolveLocally)
11261127

11271128
fileFolderFilter, _ := cmd.PersistentFlags().GetString(commonParams.ContainersFileFolderFilterFlag)
11281129
if fileFolderFilter != "" {
@@ -1141,7 +1142,7 @@ func addContainersScan(cmd *cobra.Command, resubmitConfig []wrappers.Config) (ma
11411142
containerConfig.ImagesFilter = imageTagFilter
11421143
}
11431144
userCustomImages, _ := cmd.Flags().GetString(commonParams.ContainerImagesFlag)
1144-
if userCustomImages != "" {
1145+
if userCustomImages != "" && !containerResolveLocally {
11451146
containerImagesList := strings.Split(strings.TrimSpace(userCustomImages), ",")
11461147
for _, containerImageName := range containerImagesList {
11471148
if containerImagesErr := validateContainerImageFormat(containerImageName); containerImagesErr != nil {
@@ -1156,7 +1157,7 @@ func addContainersScan(cmd *cobra.Command, resubmitConfig []wrappers.Config) (ma
11561157
return containerMapConfig, nil
11571158
}
11581159

1159-
func initializeContainersConfigWithResubmitValues(resubmitConfig []wrappers.Config, containerConfig *wrappers.ContainerConfig) {
1160+
func initializeContainersConfigWithResubmitValues(resubmitConfig []wrappers.Config, containerConfig *wrappers.ContainerConfig, containerResolveLocally bool) {
11601161
for _, config := range resubmitConfig {
11611162
if config.Type != commonParams.ContainersType {
11621163
continue
@@ -1178,7 +1179,7 @@ func initializeContainersConfigWithResubmitValues(resubmitConfig []wrappers.Conf
11781179
containerConfig.ImagesFilter = resubmitImagesFilter.(string)
11791180
}
11801181
resubmitUserCustomImages := config.Value[ConfigUserCustomImagesKey]
1181-
if resubmitUserCustomImages != nil && resubmitUserCustomImages != "" {
1182+
if resubmitUserCustomImages != nil && resubmitUserCustomImages != "" && !containerResolveLocally {
11821183
containerConfig.UserCustomImages = resubmitUserCustomImages.(string)
11831184
}
11841185
}
@@ -1702,7 +1703,7 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW
17021703

17031704
if isSingleContainerScanTriggered() && containerResolveLocally {
17041705
logger.PrintIfVerbose("Single container scan triggered: compressing only the container resolution file")
1705-
containerResolutionFilePath := filepath.Join(directoryPath, containerResolutionFileName)
1706+
containerResolutionFilePath := filepath.Join(directoryPath, ".checkmarx", "containers", containerResolutionFileName)
17061707
zipFilePath, dirPathErr = util.CompressFile(containerResolutionFilePath, containerResolutionFileName, directoryCreationPrefix)
17071708
} else if isSingleContainerScanTriggered() && containerImagesFlag != "" {
17081709
logger.PrintIfVerbose("Single container scan with external images: creating minimal zip file")

internal/commands/scan_test.go

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,12 +1854,13 @@ func TestAddContainersScan_WithCustomImages_ShouldSetUserCustomImages(t *testing
18541854
func TestInitializeContainersConfigWithResubmitValues_UserCustomImages(t *testing.T) {
18551855
// Define test cases
18561856
testCases := []struct {
1857-
name string
1858-
resubmitConfig []wrappers.Config
1859-
expectedCustomImages string
1857+
name string
1858+
resubmitConfig []wrappers.Config
1859+
containerResolveLocally bool
1860+
expectedCustomImages string
18601861
}{
18611862
{
1862-
name: "When UserCustomImages is valid string, it should be set in containerConfig",
1863+
name: "When UserCustomImages is valid string and ContainerResolveLocally is false, it should be set in containerConfig",
18631864
resubmitConfig: []wrappers.Config{
18641865
{
18651866
Type: commonParams.ContainersType,
@@ -1868,7 +1869,21 @@ func TestInitializeContainersConfigWithResubmitValues_UserCustomImages(t *testin
18681869
},
18691870
},
18701871
},
1871-
expectedCustomImages: "image1:tag1,image2:tag2",
1872+
containerResolveLocally: false,
1873+
expectedCustomImages: "image1:tag1,image2:tag2",
1874+
},
1875+
{
1876+
name: "When UserCustomImages is valid string and ContainerResolveLocally is true, it should not be set in containerConfig",
1877+
resubmitConfig: []wrappers.Config{
1878+
{
1879+
Type: commonParams.ContainersType,
1880+
Value: map[string]interface{}{
1881+
ConfigUserCustomImagesKey: "image1:tag1,image2:tag2",
1882+
},
1883+
},
1884+
},
1885+
containerResolveLocally: true,
1886+
expectedCustomImages: "",
18721887
},
18731888
{
18741889
name: "When UserCustomImages is empty string, containerConfig should not be updated",
@@ -1880,7 +1895,8 @@ func TestInitializeContainersConfigWithResubmitValues_UserCustomImages(t *testin
18801895
},
18811896
},
18821897
},
1883-
expectedCustomImages: "",
1898+
containerResolveLocally: false,
1899+
expectedCustomImages: "",
18841900
},
18851901
{
18861902
name: "When UserCustomImages is nil, containerConfig should not be updated",
@@ -1892,7 +1908,8 @@ func TestInitializeContainersConfigWithResubmitValues_UserCustomImages(t *testin
18921908
},
18931909
},
18941910
},
1895-
expectedCustomImages: "",
1911+
containerResolveLocally: false,
1912+
expectedCustomImages: "",
18961913
},
18971914
{
18981915
name: "When config.Value doesn't have UserCustomImages key, containerConfig should not be updated",
@@ -1902,7 +1919,8 @@ func TestInitializeContainersConfigWithResubmitValues_UserCustomImages(t *testin
19021919
Value: map[string]interface{}{},
19031920
},
19041921
},
1905-
expectedCustomImages: "",
1922+
containerResolveLocally: false,
1923+
expectedCustomImages: "",
19061924
},
19071925
}
19081926

@@ -1913,7 +1931,7 @@ func TestInitializeContainersConfigWithResubmitValues_UserCustomImages(t *testin
19131931
containerConfig := &wrappers.ContainerConfig{}
19141932

19151933
// Call the function under test
1916-
initializeContainersConfigWithResubmitValues(tc.resubmitConfig, containerConfig)
1934+
initializeContainersConfigWithResubmitValues(tc.resubmitConfig, containerConfig, tc.containerResolveLocally)
19171935

19181936
// Assert the result
19191937
assert.Equal(t, tc.expectedCustomImages, containerConfig.UserCustomImages,

internal/services/realtimeengine/containersrealtime/containers-realtime.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"github.com/pkg/errors"
1616
)
1717

18+
const defaultTag = "latest"
19+
1820
// ContainersRealtimeService is the service responsible for performing real-time container scanning.
1921
type ContainersRealtimeService struct {
2022
JwtWrapper wrappers.JWTWrapper
@@ -218,26 +220,31 @@ func mergeImagesToResults(listOfImages []wrappers.ContainerImageResponseItem, re
218220

219221
func getImageLocations(images *[]types.ImageModel, imageName, imageTag string) (location []realtimeengine.Location, filePath string) {
220222
for i, img := range *images {
221-
if img.Name == imageName+":"+imageTag || img.Name == imageName+"@"+imageTag {
222-
location := convertLocations(&img.ImageLocations)
223-
filePath := ""
224-
if len(img.ImageLocations) > 0 {
225-
filePath = img.ImageLocations[0].Path
226-
}
227-
*images = append((*images)[:i], (*images)[i+1:]...)
228-
return location, filePath
223+
if !isSameImage(img.Name, imageName, imageTag) {
224+
continue
229225
}
226+
location := convertLocations(&img.ImageLocations)
227+
filePath := ""
228+
if len(img.ImageLocations) > 0 {
229+
filePath = img.ImageLocations[0].Path
230+
}
231+
*images = append((*images)[:i], (*images)[i+1:]...)
232+
return location, filePath
230233
}
231234
return []realtimeengine.Location{}, ""
232235
}
233236

237+
func isSameImage(curImage, imageName, imageTag string) bool {
238+
return curImage == imageName+":"+imageTag || curImage == imageName+"@"+imageTag || curImage == imageName && imageTag == defaultTag
239+
}
240+
234241
// splitToImageAndTag splits the image string into name and tag components.
235242
func splitToImageAndTag(image string) (imageName, imageTag string) {
236243
// Split the image string by the last colon to separate name and tag
237244
lastColonIndex := strings.LastIndex(image, ":")
238245

239246
if lastColonIndex == len(image)-1 || lastColonIndex == -1 {
240-
return image, "latest" // No tag specified, default to "latest"
247+
return image, defaultTag // No tag specified, default to "latest"
241248
}
242249

243250
imageName = image[:lastColonIndex]

internal/services/realtimeengine/secretsrealtime/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import "github.com/checkmarx/ast-cli/internal/services/realtimeengine"
55
type SecretsRealtimeResult struct {
66
Title string `json:"Title"`
77
Description string `json:"Description"`
8+
SecretValue string `json:"SecretValue"`
89
FilePath string `json:"FilePath"`
910
Severity string `json:"Severity"`
1011
Locations []realtimeengine.Location `json:"Locations"`

internal/services/realtimeengine/secretsrealtime/secrets-realtime.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func (s *SecretsRealtimeService) RunSecretsRealtimeScan(filePath, ignoredFilePat
107107
}
108108

109109
results := convertToSecretsRealtimeResult(report)
110-
resultsPerLineMap := createResultsPerLineMap(results)
110+
resultsPerLineMap := createResultsPerLocationMap(results)
111111
results = filterGenericAPIKeyVulIfNeeded(results, resultsPerLineMap)
112112

113113
if ignoredFilePath == "" {
@@ -163,6 +163,7 @@ func convertSecretToResult(secret *secrets.Secret) SecretsRealtimeResult {
163163
return SecretsRealtimeResult{
164164
Title: secret.RuleID,
165165
Description: secret.RuleDescription,
166+
SecretValue: secret.Value,
166167
Severity: getSeverity(secret),
167168
FilePath: secret.Source,
168169
Locations: locations,
@@ -182,15 +183,17 @@ func getSeverity(secret *secrets.Secret) string {
182183
}
183184
}
184185

185-
func createResultsPerLineMap(results []SecretsRealtimeResult) map[string][]SecretsRealtimeResult {
186-
resultsPerLine := make(map[string][]SecretsRealtimeResult)
186+
func createResultsPerLocationMap(results []SecretsRealtimeResult) map[string][]SecretsRealtimeResult {
187+
resultsPerLocation := make(map[string][]SecretsRealtimeResult)
187188
for _, result := range results {
189+
var locationKey string
188190
for _, location := range result.Locations {
189-
lineKey := fmt.Sprintf("%s:%d", result.FilePath, location.Line)
190-
resultsPerLine[lineKey] = append(resultsPerLine[lineKey], result)
191+
locationKey = fmt.Sprintf("%s:%d", locationKey, location.Line)
191192
}
193+
resultKey := fmt.Sprintf("%s%s", result.FilePath, locationKey)
194+
resultsPerLocation[resultKey] = append(resultsPerLocation[resultKey], result)
192195
}
193-
return resultsPerLine
196+
return resultsPerLocation
194197
}
195198

196199
func filterGenericAPIKeyVulIfNeeded(

internal/services/realtimeengine/secretsrealtime/secrets-realtime_test.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,31 @@ func TestRunSecretsRealtimeScan_ValidFile_Success(t *testing.T) {
139139

140140
assert.NoError(t, err)
141141
assert.NotNil(t, results)
142-
// Note: The actual results depend on the 2ms scanner behavior
143-
// This test mainly verifies that the function completes without error
142+
}
143+
144+
func TestRunSecretsRealtimeScan_MultiLineResult_Success(t *testing.T) {
145+
mock.Flag = wrappers.FeatureFlagResponseModel{Name: wrappers.OssRealtimeEnabled, Status: true}
146+
value := "PRIVATE_KEY = \"\"\"\n-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA7v8wF+SECRETKEYEXAMPLE+QIDAQABAoIBAQC0\n-----END RSA PRIVATE KEY-----\n\"\"\""
147+
// Create a temporary file for testing
148+
tempDir := t.TempDir()
149+
tempFile := filepath.Join(tempDir, "test-secrets.txt")
150+
testContent := value
151+
err := os.WriteFile(tempFile, []byte(testContent), 0644)
152+
assert.NoError(t, err)
153+
154+
service := &SecretsRealtimeService{
155+
JwtWrapper: &mock.JWTMockWrapper{},
156+
FeatureFlagWrapper: &mock.FeatureFlagsMockWrapper{},
157+
}
158+
159+
results, err := service.RunSecretsRealtimeScan(tempFile, "")
160+
161+
assert.NoError(t, err)
162+
assert.NotNil(t, results)
163+
assert.Len(t, results, 1)
164+
assert.Len(t, results[0].Locations, 3)
165+
assert.NotEmpty(t, results[0].SecretValue)
166+
144167
}
145168

146169
func TestReadFile_ValidFile_Success(t *testing.T) {

0 commit comments

Comments
 (0)