Skip to content

Commit 365d5f1

Browse files
fix compliance score- case of passed-irrelevant (kubescape#104)
* fix compliance score- case of passed-irrelevant Signed-off-by: YiscahLevySilas1 <[email protected]> * minor refactor following review Signed-off-by: YiscahLevySilas1 <[email protected]> * delete log Signed-off-by: YiscahLevySilas1 <[email protected]> * move passed condition Signed-off-by: YiscahLevySilas1 <[email protected]> * shorten code, delete logs Signed-off-by: YiscahLevySilas1 <[email protected]> * add tests for coverage Signed-off-by: YiscahLevySilas1 <[email protected]> * clean code Signed-off-by: YiscahLevySilas1 <[email protected]> --------- Signed-off-by: YiscahLevySilas1 <[email protected]>
1 parent 7e12785 commit 365d5f1

File tree

3 files changed

+149
-28
lines changed

3 files changed

+149
-28
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package objectsenvelopes
2+
3+
import (
4+
"testing"
5+
6+
cloudsupportv1 "github.com/kubescape/k8s-interface/cloudsupport/v1"
7+
"github.com/kubescape/k8s-interface/workloadinterface"
8+
"github.com/kubescape/opa-utils/objectsenvelopes/hostsensor"
9+
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestNewObject(t *testing.T) {
14+
// Test nil input
15+
assert.Nil(t, NewObject(nil))
16+
17+
// Test valid input
18+
object := map[string]interface{}{
19+
"kind": "Pod",
20+
"apiVersion": "v1",
21+
"metadata": map[string]interface{}{
22+
"name": "test-pod",
23+
},
24+
}
25+
workloadObj := NewObject(object)
26+
assert.NotNil(t, workloadObj)
27+
assert.Equal(t, "Pod", workloadObj.GetKind())
28+
29+
// Test unsupported input
30+
unsupportedObject := map[string]interface{}{
31+
"unknown": "unknown",
32+
}
33+
assert.Nil(t, NewObject(unsupportedObject))
34+
}
35+
36+
func TestGetObjectType(t *testing.T) {
37+
// Test unsupported input
38+
unsupportedObject := map[string]interface{}{
39+
"unknown": "unknown",
40+
}
41+
assert.Equal(t, workloadinterface.TypeUnknown, GetObjectType(unsupportedObject))
42+
43+
// Test RegoResponseVectorObject
44+
relatedObjects := []map[string]interface{}{}
45+
relatedObject := getMock(role)
46+
relatedObject2 := getMock(rolebinding)
47+
relatedObjects = append(relatedObjects, relatedObject)
48+
relatedObjects = append(relatedObjects, relatedObject2)
49+
subject := map[string]interface{}{"name": "[email protected]", "kind": "User", "namespace": "default", "group": "rbac.authorization.k8s.io", RelatedObjectsKey: relatedObjects}
50+
assert.Equal(t, TypeRegoResponseVectorObject, GetObjectType(subject))
51+
52+
// Test CloudProviderDescribe
53+
cloudProviderDescribe := map[string]interface{}{
54+
"apiVersion": "container.googleapis.com/v1",
55+
"kind": "ClusterDescribe",
56+
}
57+
assert.Equal(t, cloudsupportv1.TypeCloudProviderDescribe, GetObjectType(cloudProviderDescribe))
58+
59+
// Test HostSensor
60+
hostSensor := map[string]interface{}{
61+
"apiVersion": "hostdata.kubescape.cloud/v1",
62+
"metadata": map[string]interface{}{
63+
"name": "test-pod",
64+
},
65+
}
66+
assert.Equal(t, hostsensor.TypeHostSensor, GetObjectType(hostSensor))
67+
68+
// Test LocalWorkload
69+
localWorkload := map[string]interface{}{
70+
"kind": "b",
71+
"sourcePath": "/path/file",
72+
}
73+
assert.Equal(t, localworkload.TypeLocalWorkload, GetObjectType(localWorkload))
74+
75+
// Test WorkloadObject
76+
workloadObject := map[string]interface{}{
77+
"kind": "Pod",
78+
"apiVersion": "v1",
79+
"metadata": map[string]interface{}{
80+
"name": "test-pod",
81+
},
82+
}
83+
assert.Equal(t, workloadinterface.TypeWorkloadObject, GetObjectType(workloadObject))
84+
85+
// Test ListWorkloads
86+
listWorkloads := map[string]interface{}{
87+
"kind": "List",
88+
"items": []interface{}{
89+
map[string]interface{}{
90+
"kind": "Pod",
91+
"metadata": map[string]interface{}{
92+
"name": "test-pod",
93+
},
94+
},
95+
},
96+
}
97+
assert.Equal(t, workloadinterface.TypeListWorkloads, GetObjectType(listWorkloads))
98+
}

score/score.go

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import (
88
"strings"
99
"sync"
1010

11-
"github.com/kubescape/go-logger"
12-
"github.com/kubescape/go-logger/helpers"
1311
k8sinterface "github.com/kubescape/k8s-interface/k8sinterface"
1412
"github.com/kubescape/k8s-interface/workloadinterface"
1513
armoupautils "github.com/kubescape/opa-utils/objectsenvelopes"
@@ -363,7 +361,6 @@ func (su *ScoreUtil) SetPostureReportComplianceScores(report *v2.PostureReport)
363361
for i := range report.SummaryDetails.Frameworks {
364362
// set compliance score for framework and all controls in framework
365363
report.SummaryDetails.Frameworks[i].ComplianceScore = su.GetFrameworkComplianceScore(&report.SummaryDetails.Frameworks[i])
366-
logger.L().Debug("set framework score", helpers.String("framework name", report.SummaryDetails.Frameworks[i].GetName()), helpers.Int("ComplianceScore", int(report.SummaryDetails.Frameworks[i].GetComplianceScore())))
367364
}
368365
// set compliance score per control
369366
sumScore := su.ControlsSummariesComplianceScore(&report.SummaryDetails.Controls, "")
@@ -384,15 +381,15 @@ func (su *ScoreUtil) ControlsSummariesComplianceScore(ctrls *reportsummary.Contr
384381
ctrl.Score = 0
385382
ctrl.Score = su.GetControlComplianceScore(&ctrl, frameworkName)
386383
(*ctrls)[ctrlID] = ctrl
387-
logger.L().Debug("set control score", helpers.String("controlID", ctrl.GetID()), helpers.Int("score", int(ctrl.GetScore())))
388384
sumScore += ctrl.GetScore()
389385
}
390386
return sumScore
391387
}
392388

393389
// GetFrameworkComplianceScore returns the compliance score for a given framework (as a percentage)
394390
// The framework compliance score is the average of all controls scores in that framework
395-
func (su *ScoreUtil) GetFrameworkComplianceScore(framework *reportsummary.FrameworkSummary) (frameworkScore float32) {
391+
func (su *ScoreUtil) GetFrameworkComplianceScore(framework *reportsummary.FrameworkSummary) float32 {
392+
frameworkScore := float32(0)
396393
sumScore := su.ControlsSummariesComplianceScore(&framework.Controls, framework.GetName())
397394
if len(framework.Controls) > 0 {
398395
frameworkScore = sumScore / float32(len(framework.Controls))
@@ -401,32 +398,18 @@ func (su *ScoreUtil) GetFrameworkComplianceScore(framework *reportsummary.Framew
401398
}
402399

403400
// GetControlComplianceScore returns the compliance score for a given control (as a percentage).
404-
func (su *ScoreUtil) GetControlComplianceScore(ctrl reportsummary.IControlSummary, _ /*frameworkName*/ string) (ctrlScore float32) {
405-
resourcesIDs := ctrl.ListResourcesIDs()
406-
passedResourceIDS := resourcesIDs.Passed()
407-
allResourcesIDSIter := resourcesIDs.All()
408-
409-
numOfPassedResources := float32(0)
410-
numOfAllResources := float32(0)
411-
412-
for i := range passedResourceIDS {
413-
if _, ok := su.resources[passedResourceIDS[i]]; ok {
414-
numOfPassedResources += 1
415-
}
401+
func (su *ScoreUtil) GetControlComplianceScore(ctrl reportsummary.IControlSummary, _ /*frameworkName*/ string) float32 {
402+
// If a control has status passed it should always be considered as having 100% compliance score
403+
if ctrl.GetStatus().IsPassed() {
404+
return 100
416405
}
417406

418-
for allResourcesIDSIter.HasNext() {
419-
resourceID := allResourcesIDSIter.Next()
420-
if _, ok := su.resources[resourceID]; ok {
421-
numOfAllResources += 1
422-
}
423-
}
407+
resourcesIDs := ctrl.ListResourcesIDs()
408+
numOfPassedResources := len(resourcesIDs.Passed())
409+
numOfAllResources := resourcesIDs.All().Len()
424410

425411
if numOfAllResources > 0 {
426-
ctrlScore = (numOfPassedResources / numOfAllResources) * 100
427-
} else {
428-
logger.L().Debug("no resources were given for this control, score is 0", helpers.String("controlID", ctrl.GetID()))
412+
return (float32(numOfPassedResources) / float32(numOfAllResources)) * 100
429413
}
430-
431-
return ctrlScore
414+
return 0
432415
}

score/score_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,46 @@ func TestGetControlComplianceScore(t *testing.T) {
814814
)
815815
})
816816

817+
t.Run("skipped control", func(t *testing.T) {
818+
t.Parallel()
819+
820+
resources := mockResources(t)
821+
s := ScoreUtil{isDebugMode: true, resources: resources}
822+
controlReport := reportsummary.ControlSummary{
823+
Name: "skipped-control",
824+
ControlID: "skipped1",
825+
StatusInfo: apis.StatusInfo{
826+
InnerInfo: "enable-host-scan flag not used. For more information: https://hub.armosec.io/docs/host-sensor",
827+
InnerStatus: "skipped",
828+
},
829+
ResourceIDs: helpers.AllLists{},
830+
}
831+
832+
require.Equal(t, float32(0), s.GetControlComplianceScore(&controlReport, ""),
833+
"skipped control report should return a score equals to 0",
834+
)
835+
})
836+
837+
t.Run("passed (irrelevant) control", func(t *testing.T) {
838+
t.Parallel()
839+
840+
resources := mockResources(t)
841+
s := ScoreUtil{isDebugMode: true, resources: resources}
842+
controlReport := reportsummary.ControlSummary{
843+
Name: "irrelevant-control",
844+
ControlID: "irrelevant1",
845+
StatusInfo: apis.StatusInfo{
846+
SubStatus: "irrelevant",
847+
InnerStatus: "passed",
848+
},
849+
ResourceIDs: helpers.AllLists{},
850+
}
851+
852+
require.Equal(t, float32(100), s.GetControlComplianceScore(&controlReport, ""),
853+
"passed (irrelevant) control report should return a score equals to 100",
854+
)
855+
})
856+
817857
t.Run("with control report", func(t *testing.T) {
818858
t.Parallel()
819859

0 commit comments

Comments
 (0)