From 0d43fec008a602c52d2fe4a2d23b4c027396d8cf Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Wed, 25 Aug 2021 22:32:10 +0300 Subject: [PATCH] Junit XML support --- printer/junit.go | 76 +++++++++++++++++++++++++++++++++++++++++ printer/printresults.go | 15 +++++++- 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 printer/junit.go diff --git a/printer/junit.go b/printer/junit.go new file mode 100644 index 00000000..c5d65463 --- /dev/null +++ b/printer/junit.go @@ -0,0 +1,76 @@ +package printer + +import ( + "encoding/xml" + "fmt" + "kube-escape/cautils/opapolicy" +) + +type JUnitTestSuites struct { + XMLName xml.Name `xml:"testsuites"` + Suites []JUnitTestSuite `xml:"testsuite"` +} + +// JUnitTestSuite is a single JUnit test suite which may contain many +// testcases. +type JUnitTestSuite struct { + XMLName xml.Name `xml:"testsuite"` + Tests int `xml:"tests,attr"` + Failures int `xml:"failures,attr"` + Time string `xml:"time,attr"` + Name string `xml:"name,attr"` + Properties []JUnitProperty `xml:"properties>property,omitempty"` + TestCases []JUnitTestCase `xml:"testcase"` +} + +// JUnitTestCase is a single test case with its result. +type JUnitTestCase struct { + XMLName xml.Name `xml:"testcase"` + Classname string `xml:"classname,attr"` + Name string `xml:"name,attr"` + Time string `xml:"time,attr"` + SkipMessage *JUnitSkipMessage `xml:"skipped,omitempty"` + Failure *JUnitFailure `xml:"failure,omitempty"` +} + +// JUnitSkipMessage contains the reason why a testcase was skipped. +type JUnitSkipMessage struct { + Message string `xml:"message,attr"` +} + +// JUnitProperty represents a key/value pair used to define properties. +type JUnitProperty struct { + Name string `xml:"name,attr"` + Value string `xml:"value,attr"` +} + +// JUnitFailure contains data related to a failed test. +type JUnitFailure struct { + Message string `xml:"message,attr"` + Type string `xml:"type,attr"` + Contents string `xml:",chardata"` +} + +func convertPostureReportToJunitResult(postureResult *opapolicy.PostureReport) (*JUnitTestSuites, error) { + juResult := JUnitTestSuites{XMLName: xml.Name{Local: "Kubescape scan results"}} + for _, framework := range postureResult.FrameworkReports { + suite := JUnitTestSuite{Name: framework.Name} + for _, controlReports := range framework.ControlReports { + suite.Tests = suite.Tests + 1 + testCase := JUnitTestCase{} + testCase.Name = controlReports.Name + if 0 < len(controlReports.RuleReports[0].RuleResponses) { + suite.Failures = suite.Failures + 1 + failure := JUnitFailure{} + failure.Message = fmt.Sprintf("%d resources failed", len(controlReports.RuleReports[0].RuleResponses)) + for _, ruleResponses := range controlReports.RuleReports[0].RuleResponses { + failure.Contents = fmt.Sprintf("%s\n%s", failure.Contents, ruleResponses.AlertMessage) + } + testCase.Failure = &failure + } + suite.TestCases = append(suite.TestCases, testCase) + } + juResult.Suites = append(juResult.Suites, suite) + } + return &juResult, nil +} diff --git a/printer/printresults.go b/printer/printresults.go index 22598283..c5d649fb 100644 --- a/printer/printresults.go +++ b/printer/printresults.go @@ -2,6 +2,7 @@ package printer import ( "encoding/json" + "encoding/xml" "fmt" "kube-escape/cautils" "os" @@ -47,7 +48,19 @@ func (printer *Printer) ActionPrint() { printer.PrintResults() printer.PrintSummaryTable() } else if printer.printerType == JsonPrinter { - postureReportStr, err := json.Marshal(opaSessionObj.PostureReport) + postureReportStr, err := json.Marshal(opaSessionObj.PostureReport.FrameworkReports[0]) + if err != nil { + fmt.Println("Failed to convert posture report object!") + os.Exit(1) + } + os.Stdout.Write(postureReportStr) + } else if printer.printerType == JunitResultPrinter { + junitResult, err := convertPostureReportToJunitResult(opaSessionObj.PostureReport) + if err != nil { + fmt.Println("Failed to convert posture report object!") + os.Exit(1) + } + postureReportStr, err := xml.Marshal(junitResult) if err != nil { fmt.Println("Failed to convert posture report object!") os.Exit(1)