mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
support threshold
This commit is contained in:
@@ -91,7 +91,7 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser
|
||||
| `-e`/`--exclude-namespaces` | Scan all namespaces | Namespaces to exclude from scanning. Recommended to exclude `kube-system` and `kube-public` namespaces | |
|
||||
| `--include-namespaces` | Scan all namespaces | Scan specific namespaces | |
|
||||
| `-s`/`--silent` | Display progress messages | Silent progress messages | |
|
||||
| `-t`/`--fail-threshold` | `0` (do not fail) | fail command (return exit code 1) if result is below threshold | `0` -> `100` |
|
||||
| `-t`/`--fail-threshold` | `100` (do not fail) | fail command (return exit code 1) if result is above threshold | `0` -> `100` |
|
||||
| `-f`/`--format` | `pretty-printer` | Output format | `pretty-printer`/`json`/`junit`/`prometheus` |
|
||||
| `-o`/`--output` | print to stdout | Save scan result in file | |
|
||||
| `--use-from` | | Load local framework object from specified path. If not used will download latest | |
|
||||
|
||||
@@ -180,7 +180,9 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
|
||||
}
|
||||
}
|
||||
if c.configObj.ClusterName == "" {
|
||||
c.configObj.ClusterName = adoptClusterName(k8sinterface.GetClusterName())
|
||||
c.configObj.ClusterName = AdoptClusterName(k8sinterface.GetClusterName())
|
||||
} else { // override the cluster name if it has unwanted characters
|
||||
c.configObj.ClusterName = AdoptClusterName(c.configObj.ClusterName)
|
||||
}
|
||||
|
||||
return c
|
||||
@@ -425,6 +427,6 @@ func DeleteConfigFile() error {
|
||||
return os.Remove(ConfigFileFullPath())
|
||||
}
|
||||
|
||||
func adoptClusterName(clusterName string) string {
|
||||
func AdoptClusterName(clusterName string) string {
|
||||
return strings.ReplaceAll(clusterName, "/", "-")
|
||||
}
|
||||
|
||||
@@ -65,6 +65,9 @@ func NewVersionCheckRequest(buildNumber, frameworkName, frameworkVersion, scanni
|
||||
if buildNumber == "" {
|
||||
buildNumber = UnknownBuildNumber
|
||||
}
|
||||
if scanningTarget == "" {
|
||||
scanningTarget = "unknown"
|
||||
}
|
||||
return &VersionCheckRequest{
|
||||
Client: "kubescape",
|
||||
ClientVersion: buildNumber,
|
||||
@@ -82,7 +85,7 @@ func (v *VersionCheckHandlerMock) CheckLatestVersion(versionData *VersionCheckRe
|
||||
func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckRequest) error {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Println("failed to get latest version")
|
||||
WarningDisplay(os.Stderr, "failed to get latest version\n")
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -93,7 +96,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
|
||||
|
||||
if latestVersion.ClientUpdate != "" {
|
||||
if BuildNumber != "" && BuildNumber < latestVersion.ClientUpdate {
|
||||
fmt.Println(warningMessage(latestVersion.Client, latestVersion.ClientUpdate))
|
||||
WarningDisplay(os.Stderr, warningMessage(latestVersion.Client, latestVersion.ClientUpdate), "\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +106,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
|
||||
// }
|
||||
|
||||
if latestVersion.Message != "" {
|
||||
fmt.Println(latestVersion.Message)
|
||||
InfoDisplay(os.Stderr, latestVersion.Message, "\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+58
-36
@@ -2,6 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
@@ -25,43 +26,10 @@ var downloadCmd = &cobra.Command{
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if strings.EqualFold(args[0], "framework") {
|
||||
downloadInfo.FrameworkName = strings.ToLower(args[1])
|
||||
g := getter.NewDownloadReleasedPolicy()
|
||||
if err := g.SetRegoObjects(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if downloadInfo.Path == "" {
|
||||
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName + ".json")
|
||||
}
|
||||
frameworks, err := g.GetFramework(downloadInfo.FrameworkName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = getter.SaveFrameworkInFile(frameworks, downloadInfo.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if strings.EqualFold(args[0], "control") {
|
||||
downloadInfo.ControlName = strings.ToLower(args[1])
|
||||
g := getter.NewDownloadReleasedPolicy()
|
||||
if err := g.SetRegoObjects(); err != nil {
|
||||
return err
|
||||
}
|
||||
if downloadInfo.Path == "" {
|
||||
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.ControlName + ".json")
|
||||
}
|
||||
controls, err := g.GetControl(downloadInfo.ControlName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = getter.SaveControlInFile(controls, downloadInfo.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := download(args); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -71,3 +39,57 @@ func init() {
|
||||
downloadInfo = cautils.DownloadInfo{}
|
||||
downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If specified, will store save to `~/.kubescape/<framework name>.json`")
|
||||
}
|
||||
|
||||
func download(args []string) error {
|
||||
switch strings.ToLower(args[0]) {
|
||||
case "framework":
|
||||
return downloadFramework(args[1])
|
||||
case "control":
|
||||
return downloadControl(args[1])
|
||||
// case "exceptions":
|
||||
// case "artifacts":
|
||||
default:
|
||||
return fmt.Errorf("unknown command to download")
|
||||
}
|
||||
}
|
||||
|
||||
func downloadFramework(frameworkName string) error {
|
||||
downloadInfo.FrameworkName = strings.ToLower(frameworkName)
|
||||
g := getter.NewDownloadReleasedPolicy()
|
||||
if err := g.SetRegoObjects(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if downloadInfo.Path == "" {
|
||||
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName + ".json")
|
||||
}
|
||||
frameworks, err := g.GetFramework(downloadInfo.FrameworkName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = getter.SaveFrameworkInFile(frameworks, downloadInfo.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadControl(controlName string) error {
|
||||
downloadInfo.ControlName = strings.ToLower(controlName)
|
||||
g := getter.NewDownloadReleasedPolicy()
|
||||
if err := g.SetRegoObjects(); err != nil {
|
||||
return err
|
||||
}
|
||||
if downloadInfo.Path == "" {
|
||||
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.ControlName + ".json")
|
||||
}
|
||||
controls, err := g.GetControl(downloadInfo.ControlName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = getter.SaveControlInFile(controls, downloadInfo.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ var frameworkCmd = &cobra.Command{
|
||||
cautils.SetSilentMode(scanInfo.Silent)
|
||||
err := clihandler.ScanCliSetup(&scanInfo)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -39,7 +39,7 @@ func init() {
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.ControlsInputs, "controls-config", "", "Path to an controls-config obj. If not set will download controls-config from ARMO management portal")
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from ARMO management portal")
|
||||
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Recommended: kube-system,kube-public")
|
||||
scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 0, "Failure threshold is the percent below which the command fails and returns exit code 1")
|
||||
scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 100, "Failure threshold is the percent above which the command fails and returns exit code 1")
|
||||
scanCmd.PersistentFlags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output format. Supported formats: "pretty-printer"/"json"/"junit"/"prometheus"`)
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.IncludeNamespaces, "include-namespaces", "", "scan specific namespaces. e.g: --include-namespaces ns-a,ns-b")
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to Armo backend. Use this flag if you ran with the '--submit' flag in the past and you do not want to submit your current scan results")
|
||||
@@ -50,7 +50,7 @@ func init() {
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages")
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to Armo management portal where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted")
|
||||
|
||||
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensor, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls")
|
||||
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensor, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls")
|
||||
hostF.NoOptDefVal = "true"
|
||||
hostF.DefValue = "false, for no TTY in stdin"
|
||||
}
|
||||
|
||||
@@ -121,9 +121,8 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error {
|
||||
// print report url
|
||||
interfaces.report.DisplayReportURL()
|
||||
|
||||
adjustedFailThreshold := float32(scanInfo.FailThreshold) / 100
|
||||
if score >= adjustedFailThreshold {
|
||||
return fmt.Errorf("Scan score is below threshold")
|
||||
if score >= float32(scanInfo.FailThreshold) {
|
||||
return fmt.Errorf("scan risk-score %.2f is above permitted threshold %d", score, scanInfo.FailThreshold)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -38,7 +38,6 @@ func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.Kubern
|
||||
func getReporter(tenantConfig cautils.ITenantConfig, submit bool) reporter.IReport {
|
||||
if submit {
|
||||
return reporter.NewReportEventReceiver(tenantConfig.GetConfigObj())
|
||||
|
||||
}
|
||||
return reporter.NewReportMock()
|
||||
}
|
||||
|
||||
@@ -67,9 +67,9 @@ func (opaHandler *OPAProcessorHandler) ProcessRulesListenner() {
|
||||
opap.updateResults()
|
||||
|
||||
// update score
|
||||
// opap.updateScore()
|
||||
scoreutil := score.NewScore(opaSessionObj.AllResources)
|
||||
scoreutil.Calculate(opaSessionObj.PostureReport.FrameworkReports)
|
||||
|
||||
// report
|
||||
*opaHandler.reportResults <- opaSessionObj
|
||||
}
|
||||
|
||||
@@ -35,10 +35,12 @@ func (printer *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj)
|
||||
warningResources := []string{}
|
||||
allResources := []string{}
|
||||
frameworkNames := []string{}
|
||||
frameworkScores := []float32{}
|
||||
|
||||
var overallRiskScore float32 = 0
|
||||
for _, frameworkReport := range opaSessionObj.PostureReport.FrameworkReports {
|
||||
frameworkNames = append(frameworkNames, frameworkReport.Name)
|
||||
frameworkScores = append(frameworkScores, frameworkReport.Score)
|
||||
failedResources = reporthandling.GetUniqueResourcesIDs(append(failedResources, frameworkReport.ListResourcesIDs().GetFailedResources()...))
|
||||
warningResources = reporthandling.GetUniqueResourcesIDs(append(warningResources, frameworkReport.ListResourcesIDs().GetWarningResources()...))
|
||||
allResources = reporthandling.GetUniqueResourcesIDs(append(allResources, frameworkReport.ListResourcesIDs().GetAllResources()...))
|
||||
@@ -56,7 +58,7 @@ func (printer *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj)
|
||||
}
|
||||
|
||||
printer.printResults()
|
||||
printer.printSummaryTable(frameworkNames)
|
||||
printer.printSummaryTable(frameworkNames, frameworkScores)
|
||||
|
||||
}
|
||||
|
||||
@@ -218,9 +220,9 @@ func generateFooter(printer *PrettyPrinter) []string {
|
||||
|
||||
return row
|
||||
}
|
||||
func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string) {
|
||||
func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string, frameworkScores []float32) {
|
||||
// For control scan framework will be nil
|
||||
printer.printFramework(frameworksNames)
|
||||
printer.printFramework(frameworksNames, frameworkScores)
|
||||
|
||||
summaryTable := tablewriter.NewWriter(printer.writer)
|
||||
summaryTable.SetAutoWrapText(false)
|
||||
@@ -240,16 +242,16 @@ func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string) {
|
||||
summaryTable.Render()
|
||||
}
|
||||
|
||||
func (printer *PrettyPrinter) printFramework(frameworksNames []string) {
|
||||
func (printer *PrettyPrinter) printFramework(frameworksNames []string, frameworkScores []float32) {
|
||||
if len(frameworksNames) == 1 {
|
||||
cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("%s FRAMEWORK\n", frameworksNames[0]))
|
||||
cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("FRAMEWORK %s\n", frameworksNames[0]))
|
||||
} else if len(frameworksNames) > 1 {
|
||||
p := ""
|
||||
p := "FRAMEWORKS: "
|
||||
for i := 0; i < len(frameworksNames)-1; i++ {
|
||||
p += frameworksNames[i] + ", "
|
||||
p += fmt.Sprintf("%s (risk: %.2f), ", frameworksNames[i], frameworkScores[i])
|
||||
}
|
||||
p += frameworksNames[len(frameworksNames)-1]
|
||||
cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("%s FRAMEWORKS\n", p))
|
||||
p += fmt.Sprintf("%s (risk: %.2f)\n", frameworksNames[len(frameworksNames)-1], frameworkScores[len(frameworkScores)-1])
|
||||
cautils.InfoTextDisplay(printer.writer, p)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,10 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive
|
||||
|
||||
func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASessionObj) error {
|
||||
|
||||
if report.customerGUID == "" || report.clusterName == "" {
|
||||
return fmt.Errorf("missing accout ID or cluster name. AccountID: '%s', Cluster name: '%s'", report.customerGUID, report.clusterName)
|
||||
}
|
||||
|
||||
if err := report.prepareReport(opaSessionObj.PostureReport, opaSessionObj.AllResources); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -54,7 +58,7 @@ func (report *ReportEventReceiver) SetCustomerGUID(customerGUID string) {
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) SetClusterName(clusterName string) {
|
||||
report.clusterName = clusterName
|
||||
report.clusterName = cautils.AdoptClusterName(clusterName) // clean cluster name
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) prepareReport(postureReport *reporthandling.PostureReport, allResources map[string]workloadinterface.IMetadata) error {
|
||||
|
||||
Reference in New Issue
Block a user