diff --git a/Makefile b/Makefile index 32f56ac..2200893 100755 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ TEMP = $(subst /, ,$@) OS = $(word 1, $(TEMP)) ARCH = $(word 2, $(TEMP)) -VERSION := $(VERSION_TAG)-$(COMMIT) +VERSION := $(VERSION_TAG) export GO111MODULE=on @@ -27,7 +27,7 @@ clean: go clean help: - @echo + @echo @echo "Available commands: all | image | help" @echo " make all -- to build kdt in all supported environments" @echo " make image -- to build docker image" diff --git a/cmd/release.go b/cmd/release.go index 6b9f032..1dc4509 100755 --- a/cmd/release.go +++ b/cmd/release.go @@ -1,12 +1,14 @@ /* -Copyright © 2020 Kondukto +Copyright © 2023 Kondukto */ package cmd import ( + "errors" "fmt" + "strings" "github.com/kondukto-io/kdt/client" @@ -26,7 +28,11 @@ func init() { releaseCmd.Flags().StringP("project", "p", "", "project name or id") releaseCmd.Flags().Bool("sast", false, "sast criteria status") releaseCmd.Flags().Bool("dast", false, "dast criteria status") + releaseCmd.Flags().Bool("pentest", false, "pentest criteria status") + releaseCmd.Flags().Bool("iast", false, "iast criteria status") releaseCmd.Flags().Bool("sca", false, "sca criteria status") + releaseCmd.Flags().Bool("cs", false, "cs criteria status") + releaseCmd.Flags().Bool("iac", false, "iac criteria status") _ = releaseCmd.MarkFlagRequired("project") } @@ -47,16 +53,15 @@ func releaseRootCommand(cmd *cobra.Command, _ []string) { } const statusUndefined = "undefined" - const statusFail = "fail" if rs.Status == statusUndefined { qwm(ExitCodeSuccess, "project has no release criteria") } releaseCriteriaRows := []Row{ - {Columns: []string{"STATUS", "SAST", "DAST", "SCA"}}, - {Columns: []string{"------", "----", "----", "---"}}, - {Columns: []string{rs.Status, rs.SAST.Status, rs.DAST.Status, rs.SCA.Status}}, + {Columns: []string{"STATUS", "SAST", "DAST", "PENTEST", "IAST", "SCA", "CS", "IAC"}}, + {Columns: []string{"------", "----", "----", "-------", "----", "---", "--", "---"}}, + {Columns: []string{rs.Status, rs.SAST.Status, rs.DAST.Status, rs.PENTEST.Status, rs.IAST.Status, rs.SCA.Status, rs.CS.Status, rs.IAC.Status}}, } TableWriter(releaseCriteriaRows...) @@ -67,31 +72,122 @@ func releaseRootCommand(cmd *cobra.Command, _ []string) { dast, err := cmd.Flags().GetBool("dast") if err != nil { - qwm(ExitCodeError, "failed to parse sast flag") + qwm(ExitCodeError, "failed to parse dast flag") + } + + pentest, err := cmd.Flags().GetBool("pentest") + if err != nil { + qwm(ExitCodeError, "failed to parse pentest flag") + } + + iast, err := cmd.Flags().GetBool("iast") + if err != nil { + qwm(ExitCodeError, "failed to parse iast flag") } sca, err := cmd.Flags().GetBool("sca") if err != nil { - qwm(ExitCodeError, "failed to parse sast flag") + qwm(ExitCodeError, "failed to parse sca flag") } - specific := sast || dast || sca + cs, err := cmd.Flags().GetBool("cs") + if err != nil { + qwm(ExitCodeError, "failed to parse cs flag") + } - if !specific && rs.Status == statusFail { - qwm(ExitCodeError, "project does not pass release criteria") + iac, err := cmd.Flags().GetBool("iac") + if err != nil { + qwm(ExitCodeError, "failed to parse iac flag") } - if sast && rs.SAST.Status == statusFail { - qwm(ExitCodeError, "project does not pass SAST release criteria") + isSpecific := sast || dast || pentest || iast || sca || cs || iac + + var spesificMap = make(map[string]bool, 0) + spesificMap["SAST"] = sast + spesificMap["DAST"] = dast + spesificMap["PENTEST"] = pentest + spesificMap["IAST"] = iast + spesificMap["SCA"] = sca + spesificMap["CS"] = cs + spesificMap["IAC"] = iac + + isReleaseFailed(rs, isSpecific, spesificMap) +} + +func isReleaseFailed(release *client.ReleaseStatus, isSpecific bool, specificMap map[string]bool) { + const statusFail = "fail" + + if release.Status != statusFail { + return + } + + var failedScans = make(map[string]string, 0) + + if release.SAST.Status == statusFail { + failedScans["SAST"] = release.SAST.ScanID + } + if release.DAST.Status == statusFail { + failedScans["DAST"] = release.DAST.ScanID + } + if release.PENTEST.Status == statusFail { + failedScans["PENTEST"] = release.PENTEST.ScanID + } + if release.IAST.Status == statusFail { + failedScans["IAST"] = release.IAST.ScanID + } + if release.SCA.Status == statusFail { + failedScans["SCA"] = release.SCA.ScanID + } + if release.CS.Status == statusFail { + failedScans["CS"] = release.CS.ScanID + } + if release.IAC.Status == statusFail { + failedScans["IAC"] = release.IAC.ScanID } - if dast && rs.DAST.Status == statusFail { - qwm(ExitCodeError, "project does not pass DAST release criteria") + if verbose { + c, err := client.New() + if err != nil { + qwe(ExitCodeError, err, "could not initialize Kondukto client") + } + + for toolType, scanID := range failedScans { + if isSpecific { + if !specificMap[toolType] { + continue + } + } + + fmt.Println() + fmt.Println("-----------------------------------------------------------------") + fmt.Printf("[!] project does not pass release criteria due to [%s] failure\n", toolType) + scan, err := c.FindScanByID(scanID) + if err != nil { + qwe(ExitCodeError, err, "failed to fetch scan summary") + } + + printScanSummary(scan) + fmt.Println("-----------------------------------------------------------------") + } } - if sca && rs.SCA.Status == statusFail { - qwm(ExitCodeError, "project does not pass SCA release criteria") + var failedToolTypes []string + + for toolType := range failedScans { + if isSpecific { + if specificMap[toolType] { + failedToolTypes = append(failedToolTypes, toolType) + } + } else { + failedToolTypes = append(failedToolTypes, toolType) + } } - qwm(ExitCodeSuccess, "project passes release criteria") + if len(failedToolTypes) == 0 { + returnMSG := fmt.Sprintf("project passes release criteria") + qwe(ExitCodeSuccess, errors.New(returnMSG)) + } else { + returnMSG := fmt.Sprintf("project does not pass release criteria due to [%s] failure", strings.Join(failedToolTypes, ", ")) + qwe(ExitCodeError, errors.New(returnMSG)) + } } diff --git a/cmd/scan.go b/cmd/scan.go index 3117eb1..456e48e 100755 --- a/cmd/scan.go +++ b/cmd/scan.go @@ -1069,10 +1069,10 @@ func checkRelease(scan *client.ScanDetail) error { return fmt.Errorf("failed to get release status: %w", err) } - return isReleaseFailed(rs) + return isScanReleaseFailed(rs) } -func isReleaseFailed(release *client.ReleaseStatus) error { +func isScanReleaseFailed(release *client.ReleaseStatus) error { const statusFail = "fail" if release.Status != statusFail { @@ -1123,12 +1123,12 @@ func isReleaseFailed(release *client.ReleaseStatus) error { } } - var failedTools []string + var failedToolTypes []string for toolType := range failedScans { - failedTools = append(failedTools, toolType) + failedToolTypes = append(failedToolTypes, toolType) } - returnMSG := fmt.Sprintf("project does not pass release criteria due to [%s] failure", strings.Join(failedTools, ", ")) + returnMSG := fmt.Sprintf("project does not pass release criteria due to [%s] failure", strings.Join(failedToolTypes, ", ")) return errors.New(returnMSG) }