Add option to filter audit results by severity level (#969)

This commit is contained in:
John Slivka
2023-06-28 08:38:47 -05:00
committed by GitHub
parent 5595de4ef9
commit b0d86cdcd2
3 changed files with 81 additions and 2 deletions
+14 -2
View File
@@ -51,6 +51,7 @@ var (
helmValues string
checks []string
auditNamespace string
severityLevel string
skipSslValidation bool
uploadInsights bool
clusterName string
@@ -72,6 +73,7 @@ func init() {
auditCmd.PersistentFlags().StringVar(&helmValues, "helm-values", "", "Optional flag to add helm values")
auditCmd.PersistentFlags().StringSliceVar(&checks, "checks", []string{}, "Optional flag to specify specific checks to check")
auditCmd.PersistentFlags().StringVar(&auditNamespace, "namespace", "", "Namespace to audit. Only applies to in-cluster audits")
auditCmd.PersistentFlags().StringVar(&severityLevel, "severity", "", "Severity level used to filter results. Behaves like log levels. 'danger' is the least verbose (warning, danger)")
auditCmd.PersistentFlags().BoolVar(&skipSslValidation, "skip-ssl-validation", false, "Skip https certificate verification")
auditCmd.PersistentFlags().BoolVar(&uploadInsights, "upload-insights", false, "Upload scan results to Fairwinds Insights")
auditCmd.PersistentFlags().StringVar(&clusterName, "cluster-name", "", "Set --cluster-name to a descriptive name for the cluster you're auditing")
@@ -175,7 +177,7 @@ var auditCmd = &cobra.Command{
logrus.Println("Success! You can see your results at:")
logrus.Printf("%s/orgs/%s/clusters/%s/action-items\n", insightsHost, auth.Organization, clusterName)
} else {
outputAudit(auditData, auditOutputFile, auditOutputURL, auditOutputFormat, useColor, onlyShowFailedTests)
outputAudit(auditData, auditOutputFile, auditOutputURL, auditOutputFormat, useColor, onlyShowFailedTests, severityLevel)
}
summary := auditData.GetSummary()
@@ -223,10 +225,20 @@ func ProcessHelmTemplates(helmChart, helmValues string) (string, error) {
return dir, nil
}
func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFormat string, useColor bool, onlyShowFailedTests bool) {
func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFormat string, useColor bool, onlyShowFailedTests bool, severityLevel string) {
if onlyShowFailedTests {
auditData = auditData.RemoveSuccessfulResults()
}
if severityLevel != "" {
switch severityLevel {
case "danger":
auditData = auditData.FilterResultsBySeverityLevel(cfg.SeverityDanger)
case "warning":
auditData = auditData.FilterResultsBySeverityLevel(cfg.SeverityWarning)
}
}
var outputBytes []byte
var err error
if outputFormat == "score" {
+3
View File
@@ -52,6 +52,9 @@ webhook
--resource string Audit a specific resource, in the format namespace/kind/version/name, e.g. nginx-ingress/Deployment.apps/v1/default-backend.
--set-exit-code-below-score int Set an exit code of 4 when the score is below this threshold (1-100).
--set-exit-code-on-danger Set an exit code of 3 when the audit contains danger-level issues.
--severity string Severity level used to filter results. Behaves like log levels. 'danger' is the least verbose (warning, danger)
--skip-ssl-validation Skip https certificate verification
--upload-insights Upload scan results to Fairwinds Insights
# webhook flags
--disable-webhook-config-installer disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping.
+64
View File
@@ -53,6 +53,26 @@ type AuditData struct {
Score uint
}
// FilterResultsBySeverityLevel includes results according to the provided severity level:
// 'danger' is the least verbose, 'warning' is medium verbosity, default behavior will
// include all results, which currently also includes 'ignore'
func (res AuditData) FilterResultsBySeverityLevel(severityLevel config.Severity) AuditData {
resCopy := res
resCopy.Results = []Result{}
filteredResults := funk.Map(res.Results, func(auditDataResult Result) Result {
return auditDataResult.filterResultsBySeverityLevel(severityLevel)
}).([]Result)
for _, result := range filteredResults {
if result.isNotEmpty() {
resCopy.Results = append(resCopy.Results, result)
}
}
return resCopy
}
// RemoveSuccessfulResults removes all tests that have passed
func (res AuditData) RemoveSuccessfulResults() AuditData {
resCopy := res
@@ -108,6 +128,25 @@ func (res ResultSet) removeSuccessfulResults() ResultSet {
return newResults
}
func (res ResultSet) filterResultsBySeverityLevel(severityLevel config.Severity) ResultSet {
newResults := ResultSet{}
for k, resultMessage := range res {
switch severityLevel {
case config.SeverityDanger:
if resultMessage.Severity == config.SeverityDanger {
newResults[k] = resultMessage
}
case config.SeverityWarning:
if resultMessage.Severity == config.SeverityDanger || resultMessage.Severity == config.SeverityWarning {
newResults[k] = resultMessage
}
default:
return res
}
}
return newResults
}
// Result provides results for a Kubernetes object
type Result struct {
Name string
@@ -128,6 +167,16 @@ func (res Result) removeSuccessfulResults() Result {
return resCopy
}
func (res Result) filterResultsBySeverityLevel(severityLevel config.Severity) Result {
resCopy := res
resCopy.Results = res.Results.filterResultsBySeverityLevel(severityLevel)
if res.PodResult != nil {
podCopy := res.PodResult.filterResultsBySeverityLevel(severityLevel)
resCopy.PodResult = &podCopy
}
return resCopy
}
func (res Result) isNotEmpty() bool {
if res.PodResult != nil {
return res.PodResult.isNotEmpty()
@@ -151,6 +200,15 @@ func (res PodResult) removeSuccessfulResults() PodResult {
return resCopy
}
func (res PodResult) filterResultsBySeverityLevel(severityLevel config.Severity) PodResult {
resCopy := PodResult{}
resCopy.Results = res.Results.filterResultsBySeverityLevel(severityLevel)
resCopy.ContainerResults = funk.Map(res.ContainerResults, func(containerResult ContainerResult) ContainerResult {
return containerResult.filterResultsBySeverityLevel(severityLevel)
}).([]ContainerResult)
return resCopy
}
func (res PodResult) isNotEmpty() bool {
for _, cr := range res.ContainerResults {
if cr.isNotEmpty() {
@@ -172,6 +230,12 @@ func (res ContainerResult) removeSuccessfulResults() ContainerResult {
return resCopy
}
func (res ContainerResult) filterResultsBySeverityLevel(severityLevel config.Severity) ContainerResult {
resCopy := res
resCopy.Results = res.Results.filterResultsBySeverityLevel(severityLevel)
return resCopy
}
func (res ContainerResult) isNotEmpty() bool {
return res.Results.isNotEmpty()
}