diff --git a/objectsenvelopes/objectshandler_test.go b/objectsenvelopes/objectshandler_test.go new file mode 100644 index 00000000..1198e3e5 --- /dev/null +++ b/objectsenvelopes/objectshandler_test.go @@ -0,0 +1,98 @@ +package objectsenvelopes + +import ( + "testing" + + cloudsupportv1 "github.com/kubescape/k8s-interface/cloudsupport/v1" + "github.com/kubescape/k8s-interface/workloadinterface" + "github.com/kubescape/opa-utils/objectsenvelopes/hostsensor" + "github.com/kubescape/opa-utils/objectsenvelopes/localworkload" + "github.com/stretchr/testify/assert" +) + +func TestNewObject(t *testing.T) { + // Test nil input + assert.Nil(t, NewObject(nil)) + + // Test valid input + object := map[string]interface{}{ + "kind": "Pod", + "apiVersion": "v1", + "metadata": map[string]interface{}{ + "name": "test-pod", + }, + } + workloadObj := NewObject(object) + assert.NotNil(t, workloadObj) + assert.Equal(t, "Pod", workloadObj.GetKind()) + + // Test unsupported input + unsupportedObject := map[string]interface{}{ + "unknown": "unknown", + } + assert.Nil(t, NewObject(unsupportedObject)) +} + +func TestGetObjectType(t *testing.T) { + // Test unsupported input + unsupportedObject := map[string]interface{}{ + "unknown": "unknown", + } + assert.Equal(t, workloadinterface.TypeUnknown, GetObjectType(unsupportedObject)) + + // Test RegoResponseVectorObject + relatedObjects := []map[string]interface{}{} + relatedObject := getMock(role) + relatedObject2 := getMock(rolebinding) + relatedObjects = append(relatedObjects, relatedObject) + relatedObjects = append(relatedObjects, relatedObject2) + subject := map[string]interface{}{"name": "user@example.com", "kind": "User", "namespace": "default", "group": "rbac.authorization.k8s.io", RelatedObjectsKey: relatedObjects} + assert.Equal(t, TypeRegoResponseVectorObject, GetObjectType(subject)) + + // Test CloudProviderDescribe + cloudProviderDescribe := map[string]interface{}{ + "apiVersion": "container.googleapis.com/v1", + "kind": "ClusterDescribe", + } + assert.Equal(t, cloudsupportv1.TypeCloudProviderDescribe, GetObjectType(cloudProviderDescribe)) + + // Test HostSensor + hostSensor := map[string]interface{}{ + "apiVersion": "hostdata.kubescape.cloud/v1", + "metadata": map[string]interface{}{ + "name": "test-pod", + }, + } + assert.Equal(t, hostsensor.TypeHostSensor, GetObjectType(hostSensor)) + + // Test LocalWorkload + localWorkload := map[string]interface{}{ + "kind": "b", + "sourcePath": "/path/file", + } + assert.Equal(t, localworkload.TypeLocalWorkload, GetObjectType(localWorkload)) + + // Test WorkloadObject + workloadObject := map[string]interface{}{ + "kind": "Pod", + "apiVersion": "v1", + "metadata": map[string]interface{}{ + "name": "test-pod", + }, + } + assert.Equal(t, workloadinterface.TypeWorkloadObject, GetObjectType(workloadObject)) + + // Test ListWorkloads + listWorkloads := map[string]interface{}{ + "kind": "List", + "items": []interface{}{ + map[string]interface{}{ + "kind": "Pod", + "metadata": map[string]interface{}{ + "name": "test-pod", + }, + }, + }, + } + assert.Equal(t, workloadinterface.TypeListWorkloads, GetObjectType(listWorkloads)) +} diff --git a/score/score.go b/score/score.go index 26897f5e..39f7c0ba 100644 --- a/score/score.go +++ b/score/score.go @@ -8,8 +8,6 @@ import ( "strings" "sync" - "github.com/kubescape/go-logger" - "github.com/kubescape/go-logger/helpers" k8sinterface "github.com/kubescape/k8s-interface/k8sinterface" "github.com/kubescape/k8s-interface/workloadinterface" armoupautils "github.com/kubescape/opa-utils/objectsenvelopes" @@ -363,7 +361,6 @@ func (su *ScoreUtil) SetPostureReportComplianceScores(report *v2.PostureReport) for i := range report.SummaryDetails.Frameworks { // set compliance score for framework and all controls in framework report.SummaryDetails.Frameworks[i].ComplianceScore = su.GetFrameworkComplianceScore(&report.SummaryDetails.Frameworks[i]) - logger.L().Debug("set framework score", helpers.String("framework name", report.SummaryDetails.Frameworks[i].GetName()), helpers.Int("ComplianceScore", int(report.SummaryDetails.Frameworks[i].GetComplianceScore()))) } // set compliance score per control sumScore := su.ControlsSummariesComplianceScore(&report.SummaryDetails.Controls, "") @@ -384,7 +381,6 @@ func (su *ScoreUtil) ControlsSummariesComplianceScore(ctrls *reportsummary.Contr ctrl.Score = 0 ctrl.Score = su.GetControlComplianceScore(&ctrl, frameworkName) (*ctrls)[ctrlID] = ctrl - logger.L().Debug("set control score", helpers.String("controlID", ctrl.GetID()), helpers.Int("score", int(ctrl.GetScore()))) sumScore += ctrl.GetScore() } return sumScore @@ -392,7 +388,8 @@ func (su *ScoreUtil) ControlsSummariesComplianceScore(ctrls *reportsummary.Contr // GetFrameworkComplianceScore returns the compliance score for a given framework (as a percentage) // The framework compliance score is the average of all controls scores in that framework -func (su *ScoreUtil) GetFrameworkComplianceScore(framework *reportsummary.FrameworkSummary) (frameworkScore float32) { +func (su *ScoreUtil) GetFrameworkComplianceScore(framework *reportsummary.FrameworkSummary) float32 { + frameworkScore := float32(0) sumScore := su.ControlsSummariesComplianceScore(&framework.Controls, framework.GetName()) if len(framework.Controls) > 0 { frameworkScore = sumScore / float32(len(framework.Controls)) @@ -401,32 +398,18 @@ func (su *ScoreUtil) GetFrameworkComplianceScore(framework *reportsummary.Framew } // GetControlComplianceScore returns the compliance score for a given control (as a percentage). -func (su *ScoreUtil) GetControlComplianceScore(ctrl reportsummary.IControlSummary, _ /*frameworkName*/ string) (ctrlScore float32) { - resourcesIDs := ctrl.ListResourcesIDs() - passedResourceIDS := resourcesIDs.Passed() - allResourcesIDSIter := resourcesIDs.All() - - numOfPassedResources := float32(0) - numOfAllResources := float32(0) - - for i := range passedResourceIDS { - if _, ok := su.resources[passedResourceIDS[i]]; ok { - numOfPassedResources += 1 - } +func (su *ScoreUtil) GetControlComplianceScore(ctrl reportsummary.IControlSummary, _ /*frameworkName*/ string) float32 { + // If a control has status passed it should always be considered as having 100% compliance score + if ctrl.GetStatus().IsPassed() { + return 100 } - for allResourcesIDSIter.HasNext() { - resourceID := allResourcesIDSIter.Next() - if _, ok := su.resources[resourceID]; ok { - numOfAllResources += 1 - } - } + resourcesIDs := ctrl.ListResourcesIDs() + numOfPassedResources := len(resourcesIDs.Passed()) + numOfAllResources := resourcesIDs.All().Len() if numOfAllResources > 0 { - ctrlScore = (numOfPassedResources / numOfAllResources) * 100 - } else { - logger.L().Debug("no resources were given for this control, score is 0", helpers.String("controlID", ctrl.GetID())) + return (float32(numOfPassedResources) / float32(numOfAllResources)) * 100 } - - return ctrlScore + return 0 } diff --git a/score/score_test.go b/score/score_test.go index bf24f120..152e19bd 100644 --- a/score/score_test.go +++ b/score/score_test.go @@ -814,6 +814,46 @@ func TestGetControlComplianceScore(t *testing.T) { ) }) + t.Run("skipped control", func(t *testing.T) { + t.Parallel() + + resources := mockResources(t) + s := ScoreUtil{isDebugMode: true, resources: resources} + controlReport := reportsummary.ControlSummary{ + Name: "skipped-control", + ControlID: "skipped1", + StatusInfo: apis.StatusInfo{ + InnerInfo: "enable-host-scan flag not used. For more information: https://hub.armosec.io/docs/host-sensor", + InnerStatus: "skipped", + }, + ResourceIDs: helpers.AllLists{}, + } + + require.Equal(t, float32(0), s.GetControlComplianceScore(&controlReport, ""), + "skipped control report should return a score equals to 0", + ) + }) + + t.Run("passed (irrelevant) control", func(t *testing.T) { + t.Parallel() + + resources := mockResources(t) + s := ScoreUtil{isDebugMode: true, resources: resources} + controlReport := reportsummary.ControlSummary{ + Name: "irrelevant-control", + ControlID: "irrelevant1", + StatusInfo: apis.StatusInfo{ + SubStatus: "irrelevant", + InnerStatus: "passed", + }, + ResourceIDs: helpers.AllLists{}, + } + + require.Equal(t, float32(100), s.GetControlComplianceScore(&controlReport, ""), + "passed (irrelevant) control report should return a score equals to 100", + ) + }) + t.Run("with control report", func(t *testing.T) { t.Parallel()