mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
extend exceptions support
This commit is contained in:
@@ -71,6 +71,7 @@ Want to contribute? Want to discuss something? Have an issue?
|
||||
* [Scan Kubescape on an air-gapped environment (offline support)](https://youtu.be/IGXL9s37smM)
|
||||
* [Managing exceptions in the Kubescape SaaS version](https://youtu.be/OzpvxGmCR80)
|
||||
* [Configure and run customized frameworks](https://youtu.be/12Sanq_rEhs)
|
||||
* Customize controls configurations. [Kubescape CLI](https://youtu.be/955psg6TVu4), [Kubescape SaaS](https://youtu.be/lIMVSVhH33o)
|
||||
|
||||
## Install on Windows
|
||||
|
||||
@@ -109,7 +110,7 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser
|
||||
| `--use-artifacts-from` | | Load artifacts (frameworks, control-config, exceptions) from local directory. If not used will download them | |
|
||||
| `--use-default` | `false` | Load local framework object from default path. If not used will download latest | `true`/`false` |
|
||||
| `--exceptions` | | Path to an exceptions obj, [examples](https://github.com/armosec/kubescape/tree/master/examples/exceptions/README.md). Default will download exceptions from Kubescape SaaS ||
|
||||
| `--controls-config` | | Path to a controls-config obj. If not set will download controls-config from ARMO management portal | |
|
||||
| `--controls-config` | | Path to a controls-config obj. If not set will download controls-config from ARMO management portal. [docs](https://hub.armo.cloud/docs/configuration-parameters) | |
|
||||
| `--submit` | `false` | If set, Kubescape will send the scan results to Armo management portal where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not sent | `true`/`false` |
|
||||
| `--keep-local` | `false` | Kubescape will not send scan results to Armo management portal. Use this flag if you ran with the `--submit` flag in the past and you do not want to submit your current scan results | `true`/`false` |
|
||||
| `--account` | | Armo portal account ID. Default will load account ID from configMap or config file | |
|
||||
|
||||
@@ -126,6 +126,13 @@ func (armoAPI *ArmoAPI) Post(fullURL string, headers map[string]string, body []b
|
||||
return HttpPost(armoAPI.httpClient, fullURL, headers, body)
|
||||
}
|
||||
|
||||
func (armoAPI *ArmoAPI) Delete(fullURL string, headers map[string]string) (string, error) {
|
||||
if headers == nil {
|
||||
headers = make(map[string]string)
|
||||
}
|
||||
armoAPI.appendAuthHeaders(headers)
|
||||
return HttpDelete(armoAPI.httpClient, fullURL, headers)
|
||||
}
|
||||
func (armoAPI *ArmoAPI) Get(fullURL string, headers map[string]string) (string, error) {
|
||||
if headers == nil {
|
||||
headers = make(map[string]string)
|
||||
@@ -293,7 +300,7 @@ func (armoAPI *ArmoAPI) PostExceptions(exceptions []armotypes.PostureExceptionPo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = armoAPI.Post(armoAPI.postExceptionsURL(), map[string]string{"Content-Type": "application/json"}, ex)
|
||||
_, err = armoAPI.Post(armoAPI.exceptionsURL(""), map[string]string{"Content-Type": "application/json"}, ex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -301,6 +308,14 @@ func (armoAPI *ArmoAPI) PostExceptions(exceptions []armotypes.PostureExceptionPo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (armoAPI *ArmoAPI) DeleteException(exceptionName string) error {
|
||||
|
||||
_, err := armoAPI.Delete(armoAPI.exceptionsURL(exceptionName), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (armoAPI *ArmoAPI) Login() error {
|
||||
if armoAPI.accountID == "" {
|
||||
return fmt.Errorf("failed to login, missing accountID")
|
||||
|
||||
@@ -56,7 +56,7 @@ func (armoAPI *ArmoAPI) getExceptionsURL(clusterName string) string {
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (armoAPI *ArmoAPI) postExceptionsURL() string {
|
||||
func (armoAPI *ArmoAPI) exceptionsURL(exceptionsPolicyName string) string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = armoAPI.apiURL
|
||||
@@ -64,6 +64,10 @@ func (armoAPI *ArmoAPI) postExceptionsURL() string {
|
||||
|
||||
q := u.Query()
|
||||
q.Add("customerGUID", armoAPI.getCustomerGUIDFallBack())
|
||||
if exceptionsPolicyName != "" { // for delete
|
||||
q.Add("policyName", exceptionsPolicyName)
|
||||
}
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String()
|
||||
|
||||
@@ -47,6 +47,24 @@ func JSONDecoder(origin string) *json.Decoder {
|
||||
return dec
|
||||
}
|
||||
|
||||
func HttpDelete(httpClient *http.Client, fullURL string, headers map[string]string) (string, error) {
|
||||
|
||||
req, err := http.NewRequest("DELETE", fullURL, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
setHeaders(req, headers)
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
respStr, err := httpRespToString(resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return respStr, nil
|
||||
}
|
||||
func HttpGetter(httpClient *http.Client, fullURL string, headers map[string]string) (string, error) {
|
||||
|
||||
req, err := http.NewRequest("GET", fullURL, nil)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package clihandler
|
||||
|
||||
func CliDelete() error {
|
||||
|
||||
tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface
|
||||
return tenant.DeleteCachedConfig()
|
||||
}
|
||||
+31
-3
@@ -1,7 +1,35 @@
|
||||
package clihandler
|
||||
|
||||
func CliDelete() error {
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface
|
||||
return tenant.DeleteCachedConfig()
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
"github.com/armosec/kubescape/cautils/logger"
|
||||
"github.com/armosec/kubescape/cautils/logger/helpers"
|
||||
)
|
||||
|
||||
func DeleteExceptions(accountID string, exceptions []string) error {
|
||||
|
||||
// load cached config
|
||||
getTenantConfig(accountID, "", getKubernetesApi())
|
||||
|
||||
// login kubescape SaaS
|
||||
armoAPI := getter.GetArmoAPIConnector()
|
||||
if err := armoAPI.Login(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range exceptions {
|
||||
exceptionName := exceptions[i]
|
||||
if exceptionName == "" {
|
||||
continue
|
||||
}
|
||||
logger.L().Info("Deleting exception", helpers.String("name", exceptionName))
|
||||
if err := armoAPI.DeleteException(exceptionName); err != nil {
|
||||
return fmt.Errorf("failed to delete exception '%s', reason: %s", exceptionName, err.Error())
|
||||
}
|
||||
logger.L().Success("Exception deleted successfully")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+17
-4
@@ -13,6 +13,7 @@ import (
|
||||
var listFunc = map[string]func(*cliobjects.ListPolicies) ([]string, error){
|
||||
"controls": listControls,
|
||||
"frameworks": listFrameworks,
|
||||
"exceptions": listExceptions,
|
||||
}
|
||||
|
||||
var listFormatFunc = map[string]func(*cliobjects.ListPolicies, []string){
|
||||
@@ -60,13 +61,25 @@ func listControls(listPolicies *cliobjects.ListPolicies) ([]string, error) {
|
||||
return g.ListControls(l)
|
||||
}
|
||||
|
||||
func listExceptions(listPolicies *cliobjects.ListPolicies) ([]string, error) {
|
||||
// load tenant config
|
||||
getTenantConfig(listPolicies.Account, "", getKubernetesApi())
|
||||
|
||||
var exceptionsNames []string
|
||||
armoAPI := getExceptionsGetter("")
|
||||
exceptions, err := armoAPI.GetExceptions("")
|
||||
if err != nil {
|
||||
return exceptionsNames, err
|
||||
}
|
||||
for i := range exceptions {
|
||||
exceptionsNames = append(exceptionsNames, exceptions[i].Name)
|
||||
}
|
||||
return exceptionsNames, nil
|
||||
}
|
||||
|
||||
func prettyPrintListFormat(listPolicies *cliobjects.ListPolicies, policies []string) {
|
||||
sep := "\n * "
|
||||
usageCmd := strings.TrimSuffix(listPolicies.Target, "s")
|
||||
fmt.Printf("Supported %s:%s%s\n", listPolicies.Target, sep, strings.Join(policies, sep))
|
||||
fmt.Printf("\nUsage:\n")
|
||||
fmt.Printf("$ kubescape scan %s \"name\"\n", usageCmd)
|
||||
fmt.Printf("$ kubescape scan %s \"name-0\",\"name-1\"\n\n", usageCmd)
|
||||
}
|
||||
|
||||
func jsonListFormat(listPolicies *cliobjects.ListPolicies, policies []string) {
|
||||
|
||||
@@ -3,3 +3,7 @@ package cliobjects
|
||||
type Submit struct {
|
||||
Account string
|
||||
}
|
||||
|
||||
type Delete struct {
|
||||
Account string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils/logger"
|
||||
"github.com/armosec/kubescape/clihandler"
|
||||
"github.com/armosec/kubescape/clihandler/cliobjects"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var deleteInfo cliobjects.Delete
|
||||
|
||||
var deleteExceptionsExamples = `
|
||||
# Delete single exception
|
||||
kubescape delete exceptions "exception name"
|
||||
|
||||
# Delete multiple exceptions
|
||||
kubescape delete exceptions "first exception;second exception;third exception"
|
||||
`
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
Use: "delete <command>",
|
||||
Short: "Delete configurations in Kubescape SaaS version",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
},
|
||||
}
|
||||
|
||||
var deleteExceptionsCmd = &cobra.Command{
|
||||
Use: "exceptions <exception name>",
|
||||
Short: "Delete exceptions from Kubescape SaaS version. Run 'kubescape list exceptions' for all exceptions names",
|
||||
Example: deleteExceptionsExamples,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("missing exceptions names")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
exceptionsNames := strings.Split(args[0], ";")
|
||||
if len(exceptionsNames) == 0 {
|
||||
logger.L().Fatal("missing exceptions names")
|
||||
}
|
||||
if err := clihandler.DeleteExceptions(deleteInfo.Account, exceptionsNames); err != nil {
|
||||
logger.L().Fatal(err.Error())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteCmd.PersistentFlags().StringVarP(&deleteInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
|
||||
rootCmd.AddCommand(deleteCmd)
|
||||
|
||||
deleteCmd.AddCommand(deleteExceptionsCmd)
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
)
|
||||
|
||||
var armoBEURLs = ""
|
||||
var armoBEURLsDep = ""
|
||||
var rootInfo cautils.RootInfo
|
||||
|
||||
const envFlagUsage = "Send report results to specific URL. Format:<ReportReceiver>,<Backend>,<Frontend>.\n\t\tExample:report.armo.cloud,api.armo.cloud,portal.armo.cloud"
|
||||
@@ -47,8 +48,12 @@ func init() {
|
||||
|
||||
cobra.OnInitialize(initLogger, initLoggerLevel, initEnvironment, initCacheDir)
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&armoBEURLs, "environment", "", envFlagUsage)
|
||||
rootCmd.PersistentFlags().StringVar(&armoBEURLsDep, "environment", "", envFlagUsage)
|
||||
rootCmd.PersistentFlags().StringVar(&armoBEURLs, "env", "", envFlagUsage)
|
||||
rootCmd.PersistentFlags().MarkDeprecated("environment", "use 'env' instead")
|
||||
rootCmd.PersistentFlags().MarkHidden("environment")
|
||||
rootCmd.PersistentFlags().MarkHidden("env")
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&rootInfo.Logger, "logger", "l", helpers.InfoLevel.String(), fmt.Sprintf("Logger level. Supported: %s [$KS_LOGGER]", strings.Join(helpers.SupportedLevels(), "/")))
|
||||
rootCmd.PersistentFlags().StringVar(&rootInfo.CacheDir, "cache-dir", getter.DefaultLocalStore, "Cache directory [$KS_CACHE_DIR]")
|
||||
}
|
||||
@@ -80,6 +85,9 @@ func initCacheDir() {
|
||||
logger.L().Debug("cache dir updated", helpers.String("path", getter.DefaultLocalStore))
|
||||
}
|
||||
func initEnvironment() {
|
||||
if armoBEURLsDep != "" {
|
||||
armoBEURLs = armoBEURLsDep
|
||||
}
|
||||
urlSlices := strings.Split(armoBEURLs, ",")
|
||||
if len(urlSlices) != 1 && len(urlSlices) < 3 {
|
||||
logger.L().Fatal("expected at least 3 URLs (report, api, frontend, auth)")
|
||||
|
||||
@@ -24,17 +24,6 @@
|
||||
"namespace": "kube-node-lease"
|
||||
}
|
||||
}
|
||||
],
|
||||
"posturePolicies": [
|
||||
{
|
||||
"frameworkName": "NSA"
|
||||
},
|
||||
{
|
||||
"frameworkName": "MITRE"
|
||||
},
|
||||
{
|
||||
"frameworkName": "ArmoBest"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -4,56 +4,85 @@ import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/logger"
|
||||
"github.com/armosec/kubescape/cautils/logger/helpers"
|
||||
"github.com/armosec/kubescape/resultshandling/printer"
|
||||
"github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
)
|
||||
|
||||
/*
|
||||
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:"testsuite"`
|
||||
Suites []JUnitTestCase `xml:"testsuites"`
|
||||
Frameworks []JUnitFrameworks `xml:"framework"`
|
||||
RiskScore float32 `xml:"riskScore,attr"` // test risk score
|
||||
Time string `xml:"time,attr"` // scanning time
|
||||
Controls int `xml:"testcases,attr"` // number of controls
|
||||
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 ?
|
||||
}
|
||||
|
||||
type JUnitFrameworks struct { // Frameworks
|
||||
Name string `xml:"name,attr"`
|
||||
RiskScore float32 `xml:"riskscore,attr"`
|
||||
Status string `xml:"status,attr"`
|
||||
// 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 errored
|
||||
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)
|
||||
Properties []JUnitProperty `xml:"properties>property,omitempty"`
|
||||
TestCases []JUnitTestCase `xml:"testcase"`
|
||||
}
|
||||
|
||||
// JUnitTestCase is a single test case with its result.
|
||||
type JUnitTestCase struct { // Control
|
||||
XMLName xml.Name `xml:"testcase"`
|
||||
Name string `xml:"name,attr"`
|
||||
ID string `xml:"id,attr"`
|
||||
Url string `xml:"url,attr"`
|
||||
RiskScore float32 `xml:"riskScore,attr"`
|
||||
Status string `xml:"status,attr"`
|
||||
Info string `xml:"info,attr"`
|
||||
AllResources int `xml:"allResources,attr"`
|
||||
Excluded int `xml:"excludedResources,attr"`
|
||||
Failed int `xml:"filedResources,attr"`
|
||||
Resources []JUnitResource `xml:"resource"`
|
||||
// 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"`
|
||||
}
|
||||
|
||||
type JUnitResource struct { // Single resource
|
||||
Name string `xml:"name,attr"`
|
||||
Namespace string `xml:"namespace,attr"`
|
||||
Kind string `xml:"kind,attr"`
|
||||
ApiVersion string `xml:"apiVersion,attr"`
|
||||
FailedPaths []armotypes.PosturePaths `xml:"jsonpath"`
|
||||
// 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 NewJunitPrinter(verbose bool) *JunitPrinter {
|
||||
@@ -87,70 +116,166 @@ func (junitPrinter *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionO
|
||||
}
|
||||
|
||||
func (junitPrinter *JunitPrinter) convertPostureReportToJunitResult(results *cautils.OPASessionObj) (*JUnitTestSuites, error) {
|
||||
juResult := JUnitTestSuites{
|
||||
|
||||
// // Frameworks
|
||||
// for _, frameworksReports := range results.Report.ListFrameworks().All() {
|
||||
// fw := JUnitFrameworks{}
|
||||
// fw.Name = frameworksReports.GetName()
|
||||
// fw.RiskScore = frameworksReports.GetScore()
|
||||
// fw.Status = string(frameworksReports.GetStatus().Status())
|
||||
// juResult.Frameworks = append(juResult.Frameworks, fw)
|
||||
// }
|
||||
testSuites := JUnitTestSuites{
|
||||
XMLName: xml.Name{
|
||||
Local: "Kubescape scan results",
|
||||
},
|
||||
RiskScore: results.Report.SummaryDetails.Score,
|
||||
Time: results.Report.GetTimestamp().String(),
|
||||
Controls: len(results.Report.ListControls().All()),
|
||||
Frameworks: []JUnitFrameworks{},
|
||||
}
|
||||
testSuites.Failures = results.Report.SummaryDetails.NumberOfResources().Failed()
|
||||
testSuites.Tests = results.Report.SummaryDetails.NumberOfResources().All()
|
||||
testSuites.Disabled = results.Report.SummaryDetails.NumberOfResources().Skipped()
|
||||
// summary.errors =
|
||||
// summary.Name = "?"
|
||||
|
||||
// Frameworks
|
||||
for _, frameworksReports := range results.Report.ListFrameworks().All() {
|
||||
fw := JUnitFrameworks{}
|
||||
fw.Name = frameworksReports.GetName()
|
||||
fw.RiskScore = frameworksReports.GetScore()
|
||||
fw.Status = string(frameworksReports.GetStatus().Status())
|
||||
juResult.Frameworks = append(juResult.Frameworks, fw)
|
||||
}
|
||||
// resources
|
||||
counter := 0
|
||||
for resourceID, resourceResult := range results.ResourcesResult {
|
||||
counter++
|
||||
|
||||
// controls
|
||||
for _, controlIDs := range results.Report.ListControlsIDs().All() {
|
||||
controlReport := results.Report.SummaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, controlIDs)
|
||||
|
||||
// control data
|
||||
testCase := JUnitTestCase{}
|
||||
testCase.Name = controlReport.GetName()
|
||||
testCase.ID = controlReport.GetID()
|
||||
testCase.Url = getControlURL(controlReport.GetID())
|
||||
testCase.Name = controlReport.GetName()
|
||||
testCase.Status = string(controlReport.GetStatus().Status())
|
||||
|
||||
// resources counters
|
||||
testCase.AllResources = controlReport.NumberOfResources().All()
|
||||
testCase.Excluded = controlReport.NumberOfResources().Excluded()
|
||||
testCase.Failed = controlReport.NumberOfResources().Failed()
|
||||
|
||||
// resources
|
||||
var jUnitResources []JUnitResource
|
||||
for _, resourceID := range controlReport.ListResourcesIDs().All() {
|
||||
result, ok := results.ResourcesResult[resourceID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if result.GetStatus(nil).IsPassed() && !junitPrinter.verbose { // add passed resources only in verbose mode
|
||||
continue
|
||||
}
|
||||
|
||||
jUnitResource := JUnitResource{}
|
||||
rules := result.ListRulesOfControl(controlReport.GetID(), "")
|
||||
for _, rule := range rules {
|
||||
jUnitResource.FailedPaths = append(jUnitResource.FailedPaths, rule.Paths...)
|
||||
}
|
||||
if resource, ok := results.AllResources[resourceID]; ok {
|
||||
jUnitResource.Name = resource.GetName()
|
||||
jUnitResource.Namespace = resource.GetNamespace()
|
||||
jUnitResource.Kind = resource.GetKind()
|
||||
jUnitResource.ApiVersion = resource.GetApiVersion()
|
||||
}
|
||||
|
||||
jUnitResources = append(jUnitResources, jUnitResource)
|
||||
// resource data
|
||||
testSuite := JUnitTestSuite{
|
||||
XMLName: xml.Name{
|
||||
Local: resourceID,
|
||||
},
|
||||
}
|
||||
testCase.Resources = jUnitResources
|
||||
juResult.Suites = append(juResult.Suites, testCase)
|
||||
testSuite.Name = resourceID
|
||||
testSuite.Disabled = 0
|
||||
testSuite.Errors = 0
|
||||
testSuite.Failures = len(resourceResult.ListControlsIDs(nil).Failed())
|
||||
testSuite.Hostname = ""
|
||||
testSuite.ID = counter
|
||||
// testSuite.Skipped = ""
|
||||
testSuite.Time = ""
|
||||
testSuite.Timestamp = results.PostureReport.ReportGenerationTime.String()
|
||||
|
||||
testSuite.Properties = []JUnitProperty{
|
||||
{
|
||||
Name: "ID",
|
||||
Value: resourceID,
|
||||
},
|
||||
}
|
||||
|
||||
// controls
|
||||
for _, control := range resourceResult.ListControls() {
|
||||
testCase := JUnitTestCase{
|
||||
XMLName: xml.Name{
|
||||
Local: control.GetName(),
|
||||
Space: getControlURL(control.GetID()),
|
||||
},
|
||||
}
|
||||
testCase.Name = control.GetName()
|
||||
testCase.Classname = control.GetID()
|
||||
testCase.Status = string(control.GetStatus(nil).Status())
|
||||
|
||||
if control.GetStatus(nil).IsFailed() {
|
||||
paths := failedPathsToString(&control)
|
||||
|
||||
testCaseFailure := JUnitFailure{}
|
||||
testCaseFailure.Contents = fmt.Sprintf("More deatiles: %s", getControlURL(control.GetID()))
|
||||
testCaseFailure.Message = strings.Join(paths, ";")
|
||||
testCaseFailure.Type = "" // TODO - suppot add/modify
|
||||
|
||||
testCase.Failure = &testCaseFailure
|
||||
}
|
||||
|
||||
testSuite.TestCases = append(testSuite.TestCases, testCase)
|
||||
}
|
||||
|
||||
testSuites.Suites = append(testSuites.Suites, testSuite)
|
||||
}
|
||||
|
||||
return &juResult, nil
|
||||
return &testSuites, nil
|
||||
}
|
||||
|
||||
// func (junitPrinter *JunitPrinter) convertPostureReportToJunitResult(results *cautils.OPASessionObj) (*JUnitTestSuites, error) {
|
||||
|
||||
// // // Frameworks
|
||||
// // for _, frameworksReports := range results.Report.ListFrameworks().All() {
|
||||
// // fw := JUnitFrameworks{}
|
||||
// // fw.Name = frameworksReports.GetName()
|
||||
// // fw.RiskScore = frameworksReports.GetScore()
|
||||
// // fw.Status = string(frameworksReports.GetStatus().Status())
|
||||
// // juResult.Frameworks = append(juResult.Frameworks, fw)
|
||||
// // }
|
||||
// testSuites := JUnitTestSuites{
|
||||
// XMLName: xml.Name{
|
||||
// Local: "Kubescape scan results",
|
||||
// },
|
||||
// }
|
||||
// testSuites.Failures = results.Report.SummaryDetails.NumberOfControls().Failed()
|
||||
// testSuites.Tests = results.Report.SummaryDetails.NumberOfControls().All()
|
||||
// testSuites.Disabled = results.Report.SummaryDetails.NumberOfControls().Skipped()
|
||||
// // summary.errors =
|
||||
// // summary.Name = "?"
|
||||
|
||||
// // controls
|
||||
// for _, controlIDs := range results.Report.ListControlsIDs().All() {
|
||||
// controlReport := results.Report.SummaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, controlIDs)
|
||||
|
||||
// // control data
|
||||
// testSuite := JUnitTestSuite{
|
||||
// XMLName: xml.Name{
|
||||
// Local: controlReport.GetName(),
|
||||
// Space: getControlURL(controlReport.GetID()),
|
||||
// },
|
||||
// }
|
||||
// testSuite.Name = controlReport.GetName()
|
||||
// testSuite.Disabled = 0
|
||||
// testSuite.Errors = 0
|
||||
// testSuite.Failures = controlReport.NumberOfResources().Failed()
|
||||
// testSuite.Hostname = ""
|
||||
// testSuite.ID = 0
|
||||
// // testSuite.Skipped = ""
|
||||
// testSuite.Time = ""
|
||||
// testSuite.Timestamp = ""
|
||||
|
||||
// testCase.Classname = controlReport.GetID()
|
||||
// testCase.Url = getControlURL(controlReport.GetID())
|
||||
// testCase.Name = controlReport.GetName()
|
||||
// testCase.Status = string(controlReport.GetStatus().Status())
|
||||
|
||||
// // resources counters
|
||||
// testCase.AllResources = controlReport.NumberOfResources().All()
|
||||
// testCase.Excluded = controlReport.NumberOfResources().Excluded()
|
||||
// testCase.Failed = controlReport.NumberOfResources().Failed()
|
||||
|
||||
// // resources
|
||||
// var jUnitResources []JUnitResource
|
||||
// for _, resourceID := range controlReport.ListResourcesIDs().All() {
|
||||
// result, ok := results.ResourcesResult[resourceID]
|
||||
// if !ok {
|
||||
// continue
|
||||
// }
|
||||
// if result.GetStatus(nil).IsPassed() && !junitPrinter.verbose { // add passed resources only in verbose mode
|
||||
// continue
|
||||
// }
|
||||
|
||||
// jUnitResource := JUnitResource{}
|
||||
// rules := result.ListRulesOfControl(controlReport.GetID(), "")
|
||||
// for _, rule := range rules {
|
||||
// jUnitResource.FailedPaths = append(jUnitResource.FailedPaths, rule.Paths...)
|
||||
// }
|
||||
// if resource, ok := results.AllResources[resourceID]; ok {
|
||||
// jUnitResource.Name = resource.GetName()
|
||||
// jUnitResource.Namespace = resource.GetNamespace()
|
||||
// jUnitResource.Kind = resource.GetKind()
|
||||
// jUnitResource.ApiVersion = resource.GetApiVersion()
|
||||
// }
|
||||
|
||||
// jUnitResources = append(jUnitResources, jUnitResource)
|
||||
// }
|
||||
// testCase.Resources = jUnitResources
|
||||
// juResult.Suites = append(juResult.Suites, testCase)
|
||||
// }
|
||||
|
||||
// return &juResult, nil
|
||||
// }
|
||||
|
||||
@@ -55,18 +55,8 @@ func generateResourceRows(resource workloadinterface.IMetadata, controls []resou
|
||||
|
||||
row = append(row, fmt.Sprintf("%s\nhttps://hub.armo.cloud/docs/%s", controls[i].GetName(), strings.ToLower(controls[i].GetID())))
|
||||
row = append(row, resource.GetNamespace())
|
||||
var paths []string
|
||||
for j := range controls[i].ResourceAssociatedRules {
|
||||
for k := range controls[i].ResourceAssociatedRules[j].Paths {
|
||||
if p := controls[i].ResourceAssociatedRules[j].Paths[k].FailedPath; p != "" {
|
||||
paths = append(paths, p)
|
||||
}
|
||||
if p := controls[i].ResourceAssociatedRules[j].Paths[k].FixPath.Path; p != "" {
|
||||
v := controls[i].ResourceAssociatedRules[j].Paths[k].FixPath.Value
|
||||
paths = append(paths, fmt.Sprintf("%s=%s", p, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
paths := failedPathsToString(&controls[i])
|
||||
|
||||
row = append(row, fmt.Sprintf("%s/%s\n%s", resource.GetKind(), resource.GetName(), strings.Join(paths, ";\n")))
|
||||
row = append(row, string(controls[i].GetStatus(nil).Status()))
|
||||
rows = append(rows, row)
|
||||
@@ -94,3 +84,20 @@ func (a Matrix) Less(i, j int) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func failedPathsToString(control *resourcesresults.ResourceAssociatedControl) []string {
|
||||
var paths []string
|
||||
|
||||
for j := range control.ResourceAssociatedRules {
|
||||
for k := range control.ResourceAssociatedRules[j].Paths {
|
||||
if p := control.ResourceAssociatedRules[j].Paths[k].FailedPath; p != "" {
|
||||
paths = append(paths, p)
|
||||
}
|
||||
if p := control.ResourceAssociatedRules[j].Paths[k].FixPath.Path; p != "" {
|
||||
v := control.ResourceAssociatedRules[j].Paths[k].FixPath.Value
|
||||
paths = append(paths, fmt.Sprintf("%s=%s", p, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user