Skip to content

Commit

Permalink
Merge branch 'main' into feature/alex-containers-default-cloud
Browse files Browse the repository at this point in the history
  • Loading branch information
alexc-checkmarx authored Feb 25, 2025
2 parents 62a25d7 + 45c4db1 commit 89dbaef
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 5 deletions.
2 changes: 1 addition & 1 deletion internal/commands/predicates.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 <SimilarityID> --project-id <ProjectID> --scan-type <SAST||IAC-SECURITY>
$ cx triage show --similarity-id <SimilarityID> --project-id <ProjectID> --scan-type <SAST||IAC-SECURITY||SCS>
`,
),

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"
)

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)
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])
}
}
}
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
18 changes: 17 additions & 1 deletion internal/wrappers/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,14 @@ func getCredentialsPayload(accessKeyID, accessKeySecret string) string {

func getAPIKeyPayload(astToken string) string {
logger.PrintIfVerbose("Using API key credentials.")
return fmt.Sprintf("grant_type=refresh_token&client_id=ast-app&refresh_token=%s", astToken)

clientID, err := extractAZPFromToken(astToken)
if err != nil {
logger.PrintIfVerbose("Failed to extract azp from token, using default client_id")
clientID = "ast-app"
}

return fmt.Sprintf("grant_type=refresh_token&client_id=%s&refresh_token=%s", clientID, astToken)
}

func getPasswordCredentialsPayload(username, password, adminClientID, adminClientSecret string) string {
Expand Down Expand Up @@ -782,3 +789,12 @@ func AppendIfNotExists(domainsMap map[string]struct{}, newDomain string) map[str
}
return domainsMap
}

func extractAZPFromToken(astToken string) (string, error) {
const azpClaim = "azp"
azp, err := ExtractFromTokenClaims(astToken, azpClaim)
if err != nil {
return "ast-app", nil // default value in case of error
}
return azp, nil
}
70 changes: 70 additions & 0 deletions internal/wrappers/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,73 @@ func TestConcurrentWriteCredentialsToCache(t *testing.T) {
assert.True(t, testTokenNumber >= 0 && testTokenNumber < 1000,
"The token number should be within the expected range")
}

func TestExtractAZPFromToken(t *testing.T) {
// Test cases
tests := []struct {
name string
token string
expected string
hasError bool
}{
{
name: "Valid token with azp claim",
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhenAiOiJ0ZXN0LWFwcCJ9.YqenXXXX", // token with azp: "test-app"
expected: "test-app",
hasError: false,
},
{
name: "Invalid token format",
token: "invalid-token",
expected: "ast-app", // Should return default value
hasError: false,
},
{
name: "Valid token without azp claim",
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.XXXXX",
expected: "ast-app", // Should return default value
hasError: false,
},
}

// Run tests
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := extractAZPFromToken(tt.token)

if tt.hasError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}

assert.Equal(t, tt.expected, result)
})
}
}

func TestGetAPIKeyPayload(t *testing.T) {
tests := []struct {
name string
token string
expected string
}{
{
name: "Valid token with azp claim",
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhenAiOiJ0ZXN0LWFwcCJ9.YqenXXXX",
expected: "grant_type=refresh_token&client_id=test-app&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhenAiOiJ0ZXN0LWFwcCJ9.YqenXXXX",
},
{
name: "Invalid token",
token: "invalid-token",
expected: "grant_type=refresh_token&client_id=ast-app&refresh_token=invalid-token",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := getAPIKeyPayload(tt.token)
assert.Equal(t, tt.expected, result)
})
}
}
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

// 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) {
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) {
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)
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
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
}

0 comments on commit 89dbaef

Please sign in to comment.