Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SSCS as scan type for the triage command (AST-72075) #1022

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/commands/predicates.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 <SimilarityID> --project-id <ProjectID> --scan-type <SAST||IAC-SECURITY>
$ cx triage show --similarity-id <SimilarityID> --project-id <ProjectID> --scan-type <SAST||IAC-SECURITY||SCS>
AlvoBen marked this conversation as resolved.
Show resolved Hide resolved
AlvoBen marked this conversation as resolved.
Show resolved Hide resolved
`,
),

Expand Down
59 changes: 58 additions & 1 deletion internal/commands/util/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
AlvoBen marked this conversation as resolved.
Show resolved Hide resolved
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constant defaultScsScanOverviewPath is defined but not used anywhere in the code. Please ensure that it is utilized appropriately or remove it if it's not needed.

)

func TestNewConfigCommand(t *testing.T) {
cmd := NewConfigCommand()
Expand Down Expand Up @@ -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)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file handle file is not closed after opening. Consider deferring the close operation immediately after checking the error from os.Open to avoid resource leaks.

AlvoBen marked this conversation as resolved.
Show resolved Hide resolved
asserts.NotNil(t, err)
asserts.Nil(t, file)

err = configuration.SafeWriteSingleConfigKeyString(configFilePath, cxScsScanOverviewPath, defaultScsScanOverviewPath)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test function name TestWriteSingleConfigKeyStringNonExistingFile_CreatingTheFileAndWritesTheKey_Success is misleading as it implies success, but the test does not assert any conditions to verify the success of the operation. Consider adding assertions to check that the key was written successfully.

assert.NilError(t, err)

file, err = os.Open(configFilePath)
assert.NilError(t, err)
defer func(file *os.File) {
AlvoBen marked this conversation as resolved.
Show resolved Hide resolved
_ = file.Close()
_ = os.Remove(configFilePath)
_ = os.Remove(configFilePath + ".lock")
AlvoBen marked this conversation as resolved.
Show resolved Hide resolved
}(file)
asserts.NotNil(t, file)
}

func TestChangedOnlyScsScanOverviewPathInConfigFile_ConfigFileExistsWithDefaultValues_OnlyScsScanOverviewPathChangedSuccess(t *testing.T) {
configuration.LoadConfiguration()
configFilePath, _ := configuration.GetConfigFilePath()
AlvoBen marked this conversation as resolved.
Show resolved Hide resolved

oldConfig, err := configuration.LoadConfig(configFilePath)
AlvoBen marked this conversation as resolved.
Show resolved Hide resolved
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 {
cx-ruio marked this conversation as resolved.
Show resolved Hide resolved
if key != cxScsScanOverviewPath {
cx-ruio marked this conversation as resolved.
Show resolved Hide resolved
asserts.Equal(t, value, config[key])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using a loop to check all other properties, consider using a more efficient method to compare configurations, such as reflecting the structs or using a dedicated configuration comparison function.

}
}
}
4 changes: 3 additions & 1 deletion internal/params/binds.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand Down
2 changes: 2 additions & 0 deletions internal/params/envs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions internal/params/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
30 changes: 30 additions & 0 deletions internal/wrappers/configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
AlvoBen marked this conversation as resolved.
Show resolved Hide resolved
AlvoBen marked this conversation as resolved.
Show resolved Hide resolved

// 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{})
Expand Down
4 changes: 4 additions & 0 deletions internal/wrappers/predicates-http.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
diogo-fjrocha marked this conversation as resolved.
Show resolved Hide resolved
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new condition for params.ScsType should be placed after all existing conditions to maintain consistency and readability.

triageAPIPath = viper.GetString(params.ScsResultsReadPredicatesPathKey)
} else if strings.EqualFold(strings.TrimSpace(scannerType), params.ScaType) {
return &PredicatesCollectionResponseModel{}, nil, nil
} else {
Expand Down Expand Up @@ -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) {
cx-ruio marked this conversation as resolved.
Show resolved Hide resolved
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The else if condition for params.ScsType should be consistent with the naming convention used for other scan types. Consider renaming params.ScsType to params.SscsType to match the pull request title and description indicating the addition of SSCS as a scan type.

triageAPIPath = viper.GetString(params.ScsResultsWritePredicatesPathKey)
} else {
return nil, errors.Errorf(invalidScanType, scanType)
}
Expand Down
22 changes: 21 additions & 1 deletion internal/wrappers/scan-overview-http.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@ 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"
"github.com/pkg/errors"
"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)
cx-ruio marked this conversation as resolved.
Show resolved Hide resolved
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function setDefaultPath is not defined within the provided diff. Ensure that the function is implemented and correctly handles cases where path is not provided, or is an empty string.

return &ScanOverviewHTTPWrapper{
path: path,
path: validPath,
}
}

Expand Down Expand Up @@ -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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function setDefaultPath does not seem to be related to the SSCS triaging capabilities as described in the PR. Please ensure that this function is necessary for the feature being implemented and clarify its purpose in the context of SSCS triaging.

func setDefaultPath(path string) string {
if path != defaultPath {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition if path != defaultPath is misleading because the function always returns defaultPath regardless of the input path. Consider returning the actual path that is set in the configuration.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function setDefaultPath always returns defaultPath regardless of the input path. This behavior is not intuitive based on the function name and description. Consider returning the actual path set (either the input path or defaultPath) or renaming the function to reflect its behavior more accurately.

configFilePath, err := configuration.GetConfigFilePath()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error from configuration.GetConfigFilePath() is logged but not handled. If the config file path cannot be retrieved, the function should not attempt to write to it and should handle the error appropriately.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The viper.Set call has been removed, which means the new default path is not being set in the configuration. Ensure that the configuration is updated with the new default path if this is the intended behavior.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check if path != defaultPath should be followed by a return statement if the condition is false, to avoid unnecessary operations when the path is already set to the default.

if err != nil {
logger.PrintfIfVerbose("Error getting config file path: %v", err)
}
err = configuration.SafeWriteSingleConfigKeyString(configFilePath, commonParams.ScsScanOverviewPathKey, defaultPath)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error from configuration.SafeWriteSingleConfigKeyString is logged but not handled. Consider what should happen if the configuration cannot be updated. Should the function return an error, or should there be a fallback mechanism?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function configuration.SafeWriteSingleConfigKeyString is called without handling the case where the configFilePath could not be retrieved due to an error. This could lead to unexpected behavior. Ensure that the function is not called if configFilePath cannot be determined.

if err != nil {
logger.PrintfIfVerbose("Error writing Scan Overview path to config file: %v", err)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging the error using logger.PrintfIfVerbose might not be sufficient for error handling. Consider propagating the error up to the caller of setDefaultPath so that it can be handled appropriately.

}
}
return defaultPath
}
Loading