From 86a543c6b9b770f98d96c262e6dd73a128eb6ee6 Mon Sep 17 00:00:00 2001 From: RuiO Date: Fri, 20 Dec 2024 15:56:06 +0000 Subject: [PATCH 1/5] Support scs triage capabilities --- internal/commands/predicates.go | 2 +- internal/params/binds.go | 1 + internal/params/envs.go | 1 + internal/params/keys.go | 1 + internal/wrappers/predicates-http.go | 4 ++++ 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index 17416cd1f..37661d2b1 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -72,7 +72,7 @@ func triageShowSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWra Long: "The show command provides a list of all the predicates in the issue.", Example: heredoc.Doc( ` - $ cx triage show --similarity-id --project-id --scan-type + $ cx triage show --similarity-id --project-id --scan-type `, ), diff --git a/internal/params/binds.go b/internal/params/binds.go index 772b6bf29..d3f3a09f9 100644 --- a/internal/params/binds.go +++ b/internal/params/binds.go @@ -26,6 +26,7 @@ var EnvVarsBinds = []struct { {SastResultsPredicatesPathKey, SastResultsPredicatesPathEnv, "api/sast-results-predicates"}, {KicsResultsPathKey, KicsResultsPathEnv, "api/kics-results"}, {KicsResultsPredicatesPathKey, KicsResultsPredicatesPathEnv, "api/kics-results-predicates"}, + {ScsResultsPredicatesPathKey, ScsResultsPredicatesPathEnv, "api/micro-engines/predicates"}, {BflPathKey, BflPathEnv, "api/bfl"}, {PRDecorationGithubPathKey, PRDecorationGithubPathEnv, "api/flow-publisher/pr/github"}, {PRDecorationGitlabPathKey, PRDecorationGitlabPathEnv, "api/flow-publisher/pr/gitlab"}, diff --git a/internal/params/envs.go b/internal/params/envs.go index ffe49c23b..890645f75 100644 --- a/internal/params/envs.go +++ b/internal/params/envs.go @@ -28,6 +28,7 @@ const ( SastResultsPredicatesPathEnv = "CX_SAST_RESULTS_PREDICATES_PATH" KicsResultsPathEnv = "CX_KICS_RESULTS_PATH" KicsResultsPredicatesPathEnv = "CX_KICS_RESULTS_PREDICATES_PATH" + ScsResultsPredicatesPathEnv = "CX_SCS_RESULTS_PREDICATES_PATH" BflPathEnv = "CX_BFL_PATH" PRDecorationGithubPathEnv = "CX_PR_DECORATION_GITHUB_PATH" PRDecorationGitlabPathEnv = "CX_PR_DECORATION_GITLAB_PATH" diff --git a/internal/params/keys.go b/internal/params/keys.go index af31d4ea1..0a70e8b4f 100644 --- a/internal/params/keys.go +++ b/internal/params/keys.go @@ -57,6 +57,7 @@ var ( LogsEngineLogPathKey = strings.ToLower(LogsEngineLogPathEnv) SastResultsPredicatesPathKey = strings.ToLower(SastResultsPredicatesPathEnv) KicsResultsPredicatesPathKey = strings.ToLower(KicsResultsPredicatesPathEnv) + ScsResultsPredicatesPathKey = strings.ToLower(ScsResultsPredicatesPathEnv) DescriptionsPathKey = strings.ToLower(DescriptionsPathEnv) TenantConfigurationPathKey = strings.ToLower(TenantConfigurationPathEnv) ResultsPdfReportPathKey = strings.ToLower(ResultsPdfReportPathEnv) diff --git a/internal/wrappers/predicates-http.go b/internal/wrappers/predicates-http.go index d58d7de4b..dcafc56ad 100644 --- a/internal/wrappers/predicates-http.go +++ b/internal/wrappers/predicates-http.go @@ -36,6 +36,8 @@ func (r *ResultsPredicatesHTTPWrapper) GetAllPredicatesForSimilarityID(similarit triageAPIPath = viper.GetString(params.KicsResultsPredicatesPathKey) } else if strings.EqualFold(strings.TrimSpace(scannerType), params.SastType) { triageAPIPath = viper.GetString(params.SastResultsPredicatesPathKey) + } else if strings.EqualFold(strings.TrimSpace(scannerType), params.ScsType) { + triageAPIPath = viper.GetString(params.ScsResultsPredicatesPathKey) } else if strings.EqualFold(strings.TrimSpace(scannerType), params.ScaType) { return &PredicatesCollectionResponseModel{}, nil, nil } else { @@ -78,6 +80,8 @@ func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate *Predi triageAPIPath = viper.GetString(params.SastResultsPredicatesPathKey) } else if strings.EqualFold(strings.TrimSpace(scanType), params.KicsType) || strings.EqualFold(strings.TrimSpace(scanType), params.IacType) { triageAPIPath = viper.GetString(params.KicsResultsPredicatesPathKey) + } else if strings.EqualFold(strings.TrimSpace(scanType), params.ScsType) { + triageAPIPath = viper.GetString(params.ScsResultsPredicatesPathKey) } else { return nil, errors.Errorf(invalidScanType, scanType) } From 50310bf882ac59990b850a7460241b0f57f72c08 Mon Sep 17 00:00:00 2001 From: RuiO Date: Tue, 28 Jan 2025 17:43:49 +0000 Subject: [PATCH 2/5] Divide sscs (REST) server in two (read and write) --- internal/params/binds.go | 5 +++-- internal/params/envs.go | 3 ++- internal/params/keys.go | 3 ++- internal/wrappers/predicates-http.go | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/internal/params/binds.go b/internal/params/binds.go index d3f3a09f9..349054ba7 100644 --- a/internal/params/binds.go +++ b/internal/params/binds.go @@ -21,12 +21,13 @@ var EnvVarsBinds = []struct { {ResultsPathKey, ResultsPathEnv, "api/results"}, {ScanSummaryPathKey, ScanSummaryPathEnv, "api/scan-summary"}, {RisksOverviewPathKey, RisksOverviewPathEnv, "api/apisec/static/api/scan/%s/risks-overview"}, - {ScsScanOverviewPathKey, ScsScanOverviewPathEnv, "api/micro-engines/scans/%s/scan-overview"}, + {ScsScanOverviewPathKey, ScsScanOverviewPathEnv, "api/micro-engines/read/scans/%s/scan-overview"}, {SastResultsPathKey, SastResultsPathEnv, "api/sast-results"}, {SastResultsPredicatesPathKey, SastResultsPredicatesPathEnv, "api/sast-results-predicates"}, {KicsResultsPathKey, KicsResultsPathEnv, "api/kics-results"}, {KicsResultsPredicatesPathKey, KicsResultsPredicatesPathEnv, "api/kics-results-predicates"}, - {ScsResultsPredicatesPathKey, ScsResultsPredicatesPathEnv, "api/micro-engines/predicates"}, + {ScsResultsReadPredicatesPathKey, ScsResultsReadPredicatesPathEnv, "api/micro-engines/read/predicates"}, + {ScsResultsWritePredicatesPathKey, ScsResultsWritePredicatesPathEnv, "api/micro-engines/write/predicates"}, {BflPathKey, BflPathEnv, "api/bfl"}, {PRDecorationGithubPathKey, PRDecorationGithubPathEnv, "api/flow-publisher/pr/github"}, {PRDecorationGitlabPathKey, PRDecorationGitlabPathEnv, "api/flow-publisher/pr/gitlab"}, diff --git a/internal/params/envs.go b/internal/params/envs.go index 890645f75..d27495e2d 100644 --- a/internal/params/envs.go +++ b/internal/params/envs.go @@ -28,7 +28,8 @@ const ( SastResultsPredicatesPathEnv = "CX_SAST_RESULTS_PREDICATES_PATH" KicsResultsPathEnv = "CX_KICS_RESULTS_PATH" KicsResultsPredicatesPathEnv = "CX_KICS_RESULTS_PREDICATES_PATH" - ScsResultsPredicatesPathEnv = "CX_SCS_RESULTS_PREDICATES_PATH" + ScsResultsReadPredicatesPathEnv = "CX_SCS_RESULTS_PREDICATES_READ_PATH" + ScsResultsWritePredicatesPathEnv = "CX_SCS_RESULTS_PREDICATES_WRITE_PATH" BflPathEnv = "CX_BFL_PATH" PRDecorationGithubPathEnv = "CX_PR_DECORATION_GITHUB_PATH" PRDecorationGitlabPathEnv = "CX_PR_DECORATION_GITLAB_PATH" diff --git a/internal/params/keys.go b/internal/params/keys.go index 0a70e8b4f..612974e75 100644 --- a/internal/params/keys.go +++ b/internal/params/keys.go @@ -57,7 +57,8 @@ var ( LogsEngineLogPathKey = strings.ToLower(LogsEngineLogPathEnv) SastResultsPredicatesPathKey = strings.ToLower(SastResultsPredicatesPathEnv) KicsResultsPredicatesPathKey = strings.ToLower(KicsResultsPredicatesPathEnv) - ScsResultsPredicatesPathKey = strings.ToLower(ScsResultsPredicatesPathEnv) + ScsResultsReadPredicatesPathKey = strings.ToLower(ScsResultsReadPredicatesPathEnv) + ScsResultsWritePredicatesPathKey = strings.ToLower(ScsResultsWritePredicatesPathEnv) DescriptionsPathKey = strings.ToLower(DescriptionsPathEnv) TenantConfigurationPathKey = strings.ToLower(TenantConfigurationPathEnv) ResultsPdfReportPathKey = strings.ToLower(ResultsPdfReportPathEnv) diff --git a/internal/wrappers/predicates-http.go b/internal/wrappers/predicates-http.go index dcafc56ad..2ad24b8ce 100644 --- a/internal/wrappers/predicates-http.go +++ b/internal/wrappers/predicates-http.go @@ -37,7 +37,7 @@ func (r *ResultsPredicatesHTTPWrapper) GetAllPredicatesForSimilarityID(similarit } else if strings.EqualFold(strings.TrimSpace(scannerType), params.SastType) { triageAPIPath = viper.GetString(params.SastResultsPredicatesPathKey) } else if strings.EqualFold(strings.TrimSpace(scannerType), params.ScsType) { - triageAPIPath = viper.GetString(params.ScsResultsPredicatesPathKey) + triageAPIPath = viper.GetString(params.ScsResultsReadPredicatesPathKey) } else if strings.EqualFold(strings.TrimSpace(scannerType), params.ScaType) { return &PredicatesCollectionResponseModel{}, nil, nil } else { @@ -81,7 +81,7 @@ func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate *Predi } else if strings.EqualFold(strings.TrimSpace(scanType), params.KicsType) || strings.EqualFold(strings.TrimSpace(scanType), params.IacType) { triageAPIPath = viper.GetString(params.KicsResultsPredicatesPathKey) } else if strings.EqualFold(strings.TrimSpace(scanType), params.ScsType) { - triageAPIPath = viper.GetString(params.ScsResultsPredicatesPathKey) + triageAPIPath = viper.GetString(params.ScsResultsWritePredicatesPathKey) } else { return nil, errors.Errorf(invalidScanType, scanType) } From 1a5ed3a62792fbfde2e91cb4d1556c9314a88189 Mon Sep 17 00:00:00 2001 From: RuiO Date: Thu, 6 Feb 2025 11:40:00 +0000 Subject: [PATCH 3/5] feature(sscs-env-var): guarantee that env var has default string value --- .../wrappers/configuration/configuration.go | 30 +++++++++++++++++++ internal/wrappers/scan-overview-http.go | 23 +++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/internal/wrappers/configuration/configuration.go b/internal/wrappers/configuration/configuration.go index 2af319490..a0dbfebd9 100644 --- a/internal/wrappers/configuration/configuration.go +++ b/internal/wrappers/configuration/configuration.go @@ -161,6 +161,36 @@ func SafeWriteSingleConfigKey(configFilePath, key string, value int) error { return nil } +func SafeWriteSingleConfigKeyString(configFilePath, key string, value string) error { + // Create a file lock + lock := flock.New(configFilePath + ".lock") + locked, err := lock.TryLock() + if err != nil { + return errors.Errorf("error acquiring lock: %s", err.Error()) + } + if !locked { + return errors.Errorf("could not acquire lock") + } + defer func() { + _ = lock.Unlock() + }() + + // Load existing configuration or initialize a new one + config, err := LoadConfig(configFilePath) + if err != nil { + return errors.Errorf("error loading config: %s", err.Error()) + } + + // Update the configuration key + config[key] = value + + // Save the updated configuration back to the file + if err = SaveConfig(configFilePath, config); err != nil { + return errors.Errorf("error saving config: %s", err.Error()) + } + return nil +} + // LoadConfig loads the configuration from a file. If the file does not exist, it returns an empty map. func LoadConfig(path string) (map[string]interface{}, error) { config := make(map[string]interface{}) diff --git a/internal/wrappers/scan-overview-http.go b/internal/wrappers/scan-overview-http.go index 9768d8c00..668ea4310 100644 --- a/internal/wrappers/scan-overview-http.go +++ b/internal/wrappers/scan-overview-http.go @@ -3,6 +3,8 @@ package wrappers import ( "encoding/json" "fmt" + "github.com/checkmarx/ast-cli/internal/logger" + "github.com/checkmarx/ast-cli/internal/wrappers/configuration" "net/http" commonParams "github.com/checkmarx/ast-cli/internal/params" @@ -10,13 +12,16 @@ import ( "github.com/spf13/viper" ) +const defaultPath = "api/micro-engines/read/scans/%s/scan-overview" + type ScanOverviewHTTPWrapper struct { path string } func NewHTTPScanOverviewWrapper(path string) ScanOverviewWrapper { + validPath := configurePath(path) return &ScanOverviewHTTPWrapper{ - path: path, + path: validPath, } } @@ -58,3 +63,19 @@ func (r *ScanOverviewHTTPWrapper) GetSCSOverviewByScanID(scanID string) ( return nil, nil, errors.Errorf("response status code %d", resp.StatusCode) } } + +// configurePath checks if the path is the default path, if not it writes the default path to the config file +func configurePath(path string) string { + if path != defaultPath { + viper.Set(commonParams.ScsScanOverviewPathKey, defaultPath) + configFilePath, err := configuration.GetConfigFilePath() + if err != nil { + logger.PrintfIfVerbose("Error getting config file path: %v", err) + } + err = configuration.SafeWriteSingleConfigKeyString(configFilePath, commonParams.ScsScanOverviewPathKey, defaultPath) + if err != nil { + logger.PrintfIfVerbose("Error writing Scan Overview path to config file: %v", err) + } + } + return defaultPath +} From f3fb8d1e82060ffbbd74950f8c043d05a9070bba Mon Sep 17 00:00:00 2001 From: RuiO Date: Thu, 6 Feb 2025 11:41:10 +0000 Subject: [PATCH 4/5] test(sscs-env-var): add new UTs --- internal/commands/util/configuration_test.go | 59 +++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/internal/commands/util/configuration_test.go b/internal/commands/util/configuration_test.go index 2aae7b3c0..a6b8e3d99 100644 --- a/internal/commands/util/configuration_test.go +++ b/internal/commands/util/configuration_test.go @@ -10,7 +10,11 @@ import ( "gotest.tools/assert" ) -const cxAscaPort = "cx_asca_port" +const ( + cxAscaPort = "cx_asca_port" + cxScsScanOverviewPath = "cx_scs_scan_overview_path" + defaultScsScanOverviewPath = "api/micro-engines/read/scans/%s/scan-overview" +) func TestNewConfigCommand(t *testing.T) { cmd := NewConfigCommand() @@ -94,3 +98,56 @@ func TestChangedOnlyAscaPortInConfigFile_ConfigFileExistsWithDefaultValues_OnlyA } } } + +func TestWriteSingleConfigKeyStringToExistingFile_UpdateScsScanOverviewPath_Success(t *testing.T) { + configuration.LoadConfiguration() + configFilePath, _ := configuration.GetConfigFilePath() + err := configuration.SafeWriteSingleConfigKeyString(configFilePath, cxScsScanOverviewPath, defaultScsScanOverviewPath) + assert.NilError(t, err) + + config, err := configuration.LoadConfig(configFilePath) + assert.NilError(t, err) + asserts.Equal(t, defaultScsScanOverviewPath, config[cxScsScanOverviewPath]) +} + +func TestWriteSingleConfigKeyStringNonExistingFile_CreatingTheFileAndWritesTheKey_Success(t *testing.T) { + configFilePath := "non-existing-file" + + file, err := os.Open(configFilePath) + asserts.NotNil(t, err) + asserts.Nil(t, file) + + err = configuration.SafeWriteSingleConfigKeyString(configFilePath, cxScsScanOverviewPath, defaultScsScanOverviewPath) + assert.NilError(t, err) + + file, err = os.Open(configFilePath) + assert.NilError(t, err) + defer func(file *os.File) { + _ = file.Close() + _ = os.Remove(configFilePath) + _ = os.Remove(configFilePath + ".lock") + }(file) + asserts.NotNil(t, file) +} + +func TestChangedOnlyScsScanOverviewPathInConfigFile_ConfigFileExistsWithDefaultValues_OnlyAscaPortChangedSuccess(t *testing.T) { + configuration.LoadConfiguration() + configFilePath, _ := configuration.GetConfigFilePath() + + oldConfig, err := configuration.LoadConfig(configFilePath) + assert.NilError(t, err) + + err = configuration.SafeWriteSingleConfigKeyString(configFilePath, cxScsScanOverviewPath, defaultScsScanOverviewPath) + assert.NilError(t, err) + + config, err := configuration.LoadConfig(configFilePath) + assert.NilError(t, err) + asserts.Equal(t, defaultScsScanOverviewPath, config[cxScsScanOverviewPath]) + + // Assert all the other properties are the same + for key, value := range oldConfig { + if key != cxAscaPort { + asserts.Equal(t, value, config[key]) + } + } +} From 2375f6ff41ac33fcc01203487f5cf31669f55244 Mon Sep 17 00:00:00 2001 From: RuiO Date: Thu, 6 Feb 2025 11:49:57 +0000 Subject: [PATCH 5/5] fix(sscs-env-var): fix typos --- internal/commands/util/configuration_test.go | 4 ++-- internal/wrappers/scan-overview-http.go | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/internal/commands/util/configuration_test.go b/internal/commands/util/configuration_test.go index a6b8e3d99..1b980de53 100644 --- a/internal/commands/util/configuration_test.go +++ b/internal/commands/util/configuration_test.go @@ -130,7 +130,7 @@ func TestWriteSingleConfigKeyStringNonExistingFile_CreatingTheFileAndWritesTheKe asserts.NotNil(t, file) } -func TestChangedOnlyScsScanOverviewPathInConfigFile_ConfigFileExistsWithDefaultValues_OnlyAscaPortChangedSuccess(t *testing.T) { +func TestChangedOnlyScsScanOverviewPathInConfigFile_ConfigFileExistsWithDefaultValues_OnlyScsScanOverviewPathChangedSuccess(t *testing.T) { configuration.LoadConfiguration() configFilePath, _ := configuration.GetConfigFilePath() @@ -146,7 +146,7 @@ func TestChangedOnlyScsScanOverviewPathInConfigFile_ConfigFileExistsWithDefaultV // Assert all the other properties are the same for key, value := range oldConfig { - if key != cxAscaPort { + if key != cxScsScanOverviewPath { asserts.Equal(t, value, config[key]) } } diff --git a/internal/wrappers/scan-overview-http.go b/internal/wrappers/scan-overview-http.go index 668ea4310..28246ec10 100644 --- a/internal/wrappers/scan-overview-http.go +++ b/internal/wrappers/scan-overview-http.go @@ -19,7 +19,7 @@ type ScanOverviewHTTPWrapper struct { } func NewHTTPScanOverviewWrapper(path string) ScanOverviewWrapper { - validPath := configurePath(path) + validPath := setDefaultPath(path) return &ScanOverviewHTTPWrapper{ path: validPath, } @@ -64,10 +64,9 @@ func (r *ScanOverviewHTTPWrapper) GetSCSOverviewByScanID(scanID string) ( } } -// configurePath checks if the path is the default path, if not it writes the default path to the config file -func configurePath(path string) string { +// setDefaultPath checks if the path is the default path, if not it writes the default path to the config file +func setDefaultPath(path string) string { if path != defaultPath { - viper.Set(commonParams.ScsScanOverviewPathKey, defaultPath) configFilePath, err := configuration.GetConfigFilePath() if err != nil { logger.PrintfIfVerbose("Error getting config file path: %v", err)