Skip to content
Merged

dev #11

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
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
[![CircleCI](https://circleci.com/gh/CheckmarxDev/ast-cli/tree/master.svg?style=svg&circle-token=32eeef7505db60c11294e63db64e70882bde83b0)](https://circleci.com/gh/CheckmarxDev/ast-cli/tree/master)
# ast-cli
A CLI project wrapping the AST APIs

ast (name-of-binary) [commands]

## Building from source code
### Windows
When building an executable for Windows and providing a name, be sure to explicitly specify the .exe suffix when setting the executable’s name.
Inside the command prompt run:
**env GOOS=windows GOARCH=amd64 go build -o ./bin/ast.exe ./cmd**

## Running the CLI

### Windows
"bin/ast.exe" [commands]

## Authentication
In order to authenticate with AST there are 3 environment variables that needs to be set:
**AST_AUTHENTICATION_URI**: The authentication URI used by AST
**AST_ACCESS_KEY_ID**: The access key ID
**AST_ACCESS_KEY_SECRET**: The access key secret

Both access key ID and access key secret can be overriden by the flags **--key** and **--secret** respectively
101 changes: 59 additions & 42 deletions internal/commands/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"os"

"github.com/pkg/errors"

Expand Down Expand Up @@ -71,27 +70,24 @@ func runCreateProjectCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd
var input []byte
var err error

var verbose bool
var projInputFile string
var projInput string

verbose, _ = cmd.Flags().GetBool(verboseFlag)
projInput, _ = cmd.Flags().GetString(inputFlag)
projInputFile, _ = cmd.Flags().GetString(inputFileFlag)

PrintIfVerbose(verbose, fmt.Sprintf("%s: %s", inputFlag, projInput))
PrintIfVerbose(verbose, fmt.Sprintf("%s: %s", inputFileFlag, projInputFile))
PrintIfVerbose(fmt.Sprintf("%s: %s", inputFlag, projInput))
PrintIfVerbose(fmt.Sprintf("%s: %s", inputFileFlag, projInputFile))

if projInputFile != "" {
// Reading project from input file
PrintIfVerbose(verbose, fmt.Sprintf("Reading project input from file %s", projInputFile))
PrintIfVerbose(fmt.Sprintf("Reading project input from file %s", projInputFile))
input, err = ioutil.ReadFile(projInputFile)
if err != nil {
return errors.Wrapf(err, "%s: Failed to open input file", failedCreatingProj)
}
} else if projInput != "" {
// Reading from standard input
PrintIfVerbose(verbose, "Reading project input from console")
PrintIfVerbose("Reading project input from console")
input = bytes.NewBufferString(projInput).Bytes()
} else {
// No input was given
Expand All @@ -108,7 +104,7 @@ func runCreateProjectCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd

var payload []byte
payload, _ = json.Marshal(projModel)
PrintIfVerbose(verbose, fmt.Sprintf("Payload to projects service: %s\n", string(payload)))
PrintIfVerbose(fmt.Sprintf("Payload to projects service: %s\n", string(payload)))

projResponseModel, errorModel, err = projectsWrapper.Create(&projModel)
if err != nil {
Expand All @@ -119,14 +115,10 @@ func runCreateProjectCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd
if errorModel != nil {
return errors.Errorf("%s: CODE: %d, %s\n", failedCreatingProj, errorModel.Code, errorModel.Message)
} else if projResponseModel != nil {
var responseModelJSON []byte
responseModelJSON, err = json.Marshal(projResponseModel)
err = outputProject(cmd, projResponseModel)
if err != nil {
return errors.Wrapf(err, "%s: failed to serialize project response ", failedCreatingProj)
return errors.Wrapf(err, "%s", failedCreatingProj)
}
cmdOut := cmd.OutOrStdout()
fmt.Fprintln(os.Stdout, "Project created successfully")
fmt.Fprintln(cmdOut, string(responseModelJSON))
}
return nil
}
Expand All @@ -147,25 +139,10 @@ func runListProjectsCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd *
if errorModel != nil {
return errors.Errorf("%s: CODE: %d, %s\n", failedGettingAll, errorModel.Code, errorModel.Message)
} else if allProjectsModel != nil && allProjectsModel.Projects != nil {
cmdOut := cmd.OutOrStdout()
if cmdOut != os.Stdout {
var allProjectsJSON []byte
allProjectsJSON, err = json.Marshal(allProjectsModel)
if err != nil {
return errors.Wrapf(err, "%s: failed to serialize project response ", failedGettingAll)
}
fmt.Fprintln(cmdOut, string(allProjectsJSON))
}
for _, project := range allProjectsModel.Projects {
var responseModelJSON []byte
responseModelJSON, err = json.Marshal(project)
if err != nil {
return errors.Wrapf(err, "%s: failed to serialize project response ", failedGettingAll)
}
fmt.Fprintln(os.Stdout, "----------------------------")
fmt.Fprintln(os.Stdout, string(responseModelJSON))
err = outputProjects(cmd, allProjectsModel)
if err != nil {
return err
}
fmt.Fprintln(os.Stdout, "----------------------------")
}
return nil
}
Expand All @@ -188,14 +165,10 @@ func runGetProjectByIDCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd
if errorModel != nil {
return errors.Errorf("%s: CODE: %d, %s", failedGettingProj, errorModel.Code, errorModel.Message)
} else if projectResponseModel != nil {
var responseModelJSON []byte
responseModelJSON, err = json.Marshal(projectResponseModel)
err = outputProject(cmd, projectResponseModel)
if err != nil {
return errors.Wrapf(err, "%s: failed to serialize scan response ", failedGetting)
return err
}
cmdOut := cmd.OutOrStdout()
fmt.Fprintf(os.Stdout, "-----Project ID %s-----\n", projectResponseModel.ID)
fmt.Fprintln(cmdOut, string(responseModelJSON))
}
return nil
}
Expand Down Expand Up @@ -240,10 +213,54 @@ func runGetProjectsTagsCommand(projectsWrapper wrappers.ProjectsWrapper) func(cm
if err != nil {
return errors.Wrapf(err, "%s: failed to serialize project tags response ", failedGettingTags)
}
cmdOut := cmd.OutOrStdout()
fmt.Fprintln(os.Stdout, "-----Tags-----")
fmt.Fprintln(cmdOut, string(tagsJSON))
fmt.Fprintln(cmd.OutOrStdout(), string(tagsJSON))
}
return nil
}
}

func outputProjects(cmd *cobra.Command, model *projectsRESTApi.SlicedProjectsResponseModel) error {
if IsJSONFormat() {
var allProjectsJSON []byte
allProjectsJSON, err := json.Marshal(model)
if err != nil {
return errors.Wrapf(err, "%s: failed to serialize project response ", failedGettingAll)
}
fmt.Fprintln(cmd.OutOrStdout(), string(allProjectsJSON))
} else if IsPrettyFormat() {
for _, project := range model.Projects {
outputSingleProject(&projectsRESTApi.ProjectResponseModel{
ID: project.ID,
Created: project.Created,
Updated: project.Updated,
Tags: project.Tags,
})
}
}
return nil
}

func outputProject(cmd *cobra.Command, model *projectsRESTApi.ProjectResponseModel) error {
if err := ValidateFormat(); err != nil {
return err
}

if IsJSONFormat() {
responseModelJSON, err := json.Marshal(model)
if err != nil {
return errors.Wrapf(err, "Failed to serialize project response")
}
fmt.Fprintln(cmd.OutOrStdout(), string(responseModelJSON))
} else if IsPrettyFormat() {
outputSingleProject(model)
}
return nil
}

func outputSingleProject(model *projectsRESTApi.ProjectResponseModel) {
fmt.Println("----------------------------")
fmt.Println("Project ID:", model.ID)
fmt.Println("Created at:", model.Created)
fmt.Println("Updated at:", model.Updated)
fmt.Println("Tags:", model.Tags)
}
13 changes: 13 additions & 0 deletions internal/commands/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ func TestRunCreateProjectCommandWithInput(t *testing.T) {
assert.NilError(t, err)
}

func TestRunCreateProjectCommandWithInvalidFormat(t *testing.T) {
cmd := createASTTestCommand()
err := executeTestCommand(cmd, "--format", "non-sense", "-v", "project", "create", "--input", "{\"id\": \"test_project\"}")
assert.Assert(t, err != nil)
assert.Assert(t, err.Error() == "Failed creating a project: Invalid format non-sense")
}

func TestRunCreateProjectCommandWithInputPretty(t *testing.T) {
cmd := createASTTestCommand()
err := executeTestCommand(cmd, "--format", "pretty", "-v", "project", "create", "--input", "{\"id\": \"test_project\"}")
assert.NilError(t, err)
}

func TestRunCreateProjectCommandWithInputBadFormat(t *testing.T) {
cmd := createASTTestCommand()
err := executeTestCommand(cmd, "-v", "project", "create", "--input", "[]")
Expand Down
32 changes: 27 additions & 5 deletions internal/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"strings"

"github.com/pkg/errors"

"github.com/checkmarxDev/ast-cli/internal/wrappers"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -36,6 +38,10 @@ const (
astAuthenticationURIFlagUsage = "The authentication URI for AST"
insecureFlag = "insecure"
insecureFlagUsage = "Ignore TLS certificate validations"
formatFlag = "format"
formatFlagUsage = "Format for the output. One of [json, pretty]. Default is JSON"
formatJSON = "json"
formatPretty = "pretty"
)

var (
Expand All @@ -59,6 +65,7 @@ func NewAstCLI(scansWrapper wrappers.ScansWrapper,
rootCmd.PersistentFlags().String(accessKeySecretFlag, "", accessKeySecretFlagUsage)
rootCmd.PersistentFlags().String(astAuthenticationURIFlag, "", astAuthenticationURIFlagUsage)
rootCmd.PersistentFlags().Bool(insecureFlag, false, insecureFlagUsage)
rootCmd.PersistentFlags().String(formatFlag, formatJSON, formatFlagUsage)

// 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
Expand All @@ -67,7 +74,9 @@ func NewAstCLI(scansWrapper wrappers.ScansWrapper,
_ = viper.BindPFlag(AccessKeySecretConfigKey, rootCmd.PersistentFlags().Lookup(accessKeySecretFlag))
_ = viper.BindPFlag(AstAuthenticationURIConfigKey, rootCmd.PersistentFlags().Lookup(astAuthenticationURIFlag))
// Key here is the actual flag since it doesn't use an environment variable
_ = viper.BindPFlag(verboseFlag, rootCmd.PersistentFlags().Lookup(verboseFlag))
_ = viper.BindPFlag(insecureFlag, rootCmd.PersistentFlags().Lookup(insecureFlag))
_ = viper.BindPFlag(formatFlag, rootCmd.PersistentFlags().Lookup(formatFlag))

scanCmd := NewScanCommand(scansWrapper, uploadsWrapper)
projectCmd := NewProjectCommand(projectsWrapper)
Expand All @@ -81,17 +90,30 @@ func NewAstCLI(scansWrapper wrappers.ScansWrapper,
return rootCmd
}

func PrintIfVerbose(verbose bool, msg string) {
if verbose {
func PrintIfVerbose(msg string) {
if viper.GetBool(verboseFlag) {

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.

nice!

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.

this makes us bind with viper...
but i guess its ok.

fmt.Println(msg)
}
}

func getLimitAndOffset(cmd *cobra.Command) (limit, offset uint64) {
verbose, _ := cmd.Flags().GetBool(verboseFlag)
limit, _ = cmd.Flags().GetUint64(limitFlag)
offset, _ = cmd.Flags().GetUint64(offsetFlag)
PrintIfVerbose(verbose, fmt.Sprintf("%s: %d", limitFlag, limit))
PrintIfVerbose(verbose, fmt.Sprintf("%s: %d", offsetFlag, offset))
PrintIfVerbose(fmt.Sprintf("%s: %d", limitFlag, limit))
PrintIfVerbose(fmt.Sprintf("%s: %d", offsetFlag, offset))
return
}

func IsJSONFormat() bool {
return strings.EqualFold(viper.GetString(formatFlag), formatJSON)
}
func IsPrettyFormat() bool {
return strings.EqualFold(viper.GetString(formatFlag), formatPretty)
}

func ValidateFormat() error {
if !(IsPrettyFormat() || IsJSONFormat()) {
return errors.Errorf("Invalid format %s", viper.GetString(formatFlag))
}
return nil
}
Loading