Skip to content

Commit

Permalink
Add option to filter audit results by severity level (#969)
Browse files Browse the repository at this point in the history
  • Loading branch information
jslivka committed Jun 28, 2023
1 parent 5595de4 commit b0d86cd
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 2 deletions.
16 changes: 14 additions & 2 deletions cmd/polaris/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ var (
helmValues string
checks []string
auditNamespace string
severityLevel string
skipSslValidation bool
uploadInsights bool
clusterName string
Expand All @@ -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")
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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" {
Expand Down
3 changes: 3 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
64 changes: 64 additions & 0 deletions pkg/validator/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand All @@ -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() {
Expand All @@ -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()
}
Expand Down

0 comments on commit b0d86cd

Please sign in to comment.