Skip to content
Merged
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
1 change: 0 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ linters:
- golint
- gomnd
- goprintffuncname
- gosec
- gosimple
- govet
- ineffassign
Expand Down
98 changes: 65 additions & 33 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"fmt"
"os"
"strings"

"github.com/checkmarxDev/ast-cli/internal/wrappers"

Expand All @@ -11,43 +12,63 @@ import (
)

const (
astSchema = "AST_SCHEMA"
astHost = "AST_HOST"
astPort = "80"
scansPath = "SCANS_PATH"
projectsPath = "PROJECTS_PATH"
resultsPath = "RESULTS_PATH"
uploadsPath = "UPLOADS_PATH"
logLevel = "CLI_LOG_LEVEL"
astSchemaEnv = "AST_SCHEMA"
astHostEnv = "AST_HOST"
astPortEnv = "AST_PORT"
scansPathEnv = "SCANS_PATH"
projectsPathEnv = "PROJECTS_PATH"
resultsPathEnv = "RESULTS_PATH"
uploadsPathEnv = "UPLOADS_PATH"

successfulExitCode = 0
failureExitCode = 1
)

func main() {
viper.AutomaticEnv()
viper.AddConfigPath(".")
viper.SetConfigName("config")
viper.SetConfigType("env")
_ = viper.ReadInConfig()

viper.SetDefault(astSchema, "http")
viper.SetDefault(astHost, "localhost")
viper.SetDefault(astPort, "80")
viper.SetDefault(scansPath, "scans")
viper.SetDefault(projectsPath, "projects")
viper.SetDefault(uploadsPath, "uploads")
viper.SetDefault(resultsPath, "results")
viper.SetDefault(logLevel, "DEBUG")

schema := viper.GetString(astSchema)
host := viper.GetString(astHost)
port := viper.GetString(astPort)
ast := fmt.Sprintf("%s://%s:%s/api", schema, host, port)
// Key ast_schema will be bound to AST_SCHEMA
astSchemaKey := strings.ToLower(astSchemaEnv)
err := bindKeyToEnvAndDefault(astSchemaKey, astSchemaEnv, "http")
exitIfError(err)
schema := viper.GetString(astSchemaKey)

astHostKey := strings.ToLower(astHostEnv)
err = bindKeyToEnvAndDefault(astHostKey, astHostEnv, "localhost")
exitIfError(err)
host := viper.GetString(astHostKey)

astPortKey := strings.ToLower(astPortEnv)
err = bindKeyToEnvAndDefault(astPortKey, astPortEnv, "80")
exitIfError(err)
port := viper.GetString(astPortKey)

scansPathKey := strings.ToLower(scansPathEnv)
err = bindKeyToEnvAndDefault(scansPathKey, scansPathEnv, "scans")
exitIfError(err)
scans := viper.GetString(scansPathKey)

scans := viper.GetString(scansPath)
uploads := viper.GetString(uploadsPath)
projects := viper.GetString(projectsPath)
results := viper.GetString(resultsPath)
projectsPathKey := strings.ToLower(projectsPathEnv)
err = bindKeyToEnvAndDefault(projectsPathKey, projectsPathEnv, "projects")
exitIfError(err)
projects := viper.GetString(projectsPathKey)

resultsPathKey := strings.ToLower(resultsPathEnv)
err = bindKeyToEnvAndDefault(resultsPathKey, resultsPathEnv, "results")
exitIfError(err)
results := viper.GetString(resultsPathKey)

uploadsPathKey := strings.ToLower(uploadsPathEnv)
err = bindKeyToEnvAndDefault(uploadsPathKey, uploadsPathEnv, "uploads")
exitIfError(err)
uploads := viper.GetString(uploadsPathKey)

err = bindKeyToEnvAndDefault(commands.AccessKeyIDConfigKey, commands.AccessKeyIDEnv, "")
exitIfError(err)
err = bindKeyToEnvAndDefault(commands.AccessKeySecretConfigKey, commands.AccessKeySecretEnv, "")
exitIfError(err)
err = bindKeyToEnvAndDefault(commands.AstAuthenticationHostConfigKey, commands.AstAuthenticationHostEnv, "")
exitIfError(err)

ast := fmt.Sprintf("%s://%s:%s/api", schema, host, port)

scansURL := fmt.Sprintf("%s/%s", ast, scans)
uploadsURL := fmt.Sprintf("%s/%s", ast, uploads)
Expand All @@ -60,12 +81,23 @@ func main() {
resultsWrapper := wrappers.NewHTTPResultsWrapper(resultsURL)

astCli := commands.NewAstCLI(scansWrapper, uploadsWrapper, projectsWrapper, resultsWrapper)
err := astCli.Execute()

err = astCli.Execute()
exitIfError(err)
os.Exit(successfulExitCode)
}

func exitIfError(err error) {
if err != nil {
fmt.Println(err.Error())
os.Exit(failureExitCode)
}
os.Exit(successfulExitCode)
}

func bindKeyToEnvAndDefault(key, env, defaultVal string) error {
err := viper.BindEnv(key, env)
viper.SetDefault(key, defaultVal)
return err
}

// When building an executable for Windows and providing a name,
Expand Down
8 changes: 0 additions & 8 deletions config.env

This file was deleted.

2 changes: 1 addition & 1 deletion internal/commands/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestRunCreateProjectCommandWithInput(t *testing.T) {

func TestRunCreateProjectCommandWithInputBadFormat(t *testing.T) {
cmd := createASTTestCommand()
err := executeTestCommand(cmd, "-v", "scan", "create", "--input", "[]")
err := executeTestCommand(cmd, "-v", "project", "create", "--input", "[]")
assert.Assert(t, err != nil)
}

Expand Down
72 changes: 57 additions & 15 deletions internal/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,42 @@ package commands

import (
"fmt"
"strings"

"github.com/checkmarxDev/ast-cli/internal/wrappers"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

const (
verboseFlag = "verbose"
verboseFlagSh = "v"
sourcesFlag = "sources"
sourcesFlagSh = "s"
inputFlag = "input"
inputFlagSh = "i"
inputFileFlag = "inputFile"
inputFileFlagSh = "f"
limitFlag = "limit"
limitFlagSh = "l"
limitUsage = "The number of items to return"
offsetFlag = "offset"
offsetFlagSh = "o"
offsetUsage = "The number of items to skip before collecting the results"
verboseFlag = "verbose"
verboseFlagSh = "v"
verboseUsage = "Verbose mode"
sourcesFlag = "sources"
sourcesFlagSh = "s"
inputFlag = "input"
inputFlagSh = "i"
inputFileFlag = "inputFile"
inputFileFlagSh = "f"
limitFlag = "limit"
limitFlagSh = "l"
limitUsage = "The number of items to return"
offsetFlag = "offset"
offsetFlagSh = "o"
offsetUsage = "The number of items to skip before collecting the results"
AccessKeyIDEnv = "AST_ACCESS_KEY_ID"
accessKeyIDFlag = "key"
accessKeyIDFlagUsage = "The access key ID for AST"
AccessKeySecretEnv = "AST_ACCESS_KEY_SECRET"
accessKeySecretFlag = "secret"
accessKeySecretFlagUsage = "The access key secret for AST"
AstAuthenticationHostEnv = "AST_AUTHENTICATION_HOST"
)

var (
AccessKeyIDConfigKey = strings.ToLower(AccessKeyIDEnv)
AccessKeySecretConfigKey = strings.ToLower(AccessKeySecretEnv)
AstAuthenticationHostConfigKey = strings.ToLower(AstAuthenticationHostEnv)
)

// Return an AST CLI root command to execute
Expand All @@ -33,17 +49,43 @@ func NewAstCLI(scansWrapper wrappers.ScansWrapper,
Use: "ast",
Short: "A CLI wrapping Checkmarx AST APIs",
}
rootCmd.PersistentFlags().BoolP(verboseFlag, verboseFlagSh, false, "Verbose mode")

rootCmd.PersistentFlags().BoolP(verboseFlag, verboseFlagSh, false, verboseUsage)
rootCmd.PersistentFlags().String(accessKeyIDFlag, "", accessKeyIDFlagUsage)
rootCmd.PersistentFlags().String(accessKeySecretFlag, "", accessKeySecretFlagUsage)

// Bind the viper key ast_access_key_id to flag --key of the root command and
// to the environment variable AST_ACCESS_KEY_ID so that it will be taken from environment variables first
// and can be overridden by command flag --key
_ = viper.BindPFlag(AccessKeyIDConfigKey, rootCmd.PersistentFlags().Lookup(accessKeyIDFlag))
// Bind the viper key ast_access_key_secret to flag --secret of the root command and
// to the environment variable AST_ACCESS_KEY_SECRET so that it will be taken from environment variables first
// and can be overridden by command flag --secret
_ = viper.BindPFlag(AccessKeySecretConfigKey, rootCmd.PersistentFlags().Lookup(accessKeySecretFlag))

scanCmd := NewScanCommand(scansWrapper, uploadsWrapper)
projectCmd := NewProjectCommand(projectsWrapper)
resultCmd := NewResultCommand(resultsWrapper)
versionCmd := NewVersionCommand()

rootCmd.AddCommand(scanCmd, projectCmd, resultCmd, versionCmd)
rootCmd.SilenceUsage = true
return rootCmd
}

/*
func login(cmd *cobra.Command, args []string) error {
accessKeyID := viper.GetString(AccessKeyIDConfigKey)
accessKeySecret := viper.GetString(AccessKeySecretConfigKey)
authHost := viper.GetString(AstAuthenticationHostConfigKey)
var err error

fmt.Println("Authenticating with:", authHost)
fmt.Println("Key is:", accessKeyID)
fmt.Println("Secret IS:", accessKeySecret)
return err
}
*/
func PrintIfVerbose(verbose bool, msg string) {
if verbose {
fmt.Println(msg)
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func runCreateScanCommand(scansWrapper wrappers.ScansWrapper,
if sourcesFile != "" {
// Send a request to uploads service
var preSignedURL *string
preSignedURL, err = uploadsWrapper.Create(sourcesFile)
preSignedURL, err = uploadsWrapper.UploadFile(sourcesFile)
if err != nil {
return errors.Wrapf(err, "%s: Failed to upload sources file\n", failedCreating)
}
Expand Down
7 changes: 7 additions & 0 deletions internal/wrappers/credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package wrappers

type Credentials struct {
AuthenticationHost string
AccessKeyID string
AccessKeySecret string
}
1 change: 1 addition & 0 deletions internal/wrappers/projects-http.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
type ProjectsHTTPWrapper struct {
url string
contentType string
credentials *Credentials
}

func NewHTTPProjectsWrapper(url string) ProjectsWrapper {
Expand Down
3 changes: 2 additions & 1 deletion internal/wrappers/results-http.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
type ResultsHTTPWrapper struct {
url string
contentType string
credentials *Credentials
}

func NewHTTPResultsWrapper(url string) ResultsWrapper {
Expand All @@ -24,7 +25,7 @@ func NewHTTPResultsWrapper(url string) ResultsWrapper {
}

func (r *ResultsHTTPWrapper) GetByScanID(scanID string, limit, offset uint64) ([]ResultResponseModel, *ResultError, error) {
resp, err := getRequestWithLimitAndOffset(r.url+"/scan"+scanID+"/items", limit, offset)
resp, err := getRequestWithLimitAndOffset(r.url+"/"+scanID+"/items", limit, offset)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why not use fmt.sprintf() ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right. Done

if err != nil {
return nil, nil, err
}
Expand Down
18 changes: 7 additions & 11 deletions internal/wrappers/results.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package wrappers

import (
"math/big"
)

type ResultsWrapper interface {
GetByScanID(scanID string, limit, offset uint64) ([]ResultResponseModel, *ResultError, error)
}
Expand Down Expand Up @@ -39,23 +35,23 @@ type ResultNode struct {

type ResultResponseModel struct {
// Query ID
QueryID int32 `json:"queryID,omitempty"`
QueryID string `json:"queryID,omitempty"`
// Query name
QueryName string `json:"queryName,omitempty"`
// Query group; sperate by ':'
GroupName string `json:"groupName,omitempty"`
// Severity of result
Severity string `json:"severity,omitempty"`
// Common Weakness Enumeration ID
CweID int32 `json:"cweID,omitempty"`
CweID string `json:"cweID,omitempty"`
// ID of the path. changes from scan to scan.
PathID int32 `json:"pathID,omitempty"`
// ID of the Similarity feature (Indicator to identify a result by its first and last nodes)
SimilarityID int32 `json:"similarityID,omitempty"`
SimilarityID string `json:"similarityID,omitempty"`
// Same as similarityID but can change in the future (SAST feature)
UniqueID int32 `json:"uniqueID,omitempty"`
UniqueID string `json:"uniqueID,omitempty"`
// Confidence Level of the exsitin of the result
ConfidenceLevel big.Float `json:"confidenceLevel,omitempty"`
ConfidenceLevel int32 `json:"confidenceLevel,omitempty"`

Nodes []ResultNode `json:"nodes,omitempty"`
// ID of the customer tenant
Expand All @@ -65,15 +61,15 @@ type ResultResponseModel struct {
// Creation date of the result
CreatedAt string `json:"createdAt,omitempty"`

Classification string `json:"classification,omitempty"`
Classification int32 `json:"classification,omitempty"`
// Groups arrays
Groups []string `json:"groups,omitempty"`
// ID of the customer tenant
PathSystemID string `json:"pathSystemID,omitempty"`
// ID created from queryMetaInfo + similarityID + files name
PathSystemIDBySimiAndFilesPaths string `json:"pathSystemIDBySimiAndFilesPaths,omitempty"`
// enum of the current state(new,old,fixed)
Status string `json:"status,omitempty"`
Status int32 `json:"status,omitempty"`
// TBD
MetadataJSON string `json:"metadataJSON,omitempty"`
// TBD
Expand Down
1 change: 1 addition & 0 deletions internal/wrappers/scans-http.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const (
type ScansHTTPWrapper struct {
url string
contentType string
credentials *Credentials
}

func (s *ScansHTTPWrapper) Create(model *scansApi.Scan) (*scansApi.ScanResponseModel, *scansApi.ErrorModel, error) {
Expand Down
Loading