Skip to content

Commit

Permalink
Merge branch 'main' into GitLab_Policy_Addition
Browse files Browse the repository at this point in the history
  • Loading branch information
noamd-legit authored Jan 4, 2024
2 parents 0f9de50 + dd26e70 commit 86a4750
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 0 deletions.
135 changes: 135 additions & 0 deletions internal/outputer/formatter/formatter_csv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package formatter

import (
"encoding/csv"
"bytes"
"fmt"
"strconv"
"strings"
"github.com/Legit-Labs/legitify/internal/analyzers"
"github.com/Legit-Labs/legitify/internal/outputer/scheme"
)

type CsvFormatter struct {
colorizer humanColorizer
}

func newCSVFormatter() OutputFormatter {
return &CsvFormatter{
colorizer: humanColorizer{},
}
}

func (f *CsvFormatter) csvFailedPolicies(output *scheme.Flattened, csvwriter *csv.Writer) bool {
failedPolicies := output.OnlyFailedViolations()
headers := []string{"#", "Policy Name", "Namespace", "Severity", "Threat", "Violations", "Remediation Steps"}
err := csvwriter.Write(headers)
if err != nil {
panic(err)
}

for i, policyName := range failedPolicies.AsOrderedMap().Keys() {
policyData := output.GetPolicyData(policyName)
policyInfo := policyData.PolicyInfo
rowNum := i+1
policyName := policyInfo.PolicyName
Namespace := policyInfo.Namespace
Severity := policyInfo.Severity
Threat := strings.Join([]string(policyInfo.Threat), "\n")

RemediationSteps := strings.Join([]string(policyInfo.RemediationSteps), "\n")
var entityType, Link, violationString string
var violationsSummary []string
for _, violation := range policyData.Violations {
entityType = (&violation).ViolationEntityType
Link = violation.CanonicalLink
violationString = entityType + " " + Link
violationsSummary = append(violationsSummary, violationString)
}
violationsPolicy := strings.Join([]string(violationsSummary), "\n")

row := []string{strconv.Itoa(rowNum), policyName, Namespace, Severity, Threat, violationsPolicy, RemediationSteps}
err := csvwriter.Write(row)
if err != nil {
panic(err)
}

}
err = csvwriter.Write([]string{"\n"})
if err != nil {
panic(err)
}


return true
}


func (f *CsvFormatter) formatSummary(output *scheme.Flattened, csvwriter *csv.Writer) bool {
headers := []string{"#", "Namespace", "Policy", "Severity", "Passed", "Failed", "Skipped"}
err := csvwriter.Write(headers)

for i, policyName := range output.AsOrderedMap().Keys() {
rowNum := i+1
data := output.GetPolicyData(policyName)
policyInfo := data.PolicyInfo
title := policyInfo.Title
severity := policyInfo.Severity
namespace := policyInfo.Namespace

var passed, failed, skipped int
for _, violation := range data.Violations {
switch violation.Status {
case analyzers.PolicyPassed:
passed++
case analyzers.PolicyFailed:
failed++
case analyzers.PolicySkipped:
skipped++
}
}


row := []string{strconv.Itoa(rowNum), namespace, title, severity, strconv.Itoa(passed), strconv.Itoa(failed), strconv.Itoa(skipped)}
err := csvwriter.Write(row)
if err != nil {
panic(err)
}
}

if err != nil {
panic(err)
}

return true
}

func (f *CsvFormatter) Format(output scheme.Scheme, failedOnly bool) ([]byte, error) {
var csvBuffer bytes.Buffer
csvWriter := csv.NewWriter(&csvBuffer)

typedOutput, ok := output.(*scheme.Flattened)
if !ok {
return nil, UnsupportedScheme{output}
}

if !failedOnly {
f.formatSummary(typedOutput, csvWriter)
}
f.csvFailedPolicies(typedOutput, csvWriter)
csvWriter.Flush()

// Check for errors during flushing
if err := csvWriter.Error(); err != nil {
fmt.Printf("Error: %v\n", err)
panic(err)
}

csvData := csvBuffer.Bytes()

return csvData, nil
}

func (f *CsvFormatter) IsSchemeSupported(schemeType string) bool {
return schemeType == scheme.TypeFlattened
}
20 changes: 20 additions & 0 deletions internal/outputer/formatter/formatter_csv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package formatter_test

import (
"testing"

"github.com/Legit-Labs/legitify/internal/outputer/formatter"
"github.com/Legit-Labs/legitify/internal/outputer/scheme/scheme_test"
"github.com/stretchr/testify/require"
)

func TestFormatCsv(t *testing.T) {
sample := scheme_test.SchemeSample()

for _, f := range []bool{true, false} {
bytes, err := formatter.Format(formatter.Csv, formatter.DefaultOutputIndent, sample, f)
require.Nilf(t, err, "Error formatting csv: %v", err)
require.NotNil(t, bytes, "Error formatting csv")
require.NotEmpty(t, bytes, "Error formatting csv")
}
}
2 changes: 2 additions & 0 deletions internal/outputer/formatter/output_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const (
Json FormatName = "json"
Sarif FormatName = "sarif"
Markdown FormatName = "markdown"
Csv FormatName = "csv"
)

type OutputFormatter interface {
Expand All @@ -27,6 +28,7 @@ var outputFormatters = map[FormatName]NewFormatFunc{
Json: NewJsonFormatter,
Markdown: newMarkdownFormatter,
Sarif: newSarifFormatter,
Csv: newCSVFormatter,
}

func ValidateOutputFormat(outputFormat FormatName, schemeType scheme.SchemeType) error {
Expand Down
3 changes: 3 additions & 0 deletions internal/outputer/formatter/output_format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ func TestOutputFormats(t *testing.T) {
case formatter.Sarif:
// sarif has dedicated tests
continue
case formatter.Csv:
// csv has dedicated tests
continue

default:
t.Fatalf("unexpected format: %s", name)
Expand Down

0 comments on commit 86a4750

Please sign in to comment.