From e1eec47a22e0dc24780f017d4ce4eaafab9ec1da Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 4 Jan 2022 18:28:48 +0200 Subject: [PATCH] fixed report --- opaprocessor/processorhandler.go | 25 +++++--- opaprocessor/processorhandler_test.go | 36 +++++------ opaprocessor/utils.go | 2 + resultshandling/results.go | 88 +++++++++++++-------------- resultshandling/results_test.go | 7 +++ 5 files changed, 83 insertions(+), 75 deletions(-) diff --git a/opaprocessor/processorhandler.go b/opaprocessor/processorhandler.go index 7bb9e010..5f5c2810 100644 --- a/opaprocessor/processorhandler.go +++ b/opaprocessor/processorhandler.go @@ -8,6 +8,7 @@ import ( "github.com/armosec/kubescape/cautils" "github.com/armosec/opa-utils/objectsenvelopes" "github.com/armosec/opa-utils/reporthandling" + "github.com/armosec/opa-utils/reporthandling/apis" "github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults" "github.com/open-policy-agent/opa/storage" @@ -176,7 +177,11 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (map[stri } inputResources = objectsenvelopes.ListMapToMeta(enumeratedData) for i := range inputResources { - resources[inputResources[i].GetID()] = nil + resources[inputResources[i].GetID()] = &resourcesresults.ResourceAssociatedRule{ + Name: rule.Name, + ControlConfigurations: postureControlInputs, + Status: apis.StatusPassed, + } opap.AllResources[inputResources[i].GetID()] = inputResources[i] } @@ -185,19 +190,19 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (map[stri // TODO - Handle error glog.Error(err) } else { - // ruleResponse to ruleResult for i := range ruleResponses { - ruleResult := resourcesresults.ResourceAssociatedRule{} - ruleResult.SetName(rule.Name) - ruleResult.ControlConfigurations = postureControlInputs - - for j := range ruleResponses[i].FailedPaths { - ruleResult.Paths = append(ruleResult.Paths, resourcesresults.Path{FailedPath: ruleResponses[i].FailedPaths[j]}) - } failedResources := objectsenvelopes.ListMapToMeta(ruleResponses[i].GetFailedResources()) for j := range failedResources { - resources[failedResources[j].GetID()] = &ruleResult + ruleResult := &resourcesresults.ResourceAssociatedRule{} + if r, k := resources[failedResources[j].GetID()]; k { + ruleResult = r + } + ruleResult.Status = apis.StatusFailed + for j := range ruleResponses[i].FailedPaths { + ruleResult.Paths = append(ruleResult.Paths, resourcesresults.Path{FailedPath: ruleResponses[i].FailedPaths[j]}) + } + resources[failedResources[j].GetID()] = ruleResult } } } diff --git a/opaprocessor/processorhandler_test.go b/opaprocessor/processorhandler_test.go index 619ffcfc..50882127 100644 --- a/opaprocessor/processorhandler_test.go +++ b/opaprocessor/processorhandler_test.go @@ -81,29 +81,29 @@ func TestProcessResourcesResult(t *testing.T) { assert.Equal(t, 1, len(opaSessionObj.ResourcesResult)) res := opaSessionObj.ResourcesResult[deployment.GetID()] - assert.Equal(t, 2, len(res.ListControls(nil).All())) - assert.Equal(t, 1, len(res.ListControls(nil).Failed())) - assert.Equal(t, 1, len(res.ListControls(nil).Passed())) + assert.Equal(t, 2, len(res.ListControlsIDs(nil).All())) + assert.Equal(t, 1, len(res.ListControlsIDs(nil).Failed())) + assert.Equal(t, 1, len(res.ListControlsIDs(nil).Passed())) assert.True(t, res.GetStatus(nil).IsFailed()) assert.False(t, res.GetStatus(nil).IsPassed()) assert.Equal(t, deployment.GetID(), opaSessionObj.ResourcesResult[deployment.GetID()].ResourceID) opap.updateResults() res = opaSessionObj.ResourcesResult[deployment.GetID()] - assert.Equal(t, 2, len(res.ListControls(nil).All())) - assert.Equal(t, 2, len(res.ListControls(nil).All())) - assert.Equal(t, 1, len(res.ListControls(nil).Failed())) - assert.Equal(t, 1, len(res.ListControls(nil).Passed())) + assert.Equal(t, 2, len(res.ListControlsIDs(nil).All())) + assert.Equal(t, 2, len(res.ListControlsIDs(nil).All())) + assert.Equal(t, 1, len(res.ListControlsIDs(nil).Failed())) + assert.Equal(t, 1, len(res.ListControlsIDs(nil).Passed())) assert.True(t, res.GetStatus(nil).IsFailed()) assert.False(t, res.GetStatus(nil).IsPassed()) assert.Equal(t, deployment.GetID(), opaSessionObj.ResourcesResult[deployment.GetID()].ResourceID) // test resource counters summaryDetails := opaSessionObj.Report.SummaryDetails - assert.Equal(t, 1, summaryDetails.NumberOf().All()) - assert.Equal(t, 1, summaryDetails.NumberOf().Failed()) - assert.Equal(t, 0, summaryDetails.NumberOf().Excluded()) - assert.Equal(t, 0, summaryDetails.NumberOf().Passed()) + assert.Equal(t, 1, summaryDetails.NumberOfResources().All()) + assert.Equal(t, 1, summaryDetails.NumberOfResources().Failed()) + assert.Equal(t, 0, summaryDetails.NumberOfResources().Excluded()) + assert.Equal(t, 0, summaryDetails.NumberOfResources().Passed()) // test resource listing assert.Equal(t, 1, len(summaryDetails.ListResourcesIDs().All())) @@ -112,19 +112,19 @@ func TestProcessResourcesResult(t *testing.T) { assert.Equal(t, 0, len(summaryDetails.ListResourcesIDs().Passed())) // test control listing - assert.Equal(t, len(res.ListControls(nil).All()), len(summaryDetails.ListControls().All())) - assert.Equal(t, len(res.ListControls(nil).Passed()), len(summaryDetails.ListControls().Passed())) - assert.Equal(t, len(res.ListControls(nil).Failed()), len(summaryDetails.ListControls().Failed())) - assert.Equal(t, len(res.ListControls(nil).Excluded()), len(summaryDetails.ListControls().Excluded())) + assert.Equal(t, len(res.ListControlsIDs(nil).All()), len(summaryDetails.ListControls().All())) + assert.Equal(t, len(res.ListControlsIDs(nil).Passed()), len(summaryDetails.ListControls().Passed())) + assert.Equal(t, len(res.ListControlsIDs(nil).Failed()), len(summaryDetails.ListControls().Failed())) + assert.Equal(t, len(res.ListControlsIDs(nil).Excluded()), len(summaryDetails.ListControls().Excluded())) assert.True(t, summaryDetails.GetStatus().IsFailed()) opaSessionObj.Exceptions = []armotypes.PostureExceptionPolicy{*mocks.MockExceptionAllKinds(&armotypes.PosturePolicy{FrameworkName: frameworks[0].Name})} opap.updateResults() res = opaSessionObj.ResourcesResult[deployment.GetID()] - assert.Equal(t, 2, len(res.ListControls(nil).All())) - assert.Equal(t, 1, len(res.ListControls(nil).Excluded())) - assert.Equal(t, 1, len(res.ListControls(nil).Passed())) + assert.Equal(t, 2, len(res.ListControlsIDs(nil).All())) + assert.Equal(t, 1, len(res.ListControlsIDs(nil).Excluded())) + assert.Equal(t, 1, len(res.ListControlsIDs(nil).Passed())) assert.True(t, res.GetStatus(nil).IsExcluded()) assert.False(t, res.GetStatus(nil).IsPassed()) assert.False(t, res.GetStatus(nil).IsFailed()) diff --git a/opaprocessor/utils.go b/opaprocessor/utils.go index e8c0d992..b3cf3e57 100644 --- a/opaprocessor/utils.go +++ b/opaprocessor/utils.go @@ -24,6 +24,8 @@ func ConvertFrameworksToSummaryDetails(summaryDetails *reportsummary.SummaryDeta id := frameworks[i].Controls[j].ControlID c := reportsummary.ControlSummary{ Name: frameworks[i].Controls[j].Name, + ControlID: id, + ScoreFactor: frameworks[i].Controls[j].BaseScore, Description: frameworks[i].Controls[j].Description, Remediation: frameworks[i].Controls[j].Remediation, } diff --git a/resultshandling/results.go b/resultshandling/results.go index b04c9543..da22171f 100644 --- a/resultshandling/results.go +++ b/resultshandling/results.go @@ -30,7 +30,7 @@ func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo) opaSessionObj := <-*resultsHandler.opaSessionObj - resultsHandler.reportV2ToV1(opaSessionObj) + reportV2ToV1(opaSessionObj) resultsHandler.printerObj.ActionPrint(opaSessionObj) @@ -61,7 +61,7 @@ func CalculatePostureScore(postureReport *reporthandling.PostureReport) float32 return (float32(len(allResources)) - float32(len(failedResources))) / float32(len(allResources)) } -func (resultsHandler *ResultsHandler) reportV2ToV1(opaSessionObj *cautils.OPASessionObj) { +func reportV2ToV1(opaSessionObj *cautils.OPASessionObj) { opaSessionObj.PostureReport.ReportID = opaSessionObj.Report.ReportID opaSessionObj.PostureReport.CustomerGUID = opaSessionObj.Report.CustomerGUID @@ -75,9 +75,6 @@ func (resultsHandler *ResultsHandler) reportV2ToV1(opaSessionObj *cautils.OPASes fwv1 := reporthandling.FrameworkReport{} fwv1.Name = fwv2.GetName() fwv1.Score = fwv2.GetScore() - fwv1.WarningResources = fwv2.NumberOf().Excluded() - fwv1.FailedResources = fwv2.NumberOf().Failed() - fwv1.TotalResources = fwv2.NumberOf().All() fwv1.ControlReports = append(fwv1.ControlReports, controlReportV2ToV1(opaSessionObj, fwv2.GetName(), fwv2.Controls)...) frameworks = append(frameworks, fwv1) @@ -87,9 +84,6 @@ func (resultsHandler *ResultsHandler) reportV2ToV1(opaSessionObj *cautils.OPASes fwv1 := reporthandling.FrameworkReport{} fwv1.Name = "" fwv1.Score = 0 - fwv1.WarningResources = opaSessionObj.Report.SummaryDetails.NumberOf().Excluded() - fwv1.FailedResources = opaSessionObj.Report.SummaryDetails.NumberOf().Failed() - fwv1.TotalResources = opaSessionObj.Report.SummaryDetails.NumberOf().All() fwv1.ControlReports = append(fwv1.ControlReports, controlReportV2ToV1(opaSessionObj, "", opaSessionObj.Report.SummaryDetails.Controls)...) frameworks = append(frameworks, fwv1) @@ -97,18 +91,15 @@ func (resultsHandler *ResultsHandler) reportV2ToV1(opaSessionObj *cautils.OPASes for f := range frameworks { // // set exceptions - // exceptions.SetFrameworkExceptions(&opap.PostureReport.FrameworkReports[f], opap.Exceptions, cautils.ClusterName) + // exceptions.SetFrameworkExceptions(frameworks, opap.Exceptions, cautils.ClusterName) - // // set counters - // reporthandling.SetUniqueResourcesCounter(&opap.PostureReport.FrameworkReports[f]) + // set counters + reporthandling.SetUniqueResourcesCounter(&frameworks[f]) // set default score reporthandling.SetDefaultScore(&frameworks[f]) } - // vv, _ := json.Marshal(frameworks) - // fmt.Printf("\n\n\n\n%s\n\n\n\n", vv) - // update score scoreutil := score.NewScore(opaSessionObj.AllResources) scoreutil.Calculate(frameworks) @@ -137,14 +128,13 @@ func (resultsHandler *ResultsHandler) reportV2ToV1(opaSessionObj *cautils.OPASes func controlReportV2ToV1(opaSessionObj *cautils.OPASessionObj, frameworkName string, controls map[string]reportsummary.ControlSummary) []reporthandling.ControlReport { controlRepors := []reporthandling.ControlReport{} - for _, crv2 := range controls { + for controlID, crv2 := range controls { crv1 := reporthandling.ControlReport{} - crv1.ControlID = crv2.GetID() + crv1.ControlID = controlID + crv1.BaseScore = crv2.ScoreFactor crv1.Name = crv2.GetName() + crv1.Score = crv2.GetScore() - crv1.WarningResources = crv2.NumberOf().Excluded() - crv1.FailedResources = crv2.NumberOf().Failed() - crv1.TotalResources = crv2.NumberOf().All() // TODO - add fields crv1.Description = crv2.Description @@ -153,43 +143,47 @@ func controlReportV2ToV1(opaSessionObj *cautils.OPASessionObj, frameworkName str rulesv1 := map[string]reporthandling.RuleReport{} // ruleName: rules for _, resourceID := range crv2.List().All() { - if resource, ok := opaSessionObj.ResourcesResult[resourceID]; ok { - for _, rulev2 := range resource.ListRules() { + if result, ok := opaSessionObj.ResourcesResult[resourceID]; ok { + for _, rulev2 := range result.ListRulesOfControl(crv2.GetID(), "") { // add to rule if _, ok := rulesv1[rulev2.GetName()]; !ok { - rulesv1[rulev2.GetName()] = reporthandling.RuleReport{} + rulesv1[rulev2.GetName()] = reporthandling.RuleReport{ + Name: rulev2.GetName(), + } } + } + } + } + + for _, resourceID := range crv2.List().All() { + if result, ok := opaSessionObj.ResourcesResult[resourceID]; ok { + for _, rulev2 := range result.ListRulesOfControl(crv2.GetID(), "") { + rulev1 := rulesv1[rulev2.GetName()] - rulev1.Name = rulev2.GetName() + status := rulev2.GetStatus(&v1.Filters{FrameworkNames: []string{frameworkName}}) + + if status.IsFailed() || status.IsExcluded() { + + // rule response + ruleResponse := reporthandling.RuleResponse{} + ruleResponse.Rulename = rulev2.GetName() + for i := range rulev2.Paths { + ruleResponse.FailedPaths = append(ruleResponse.FailedPaths, rulev2.Paths[i].FailedPath) + } + ruleResponse.RuleStatus = string(status.Status()) + if len(rulev2.Exception) > 0 { + ruleResponse.Exception = &rulev2.Exception[0] + } + + if fullRessource, ok := opaSessionObj.AllResources[resourceID]; ok { + ruleResponse.AlertObject.K8SApiObjects = append(ruleResponse.AlertObject.K8SApiObjects, fullRessource.GetObject()) + } + rulev1.RuleResponses = append(rulev1.RuleResponses, ruleResponse) - // rule response - ruleResponse := reporthandling.RuleResponse{} - ruleResponse.Rulename = rulev2.GetName() - for i := range rulev2.Paths { - ruleResponse.FailedPaths = append(ruleResponse.FailedPaths, rulev2.Paths[i].FailedPath) - } - ruleResponse.RuleStatus = string(rulev2.GetStatus(&v1.Filters{FrameworkNames: []string{frameworkName}}).Status()) - if len(rulev2.Exception) > 0 { - ruleResponse.Exception = &rulev2.Exception[0] } - if fullRessource, ok := opaSessionObj.AllResources[resourceID]; ok { - ruleResponse.AlertObject.K8SApiObjects = append(ruleResponse.AlertObject.K8SApiObjects, fullRessource.GetObject()) - } - - rulev1.ResourceUniqueCounter.TotalResources++ - if rulev2.GetStatus(&v1.Filters{FrameworkNames: []string{frameworkName}}).IsFailed() { - rulev1.ResourceUniqueCounter.FailedResources++ - } else if rulev2.GetStatus(&v1.Filters{FrameworkNames: []string{frameworkName}}).IsExcluded() { - rulev1.ResourceUniqueCounter.WarningResources++ - } else { - rulev1.ResourceUniqueCounter.TotalResources++ - } - - rulev1.RuleResponses = append(rulev1.RuleResponses, ruleResponse) rulev1.ListInputKinds = append(rulev1.ListInputKinds, resourceID) - rulesv1[rulev2.GetName()] = rulev1 } } diff --git a/resultshandling/results_test.go b/resultshandling/results_test.go index 29e89cfa..57a50927 100644 --- a/resultshandling/results_test.go +++ b/resultshandling/results_test.go @@ -2,3 +2,10 @@ package resultshandling var mockFramework_0044 = `{"guid":"","name":"fw-0044","attributes":{"armoBuiltin":true},"creationTime":"","description":"Implement NSA security advices for K8s ","controls":[{"guid":"","name":"Container hostPort","attributes":{"armoBuiltin":true},"id":"C-0044","controlID":"C-0044","creationTime":"","description":"Configuring hostPort limits you to a particular port, and if any two workloads that specify the same HostPort they cannot be deployed to the same node. Therefore, if the number of replica of such workload is higher than the number of nodes, the deployment will fail.","remediation":"Avoid usage of hostPort unless it is absolutely necessary. Use NodePort / ClusterIP instead.","rules":[{"guid":"","name":"container-hostPort","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# Fails if pod has container with hostPort\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n container := pod.spec.containers[i]\n\tbegginingOfPath := \"spec.\"\n\tpath := isHostPort(container, i, begginingOfPath)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v has Host-port\", [ container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": path,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n# Fails if workload has container with hostPort\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[i]\n\tbegginingOfPath := \"spec.template.spec.\"\n path := isHostPort(container, i, begginingOfPath)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v in %v: %v has Host-port\", [ container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": path,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# Fails if cronjob has container with hostPort\ndeny[msga] {\n \twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[i]\n\tbegginingOfPath := \"spec.jobTemplate.spec.template.spec.\"\n path := isHostPort(container, i, begginingOfPath)\n msga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v in %v: %v has Host-port\", [ container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": path,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n\nisHostPort(container, i, begginingOfPath) = path {\n\tpath = [sprintf(\"%vcontainers[%v].ports[%v].hostPort\", [begginingOfPath, format_int(i, 10), format_int(j, 10)]) | port = container.ports[j]; port.hostPort]\n\tcount(path) > 0\n}\n","resourceEnumerator":"","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"configInputs":null,"controlConfigInputs":null,"description":"fails if container has hostPort","remediation":"Make sure you do not configure hostPort for the container, if necessary use NodePort / ClusterIP","ruleQuery":"armo_builtins"}],"rulesIDs":[""],"baseScore":4}]}` var mockFramework_0006_0013 = `{"guid":"","name":"fw-0006-0013","attributes":{"armoBuiltin":true},"creationTime":"","description":"Implement NSA security advices for K8s ","controls":[{"guid":"","name":"Allowed hostPath","attributes":{"armoBuiltin":true},"id":"C-0006","controlID":"C-0006","creationTime":"","description":"Mounting host directory to the container can be abused to get access to sensitive data and gain persistence on the host machine.","remediation":"Refrain from using host path mount.","rules":[{"guid":"","name":"alert-rw-hostpath","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Persistance::Writable hostPath mount, Lateral Movement::Writable volume mounts on the host"},"creationTime":"","rule":"package armo_builtins\n\n# input: pod\n# apiversion: v1\n# does: returns hostPath volumes\n\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n volumes := pod.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := pod.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n podname := pod.metadata.name\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod: %v has: %v as hostPath volume\", [podname, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n#handles majority of workload resources\ndeny[msga] {\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n volumes := wl.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := wl.spec.template.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.template.spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t\n\t}\n}\n\n#handles CronJobs\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n volumes := wl.spec.jobTemplate.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.jobTemplate.spec.template.spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n\tmsga := {\n\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\"packagename\": \"armo_builtins\",\n\t\"alertScore\": 7,\n\t\"failedPaths\": [result],\n\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\nisRWMount(mount, begginingOfPath, i, k) = path {\n not mount.readOnly == true\n not mount.readOnly == false\n path = \"\"\n}\nisRWMount(mount, begginingOfPath, i, k) = path {\n mount.readOnly == false\n path = sprintf(\"%vcontainers[%v].volumeMounts[%v].readOnly\", [begginingOfPath, format_int(i, 10), format_int(k, 10)])\n} ","resourceEnumerator":"","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","CronJob","Pod"]}],"ruleDependencies":[{"packageName":"cautils"},{"packageName":"kubernetes.api.client"}],"configInputs":null,"controlConfigInputs":null,"description":"determines if any workload contains a hostPath volume with rw permissions","remediation":"Set the readOnly field of the mount to true","ruleQuery":""}],"rulesIDs":[""],"baseScore":6},{"guid":"","name":"Non-root containers","attributes":{"armoBuiltin":true},"id":"C-0013","controlID":"C-0013","creationTime":"","description":"Potential attackers may gain access to a container and leverage its existing privileges to conduct an attack. Therefore, it is not recommended to deploy containers with root privileges unless it is absolutely necessary. This contol identifies all the Pods running as root or can escalate to root.","remediation":"If your application does not need root privileges, make sure to define the runAsUser or runAsGroup under the PodSecurityContext and use user ID 1000 or higher. Do not turn on allowPrivlegeEscalation bit and make sure runAsNonRoot is true.","rules":[{"guid":"","name":"non-root-containers","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# Fails if pod has container configured to run as root\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n\tcontainer := pod.spec.containers[i]\n\tbegginingOfPath := \"spec.\"\n result := isRootContainer(container, i, begginingOfPath)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in pod: %v may run as root\", [container.name, pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n# Fails if pod has container configured to run as root\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n\tcontainer := pod.spec.containers[i]\n\tbegginingOfPath =\"spec.\"\n result := isRootPod(pod, container, i, begginingOfPath)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in pod: %v may run as root\", [container.name, pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n\n\n# Fails if workload has container configured to run as root\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[i]\n\tbegginingOfPath := \"spec.template.spec.\"\n result := isRootContainer(container, i, begginingOfPath)\n msga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v may run as root\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# Fails if workload has container configured to run as root\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[i]\n\tbegginingOfPath := \"spec.template.spec.\"\n result := isRootPod(wl.spec.template, container, i, begginingOfPath)\n msga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v may run as root\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n# Fails if cronjob has a container configured to run as root\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[i]\n\tbegginingOfPath := \"spec.jobTemplate.spec.template.spec.\"\n\tresult := isRootContainer(container, i, begginingOfPath)\n msga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v may run as root\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n\n# Fails if workload has container configured to run as root\ndeny[msga] {\n \twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[i]\n\tbegginingOfPath := \"spec.jobTemplate.spec.template.spec.\"\n result := isRootPod(wl.spec.jobTemplate.spec.template, container, i, begginingOfPath)\n msga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v may run as root\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\nisRootPod(pod, container, i, begginingOfPath) = path {\n\tpath = \"\"\n not container.securityContext.runAsUser\n pod.spec.securityContext.runAsUser == 0\n\tpath = \"spec.securityContext.runAsUser\"\n}\n\nisRootPod(pod, container, i, begginingOfPath) = path {\n\tpath = \"\"\n not container.securityContext.runAsUser\n\tnot container.securityContext.runAsGroup\n\tnot container.securityContext.runAsNonRoot\n not pod.spec.securityContext.runAsUser\n\tnot pod.spec.securityContext.runAsGroup\n pod.spec.securityContext.runAsNonRoot == false\n\tpath = \"spec.securityContext.runAsNonRoot\"\n}\n\nisRootPod(pod, container, i, begginingOfPath) = path {\n\tpath = \"\"\n not container.securityContext.runAsGroup\n pod.spec.securityContext.runAsGroup == 0\n\tpath = sprintf(\"%vsecurityContext.runAsGroup\", [begginingOfPath])\n}\n\nisRootPod(pod, container, i, begginingOfPath)= path {\n\tpath = \"\"\n\tnot pod.spec.securityContext.runAsGroup\n\tnot pod.spec.securityContext.runAsUser\n \tcontainer.securityContext.runAsNonRoot == false\n\tpath = sprintf(\"%vcontainers[%v].securityContext.runAsNonRoot\", [begginingOfPath, format_int(i, 10)])\n}\n\nisRootContainer(container, i, begginingOfPath) = path {\n\tpath = \"\"\n container.securityContext.runAsUser == 0\n\tpath = sprintf(\"%vcontainers[%v].securityContext.runAsUser\", [begginingOfPath, format_int(i, 10)])\n}\n\nisRootContainer(container, i, begginingOfPath) = path {\n\tpath = \"\"\n container.securityContext.runAsGroup == 0\n\t path = sprintf(\"%vcontainers[%v].securityContext.runAsGroup\", [begginingOfPath, format_int(i, 10)])\n}","resourceEnumerator":"","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"configInputs":null,"controlConfigInputs":null,"description":"fails if container can run as root","remediation":"Make sure that the user/group in the securityContext of pod/container is set to an id less than 1000, or the runAsNonRoot flag is set to true. Also make sure that the allowPrivilegeEscalation field is set to false","ruleQuery":"armo_builtins"}],"rulesIDs":[""],"baseScore":6}]}` + +// func TestReportV2ToV1(t *testing.T) { +// opaSessionObj := cautils.OPASessionObj{} +// opaSessionObj.AllResources = map[string]workloadinterface.IMetadata{} +// opaSessionObj.PostureReport +// // opaSessionObj.Exceptions +// }