mirror of
https://github.com/kubescape/kubescape.git
synced 2026-03-17 09:00:25 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd24f35738 | ||
|
|
5cf3244918 | ||
|
|
934c9ccc8b | ||
|
|
41dfdfd1e8 | ||
|
|
427fb59c99 | ||
|
|
ae825800f6 | ||
|
|
d72700acf6 | ||
|
|
3310a6a26f | ||
|
|
740b5aa772 | ||
|
|
04b55e764a | ||
|
|
beb4062bb1 | ||
|
|
5d4cd4acdc | ||
|
|
aec8198131 | ||
|
|
0a850e47df | ||
|
|
4f466d517a | ||
|
|
548201c256 | ||
|
|
a54e5d9f8b | ||
|
|
536257afa1 | ||
|
|
d194dd173f |
18
README.md
18
README.md
@@ -98,15 +98,21 @@ kubescape scan framework nsa --submit
|
||||
kubescape scan framework mitre --submit
|
||||
```
|
||||
|
||||
|
||||
* Scan a running Kubernetes cluster with a specific control using the control name or control ID. [List of controls](https://hub.armo.cloud/docs/controls)
|
||||
```
|
||||
kubescape scan control "Privileged container"
|
||||
```
|
||||
|
||||
* Scan local `yaml`/`json` files before deploying. [Take a look at the demonstration](https://youtu.be/Ox6DaR7_4ZI)
|
||||
```
|
||||
kubescape scan framework nsa *.yaml
|
||||
```
|
||||
|
||||
|
||||
* Scan `yaml`/`json` files from url
|
||||
* Scan kubernetes manifest files from a public github repository
|
||||
```
|
||||
kubescape scan framework nsa https://raw.githubusercontent.com/GoogleCloudPlatform/microservices-demo/master/release/kubernetes-manifests.yaml
|
||||
kubescape scan framework nsa https://github.com/armosec/kubescape
|
||||
```
|
||||
|
||||
* Output in `json` format
|
||||
@@ -193,7 +199,13 @@ go build -o kubescape .
|
||||
|
||||
4. Enjoy :zany_face:
|
||||
|
||||
## How to build in Docker
|
||||
## Docker Support
|
||||
|
||||
### Official Docker image
|
||||
```
|
||||
quay.io/armosec/kubescape
|
||||
```
|
||||
### Build your own Docker image
|
||||
|
||||
1. Clone Project
|
||||
```
|
||||
|
||||
2
build.py
2
build.py
@@ -41,7 +41,7 @@ def main():
|
||||
|
||||
# Set some variables
|
||||
packageName = getPackageName()
|
||||
buildUrl = "github.com/armosec/kubescape/cmd.BuildNumber"
|
||||
buildUrl = "github.com/armosec/kubescape/clihandler/cmd.BuildNumber"
|
||||
releaseVersion = os.getenv("RELEASE")
|
||||
ArmoBEServer = os.getenv("ArmoBEServer")
|
||||
ArmoERServer = os.getenv("ArmoERServer")
|
||||
|
||||
@@ -24,4 +24,4 @@ COPY --from=builder /work/build/ubuntu-latest/kubescape /usr/bin/kubescape
|
||||
# # Download the frameworks. Use the "--use-default" flag when running kubescape
|
||||
# RUN kubescape download framework nsa && kubescape download framework mitre
|
||||
|
||||
CMD ["kubescape"]
|
||||
ENTRYPOINT ["kubescape"]
|
||||
|
||||
@@ -109,8 +109,10 @@ func ClusterConfigSetup(scanInfo *ScanInfo, k8s *k8sinterface.KubernetesApi, beA
|
||||
return NewEmptyConfig() // local - Delete local config & Do not send report
|
||||
}
|
||||
if scanInfo.Local {
|
||||
scanInfo.Submit = false
|
||||
return NewEmptyConfig() // local - Do not send report
|
||||
}
|
||||
scanInfo.Submit = true
|
||||
return clusterConfig // submit/default - Submit report
|
||||
}
|
||||
|
||||
@@ -158,6 +160,7 @@ func (c *ClusterConfig) GetDefaultNS() string { return c.defau
|
||||
func (c *ClusterConfig) GetBackendAPI() getter.IBackend { return c.backendAPI }
|
||||
|
||||
func (c *ClusterConfig) GenerateURL() {
|
||||
message := "Checkout for more cool features: "
|
||||
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
@@ -165,9 +168,8 @@ func (c *ClusterConfig) GenerateURL() {
|
||||
if c.configObj == nil {
|
||||
return
|
||||
}
|
||||
message := fmt.Sprintf("\nCheckout for more cool features: https://%s\n", getter.GetArmoAPIConnector().GetFrontendURL())
|
||||
if c.configObj.CustomerAdminEMail != "" {
|
||||
InfoTextDisplay(os.Stdout, message+"\n")
|
||||
InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n")
|
||||
return
|
||||
}
|
||||
u.Path = "account/sign-up"
|
||||
@@ -176,8 +178,7 @@ func (c *ClusterConfig) GenerateURL() {
|
||||
q.Add("customerGUID", c.configObj.CustomerGUID)
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
InfoTextDisplay(os.Stdout, message+"\n")
|
||||
|
||||
InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n")
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) GetCustomerGUID() string {
|
||||
|
||||
@@ -18,7 +18,7 @@ type DownloadReleasedPolicy struct {
|
||||
|
||||
func NewDownloadReleasedPolicy() *DownloadReleasedPolicy {
|
||||
return &DownloadReleasedPolicy{
|
||||
gs: gitregostore.InitDefaultGitRegoStore(),
|
||||
gs: gitregostore.InitDefaultGitRegoStore(-1),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,10 @@ func (lp *LoadPolicy) GetControl(controlName string) (*reporthandling.Control, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(f, control)
|
||||
if err = json.Unmarshal(f, control); err != nil {
|
||||
return control, err
|
||||
}
|
||||
|
||||
if controlName != "" && !strings.EqualFold(controlName, control.Name) && !strings.EqualFold(controlName, control.ControlID) {
|
||||
return nil, fmt.Errorf("control from file not matching")
|
||||
}
|
||||
@@ -50,7 +53,10 @@ func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framew
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(f, framework)
|
||||
if err = json.Unmarshal(f, framework); err != nil {
|
||||
return framework, err
|
||||
}
|
||||
|
||||
if frameworkName != "" && !strings.EqualFold(frameworkName, framework.Name) {
|
||||
return nil, fmt.Errorf("framework from file not matching")
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package cautils
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
)
|
||||
@@ -84,22 +83,3 @@ func (scanInfo *ScanInfo) setOutputFile() {
|
||||
func (scanInfo *ScanInfo) ScanRunningCluster() bool {
|
||||
return len(scanInfo.InputPatterns) == 0
|
||||
}
|
||||
|
||||
func (scanInfo *ScanInfo) SetClusterConfig() (IClusterConfig, *k8sinterface.KubernetesApi) {
|
||||
var clusterConfig IClusterConfig
|
||||
var k8s *k8sinterface.KubernetesApi
|
||||
if !scanInfo.ScanRunningCluster() {
|
||||
k8sinterface.ConnectedToCluster = false
|
||||
clusterConfig = NewEmptyConfig()
|
||||
} else {
|
||||
k8s = k8sinterface.NewKubernetesApi()
|
||||
// setup cluster config
|
||||
clusterConfig = ClusterConfigSetup(scanInfo, k8s, getter.GetArmoAPIConnector())
|
||||
}
|
||||
return clusterConfig, k8s
|
||||
}
|
||||
|
||||
// func (scanInfo *ScanInfo) ConnectedToCluster(k8s k8sinterface.) bool {
|
||||
// _, err := k8s.KubernetesClient.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{})
|
||||
// return err == nil
|
||||
// }
|
||||
|
||||
@@ -31,7 +31,7 @@ var controlCmd = &cobra.Command{
|
||||
scanInfo.PolicyIdentifier.Kind = reporthandling.KindControl
|
||||
scanInfo.Init()
|
||||
cautils.SetSilentMode(scanInfo.Silent)
|
||||
err := clihandler.CliSetup(scanInfo)
|
||||
err := clihandler.CliSetup(&scanInfo)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -54,7 +54,7 @@ var frameworkCmd = &cobra.Command{
|
||||
}
|
||||
scanInfo.Init()
|
||||
cautils.SetSilentMode(scanInfo.Silent)
|
||||
err := clihandler.CliSetup(scanInfo)
|
||||
err := clihandler.CliSetup(&scanInfo)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -25,21 +25,21 @@ func GetLatestVersion() (string, error) {
|
||||
latestVersion := "https://api.github.com/repos/armosec/kubescape/releases/latest"
|
||||
resp, err := http.Get(latestVersion)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get latest releases from '%s', reason: %s", latestVersion, err.Error())
|
||||
return "unknown", fmt.Errorf("failed to get latest releases from '%s', reason: %s", latestVersion, err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode < 200 || 301 < resp.StatusCode {
|
||||
return "", fmt.Errorf("failed to download file, status code: %s", resp.Status)
|
||||
return "unknown", fmt.Errorf("failed to download file, status code: %s", resp.Status)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read response body from '%s', reason: %s", latestVersion, err.Error())
|
||||
return "unknown", fmt.Errorf("failed to read response body from '%s', reason: %s", latestVersion, err.Error())
|
||||
}
|
||||
var data map[string]interface{}
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to unmarshal response body from '%s', reason: %s", latestVersion, err.Error())
|
||||
return "unknown", fmt.Errorf("failed to unmarshal response body from '%s', reason: %s", latestVersion, err.Error())
|
||||
}
|
||||
return fmt.Sprintf("%v", data["tag_name"]), nil
|
||||
}
|
||||
|
||||
@@ -6,9 +6,12 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
"github.com/armosec/kubescape/opaprocessor"
|
||||
"github.com/armosec/kubescape/policyhandler"
|
||||
"github.com/armosec/kubescape/resourcehandler"
|
||||
"github.com/armosec/kubescape/resultshandling"
|
||||
"github.com/armosec/kubescape/resultshandling/printer"
|
||||
"github.com/armosec/kubescape/resultshandling/reporter"
|
||||
@@ -23,25 +26,77 @@ type CLIHandler struct {
|
||||
var SupportedFrameworks = []string{"nsa", "mitre"}
|
||||
var ValidFrameworks = strings.Join(SupportedFrameworks, ", ")
|
||||
|
||||
func CliSetup(scanInfo cautils.ScanInfo) error {
|
||||
type componentInterfaces struct {
|
||||
clusterConfig cautils.IClusterConfig
|
||||
resourceHandler resourcehandler.IResourceHandler
|
||||
report reporter.IReport
|
||||
printerHandler printer.IPrinter
|
||||
}
|
||||
|
||||
clusterConfig, k8s := scanInfo.SetClusterConfig()
|
||||
func getReporter(scanInfo *cautils.ScanInfo) reporter.IReport {
|
||||
if !scanInfo.Submit {
|
||||
return reporter.NewReportMock()
|
||||
}
|
||||
if !scanInfo.FrameworkScan {
|
||||
return reporter.NewReportMock()
|
||||
}
|
||||
|
||||
return reporter.NewReportEventReceiver()
|
||||
}
|
||||
func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
|
||||
var resourceHandler resourcehandler.IResourceHandler
|
||||
var clusterConfig cautils.IClusterConfig
|
||||
var reportHandler reporter.IReport
|
||||
|
||||
if !scanInfo.ScanRunningCluster() {
|
||||
k8sinterface.ConnectedToCluster = false
|
||||
clusterConfig = cautils.NewEmptyConfig()
|
||||
|
||||
// load fom file
|
||||
resourceHandler = resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns)
|
||||
|
||||
// set mock report (do not send report)
|
||||
reportHandler = reporter.NewReportMock()
|
||||
} else {
|
||||
k8s := k8sinterface.NewKubernetesApi()
|
||||
resourceHandler = resourcehandler.NewK8sResourceHandler(k8s, scanInfo.ExcludedNamespaces)
|
||||
clusterConfig = cautils.ClusterConfigSetup(scanInfo, k8s, getter.GetArmoAPIConnector())
|
||||
|
||||
// setup reporter
|
||||
reportHandler = getReporter(scanInfo)
|
||||
}
|
||||
|
||||
// setup printer
|
||||
printerHandler := printer.GetPrinter(scanInfo.Format)
|
||||
printerHandler.SetWriter(scanInfo.Output)
|
||||
|
||||
return componentInterfaces{
|
||||
clusterConfig: clusterConfig,
|
||||
resourceHandler: resourceHandler,
|
||||
report: reportHandler,
|
||||
printerHandler: printerHandler,
|
||||
}
|
||||
}
|
||||
|
||||
func CliSetup(scanInfo *cautils.ScanInfo) error {
|
||||
|
||||
interfaces := getInterfaces(scanInfo)
|
||||
|
||||
processNotification := make(chan *cautils.OPASessionObj)
|
||||
reportResults := make(chan *cautils.OPASessionObj)
|
||||
|
||||
// policy handler setup
|
||||
policyHandler := policyhandler.NewPolicyHandler(&processNotification, k8s)
|
||||
|
||||
if err := clusterConfig.SetConfig(scanInfo.Account); err != nil {
|
||||
if err := interfaces.clusterConfig.SetConfig(scanInfo.Account); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
cautils.ClusterName = clusterConfig.GetClusterName()
|
||||
cautils.CustomerGUID = clusterConfig.GetCustomerGUID()
|
||||
|
||||
cautils.ClusterName = interfaces.clusterConfig.GetClusterName() // TODO - Deprecated
|
||||
cautils.CustomerGUID = interfaces.clusterConfig.GetCustomerGUID() // TODO - Deprecated
|
||||
interfaces.report.SetClusterName(interfaces.clusterConfig.GetClusterName())
|
||||
interfaces.report.SetCustomerGUID(interfaces.clusterConfig.GetCustomerGUID())
|
||||
// cli handler setup
|
||||
go func() {
|
||||
// policy handler setup
|
||||
policyHandler := policyhandler.NewPolicyHandler(&processNotification, interfaces.resourceHandler)
|
||||
cli := NewCLIHandler(policyHandler, scanInfo)
|
||||
if err := cli.Scan(); err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -55,13 +110,11 @@ func CliSetup(scanInfo cautils.ScanInfo) error {
|
||||
opaprocessorObj.ProcessRulesListenner()
|
||||
}()
|
||||
|
||||
resultsHandling := resultshandling.NewResultsHandler(&reportResults, reporter.NewReportEventReceiver(), printer.NewPrinter(scanInfo.Format, scanInfo.Output))
|
||||
resultsHandling := resultshandling.NewResultsHandler(&reportResults, interfaces.report, interfaces.printerHandler)
|
||||
score := resultsHandling.HandleResults(scanInfo)
|
||||
|
||||
// print report url
|
||||
if scanInfo.FrameworkScan {
|
||||
clusterConfig.GenerateURL()
|
||||
}
|
||||
interfaces.clusterConfig.GenerateURL()
|
||||
|
||||
adjustedFailThreshold := float32(scanInfo.FailThreshold) / 100
|
||||
if score < adjustedFailThreshold {
|
||||
@@ -71,9 +124,9 @@ func CliSetup(scanInfo cautils.ScanInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewCLIHandler(policyHandler *policyhandler.PolicyHandler, scanInfo cautils.ScanInfo) *CLIHandler {
|
||||
func NewCLIHandler(policyHandler *policyhandler.PolicyHandler, scanInfo *cautils.ScanInfo) *CLIHandler {
|
||||
return &CLIHandler{
|
||||
scanInfo: &scanInfo,
|
||||
scanInfo: scanInfo,
|
||||
policyHandler: policyHandler,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,10 +72,8 @@ metadata:
|
||||
data:
|
||||
config.json: |
|
||||
{
|
||||
"adminMail": "",
|
||||
"customerGUID": "<MyGUID>",
|
||||
"clusterName": "<MyK8sClusterName>",
|
||||
"invitationParam": ""
|
||||
"customerGUID": <MyGUID>,
|
||||
"clusterName": <MyK8sClusterName>
|
||||
}
|
||||
|
||||
---
|
||||
@@ -104,12 +102,11 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: kubescape
|
||||
image: quay.io/armosec/kubescape:latset
|
||||
image: quay.io/armosec/kubescape:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/bin/sh","-c"]
|
||||
args:
|
||||
- kubescape scan framework nsa --exclude-namespaces kube-system,kube-public
|
||||
#- kubescape scan framework nsa --use-default --exclude-namespaces kube-system,kube-public
|
||||
- kubescape scan framework nsa --submit
|
||||
volumeMounts:
|
||||
- name: kubescape-config-volume
|
||||
mountPath: /root/.kubescape/config.json
|
||||
|
||||
4
go.mod
4
go.mod
@@ -4,8 +4,8 @@ go 1.17
|
||||
|
||||
require (
|
||||
github.com/armosec/armoapi-go v0.0.8
|
||||
github.com/armosec/k8s-interface v0.0.5
|
||||
github.com/armosec/opa-utils v0.0.13
|
||||
github.com/armosec/k8s-interface v0.0.8
|
||||
github.com/armosec/opa-utils v0.0.18
|
||||
github.com/armosec/utils-go v0.0.3
|
||||
github.com/briandowns/spinner v1.16.0
|
||||
github.com/enescakir/emoji v1.0.0
|
||||
|
||||
7
go.sum
7
go.sum
@@ -87,10 +87,11 @@ github.com/armosec/armoapi-go v0.0.2/go.mod h1:vIK17yoKbJRQyZXWWLe3AqfqCRITxW8qm
|
||||
github.com/armosec/armoapi-go v0.0.7/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
|
||||
github.com/armosec/armoapi-go v0.0.8 h1:JPa9rZynuE2RucamDh6dsy/sjCScmWDsyt1zagJFCDo=
|
||||
github.com/armosec/armoapi-go v0.0.8/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
|
||||
github.com/armosec/k8s-interface v0.0.5 h1:DWQXZNMSsYQeLQ6xpB21ueFMR9oFnz28iWQTNn31TAk=
|
||||
github.com/armosec/k8s-interface v0.0.5/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM=
|
||||
github.com/armosec/opa-utils v0.0.13 h1:QkmmYX0lzC7ZNGetyD8ysRKQHgJhjMfvRUW2cp+hz2o=
|
||||
github.com/armosec/opa-utils v0.0.13/go.mod h1:E0mFTVx+4BYAVvO2hxWnIniv/IZIogRCak8BkKd7KK4=
|
||||
github.com/armosec/k8s-interface v0.0.8 h1:Eo3Qen4yFXxzVem49FNeij2ckyzHSAJ0w6PZMaSEIm8=
|
||||
github.com/armosec/k8s-interface v0.0.8/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM=
|
||||
github.com/armosec/opa-utils v0.0.18 h1:1hL5v2KCD8yStuwzul+gq1zg9+RCV9N3kHoRepKnrg0=
|
||||
github.com/armosec/opa-utils v0.0.18/go.mod h1:E0mFTVx+4BYAVvO2hxWnIniv/IZIogRCak8BkKd7KK4=
|
||||
github.com/armosec/utils-go v0.0.2/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
|
||||
github.com/armosec/utils-go v0.0.3 h1:uyQI676yRciQM0sSN9uPoqHkbspTxHO0kmzXhBeE/xU=
|
||||
github.com/armosec/utils-go v0.0.3/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/opa-utils/exceptions"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/armosec/opa-utils/score"
|
||||
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
|
||||
@@ -226,17 +225,6 @@ func (opap *OPAProcessor) regoEval(inputObj []map[string]interface{}, compiledRe
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (opap *OPAProcessor) updateScore() {
|
||||
|
||||
if !k8sinterface.ConnectedToCluster {
|
||||
return
|
||||
}
|
||||
|
||||
// calculate score
|
||||
s := score.NewScore(k8sinterface.NewKubernetesApi(), ScoreConfigPath)
|
||||
s.Calculate(opap.PostureReport.FrameworkReports)
|
||||
}
|
||||
|
||||
func (opap *OPAProcessor) updateResults() {
|
||||
for f := range opap.PostureReport.FrameworkReports {
|
||||
// set exceptions
|
||||
|
||||
@@ -4,30 +4,25 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/resourcehandler"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
)
|
||||
|
||||
var supportedFrameworks = []reporthandling.PolicyIdentifier{
|
||||
{Kind: "Framework", Name: "nsa"},
|
||||
{Kind: "Framework", Name: "mitre"},
|
||||
}
|
||||
|
||||
// PolicyHandler -
|
||||
type PolicyHandler struct {
|
||||
k8s *k8sinterface.KubernetesApi
|
||||
resourceHandler resourcehandler.IResourceHandler
|
||||
// we are listening on this chan in opaprocessor/processorhandler.go/ProcessRulesListenner func
|
||||
processPolicy *chan *cautils.OPASessionObj
|
||||
getters *cautils.Getters
|
||||
}
|
||||
|
||||
// CreatePolicyHandler Create ws-handler obj
|
||||
func NewPolicyHandler(processPolicy *chan *cautils.OPASessionObj, k8s *k8sinterface.KubernetesApi) *PolicyHandler {
|
||||
func NewPolicyHandler(processPolicy *chan *cautils.OPASessionObj, resourceHandler resourcehandler.IResourceHandler) *PolicyHandler {
|
||||
return &PolicyHandler{
|
||||
k8s: k8s,
|
||||
processPolicy: processPolicy,
|
||||
resourceHandler: resourceHandler,
|
||||
processPolicy: processPolicy,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,16 +77,7 @@ func (policyHandler *PolicyHandler) getPolicies(notification *reporthandling.Pol
|
||||
}
|
||||
|
||||
func (policyHandler *PolicyHandler) getResources(notification *reporthandling.PolicyNotification, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) (*cautils.K8SResources, error) {
|
||||
var k8sResources *cautils.K8SResources
|
||||
var err error
|
||||
if k8sinterface.ConnectedToCluster { // TODO - use interface
|
||||
if opaSessionObj.PostureReport.ClusterAPIServerInfo, err = policyHandler.k8s.KubernetesClient.Discovery().ServerVersion(); err != nil {
|
||||
cautils.ErrorDisplay(fmt.Sprintf("Failed to discover API server inforamtion: %v", err))
|
||||
}
|
||||
k8sResources, err = policyHandler.getK8sResources(opaSessionObj.Frameworks, ¬ification.Designators, scanInfo.ExcludedNamespaces)
|
||||
} else {
|
||||
k8sResources, err = policyHandler.loadResources(opaSessionObj.Frameworks, scanInfo)
|
||||
}
|
||||
|
||||
return k8sResources, err
|
||||
opaSessionObj.PostureReport.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo()
|
||||
return policyHandler.resourceHandler.GetResources(opaSessionObj.Frameworks, ¬ification.Designators)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package policyhandler
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
"github.com/armosec/k8s-interface/workloadinterface"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
@@ -29,11 +31,22 @@ const (
|
||||
JSON_FILE_FORMAT FileFormat = "json"
|
||||
)
|
||||
|
||||
func (policyHandler *PolicyHandler) loadResources(frameworks []reporthandling.Framework, scanInfo *cautils.ScanInfo) (*cautils.K8SResources, error) {
|
||||
// FileResourceHandler handle resources from files and URLs
|
||||
type FileResourceHandler struct {
|
||||
inputPatterns []string
|
||||
}
|
||||
|
||||
func NewFileResourceHandler(inputPatterns []string) *FileResourceHandler {
|
||||
return &FileResourceHandler{
|
||||
inputPatterns: inputPatterns,
|
||||
}
|
||||
}
|
||||
|
||||
func (fileHandler *FileResourceHandler) GetResources(frameworks []reporthandling.Framework, designator *armotypes.PortalDesignator) (*cautils.K8SResources, error) {
|
||||
workloads := []k8sinterface.IWorkload{}
|
||||
|
||||
// load resource from local file system
|
||||
w, err := loadResourcesFromFiles(scanInfo.InputPatterns)
|
||||
w, err := loadResourcesFromFiles(fileHandler.inputPatterns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -42,7 +55,7 @@ func (policyHandler *PolicyHandler) loadResources(frameworks []reporthandling.Fr
|
||||
}
|
||||
|
||||
// load resources from url
|
||||
w, err = loadResourcesFromUrl(scanInfo.InputPatterns)
|
||||
w, err = loadResourcesFromUrl(fileHandler.inputPatterns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -59,7 +72,7 @@ func (policyHandler *PolicyHandler) loadResources(frameworks []reporthandling.Fr
|
||||
|
||||
// build resources map
|
||||
// map resources based on framework required resources: map["/group/version/kind"][]<k8s workloads>
|
||||
k8sResources := setResourceMap(frameworks)
|
||||
k8sResources := setResourceMap(frameworks) // TODO - support designators
|
||||
|
||||
// save only relevant resources
|
||||
for i := range allResources {
|
||||
@@ -72,6 +85,10 @@ func (policyHandler *PolicyHandler) loadResources(frameworks []reporthandling.Fr
|
||||
|
||||
}
|
||||
|
||||
func (fileHandler *FileResourceHandler) GetClusterAPIServerInfo() *version.Info {
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadResourcesFromFiles(inputPatterns []string) ([]k8sinterface.IWorkload, error) {
|
||||
files, errs := listFiles(inputPatterns)
|
||||
if len(errs) > 0 {
|
||||
@@ -1,4 +1,4 @@
|
||||
package policyhandler
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,6 +1,7 @@
|
||||
package policyhandler
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -15,12 +16,23 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
k8slabels "k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
const SelectAllResources = "*"
|
||||
type K8sResourceHandler struct {
|
||||
k8s *k8sinterface.KubernetesApi
|
||||
excludedNamespaces string // excluded namespaces (separated by comma)
|
||||
}
|
||||
|
||||
func (policyHandler *PolicyHandler) getK8sResources(frameworks []reporthandling.Framework, designator *armotypes.PortalDesignator, excludedNamespaces string) (*cautils.K8SResources, error) {
|
||||
func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, excludedNamespaces string) *K8sResourceHandler {
|
||||
return &K8sResourceHandler{
|
||||
k8s: k8s,
|
||||
excludedNamespaces: excludedNamespaces,
|
||||
}
|
||||
}
|
||||
|
||||
func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.Framework, designator *armotypes.PortalDesignator) (*cautils.K8SResources, error) {
|
||||
// get k8s resources
|
||||
cautils.ProgressTextDisplay("Accessing Kubernetes objects")
|
||||
|
||||
@@ -31,7 +43,7 @@ func (policyHandler *PolicyHandler) getK8sResources(frameworks []reporthandling.
|
||||
_, namespace, labels := armotypes.DigestPortalDesignator(designator)
|
||||
|
||||
// pull k8s recourses
|
||||
if err := policyHandler.pullResources(k8sResourcesMap, namespace, labels, excludedNamespaces); err != nil {
|
||||
if err := k8sHandler.pullResources(k8sResourcesMap, namespace, labels, k8sHandler.excludedNamespaces); err != nil {
|
||||
return k8sResourcesMap, err
|
||||
}
|
||||
|
||||
@@ -39,13 +51,21 @@ func (policyHandler *PolicyHandler) getK8sResources(frameworks []reporthandling.
|
||||
return k8sResourcesMap, nil
|
||||
}
|
||||
|
||||
func (policyHandler *PolicyHandler) pullResources(k8sResources *cautils.K8SResources, namespace string, labels map[string]string, excludedNamespaces string) error {
|
||||
func (k8sHandler *K8sResourceHandler) GetClusterAPIServerInfo() *version.Info {
|
||||
clusterAPIServerInfo, err := k8sHandler.k8s.KubernetesClient.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
cautils.ErrorDisplay(fmt.Sprintf("Failed to discover API server information: %v", err))
|
||||
return nil
|
||||
}
|
||||
return clusterAPIServerInfo
|
||||
}
|
||||
func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SResources, namespace string, labels map[string]string, excludedNamespaces string) error {
|
||||
|
||||
var errs error
|
||||
for groupResource := range *k8sResources {
|
||||
apiGroup, apiVersion, resource := k8sinterface.StringToResourceGroup(groupResource)
|
||||
gvr := schema.GroupVersionResource{Group: apiGroup, Version: apiVersion, Resource: resource}
|
||||
result, err := policyHandler.pullSingleResource(&gvr, namespace, labels, excludedNamespaces)
|
||||
result, err := k8sHandler.pullSingleResource(&gvr, namespace, labels, excludedNamespaces)
|
||||
if err != nil {
|
||||
// handle error
|
||||
if errs == nil {
|
||||
@@ -61,7 +81,7 @@ func (policyHandler *PolicyHandler) pullResources(k8sResources *cautils.K8SResou
|
||||
return errs
|
||||
}
|
||||
|
||||
func (policyHandler *PolicyHandler) pullSingleResource(resource *schema.GroupVersionResource, namespace string, labels map[string]string, excludedNamespaces string) ([]unstructured.Unstructured, error) {
|
||||
func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupVersionResource, namespace string, labels map[string]string, excludedNamespaces string) ([]unstructured.Unstructured, error) {
|
||||
|
||||
// set labels
|
||||
listOptions := metav1.ListOptions{}
|
||||
@@ -76,13 +96,13 @@ func (policyHandler *PolicyHandler) pullSingleResource(resource *schema.GroupVer
|
||||
// set dynamic object
|
||||
var clientResource dynamic.ResourceInterface
|
||||
if namespace != "" && k8sinterface.IsNamespaceScope(resource.Group, resource.Resource) {
|
||||
clientResource = policyHandler.k8s.DynamicClient.Resource(*resource).Namespace(namespace)
|
||||
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource).Namespace(namespace)
|
||||
} else {
|
||||
clientResource = policyHandler.k8s.DynamicClient.Resource(*resource)
|
||||
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource)
|
||||
}
|
||||
|
||||
// list resources
|
||||
result, err := clientResource.List(policyHandler.k8s.Context, listOptions)
|
||||
result, err := clientResource.List(context.Background(), listOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get resource: %v, namespace: %s, labelSelector: %v, reason: %s", resource, namespace, listOptions.LabelSelector, err.Error())
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package policyhandler
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
@@ -1,4 +1,4 @@
|
||||
package policyhandler
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
@@ -1,4 +1,4 @@
|
||||
package policyhandler
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
13
resourcehandler/resourceshandler.go
Normal file
13
resourcehandler/resourceshandler.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
)
|
||||
|
||||
type IResourceHandler interface {
|
||||
GetResources(frameworks []reporthandling.Framework, designator *armotypes.PortalDesignator) (*cautils.K8SResources, error)
|
||||
GetClusterAPIServerInfo() *version.Info
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package policyhandler
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
34
resultshandling/printer/jsonprinter.go
Normal file
34
resultshandling/printer/jsonprinter.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
)
|
||||
|
||||
type JsonPrinter struct {
|
||||
writer *os.File
|
||||
}
|
||||
|
||||
func NewJsonPrinter() *JsonPrinter {
|
||||
return &JsonPrinter{}
|
||||
}
|
||||
|
||||
func (jsonPrinter *JsonPrinter) SetWriter(outputFile string) {
|
||||
jsonPrinter.writer = getWriter(outputFile)
|
||||
}
|
||||
|
||||
func (jsonPrinter *JsonPrinter) Score(score float32) {
|
||||
fmt.Printf("\nFinal score: %d", int(score*100))
|
||||
}
|
||||
|
||||
func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
||||
postureReportStr, err := json.Marshal(opaSessionObj.PostureReport.FrameworkReports[0])
|
||||
if err != nil {
|
||||
fmt.Println("Failed to convert posture report object!")
|
||||
os.Exit(1)
|
||||
}
|
||||
jsonPrinter.writer.Write(postureReportStr)
|
||||
}
|
||||
@@ -3,10 +3,42 @@ package printer
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
)
|
||||
|
||||
type JunitPrinter struct {
|
||||
writer *os.File
|
||||
}
|
||||
|
||||
func NewJunitPrinter() *JunitPrinter {
|
||||
return &JunitPrinter{}
|
||||
}
|
||||
|
||||
func (junitPrinter *JunitPrinter) SetWriter(outputFile string) {
|
||||
junitPrinter.writer = getWriter(outputFile)
|
||||
}
|
||||
|
||||
func (junitPrinter *JunitPrinter) Score(score float32) {
|
||||
fmt.Printf("\nFinal score: %d", int(score*100))
|
||||
}
|
||||
|
||||
func (junitPrinter *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
||||
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)
|
||||
}
|
||||
junitPrinter.writer.Write(postureReportStr)
|
||||
}
|
||||
|
||||
type JUnitTestSuites struct {
|
||||
XMLName xml.Name `xml:"testsuites"`
|
||||
Suites []JUnitTestSuite `xml:"testsuite"`
|
||||
@@ -74,9 +106,9 @@ func convertPostureReportToJunitResult(postureResult *reporthandling.PostureRepo
|
||||
testCase.Time = "0"
|
||||
if 0 < len(controlReports.RuleReports[0].RuleResponses) {
|
||||
|
||||
testCase.Resources = framework.GetNumberOfResources()
|
||||
testCase.Excluded = framework.GetNumberOfWarningResources()
|
||||
testCase.Failed = framework.GetNumberOfFailedResources()
|
||||
testCase.Resources = controlReports.GetNumberOfResources()
|
||||
testCase.Excluded = controlReports.GetNumberOfWarningResources()
|
||||
testCase.Failed = controlReports.GetNumberOfFailedResources()
|
||||
failure := JUnitFailure{}
|
||||
failure.Message = fmt.Sprintf("%d resources failed", testCase.Failed)
|
||||
for _, ruleResponses := range controlReports.RuleReports[0].RuleResponses {
|
||||
|
||||
219
resultshandling/printer/prettyprinter.go
Normal file
219
resultshandling/printer/prettyprinter.go
Normal file
@@ -0,0 +1,219 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/enescakir/emoji"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
type PrettyPrinter struct {
|
||||
writer *os.File
|
||||
summary Summary
|
||||
sortedControlNames []string
|
||||
frameworkSummary ControlSummary
|
||||
}
|
||||
|
||||
func NewPrettyPrinter() *PrettyPrinter {
|
||||
return &PrettyPrinter{
|
||||
summary: NewSummary(),
|
||||
}
|
||||
}
|
||||
|
||||
func (printer *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
||||
// score := calculatePostureScore(opaSessionObj.PostureReport)
|
||||
|
||||
printer.summarySetup(opaSessionObj.PostureReport)
|
||||
printer.printResults()
|
||||
printer.printSummaryTable()
|
||||
|
||||
// return score
|
||||
}
|
||||
|
||||
func (printer *PrettyPrinter) SetWriter(outputFile string) {
|
||||
printer.writer = getWriter(outputFile)
|
||||
}
|
||||
|
||||
func (printer *PrettyPrinter) Score(score float32) {
|
||||
}
|
||||
|
||||
func (printer *PrettyPrinter) summarySetup(postureReport *reporthandling.PostureReport) {
|
||||
for _, fr := range postureReport.FrameworkReports {
|
||||
printer.frameworkSummary = ControlSummary{
|
||||
TotalResources: fr.GetNumberOfResources(),
|
||||
TotalFailed: fr.GetNumberOfFailedResources(),
|
||||
TotalWarnign: fr.GetNumberOfWarningResources(),
|
||||
}
|
||||
for _, cr := range fr.ControlReports {
|
||||
if len(cr.RuleReports) == 0 {
|
||||
continue
|
||||
}
|
||||
workloadsSummary := listResultSummary(cr.RuleReports)
|
||||
|
||||
printer.summary[cr.Name] = ControlSummary{
|
||||
TotalResources: cr.GetNumberOfResources(),
|
||||
TotalFailed: cr.GetNumberOfFailedResources(),
|
||||
TotalWarnign: cr.GetNumberOfWarningResources(),
|
||||
FailedWorkloads: groupByNamespace(workloadsSummary, workloadSummaryFailed),
|
||||
ExcludedWorkloads: groupByNamespace(workloadsSummary, workloadSummaryExclude),
|
||||
Description: cr.Description,
|
||||
Remediation: cr.Remediation,
|
||||
ListInputKinds: cr.ListControlsInputKinds(),
|
||||
}
|
||||
}
|
||||
}
|
||||
printer.sortedControlNames = printer.getSortedControlsNames()
|
||||
}
|
||||
func (printer *PrettyPrinter) printResults() {
|
||||
for i := 0; i < len(printer.sortedControlNames); i++ {
|
||||
controlSummary := printer.summary[printer.sortedControlNames[i]]
|
||||
printer.printTitle(printer.sortedControlNames[i], &controlSummary)
|
||||
printer.printResources(&controlSummary)
|
||||
if printer.summary[printer.sortedControlNames[i]].TotalResources > 0 {
|
||||
printer.printSummary(printer.sortedControlNames[i], &controlSummary)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (printer *PrettyPrinter) printSummary(controlName string, controlSummary *ControlSummary) {
|
||||
cautils.SimpleDisplay(printer.writer, "Summary - ")
|
||||
cautils.SuccessDisplay(printer.writer, "Passed:%v ", controlSummary.TotalResources-controlSummary.TotalFailed-controlSummary.TotalWarnign)
|
||||
cautils.WarningDisplay(printer.writer, "Excluded:%v ", controlSummary.TotalWarnign)
|
||||
cautils.FailureDisplay(printer.writer, "Failed:%v ", controlSummary.TotalFailed)
|
||||
cautils.InfoDisplay(printer.writer, "Total:%v\n", controlSummary.TotalResources)
|
||||
if controlSummary.TotalFailed > 0 {
|
||||
cautils.DescriptionDisplay(printer.writer, "Remediation: %v\n", controlSummary.Remediation)
|
||||
}
|
||||
cautils.DescriptionDisplay(printer.writer, "\n")
|
||||
|
||||
}
|
||||
|
||||
func (printer *PrettyPrinter) printTitle(controlName string, controlSummary *ControlSummary) {
|
||||
cautils.InfoDisplay(printer.writer, "[control: %s] ", controlName)
|
||||
if controlSummary.TotalResources == 0 {
|
||||
cautils.InfoDisplay(printer.writer, "resources not found %v\n", emoji.ConfusedFace)
|
||||
} else if controlSummary.TotalFailed != 0 {
|
||||
cautils.FailureDisplay(printer.writer, "failed %v\n", emoji.SadButRelievedFace)
|
||||
} else if controlSummary.TotalWarnign != 0 {
|
||||
cautils.WarningDisplay(printer.writer, "excluded %v\n", emoji.NeutralFace)
|
||||
} else {
|
||||
cautils.SuccessDisplay(printer.writer, "passed %v\n", emoji.ThumbsUp)
|
||||
}
|
||||
|
||||
cautils.DescriptionDisplay(printer.writer, "Description: %s\n", controlSummary.Description)
|
||||
|
||||
}
|
||||
func (printer *PrettyPrinter) printResources(controlSummary *ControlSummary) {
|
||||
|
||||
if len(controlSummary.FailedWorkloads) > 0 {
|
||||
cautils.FailureDisplay(printer.writer, "Failed:\n")
|
||||
printer.printGroupedResources(controlSummary.FailedWorkloads)
|
||||
}
|
||||
if len(controlSummary.ExcludedWorkloads) > 0 {
|
||||
cautils.WarningDisplay(printer.writer, "Excluded:\n")
|
||||
printer.printGroupedResources(controlSummary.ExcludedWorkloads)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (printer *PrettyPrinter) printGroupedResources(workloads map[string][]WorkloadSummary) {
|
||||
|
||||
indent := INDENT
|
||||
|
||||
for ns, rsc := range workloads {
|
||||
preIndent := indent
|
||||
if ns != "" {
|
||||
cautils.SimpleDisplay(printer.writer, "%sNamespace %s\n", indent, ns)
|
||||
}
|
||||
preIndent2 := indent
|
||||
for r := range rsc {
|
||||
indent += indent
|
||||
cautils.SimpleDisplay(printer.writer, fmt.Sprintf("%s%s - %s\n", indent, rsc[r].Kind, rsc[r].Name))
|
||||
indent = preIndent2
|
||||
}
|
||||
indent = preIndent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func generateRow(control string, cs ControlSummary) []string {
|
||||
row := []string{control}
|
||||
row = append(row, cs.ToSlice()...)
|
||||
if cs.TotalResources != 0 {
|
||||
row = append(row, fmt.Sprintf("%d%s", percentage(cs.TotalResources, cs.TotalFailed), "%"))
|
||||
} else {
|
||||
row = append(row, EmptyPercentage)
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
func generateHeader() []string {
|
||||
return []string{"Control Name", "Failed Resources", "Excluded Resources", "All Resources", "% success"}
|
||||
}
|
||||
|
||||
func percentage(big, small int) int {
|
||||
if big == 0 {
|
||||
if small == 0 {
|
||||
return 100
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return int(float64(float64(big-small)/float64(big)) * 100)
|
||||
}
|
||||
func generateFooter(numControlers, sumFailed, sumWarning, sumTotal int) []string {
|
||||
// Control name | # failed resources | all resources | % success
|
||||
row := []string{}
|
||||
row = append(row, "Resource Summary") //fmt.Sprintf(""%d", numControlers"))
|
||||
row = append(row, fmt.Sprintf("%d", sumFailed))
|
||||
row = append(row, fmt.Sprintf("%d", sumWarning))
|
||||
row = append(row, fmt.Sprintf("%d", sumTotal))
|
||||
if sumTotal != 0 {
|
||||
row = append(row, fmt.Sprintf("%d%s", percentage(sumTotal, sumFailed), "%"))
|
||||
} else {
|
||||
row = append(row, EmptyPercentage)
|
||||
}
|
||||
return row
|
||||
}
|
||||
func (printer *PrettyPrinter) printSummaryTable() {
|
||||
summaryTable := tablewriter.NewWriter(printer.writer)
|
||||
summaryTable.SetAutoWrapText(false)
|
||||
summaryTable.SetHeader(generateHeader())
|
||||
summaryTable.SetHeaderLine(true)
|
||||
alignments := []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER}
|
||||
summaryTable.SetColumnAlignment(alignments)
|
||||
|
||||
for i := 0; i < len(printer.sortedControlNames); i++ {
|
||||
controlSummary := printer.summary[printer.sortedControlNames[i]]
|
||||
summaryTable.Append(generateRow(printer.sortedControlNames[i], controlSummary))
|
||||
}
|
||||
summaryTable.SetFooter(generateFooter(len(printer.summary), printer.frameworkSummary.TotalFailed, printer.frameworkSummary.TotalWarnign, printer.frameworkSummary.TotalResources))
|
||||
summaryTable.Render()
|
||||
}
|
||||
|
||||
func (printer *PrettyPrinter) getSortedControlsNames() []string {
|
||||
controlNames := make([]string, 0, len(printer.summary))
|
||||
for k := range printer.summary {
|
||||
controlNames = append(controlNames, k)
|
||||
}
|
||||
sort.Strings(controlNames)
|
||||
return controlNames
|
||||
}
|
||||
|
||||
func getWriter(outputFile string) *os.File {
|
||||
os.Remove(outputFile)
|
||||
if outputFile != "" {
|
||||
f, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Error opening file")
|
||||
return os.Stdout
|
||||
}
|
||||
return f
|
||||
}
|
||||
return os.Stdout
|
||||
|
||||
}
|
||||
@@ -1,17 +1,7 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
|
||||
"github.com/enescakir/emoji"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
var INDENT = " "
|
||||
@@ -19,253 +9,24 @@ var INDENT = " "
|
||||
const EmptyPercentage = "NaN"
|
||||
|
||||
const (
|
||||
PrettyPrinter string = "pretty-printer"
|
||||
JsonPrinter string = "json"
|
||||
PrettyFormat string = "pretty-printer"
|
||||
JsonFormat string = "json"
|
||||
JunitResultPrinter string = "junit"
|
||||
)
|
||||
|
||||
type Printer struct {
|
||||
writer *os.File
|
||||
summary Summary
|
||||
sortedControlNames []string
|
||||
printerType string
|
||||
frameworkSummary ControlSummary
|
||||
type IPrinter interface {
|
||||
ActionPrint(opaSessionObj *cautils.OPASessionObj)
|
||||
SetWriter(outputFile string)
|
||||
Score(score float32)
|
||||
}
|
||||
|
||||
func NewPrinter(printerType, outputFile string) *Printer {
|
||||
return &Printer{
|
||||
summary: NewSummary(),
|
||||
writer: getWriter(outputFile),
|
||||
printerType: printerType,
|
||||
func GetPrinter(printFormat string) IPrinter {
|
||||
switch printFormat {
|
||||
case JsonFormat:
|
||||
return NewJsonPrinter()
|
||||
case JunitResultPrinter:
|
||||
return NewJunitPrinter()
|
||||
default:
|
||||
return NewPrettyPrinter()
|
||||
}
|
||||
}
|
||||
|
||||
func calculatePostureScore(postureReport *reporthandling.PostureReport) float32 {
|
||||
totalResources := 0
|
||||
totalFailed := 0
|
||||
for _, frameworkReport := range postureReport.FrameworkReports {
|
||||
totalFailed += frameworkReport.GetNumberOfFailedResources()
|
||||
totalResources += frameworkReport.GetNumberOfResources()
|
||||
}
|
||||
if totalResources == 0 {
|
||||
return float32(0)
|
||||
}
|
||||
return (float32(totalResources) - float32(totalFailed)) / float32(totalResources)
|
||||
}
|
||||
|
||||
func (printer *Printer) ActionPrint(opaSessionObj *cautils.OPASessionObj) float32 {
|
||||
score := calculatePostureScore(opaSessionObj.PostureReport)
|
||||
|
||||
if printer.printerType == PrettyPrinter {
|
||||
printer.SummarySetup(opaSessionObj.PostureReport)
|
||||
printer.PrintResults()
|
||||
printer.PrintSummaryTable()
|
||||
} else if printer.printerType == JsonPrinter {
|
||||
postureReportStr, err := json.Marshal(opaSessionObj.PostureReport.FrameworkReports[0])
|
||||
if err != nil {
|
||||
fmt.Println("Failed to convert posture report object!")
|
||||
os.Exit(1)
|
||||
}
|
||||
printer.writer.Write(postureReportStr)
|
||||
fmt.Printf("\nFinal score: %d\n", int(score*100))
|
||||
} 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)
|
||||
}
|
||||
printer.writer.Write(postureReportStr)
|
||||
fmt.Printf("\nFinal score: %d\n", int(score*100))
|
||||
} else if !cautils.IsSilent() {
|
||||
fmt.Println("unknown output printer")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
func (printer *Printer) SummarySetup(postureReport *reporthandling.PostureReport) {
|
||||
for _, fr := range postureReport.FrameworkReports {
|
||||
printer.frameworkSummary = ControlSummary{
|
||||
TotalResources: fr.GetNumberOfResources(),
|
||||
TotalFailed: fr.GetNumberOfFailedResources(),
|
||||
TotalWarnign: fr.GetNumberOfWarningResources(),
|
||||
}
|
||||
for _, cr := range fr.ControlReports {
|
||||
if len(cr.RuleReports) == 0 {
|
||||
continue
|
||||
}
|
||||
workloadsSummary := listResultSummary(cr.RuleReports)
|
||||
|
||||
printer.summary[cr.Name] = ControlSummary{
|
||||
TotalResources: cr.GetNumberOfResources(),
|
||||
TotalFailed: cr.GetNumberOfFailedResources(),
|
||||
TotalWarnign: cr.GetNumberOfWarningResources(),
|
||||
FailedWorkloads: groupByNamespace(workloadsSummary, workloadSummaryFailed),
|
||||
ExcludedWorkloads: groupByNamespace(workloadsSummary, workloadSummaryExclude),
|
||||
Description: cr.Description,
|
||||
Remediation: cr.Remediation,
|
||||
ListInputKinds: cr.ListControlsInputKinds(),
|
||||
}
|
||||
}
|
||||
}
|
||||
printer.sortedControlNames = printer.getSortedControlsNames()
|
||||
}
|
||||
func (printer *Printer) PrintResults() {
|
||||
for i := 0; i < len(printer.sortedControlNames); i++ {
|
||||
controlSummary := printer.summary[printer.sortedControlNames[i]]
|
||||
printer.printTitle(printer.sortedControlNames[i], &controlSummary)
|
||||
printer.printResources(&controlSummary)
|
||||
if printer.summary[printer.sortedControlNames[i]].TotalResources > 0 {
|
||||
printer.printSummary(printer.sortedControlNames[i], &controlSummary)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (printer *Printer) printSummary(controlName string, controlSummary *ControlSummary) {
|
||||
cautils.SimpleDisplay(printer.writer, "Summary - ")
|
||||
cautils.SuccessDisplay(printer.writer, "Passed:%v ", controlSummary.TotalResources-controlSummary.TotalFailed-controlSummary.TotalWarnign)
|
||||
cautils.WarningDisplay(printer.writer, "Excluded:%v ", controlSummary.TotalWarnign)
|
||||
cautils.FailureDisplay(printer.writer, "Failed:%v ", controlSummary.TotalFailed)
|
||||
cautils.InfoDisplay(printer.writer, "Total:%v\n", controlSummary.TotalResources)
|
||||
if controlSummary.TotalFailed > 0 {
|
||||
cautils.DescriptionDisplay(printer.writer, "Remediation: %v\n", controlSummary.Remediation)
|
||||
}
|
||||
cautils.DescriptionDisplay(printer.writer, "\n")
|
||||
|
||||
}
|
||||
|
||||
func (printer *Printer) printTitle(controlName string, controlSummary *ControlSummary) {
|
||||
cautils.InfoDisplay(printer.writer, "[control: %s] ", controlName)
|
||||
if controlSummary.TotalResources == 0 {
|
||||
cautils.InfoDisplay(printer.writer, "resources not found %v\n", emoji.ConfusedFace)
|
||||
} else if controlSummary.TotalFailed != 0 {
|
||||
cautils.FailureDisplay(printer.writer, "failed %v\n", emoji.SadButRelievedFace)
|
||||
} else if controlSummary.TotalWarnign != 0 {
|
||||
cautils.WarningDisplay(printer.writer, "excluded %v\n", emoji.NeutralFace)
|
||||
} else {
|
||||
cautils.SuccessDisplay(printer.writer, "passed %v\n", emoji.ThumbsUp)
|
||||
}
|
||||
|
||||
cautils.DescriptionDisplay(printer.writer, "Description: %s\n", controlSummary.Description)
|
||||
|
||||
}
|
||||
func (printer *Printer) printResources(controlSummary *ControlSummary) {
|
||||
|
||||
if len(controlSummary.FailedWorkloads) > 0 {
|
||||
cautils.FailureDisplay(printer.writer, "Failed:\n")
|
||||
printer.printGroupedResources(controlSummary.FailedWorkloads)
|
||||
}
|
||||
if len(controlSummary.ExcludedWorkloads) > 0 {
|
||||
cautils.WarningDisplay(printer.writer, "Excluded:\n")
|
||||
printer.printGroupedResources(controlSummary.ExcludedWorkloads)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (printer *Printer) printGroupedResources(workloads map[string][]WorkloadSummary) {
|
||||
|
||||
indent := INDENT
|
||||
|
||||
for ns, rsc := range workloads {
|
||||
preIndent := indent
|
||||
if ns != "" {
|
||||
cautils.SimpleDisplay(printer.writer, "%sNamespace %s\n", indent, ns)
|
||||
}
|
||||
preIndent2 := indent
|
||||
for r := range rsc {
|
||||
indent += indent
|
||||
cautils.SimpleDisplay(printer.writer, fmt.Sprintf("%s%s - %s\n", indent, rsc[r].Kind, rsc[r].Name))
|
||||
indent = preIndent2
|
||||
}
|
||||
indent = preIndent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (printer *Printer) PrintUrl(url string) {
|
||||
cautils.InfoTextDisplay(printer.writer, url)
|
||||
}
|
||||
|
||||
func generateRow(control string, cs ControlSummary) []string {
|
||||
row := []string{control}
|
||||
row = append(row, cs.ToSlice()...)
|
||||
if cs.TotalResources != 0 {
|
||||
row = append(row, fmt.Sprintf("%d%s", percentage(cs.TotalResources, cs.TotalFailed), "%"))
|
||||
} else {
|
||||
row = append(row, EmptyPercentage)
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
func generateHeader() []string {
|
||||
return []string{"Control Name", "Failed Resources", "Excluded Resources", "All Resources", "% success"}
|
||||
}
|
||||
|
||||
func percentage(big, small int) int {
|
||||
if big == 0 {
|
||||
if small == 0 {
|
||||
return 100
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return int(float64(float64(big-small)/float64(big)) * 100)
|
||||
}
|
||||
func generateFooter(numControlers, sumFailed, sumWarning, sumTotal int) []string {
|
||||
// Control name | # failed resources | all resources | % success
|
||||
row := []string{}
|
||||
row = append(row, "Resource Summary") //fmt.Sprintf(""%d", numControlers"))
|
||||
row = append(row, fmt.Sprintf("%d", sumFailed))
|
||||
row = append(row, fmt.Sprintf("%d", sumWarning))
|
||||
row = append(row, fmt.Sprintf("%d", sumTotal))
|
||||
if sumTotal != 0 {
|
||||
row = append(row, fmt.Sprintf("%d%s", percentage(sumTotal, sumFailed), "%"))
|
||||
} else {
|
||||
row = append(row, EmptyPercentage)
|
||||
}
|
||||
return row
|
||||
}
|
||||
func (printer *Printer) PrintSummaryTable() {
|
||||
summaryTable := tablewriter.NewWriter(printer.writer)
|
||||
summaryTable.SetAutoWrapText(false)
|
||||
summaryTable.SetHeader(generateHeader())
|
||||
summaryTable.SetHeaderLine(true)
|
||||
alignments := []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER}
|
||||
summaryTable.SetColumnAlignment(alignments)
|
||||
|
||||
for i := 0; i < len(printer.sortedControlNames); i++ {
|
||||
controlSummary := printer.summary[printer.sortedControlNames[i]]
|
||||
summaryTable.Append(generateRow(printer.sortedControlNames[i], controlSummary))
|
||||
}
|
||||
summaryTable.SetFooter(generateFooter(len(printer.summary), printer.frameworkSummary.TotalFailed, printer.frameworkSummary.TotalWarnign, printer.frameworkSummary.TotalResources))
|
||||
summaryTable.Render()
|
||||
}
|
||||
|
||||
func (printer *Printer) getSortedControlsNames() []string {
|
||||
controlNames := make([]string, 0, len(printer.summary))
|
||||
for k := range printer.summary {
|
||||
controlNames = append(controlNames, k)
|
||||
}
|
||||
sort.Strings(controlNames)
|
||||
return controlNames
|
||||
}
|
||||
|
||||
func getWriter(outputFile string) *os.File {
|
||||
os.Remove(outputFile)
|
||||
if outputFile != "" {
|
||||
f, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Error opening file")
|
||||
return os.Stdout
|
||||
}
|
||||
return f
|
||||
}
|
||||
return os.Stdout
|
||||
|
||||
}
|
||||
|
||||
11
resultshandling/printer/silentprinter.go
Normal file
11
resultshandling/printer/silentprinter.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
)
|
||||
|
||||
type SilentPrinter struct {
|
||||
}
|
||||
|
||||
func (silentPrinter *SilentPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
||||
}
|
||||
17
resultshandling/reporter/mockreporter.go
Normal file
17
resultshandling/reporter/mockreporter.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package reporter
|
||||
|
||||
import "github.com/armosec/kubescape/cautils"
|
||||
|
||||
type ReportMock struct {
|
||||
}
|
||||
|
||||
func NewReportMock() *ReportMock {
|
||||
return &ReportMock{}
|
||||
}
|
||||
func (reportMock *ReportMock) ActionSendReport(opaSessionObj *cautils.OPASessionObj) {}
|
||||
|
||||
func (reportMock *ReportMock) SetCustomerGUID(customerGUID string) {
|
||||
}
|
||||
|
||||
func (reportMock *ReportMock) SetClusterName(clusterName string) {
|
||||
}
|
||||
@@ -5,47 +5,55 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
)
|
||||
|
||||
type IReport interface {
|
||||
ActionSendReport(opaSessionObj *cautils.OPASessionObj)
|
||||
SetCustomerGUID(customerGUID string)
|
||||
SetClusterName(clusterName string)
|
||||
}
|
||||
|
||||
type ReportEventReceiver struct {
|
||||
httpClient http.Client
|
||||
host url.URL
|
||||
httpClient http.Client
|
||||
clusterName string
|
||||
customerGUID string
|
||||
}
|
||||
|
||||
func NewReportEventReceiver() *ReportEventReceiver {
|
||||
hostURL := initEventReceiverURL()
|
||||
return &ReportEventReceiver{
|
||||
httpClient: http.Client{},
|
||||
host: *hostURL,
|
||||
}
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) ActionSendReportListenner(opaSessionObj *cautils.OPASessionObj) {
|
||||
if cautils.CustomerGUID == "" {
|
||||
return
|
||||
}
|
||||
//Add score
|
||||
|
||||
func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASessionObj) {
|
||||
// Remove data before reporting
|
||||
keepFields := []string{"kind", "apiVersion", "metadata"}
|
||||
keepMetadataFields := []string{"name", "namespace", "labels"}
|
||||
opaSessionObj.PostureReport.RemoveData(keepFields, keepMetadataFields)
|
||||
|
||||
if err := report.Send(opaSessionObj.PostureReport); err != nil {
|
||||
if err := report.send(opaSessionObj.PostureReport); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
func (report *ReportEventReceiver) Send(postureReport *reporthandling.PostureReport) error {
|
||||
|
||||
func (report *ReportEventReceiver) SetCustomerGUID(customerGUID string) {
|
||||
report.customerGUID = customerGUID
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) SetClusterName(clusterName string) {
|
||||
report.clusterName = clusterName
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) send(postureReport *reporthandling.PostureReport) error {
|
||||
|
||||
reqBody, err := json.Marshal(*postureReport)
|
||||
if err != nil {
|
||||
return fmt.Errorf("in 'Send' failed to json.Marshal, reason: %v", err)
|
||||
}
|
||||
host := hostToString(&report.host, postureReport.ReportID)
|
||||
host := hostToString(report.initEventReceiverURL(), postureReport.ReportID)
|
||||
|
||||
req, err := http.NewRequest("POST", host, bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
@@ -33,15 +32,15 @@ func httpRespToString(resp *http.Response) (string, error) {
|
||||
return strBuilder.String(), err
|
||||
}
|
||||
|
||||
func initEventReceiverURL() *url.URL {
|
||||
func (report *ReportEventReceiver) initEventReceiverURL() *url.URL {
|
||||
urlObj := url.URL{}
|
||||
|
||||
urlObj.Scheme = "https"
|
||||
urlObj.Host = getter.GetArmoAPIConnector().GetReportReceiverURL()
|
||||
urlObj.Path = "/k8s/postureReport"
|
||||
q := urlObj.Query()
|
||||
q.Add("customerGUID", uuid.FromStringOrNil(cautils.CustomerGUID).String())
|
||||
q.Add("clusterName", cautils.ClusterName)
|
||||
q.Add("customerGUID", uuid.FromStringOrNil(report.customerGUID).String())
|
||||
q.Add("clusterName", report.clusterName)
|
||||
|
||||
urlObj.RawQuery = q.Encode()
|
||||
|
||||
|
||||
@@ -4,15 +4,16 @@ import (
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/resultshandling/printer"
|
||||
"github.com/armosec/kubescape/resultshandling/reporter"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
)
|
||||
|
||||
type ResultsHandler struct {
|
||||
opaSessionObj *chan *cautils.OPASessionObj
|
||||
reporterObj *reporter.ReportEventReceiver
|
||||
printerObj *printer.Printer
|
||||
reporterObj reporter.IReport
|
||||
printerObj printer.IPrinter
|
||||
}
|
||||
|
||||
func NewResultsHandler(opaSessionObj *chan *cautils.OPASessionObj, reporterObj *reporter.ReportEventReceiver, printerObj *printer.Printer) *ResultsHandler {
|
||||
func NewResultsHandler(opaSessionObj *chan *cautils.OPASessionObj, reporterObj reporter.IReport, printerObj printer.IPrinter) *ResultsHandler {
|
||||
return &ResultsHandler{
|
||||
opaSessionObj: opaSessionObj,
|
||||
reporterObj: reporterObj,
|
||||
@@ -20,16 +21,36 @@ func NewResultsHandler(opaSessionObj *chan *cautils.OPASessionObj, reporterObj *
|
||||
}
|
||||
}
|
||||
|
||||
func (resultsHandler *ResultsHandler) HandleResults(scanInfo cautils.ScanInfo) float32 {
|
||||
func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo) float32 {
|
||||
|
||||
opaSessionObj := <-*resultsHandler.opaSessionObj
|
||||
|
||||
score := resultsHandler.printerObj.ActionPrint(opaSessionObj)
|
||||
resultsHandler.printerObj.ActionPrint(opaSessionObj)
|
||||
|
||||
// Don't send report for control scan
|
||||
if scanInfo.FrameworkScan { // TODO - use interface for ActionSendReportListenner
|
||||
resultsHandler.reporterObj.ActionSendReportListenner(opaSessionObj)
|
||||
}
|
||||
resultsHandler.reporterObj.ActionSendReport(opaSessionObj)
|
||||
|
||||
// TODO - get score from table
|
||||
score := CalculatePostureScore(opaSessionObj.PostureReport)
|
||||
resultsHandler.printerObj.Score(score)
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
// CalculatePostureScore calculate final score
|
||||
func CalculatePostureScore(postureReport *reporthandling.PostureReport) float32 {
|
||||
lowestScore := float32(100)
|
||||
for _, frameworkReport := range postureReport.FrameworkReports {
|
||||
totalFailed := frameworkReport.GetNumberOfFailedResources()
|
||||
totalResources := frameworkReport.GetNumberOfResources()
|
||||
|
||||
frameworkScore := float32(0)
|
||||
if float32(totalResources) > 0 {
|
||||
frameworkScore = (float32(totalResources) - float32(totalFailed)) / float32(totalResources)
|
||||
}
|
||||
if lowestScore > frameworkScore {
|
||||
lowestScore = frameworkScore
|
||||
}
|
||||
}
|
||||
|
||||
return lowestScore
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user