mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
+17









David Wertenteil
Vlad Klokun
Moshe-Rappaport-CA
Moshe Rappaport
Om Raut
Kamal Nayan
Chirag Arora
shm12
Amir Malka
Krishna Agarwal
Saswata Senapati
Rahul Singh
deepuyadav004
kartik
Rounak-28
pwnb0y
Ben Hirschberg
Saptarshi Sarkar
Rahul Surwade
Suhas Gumma
TarangVerma
avikittu
satyam kale
Aditya Pratap Singh
Ashray Shetty
Anubhav Gupta
Meyazhagan
eb16440ba6
* Fix issue for scanning list obj * Fix go mod in httphandler pkg * Broken links fix in roadmap.md Planning, backlog, and wishlist links were not taking to the required section. * override infoMap only if it's not nil * improved icon of kubescape in readme * Support scanning several files * gramatical improvements * docs(readme): Star → star * Fix issues according to review * Handle with issues caused by updating opa-utils * Fix scanning ListObj following reviews * Update core/pkg/resourcehandler/filesloader.go Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com> * Update completion.go * Added fixed control input * update go.mod * Print chart name log when fail to generate * Change formatting to %s * Added resource prioritization information, raw resource will be sent on the result object * Merging typo fixes from master (#772) * greetings * Update aws.sh simplified the comment * typo: In the title and h1 element Their was a typo in index.html file. * punctuation changes * docs : added gitpod badge in readme.md * fixed typos * some grammar mistake is corrected inPULL_REQUEST_TEMPLATE.md file * Updated README.md file Added link to CONTRIBUTING.md file in a line in README. * Added link to code of conduct file I have added link to the code of conduct file and fixed some problems in the Readme file. * Fixed readme * Added alpine tag Adding alpine tag instead of latest and removing repeating commands * roadmap.md file is modified * Automatically Close "Typo" labelled Issue * build.py is modified * modified PR template * Fixed some typos in feature_request.md "." at the end of the headings were missing and all the text were in same line. Now this gives a clear and concise view of the texts. * fixed the typo in docs/index.html Found and fixed typo in the 'alt' attribute of img tag * Update PULL_REQUEST_TEMPLATE.md Co-authored-by: Krishna Agarwal <dmkrishna.agarwal@gmail.com> Co-authored-by: Saswata Senapati <74651639+saswat16@users.noreply.github.com> Co-authored-by: Rahul Singh <110548934+rahuldhirendersingh@users.noreply.github.com> Co-authored-by: deepuyadav004 <deepuyadavze@gmail.com> Co-authored-by: kartik <97971066+kartikgajjar7@users.noreply.github.com> Co-authored-by: Rounak-28 <95576871+Rounak-28@users.noreply.github.com> Co-authored-by: pwnb0y <vickykr07@yahoo.com> Co-authored-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com> Co-authored-by: Saptarshi Sarkar <saptarshi.programmer@gmail.com> Co-authored-by: Rahul Surwade <93492791+RahulSurwade08@users.noreply.github.com> Co-authored-by: Suhas Gumma <43647369+suhasgumma@users.noreply.github.com> Co-authored-by: Kamal Nayan <95926324+legendarykamal@users.noreply.github.com> Co-authored-by: TarangVerma <90996971+TarangVerma@users.noreply.github.com> Co-authored-by: avikittu <65793296+avikittu@users.noreply.github.com> * update logger version * update logger version (#773) * Fixed: Kubescape fails to authenticate remote private Github repo (#721) * grammar error fixer in CONTRIBUTING.md * scanning private git repository is available * giturl to gitapi * NO TOKEN error functionality added * Used GetToken method of giturl.IGitAPPI for auth Co-authored-by: satyam kale <satyamkale271@gmail.com> Co-authored-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com> * bump opa-utils to 181 * Option to force enable color output (closes #560) (#767) * Option to force enable color output (closes #560) (cherry picked from commit 4f951781ee8dd6bb451ac7d159787f47e4b07379) * Update go.mod * update scanner image * Update host scanner image (#774) * update logger version * update scanner image * remove windows exe extension * Remove windows extension build (#775) * update logger version * update scanner image * remove windows exe extension * commened out prioritization logic * Edit Junit output (#802) * Edit Junit output * Update go sum * Following review * update AdoptClusterName * Print line separator only if some controls failed (#813) * removed the extra 'download' word from the example (#810) it was confusing to understand the download command because there was an extra 'download' mentioned * Prioritization (#815) * removed commented out code * Added attack tracks information to prioritization algorithm * bump opa-utils * go mod tidy * go mod tidy * CR changes * Issue 613 cluster name (#783) * added --clusterName flag (#613) Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com> * update flag name to --cluster-name Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com> Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com> * Per 307 fail on severity counters (#831) * feat: fail on exceeding severity thresholds (#830) - Add support for severity counters - Add support for CLI flags that set severity thresholds - Terminate Kubescape with an exit code 1 if scan results exceed the severity thresholds * Update opa-utils pkg version Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com> * Fix merge conflict * typo in .gitignore file (#833) * remove unsupported installation method * fixed welcome message * fixed merge * fixed attack tracks loading logic Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com> Co-authored-by: Moshe-Rappaport-CA <moshep@armosec.io> Co-authored-by: Moshe Rappaport <89577611+Moshe-Rappaport-CA@users.noreply.github.com> Co-authored-by: Om Raut <33827410+om2137@users.noreply.github.com> Co-authored-by: Kamal Nayan <95926324+legendarykamal@users.noreply.github.com> Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com> Co-authored-by: Chirag Arora <84070677+Chirag8023@users.noreply.github.com> Co-authored-by: shm12 <shmuelb@armosec.io> Co-authored-by: Amir Malka <amirm@armosec.io> Co-authored-by: Krishna Agarwal <dmkrishna.agarwal@gmail.com> Co-authored-by: Saswata Senapati <74651639+saswat16@users.noreply.github.com> Co-authored-by: Rahul Singh <110548934+rahuldhirendersingh@users.noreply.github.com> Co-authored-by: deepuyadav004 <deepuyadavze@gmail.com> Co-authored-by: kartik <97971066+kartikgajjar7@users.noreply.github.com> Co-authored-by: Rounak-28 <95576871+Rounak-28@users.noreply.github.com> Co-authored-by: pwnb0y <vickykr07@yahoo.com> Co-authored-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com> Co-authored-by: Saptarshi Sarkar <saptarshi.programmer@gmail.com> Co-authored-by: Rahul Surwade <93492791+RahulSurwade08@users.noreply.github.com> Co-authored-by: Suhas Gumma <43647369+suhasgumma@users.noreply.github.com> Co-authored-by: TarangVerma <90996971+TarangVerma@users.noreply.github.com> Co-authored-by: avikittu <65793296+avikittu@users.noreply.github.com> Co-authored-by: satyam kale <satyamkale271@gmail.com> Co-authored-by: Aditya Pratap Singh <adityapratapsingh51@gmail.com> Co-authored-by: Ashray Shetty <ashrayshetty1999@gmail.com> Co-authored-by: Anubhav Gupta <mail.anubhav06@gmail.com> Co-authored-by: Meyazhagan <meyazhagan.ofcl@gmail.com>
242 lines
9.5 KiB
Go
242 lines
9.5 KiB
Go
package v2
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
logger "github.com/kubescape/go-logger"
|
|
"github.com/kubescape/go-logger/helpers"
|
|
"github.com/kubescape/k8s-interface/workloadinterface"
|
|
"github.com/kubescape/kubescape/v2/core/cautils"
|
|
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
|
|
"github.com/kubescape/opa-utils/reporthandling/apis"
|
|
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
|
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
|
|
)
|
|
|
|
/*
|
|
riskScore
|
|
status
|
|
*/
|
|
type JunitPrinter struct {
|
|
writer *os.File
|
|
verbose bool
|
|
}
|
|
|
|
// https://llg.cubic.org/docs/junit/
|
|
|
|
type JUnitXML struct {
|
|
TestSuites JUnitTestSuites `xml:"testsuites"`
|
|
}
|
|
|
|
// JUnitTestSuites represents the test summary
|
|
type JUnitTestSuites struct {
|
|
XMLName xml.Name `xml:"testsuites"`
|
|
Suites []JUnitTestSuite `xml:"testsuite"` // list of controls
|
|
Errors int `xml:"errors,attr"` // total number of tests with error result from all testsuites
|
|
Disabled int `xml:"disabled,attr"` // total number of disabled tests from all testsuites
|
|
Failures int `xml:"failures,attr"` // total number of failed tests from all testsuites
|
|
Tests int `xml:"tests,attr"` // total number of tests from all testsuites. Some software may expect to only see the number of successful tests from all testsuites though
|
|
Time string `xml:"time,attr"` // time in seconds to execute all test suites
|
|
Name string `xml:"name,attr"` // ? Add framework names ?
|
|
}
|
|
|
|
// JUnitTestSuite represents a single control
|
|
type JUnitTestSuite struct {
|
|
XMLName xml.Name `xml:"testsuite"`
|
|
Name string `xml:"name,attr"` // Full (class) name of the test for non-aggregated testsuite documents. Class name without the package for aggregated testsuites documents. Required
|
|
Disabled int `xml:"disabled,attr"` // The total number of disabled tests in the suite. optional. not supported by maven surefire.
|
|
Errors int `xml:"errors,attr"` // The total number of tests in the suite that errors
|
|
Failures int `xml:"failures,attr"` // The total number of tests in the suite that failed
|
|
Hostname string `xml:"hostname,attr"` // Host on which the tests were executed ? cluster name ?
|
|
ID int `xml:"id,attr"` // Starts at 0 for the first testsuite and is incremented by 1 for each following testsuite
|
|
Skipped string `xml:"skipped,attr"` // The total number of skipped tests
|
|
Time string `xml:"time,attr"` // Time taken (in seconds) to execute the tests in the suite
|
|
Timestamp string `xml:"timestamp,attr"` // when the test was executed in ISO 8601 format (2014-01-21T16:17:18)
|
|
File string `xml:"file,attr"` // The file be tested
|
|
Properties []JUnitProperty `xml:"properties>property,omitempty"`
|
|
TestCases []JUnitTestCase `xml:"testcase"`
|
|
}
|
|
|
|
// JUnitTestCase represents a single resource
|
|
type JUnitTestCase struct {
|
|
XMLName xml.Name `xml:"testcase"`
|
|
Classname string `xml:"classname,attr"` // Full class name for the class the test method is in. required
|
|
Status string `xml:"status,attr"` // Status
|
|
Name string `xml:"name,attr"` // Name of the test method, required
|
|
Time string `xml:"time,attr"` // Time taken (in seconds) to execute the test. optional
|
|
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"`
|
|
}
|
|
|
|
const (
|
|
lineSeparator = "\n===================================================================================================================\n\n"
|
|
testCaseTypeResources = "Resources"
|
|
)
|
|
|
|
func NewJunitPrinter(verbose bool) *JunitPrinter {
|
|
return &JunitPrinter{
|
|
verbose: verbose,
|
|
}
|
|
}
|
|
|
|
func (junitPrinter *JunitPrinter) SetWriter(outputFile string) {
|
|
junitPrinter.writer = printer.GetWriter(outputFile)
|
|
}
|
|
|
|
func (junitPrinter *JunitPrinter) Score(score float32) {
|
|
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", cautils.Float32ToInt(score))
|
|
}
|
|
|
|
func (junitPrinter *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
|
junitResult := testsSuites(opaSessionObj)
|
|
postureReportStr, err := xml.Marshal(junitResult)
|
|
if err != nil {
|
|
logger.L().Fatal("failed to Marshal xml result object", helpers.Error(err))
|
|
}
|
|
|
|
logOUtputFile(junitPrinter.writer.Name())
|
|
if _, err := junitPrinter.writer.Write(postureReportStr); err != nil {
|
|
logger.L().Error("failed to write results", helpers.Error(err))
|
|
}
|
|
}
|
|
|
|
func testsSuites(results *cautils.OPASessionObj) *JUnitTestSuites {
|
|
return &JUnitTestSuites{
|
|
Suites: listTestsSuite(results),
|
|
Tests: results.Report.SummaryDetails.NumberOfResources().All(),
|
|
Name: "Kubescape Scanning",
|
|
Failures: results.Report.SummaryDetails.NumberOfResources().Failed(),
|
|
}
|
|
}
|
|
|
|
// aggregate resources source to a list of resources results
|
|
func sourceToResourcesResults(results *cautils.OPASessionObj) map[string][]resourcesresults.Result {
|
|
resourceResults := make(map[string][]resourcesresults.Result)
|
|
for i := range results.ResourceSource {
|
|
if r, ok := results.ResourcesResult[i]; ok {
|
|
if _, ok := resourceResults[results.ResourceSource[i].RelativePath]; !ok {
|
|
resourceResults[results.ResourceSource[i].RelativePath] = []resourcesresults.Result{}
|
|
}
|
|
resourceResults[results.ResourceSource[i].RelativePath] = append(resourceResults[results.ResourceSource[i].RelativePath], r)
|
|
}
|
|
}
|
|
return resourceResults
|
|
}
|
|
|
|
// listTestsSuite returns a list of testsuites
|
|
func listTestsSuite(results *cautils.OPASessionObj) []JUnitTestSuite {
|
|
var testSuites []JUnitTestSuite
|
|
resourceResults := sourceToResourcesResults(results)
|
|
counter := 0
|
|
// control scan
|
|
for path, resourcesResult := range resourceResults {
|
|
testSuite := JUnitTestSuite{}
|
|
testSuite.Timestamp = results.Report.ReportGenerationTime.String()
|
|
testSuite.ID = counter
|
|
counter++
|
|
testSuite.File = path
|
|
testSuite.TestCases = testsCases(results, resourcesResult)
|
|
if len(testSuite.TestCases) > 0 {
|
|
testSuites = append(testSuites, testSuite)
|
|
}
|
|
}
|
|
|
|
return testSuites
|
|
}
|
|
|
|
func failedControlsToFailureMessage(results *cautils.OPASessionObj, controls []resourcesresults.ResourceAssociatedControl, severityCounter []int) string {
|
|
msg := ""
|
|
for _, c := range controls {
|
|
control := results.Report.SummaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, c.GetID())
|
|
if c.GetStatus(nil).IsFailed() {
|
|
msg += fmt.Sprintf("Test: %s\n", control.GetName())
|
|
msg += fmt.Sprintf("Severity: %s\n", apis.ControlSeverityToString(control.GetScoreFactor()))
|
|
msg += fmt.Sprintf("Remediation: %s\n", control.GetRemediation())
|
|
msg += fmt.Sprintf("Link: %s\n", getControlLink(control.GetID()))
|
|
if failedPaths := failedPathsToString(&c); len(failedPaths) > 0 {
|
|
msg += fmt.Sprintf("Failed paths: \n - %s\n", strings.Join(failedPaths, "\n - "))
|
|
}
|
|
if fixPaths := fixPathsToString(&c); len(fixPaths) > 0 {
|
|
msg += fmt.Sprintf("Available fix: \n - %s\n", strings.Join(fixPaths, "\n - "))
|
|
}
|
|
msg += "\n"
|
|
|
|
severityCounter[apis.ControlSeverityToInt(control.GetScoreFactor())] += 1
|
|
}
|
|
}
|
|
return msg
|
|
}
|
|
|
|
// Every testCase includes a file (even if the file contains several resources)
|
|
func testsCases(results *cautils.OPASessionObj, resourcesResult []resourcesresults.Result) []JUnitTestCase {
|
|
var testCases []JUnitTestCase
|
|
testCase := JUnitTestCase{}
|
|
testCaseFailure := JUnitFailure{}
|
|
testCaseFailure.Type = testCaseTypeResources
|
|
message := ""
|
|
|
|
// severityCounter represents the severities, 0: Unknown, 1: Low, 2: Medium, 3: High, 4: Critical
|
|
severityCounter := make([]int, apis.NumberOfSeverities, apis.NumberOfSeverities)
|
|
|
|
for i := range resourcesResult {
|
|
if failedControls := failedControlsToFailureMessage(results, resourcesResult[i].ListControls(), severityCounter); failedControls != "" {
|
|
message += fmt.Sprintf("%sResource: %s\n\n%s", lineSeparator, resourceNameToString(results.AllResources[resourcesResult[i].GetResourceID()]), failedControls)
|
|
}
|
|
}
|
|
testCaseFailure.Message += fmt.Sprintf("%s\n%s", getSummaryMessage(severityCounter), message)
|
|
|
|
testCase.Failure = &testCaseFailure
|
|
if testCase.Failure.Message != "" {
|
|
testCases = append(testCases, testCase)
|
|
}
|
|
|
|
return testCases
|
|
}
|
|
|
|
func getSummaryMessage(severityCounter []int) string {
|
|
total := 0
|
|
severities := ""
|
|
for i, count := range severityCounter {
|
|
if apis.SeverityNumberToString(i) == apis.SeverityNumberToString(apis.SeverityUnknown) {
|
|
continue
|
|
}
|
|
severities += fmt.Sprintf("%s: %d, ", apis.SeverityNumberToString(i), count)
|
|
total += count
|
|
}
|
|
if len(severities) == 0 {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("Total: %d (%s)", total, severities[:len(severities)-2])
|
|
}
|
|
|
|
func resourceNameToString(resource workloadinterface.IMetadata) string {
|
|
s := ""
|
|
s += fmt.Sprintf("kind=%s/", resource.GetKind())
|
|
if resource.GetNamespace() != "" {
|
|
s += fmt.Sprintf("namespace=%s/", resource.GetNamespace())
|
|
}
|
|
s += fmt.Sprintf("name=%s", resource.GetName())
|
|
return s
|
|
}
|