diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index 3579f7fdc..20af0aa83 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -81,7 +81,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/commands/util/configuration_test.go b/internal/commands/util/configuration_test.go index 2aae7b3c0..1b980de53 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_OnlyScsScanOverviewPathChangedSuccess(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 != cxScsScanOverviewPath { + asserts.Equal(t, value, config[key]) + } + } +} diff --git a/internal/params/binds.go b/internal/params/binds.go index 772b6bf29..349054ba7 100644 --- a/internal/params/binds.go +++ b/internal/params/binds.go @@ -21,11 +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"}, + {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 ffe49c23b..d27495e2d 100644 --- a/internal/params/envs.go +++ b/internal/params/envs.go @@ -28,6 +28,8 @@ const ( SastResultsPredicatesPathEnv = "CX_SAST_RESULTS_PREDICATES_PATH" KicsResultsPathEnv = "CX_KICS_RESULTS_PATH" KicsResultsPredicatesPathEnv = "CX_KICS_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 af31d4ea1..612974e75 100644 --- a/internal/params/keys.go +++ b/internal/params/keys.go @@ -57,6 +57,8 @@ var ( LogsEngineLogPathKey = strings.ToLower(LogsEngineLogPathEnv) SastResultsPredicatesPathKey = strings.ToLower(SastResultsPredicatesPathEnv) KicsResultsPredicatesPathKey = strings.ToLower(KicsResultsPredicatesPathEnv) + 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/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/predicates-http.go b/internal/wrappers/predicates-http.go index 065a7fe7f..6f0905728 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.ScsResultsReadPredicatesPathKey) } 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.ScsResultsWritePredicatesPathKey) } else { return nil, errors.Errorf(invalidScanType, scanType) } diff --git a/internal/wrappers/scan-overview-http.go b/internal/wrappers/scan-overview-http.go index 9768d8c00..28246ec10 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 := setDefaultPath(path) return &ScanOverviewHTTPWrapper{ - path: path, + path: validPath, } } @@ -58,3 +63,18 @@ func (r *ScanOverviewHTTPWrapper) GetSCSOverviewByScanID(scanID string) ( return nil, nil, errors.Errorf("response status code %d", resp.StatusCode) } } + +// 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 { + 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 +}