Files
+17 eb16440ba6 Enhancing CLI flags (#835)
* 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>
2022-09-19 08:41:40 +03:00

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
}