Skip to content

Commit

Permalink
feat(policies): add summary with counts to policy report (#168)
Browse files Browse the repository at this point in the history
* feat: add summary to policy report

* fix: add parent information to application level encryption rego

* fix: clean up

* chore: update snapshots

* fix: omit empty parents from json
  • Loading branch information
elsapet committed Nov 28, 2022
1 parent 977b62f commit b9c4f0e
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ high[item] {
item = {
"category_group": category.group_name,
"filename": location.filename,
"line_number": location.line_number,
"parent_line_number": detector.parent.line_number,
"parent_content": detector.parent.content

}
}

Expand All @@ -38,5 +42,8 @@ critical[item] {
item = {
"category_group": category.group_name,
"filename": location.filename,
"line_number": location.line_number,
"parent_line_number": detector.parent.line_number,
"parent_content": detector.parent.content
}
}
8 changes: 6 additions & 2 deletions pkg/report/output/dataflow/datatypes/datatypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/bearer/curio/pkg/classification/db"
"github.com/bearer/curio/pkg/report/output/dataflow/detectiondecoder"
"github.com/bearer/curio/pkg/report/output/dataflow/types"
"github.com/bearer/curio/pkg/report/schema"

"github.com/bearer/curio/pkg/report/detections"
"github.com/bearer/curio/pkg/util/classify"
Expand All @@ -19,6 +20,7 @@ type datatypeHolder struct {
name string
uuid string
categoryUUID string
parent *schema.Parent
detectors map[string]*detectorHolder // group detectors by detectorName
}

Expand Down Expand Up @@ -57,14 +59,14 @@ func (holder *Holder) AddSchema(detection detections.Detection, extras *extraFie
}

if classification.Decision.State == classify.Valid {
holder.addDatatype(classification.DataType, string(detection.DetectorType), detection.Source.Filename, *detection.Source.LineNumber, extras)
holder.addDatatype(classification.DataType, string(detection.DetectorType), detection.Source.Filename, *detection.Source.LineNumber, extras, schema.Parent)
}

return nil
}

// addDatatype adds datatype to hash list and at the same time blocks duplicates
func (holder *Holder) addDatatype(classification *db.DataType, detectorName string, fileName string, lineNumber int, extras *extraFields) {
func (holder *Holder) addDatatype(classification *db.DataType, detectorName string, fileName string, lineNumber int, extras *extraFields, parent *schema.Parent) {
// create datatype entry if it doesn't exist
if _, exists := holder.datatypes[classification.Name]; !exists {
datatype := datatypeHolder{
Expand All @@ -75,6 +77,7 @@ func (holder *Holder) addDatatype(classification *db.DataType, detectorName stri
if holder.isInternal {
datatype.categoryUUID = classification.CategoryUUID
datatype.uuid = classification.UUID
datatype.parent = parent
}

holder.datatypes[classification.Name] = datatype
Expand Down Expand Up @@ -133,6 +136,7 @@ func (holder *Holder) ToDataFlow() []types.Datatype {
constructedDetector := types.DatatypeDetector{
Name: detectorHolder.name,
Locations: make([]types.DatatypeLocation, 0),
Parent: datatype.parent,
}

for _, fileHolder := range maputil.ToSortedSlice(detectorHolder.files) {
Expand Down
3 changes: 3 additions & 0 deletions pkg/report/output/dataflow/types/datatypes.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package types

import "github.com/bearer/curio/pkg/report/schema"

type Datatype struct {
UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty"`
CategoryUUID string `json:"category_uuid,omitempty" yaml:"category_uuid,omitempty"`
Expand All @@ -10,6 +12,7 @@ type Datatype struct {
type DatatypeDetector struct {
Name string `json:"name" yaml:"name"`
Locations []DatatypeLocation `json:"locations" yaml:"locations"`
Parent *schema.Parent `json:"parent,omitempty" yaml:"parent,omitempty"`
}

type DatatypeLocation struct {
Expand Down
117 changes: 92 additions & 25 deletions pkg/report/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"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"
Expand All @@ -30,31 +31,39 @@ var severityColorFns = map[string]func(x ...interface{}) string{
}

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

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

for _, policyBreach := range outputPolicies[settings.LevelCritical] {
writePolicyBreachToOutput(outputStr, policyBreach, settings.LevelCritical)
}
writePolicyListToString(reportStr, config.Policies)

for _, policyBreach := range outputPolicies[settings.LevelHigh] {
writePolicyBreachToOutput(outputStr, policyBreach, settings.LevelHigh)
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 _, policyBreach := range outputPolicies[settings.LevelMedium] {
writePolicyBreachToOutput(outputStr, policyBreach, settings.LevelMedium)
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)
}
}

for _, policyBreach := range outputPolicies[settings.LevelLow] {
writePolicyBreachToOutput(outputStr, policyBreach, settings.LevelLow)
}
writeSummaryToString(reportStr, policyResults, len(config.Policies), breachedPolicies)

output.Msg(outputStr.String())
output.Msg(reportStr.String())

return nil
}
Expand Down Expand Up @@ -149,17 +158,75 @@ func getDataflow(report types.Report, config settings.Config, isInternal bool) (
return dataflow.GetOutput(reportedDetections, config, isInternal)
}

func writePolicyBreachToOutput(outputStr *strings.Builder, policyBreach policies.PolicyResult, policySeverity string) {
outputStr.WriteString("\n\n")
outputStr.WriteString(formatSeverity(policySeverity))
outputStr.WriteString(policyBreach.PolicyName + " policy breach with " + policyBreach.CategoryGroup + "\n")
outputStr.WriteString(color.HiBlackString(policyBreach.PolicyDescription + "\n"))
outputStr.WriteString("\n")
outputStr.WriteString(color.HiBlueString("File: " + underline(policyBreach.Filename+":"+fmt.Sprint(policyBreach.LineNumber)) + "\n"))
outputStr.WriteString("\n")
outputStr.WriteString(highlightCodeExtract(policyBreach.LineNumber, policyBreach.ParentLineNumber, policyBreach.ParentContent))
outputStr.WriteString("\n\n")
outputStr.WriteString("=====================================")
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 {
Expand Down

0 comments on commit b9c4f0e

Please sign in to comment.