Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions reporthandling/apis/statuses.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
SubStatusIntegration ScanningSubStatus = "integration"
SubStatusRequiresReview ScanningSubStatus = "requires review"
SubStatusManualReview ScanningSubStatus = "manual review"
SubStatusNotEvaluated ScanningSubStatus = "notEvaluated"
SubStatusUnknown ScanningSubStatus = "" // keep this empty
StatusUnknown ScanningStatus = "" // keep this empty

Expand All @@ -25,6 +26,7 @@ const (
SubStatusConfigurationInfo StatusMsg = "Control configurations are empty (docs: https://kubescape.io/docs/frameworks-and-controls/configuring-controls)"
SubStatusRequiresReviewInfo StatusMsg = "Control type is requires-review"
SubStatusManualReviewInfo StatusMsg = "Control type is manual-review"
SubStatusNotEvaluatedInfo StatusMsg = "Control was not evaluated because required resource types could not be collected"
)

// IStatus interface handling status
Expand Down Expand Up @@ -70,8 +72,8 @@ func Compare(a, b ScanningStatus) ScanningStatus {
1. status=failed or status=unknown:
sub status = ""
2. status=skipped:
if aSub or bSub are configuration/integration/review:
sub status = aSub or bSub
if aSub or bSub are notEvaluated/configuration/integration/review:
sub status = aSub or bSub (notEvaluated takes precedence)
else:
sub status = status=unknown
3. status=passed:
Expand All @@ -94,6 +96,9 @@ func CompareStatusAndSubStatus(a, b ScanningStatus, aSub, bSub ScanningSubStatus
}

case StatusSkipped:
if aSub == SubStatusNotEvaluated || bSub == SubStatusNotEvaluated {
return status, SubStatusNotEvaluated
}
if aSub == SubStatusConfiguration || bSub == SubStatusConfiguration {
return status, SubStatusConfiguration
}
Expand Down
7 changes: 7 additions & 0 deletions reporthandling/apis/statuses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ func TestCompareStatusAndSubStatus(t *testing.T) {
assert.Equal(t, makeIS(StatusSkipped, SubStatusIntegration), makeIS(CompareStatusAndSubStatus(StatusSkipped, StatusPassed, SubStatusIntegration, SubStatusUnknown)))
assert.Equal(t, makeIS(StatusSkipped, SubStatusManualReview), makeIS(CompareStatusAndSubStatus(StatusPassed, StatusSkipped, SubStatusUnknown, SubStatusManualReview)))
assert.Equal(t, makeIS(StatusSkipped, SubStatusRequiresReview), makeIS(CompareStatusAndSubStatus(StatusPassed, StatusSkipped, SubStatusUnknown, SubStatusRequiresReview)))

// notEvaluated should win over other skipped substatuses since it signals a real cluster/RBAC gap
assert.Equal(t, makeIS(StatusSkipped, SubStatusNotEvaluated), makeIS(CompareStatusAndSubStatus(StatusSkipped, StatusPassed, SubStatusNotEvaluated, SubStatusUnknown)))
assert.Equal(t, makeIS(StatusSkipped, SubStatusNotEvaluated), makeIS(CompareStatusAndSubStatus(StatusSkipped, StatusSkipped, SubStatusNotEvaluated, SubStatusConfiguration)))
assert.Equal(t, makeIS(StatusSkipped, SubStatusNotEvaluated), makeIS(CompareStatusAndSubStatus(StatusSkipped, StatusSkipped, SubStatusManualReview, SubStatusNotEvaluated)))
// failed still beats notEvaluated
assert.Equal(t, makeIS(StatusFailed, SubStatusUnknown), makeIS(CompareStatusAndSubStatus(StatusFailed, StatusSkipped, SubStatusUnknown, SubStatusNotEvaluated)))
}

func TestConvertStatusToNewStatus(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ func (controlSummary *ControlSummary) calculateNSetSubStatus(subStatus apis.Scan
controlSummary.StatusInfo.InnerInfo = ""
}
case apis.StatusSkipped:
if subStatus == apis.SubStatusConfiguration || controlSummary.StatusInfo.SubStatus == apis.SubStatusConfiguration {
if subStatus == apis.SubStatusNotEvaluated || controlSummary.StatusInfo.SubStatus == apis.SubStatusNotEvaluated {
controlSummary.StatusInfo.SubStatus = apis.SubStatusNotEvaluated
controlSummary.StatusInfo.InnerInfo = string(apis.SubStatusNotEvaluatedInfo)
} else if subStatus == apis.SubStatusConfiguration || controlSummary.StatusInfo.SubStatus == apis.SubStatusConfiguration {
controlSummary.StatusInfo.SubStatus = apis.SubStatusConfiguration
controlSummary.StatusInfo.InnerInfo = string(apis.SubStatusConfigurationInfo)
} else if subStatus == apis.SubStatusManualReview || controlSummary.StatusInfo.SubStatus == apis.SubStatusManualReview {
Expand Down
5 changes: 3 additions & 2 deletions reporthandling/results/v1/resourcesresults/controls.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ func (control *ResourceAssociatedControl) SetStatus(c reporthandling.Control) {
statusInfo = string(apis.SubStatusManualReviewInfo)
}

// If the control type is configuration and the configuration is not set, the status is skipped and the sub status is configuration
if actionRequired == apis.SubStatusConfiguration && controlMissingAllConfigurations(control) {
// If the control type is configuration and the configuration is not set, the status is skipped and the sub status is configuration.
// Do not overwrite notEvaluated: a GVR collection gap takes precedence over a missing-config skip.
if actionRequired == apis.SubStatusConfiguration && controlMissingAllConfigurations(control) && subStatus != apis.SubStatusNotEvaluated {
status = apis.StatusSkipped
subStatus = apis.SubStatusConfiguration
statusInfo = string(apis.SubStatusConfigurationInfo)
Expand Down
8 changes: 8 additions & 0 deletions reporthandling/results/v1/resourcesresults/controls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ func TestSetStatus(t *testing.T) {
assert.False(t, r6.GetStatus(nil).IsFailed())
assert.True(t, r6.GetStatus(nil).IsSkipped())

// notEvaluated must not be overwritten by configuration even when configs are missing.
// A GVR collection gap (RBAC denied, missing CRD) takes precedence over a missing-config skip.
r7 := mockResourceAssociatedControlNotEvaluated()
r7.SetStatus(*mockControlWithActionRequiredConfiguration())
assert.Equal(t, apis.StatusSkipped, r7.GetStatus(nil).Status())
assert.Equal(t, apis.SubStatusNotEvaluated, r7.GetSubStatus())
assert.True(t, r7.GetStatus(nil).IsSkipped())

}

func TestResourceAssociatedControl_SetName(t *testing.T) {
Expand Down
21 changes: 21 additions & 0 deletions reporthandling/results/v1/resourcesresults/datastructures_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,27 @@ func mockControlWithActionRequiredManualReview() *reporthandling.Control {
}
}

func mockResourceAssociatedRuleNotEvaluated() *ResourceAssociatedRule {
return &ResourceAssociatedRule{
Name: "ruleNotEvaluated",
Status: apis.StatusSkipped,
SubStatus: apis.SubStatusNotEvaluated,
Paths: []armotypes.PosturePaths{},
Exception: []armotypes.PostureExceptionPolicy{},
ControlConfigurations: nil,
}
}

func mockResourceAssociatedControlNotEvaluated() *ResourceAssociatedControl {
return &ResourceAssociatedControl{
ControlID: "C-0089",
Name: "0089",
ResourceAssociatedRules: []ResourceAssociatedRule{
*mockResourceAssociatedRuleNotEvaluated(),
},
}
}

// func mockResourceAssociatedRuleWithFWException() *ResourceAssociatedRule {
// return &ResourceAssociatedRule{
// Name: "ruleB",
Expand Down
Loading