Skip to content

Commit

Permalink
feat(policies): disable color in policy report when writing to file (#…
Browse files Browse the repository at this point in the history
…170)

feat: disable color in policy report when writing to file

refactor: move policy report methods to output/policy.go
  • Loading branch information
elsapet committed Nov 28, 2022
1 parent b9c4f0e commit 8ee6c95
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 131 deletions.
133 changes: 2 additions & 131 deletions pkg/report/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,64 +4,30 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"

"github.com/bearer/curio/pkg/commands/process/settings"
"github.com/bearer/curio/pkg/flag"
"github.com/bearer/curio/pkg/report/output/dataflow"
"golang.org/x/exp/maps"

"github.com/bearer/curio/pkg/report/output/detectors"
"github.com/bearer/curio/pkg/report/output/policies"
"github.com/bearer/curio/pkg/report/output/stats"
"github.com/bearer/curio/pkg/types"
"github.com/fatih/color"
"gopkg.in/yaml.v3"

"github.com/rs/zerolog"
)

var ErrUndefinedFormat = errors.New("undefined output format")
var underline = color.New(color.Underline).SprintFunc()
var severityColorFns = map[string]func(x ...interface{}) string{
settings.LevelCritical: color.New(color.FgRed).SprintFunc(),
settings.LevelHigh: color.New(color.FgHiRed).SprintFunc(),
settings.LevelMedium: color.New(color.FgYellow).SprintFunc(),
settings.LevelLow: color.New(color.FgBlue).SprintFunc(),
}

func ReportPolicies(report types.Report, output *zerolog.Event, config settings.Config) error {
policyResults, err := getPolicyReportOutput(report, config)
if err != nil {
return err
}

reportStr := &strings.Builder{}
reportStr.WriteString("\n\nPolicy Report\n")
reportStr.WriteString("\n=====================================")

writePolicyListToString(reportStr, config.Policies)

breachedPolicies := map[string]map[string]bool{
settings.LevelCritical: make(map[string]bool),
settings.LevelHigh: make(map[string]bool),
settings.LevelMedium: make(map[string]bool),
settings.LevelLow: make(map[string]bool),
}

for _, policyLevel := range []string{
settings.LevelCritical,
settings.LevelHigh,
settings.LevelMedium,
settings.LevelLow,
} {
for _, policyBreach := range policyResults[policyLevel] {
breachedPolicies[policyLevel][policyBreach.PolicyName] = true
writePolicyBreachToString(reportStr, policyBreach, policyLevel)
}
}

writeSummaryToString(reportStr, policyResults, len(config.Policies), breachedPolicies)
outputToFile := config.Report.Output != ""
reportStr := policies.BuildReportString(policyResults, config.Policies, outputToFile)

output.Msg(reportStr.String())

Expand Down Expand Up @@ -157,98 +123,3 @@ func getDataflow(report types.Report, config settings.Config, isInternal bool) (

return dataflow.GetOutput(reportedDetections, config, isInternal)
}

func writePolicyListToString(reportStr *strings.Builder, policies map[string]*settings.Policy) {
// list policies that were run
reportStr.WriteString("\nPolicy list: \n\n")
for key := range policies {
policy := policies[key]
reportStr.WriteString(color.HiBlackString("- " + policy.Name + "\n"))
}
}

func writeSummaryToString(
reportStr *strings.Builder,
policyResults map[string][]policies.PolicyResult,
policyCount int, breachedPolicies map[string]map[string]bool,
) {
reportStr.WriteString("\n=====================================")

// give summary including counts
if len(policyResults) == 0 {
reportStr.WriteString("\n\n")
reportStr.WriteString(color.HiGreenString("SUCCESS\n\n"))
reportStr.WriteString(fmt.Sprint(policyCount) + " policies were run and no breaches were detected.\n\n")
return
}

criticalCount := len(policyResults[settings.LevelCritical])
highCount := len(policyResults[settings.LevelHigh])
mediumCount := len(policyResults[settings.LevelMedium])
lowCount := len(policyResults[settings.LevelLow])

totalCount := criticalCount + highCount + mediumCount + lowCount

reportStr.WriteString("\n\n")
reportStr.WriteString(color.RedString("Policy breaches detected\n\n"))
reportStr.WriteString(fmt.Sprint(policyCount) + " policies were run ")
reportStr.WriteString("and " + fmt.Sprint(totalCount) + " breaches were detected.\n\n")

// critical count
reportStr.WriteString(formatSeverity(settings.LevelCritical) + fmt.Sprint(criticalCount))
if len(breachedPolicies[settings.LevelCritical]) > 0 {
reportStr.WriteString(" (" + strings.Join(maps.Keys(breachedPolicies[settings.LevelCritical]), ", ") + ")")
}
// high count
reportStr.WriteString("\n" + formatSeverity(settings.LevelHigh) + fmt.Sprint(highCount))
if len(breachedPolicies[settings.LevelHigh]) > 0 {
reportStr.WriteString(" (" + strings.Join(maps.Keys(breachedPolicies[settings.LevelHigh]), ", ") + ")")
}
// medium count
reportStr.WriteString("\n" + formatSeverity(settings.LevelMedium) + fmt.Sprint(mediumCount))
if len(breachedPolicies[settings.LevelMedium]) > 0 {
reportStr.WriteString(" (" + strings.Join(maps.Keys(breachedPolicies[settings.LevelMedium]), ", ") + ")")
}
// low count
reportStr.WriteString("\n" + formatSeverity(settings.LevelLow) + fmt.Sprint(lowCount))
if len(breachedPolicies[settings.LevelLow]) > 0 {
reportStr.WriteString(" (" + strings.Join(maps.Keys(breachedPolicies[settings.LevelLow]), ", ") + ")")
}

reportStr.WriteString("\n\n")
}

func writePolicyBreachToString(reportStr *strings.Builder, policyBreach policies.PolicyResult, policySeverity string) {
reportStr.WriteString("\n\n")
reportStr.WriteString(formatSeverity(policySeverity))
reportStr.WriteString(policyBreach.PolicyName + " policy breach with " + policyBreach.CategoryGroup + "\n")
reportStr.WriteString(color.HiBlackString(policyBreach.PolicyDescription + "\n"))
reportStr.WriteString("\n")
reportStr.WriteString(color.HiBlueString("File: " + underline(policyBreach.Filename+":"+fmt.Sprint(policyBreach.LineNumber)) + "\n"))
reportStr.WriteString("\n")
reportStr.WriteString(highlightCodeExtract(policyBreach.LineNumber, policyBreach.ParentLineNumber, policyBreach.ParentContent))
}

func formatSeverity(policySeverity string) string {
severityColorFn, ok := severityColorFns[policySeverity]
if !ok {
return strings.ToUpper(policySeverity)
}
return severityColorFn(strings.ToUpper(policySeverity + ": "))
}

func highlightCodeExtract(lineNumber int, extractStartLineNumber int, extract string) string {
result := ""
targetIndex := lineNumber - extractStartLineNumber
for index, line := range strings.Split(extract, "\n") {
if index == targetIndex {
result += color.MagentaString(" " + fmt.Sprint(extractStartLineNumber+index) + " ")
result += color.MagentaString(line) + "\n"
} else {
result += " " + fmt.Sprint(extractStartLineNumber+index) + " "
result += line + "\n"
}
}

return result
}
145 changes: 145 additions & 0 deletions pkg/report/output/policies/policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,26 @@ package policies

import (
"encoding/json"
"fmt"
"strings"

"github.com/bearer/curio/pkg/classification/db"
"github.com/bearer/curio/pkg/commands/process/settings"
"github.com/bearer/curio/pkg/util/rego"
"github.com/fatih/color"
"golang.org/x/exp/maps"

"github.com/bearer/curio/pkg/report/output/dataflow"
)

var underline = color.New(color.Underline).SprintFunc()
var severityColorFns = map[string]func(x ...interface{}) string{
settings.LevelCritical: color.New(color.FgRed).SprintFunc(),
settings.LevelHigh: color.New(color.FgHiRed).SprintFunc(),
settings.LevelMedium: color.New(color.FgYellow).SprintFunc(),
settings.LevelLow: color.New(color.FgBlue).SprintFunc(),
}

type PolicyInput struct {
PolicyId string `json:"policy_id" yaml:"policy_id"`
Dataflow *dataflow.DataFlow `json:"dataflow" yaml:"dataflow"`
Expand Down Expand Up @@ -89,10 +101,143 @@ func GetOutput(dataflow *dataflow.DataFlow, config settings.Config) (map[string]
return result, nil
}

func BuildReportString(policyResults map[string][]PolicyResult, policies map[string]*settings.Policy, withoutColor bool) *strings.Builder {
reportStr := &strings.Builder{}
reportStr.WriteString("\n\nPolicy Report\n")
reportStr.WriteString("\n=====================================")

initialColorSetting := color.NoColor
if withoutColor && !initialColorSetting {
color.NoColor = true
}

writePolicyListToString(reportStr, policies)

breachedPolicies := map[string]map[string]bool{
settings.LevelCritical: make(map[string]bool),
settings.LevelHigh: make(map[string]bool),
settings.LevelMedium: make(map[string]bool),
settings.LevelLow: make(map[string]bool),
}

for _, policyLevel := range []string{
settings.LevelCritical,
settings.LevelHigh,
settings.LevelMedium,
settings.LevelLow,
} {
for _, policyBreach := range policyResults[policyLevel] {
breachedPolicies[policyLevel][policyBreach.PolicyName] = true
writePolicyBreachToString(reportStr, policyBreach, policyLevel)
}
}

writeSummaryToString(reportStr, policyResults, len(policies), breachedPolicies)

color.NoColor = initialColorSetting

return reportStr
}

func getFullFilename(path string, filename string) string {
if filename == "." {
return path
}

return path + filename
}

func writePolicyListToString(reportStr *strings.Builder, policies map[string]*settings.Policy) {
// list policies that were run
reportStr.WriteString("\nPolicy list: \n\n")
for key := range policies {
policy := policies[key]
reportStr.WriteString(color.HiBlackString("- " + policy.Name + "\n"))
}
}

func writeSummaryToString(
reportStr *strings.Builder,
policyResults map[string][]PolicyResult,
policyCount int, breachedPolicies map[string]map[string]bool,
) {
reportStr.WriteString("\n=====================================")

// give summary including counts
if len(policyResults) == 0 {
reportStr.WriteString("\n\n")
reportStr.WriteString(color.HiGreenString("SUCCESS\n\n"))
reportStr.WriteString(fmt.Sprint(policyCount) + " policies were run and no breaches were detected.\n\n")
return
}

criticalCount := len(policyResults[settings.LevelCritical])
highCount := len(policyResults[settings.LevelHigh])
mediumCount := len(policyResults[settings.LevelMedium])
lowCount := len(policyResults[settings.LevelLow])

totalCount := criticalCount + highCount + mediumCount + lowCount

reportStr.WriteString("\n\n")
reportStr.WriteString(color.RedString("Policy breaches detected\n\n"))
reportStr.WriteString(fmt.Sprint(policyCount) + " policies were run ")
reportStr.WriteString("and " + fmt.Sprint(totalCount) + " breaches were detected.\n\n")

// critical count
reportStr.WriteString(formatSeverity(settings.LevelCritical) + fmt.Sprint(criticalCount))
if len(breachedPolicies[settings.LevelCritical]) > 0 {
reportStr.WriteString(" (" + strings.Join(maps.Keys(breachedPolicies[settings.LevelCritical]), ", ") + ")")
}
// high count
reportStr.WriteString("\n" + formatSeverity(settings.LevelHigh) + fmt.Sprint(highCount))
if len(breachedPolicies[settings.LevelHigh]) > 0 {
reportStr.WriteString(" (" + strings.Join(maps.Keys(breachedPolicies[settings.LevelHigh]), ", ") + ")")
}
// medium count
reportStr.WriteString("\n" + formatSeverity(settings.LevelMedium) + fmt.Sprint(mediumCount))
if len(breachedPolicies[settings.LevelMedium]) > 0 {
reportStr.WriteString(" (" + strings.Join(maps.Keys(breachedPolicies[settings.LevelMedium]), ", ") + ")")
}
// low count
reportStr.WriteString("\n" + formatSeverity(settings.LevelLow) + fmt.Sprint(lowCount))
if len(breachedPolicies[settings.LevelLow]) > 0 {
reportStr.WriteString(" (" + strings.Join(maps.Keys(breachedPolicies[settings.LevelLow]), ", ") + ")")
}

reportStr.WriteString("\n\n")
}

func writePolicyBreachToString(reportStr *strings.Builder, policyBreach PolicyResult, policySeverity string) {
reportStr.WriteString("\n\n")
reportStr.WriteString(formatSeverity(policySeverity))
reportStr.WriteString(policyBreach.PolicyName + " policy breach with " + policyBreach.CategoryGroup + "\n")
reportStr.WriteString(color.HiBlackString(policyBreach.PolicyDescription + "\n"))
reportStr.WriteString("\n")
reportStr.WriteString(color.HiBlueString("File: " + underline(policyBreach.Filename+":"+fmt.Sprint(policyBreach.LineNumber)) + "\n"))
reportStr.WriteString("\n")
reportStr.WriteString(highlightCodeExtract(policyBreach.LineNumber, policyBreach.ParentLineNumber, policyBreach.ParentContent))
}

func formatSeverity(policySeverity string) string {
severityColorFn, ok := severityColorFns[policySeverity]
if !ok {
return strings.ToUpper(policySeverity)
}
return severityColorFn(strings.ToUpper(policySeverity + ": "))
}

func highlightCodeExtract(lineNumber int, extractStartLineNumber int, extract string) string {
result := ""
targetIndex := lineNumber - extractStartLineNumber
for index, line := range strings.Split(extract, "\n") {
if index == targetIndex {
result += color.MagentaString(" " + fmt.Sprint(extractStartLineNumber+index) + " ")
result += color.MagentaString(line) + "\n"
} else {
result += " " + fmt.Sprint(extractStartLineNumber+index) + " "
result += line + "\n"
}
}

return result
}

0 comments on commit 8ee6c95

Please sign in to comment.