Skip to content

Commit ae5f468

Browse files
authored
Fix applicability status calculation (#613)
1 parent 93636fe commit ae5f468

File tree

6 files changed

+112
-123
lines changed

6 files changed

+112
-123
lines changed

commands/audit/audit_test.go

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,7 @@ func TestAuditWithConfigProfile(t *testing.T) {
326326
}},
327327
IsDefault: false,
328328
},
329-
expectedCaApplicable: 3,
330-
expectedCaUndetermined: 6,
331-
expectedCaNotCovered: 4,
332-
expectedCaNotApplicable: 2,
329+
expectedCaNotCovered: 15,
333330
},
334331
// TODO Add testcase for Sca and Applicability with exclusions after resolving the Glob patterns issues
335332
{
@@ -550,13 +547,10 @@ func TestAuditWithConfigProfile(t *testing.T) {
550547
}},
551548
IsDefault: false,
552549
},
553-
expectedSastIssues: 4,
554-
expectedSecretsIssues: 16,
555-
expectedIacIssues: 9,
556-
expectedCaApplicable: 3,
557-
expectedCaUndetermined: 6,
558-
expectedCaNotCovered: 4,
559-
expectedCaNotApplicable: 2,
550+
expectedSastIssues: 4,
551+
expectedSecretsIssues: 16,
552+
expectedIacIssues: 9,
553+
expectedCaNotCovered: 15,
560554
},
561555
{
562556
name: "All scanners enabled but some with exclude patterns",
@@ -589,13 +583,10 @@ func TestAuditWithConfigProfile(t *testing.T) {
589583
}},
590584
IsDefault: false,
591585
},
592-
expectedSastIssues: 0,
593-
expectedSecretsIssues: 7,
594-
expectedIacIssues: 9,
595-
expectedCaApplicable: 3,
596-
expectedCaUndetermined: 6,
597-
expectedCaNotCovered: 4,
598-
expectedCaNotApplicable: 2,
586+
expectedSastIssues: 0,
587+
expectedSecretsIssues: 7,
588+
expectedIacIssues: 9,
589+
expectedCaNotCovered: 15,
599590
},
600591
}
601592
assert.NoError(t, securityTestUtils.PrepareAnalyzerManagerResource())

sca/bom/buildinfo/technologies/conan/conan_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ var expectedResult = &xrayUtils.GraphNode{
1818
Nodes: []*xrayUtils.GraphNode{
1919
{Id: "conan://zlib:1.3.1"},
2020
{Id: "conan://openssl:3.0.9", Nodes: []*xrayUtils.GraphNode{{Id: "conan://zlib:1.3.1"}}},
21-
{Id: "conan://meson:1.4.1", Nodes: []*xrayUtils.GraphNode{{Id: "conan://ninja:1.13.1"}}},
21+
{Id: "conan://meson:1.4.1", Nodes: []*xrayUtils.GraphNode{{Id: "conan://ninja:1.13.2"}}},
2222
},
2323
}
24-
var expectedUniqueDeps = []string{"conan://openssl:3.0.9", "conan://zlib:1.3.1", "conan://meson:1.4.1", "conan://ninja:1.13.1"}
24+
var expectedUniqueDeps = []string{"conan://openssl:3.0.9", "conan://zlib:1.3.1", "conan://meson:1.4.1", "conan://ninja:1.13.2"}
2525

2626
func TestParseConanDependencyTree(t *testing.T) {
2727
_, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("other", "conan"))
@@ -58,7 +58,7 @@ func TestCalculateUniqueDeps(t *testing.T) {
5858
"1": {Name: "zlib", Version: "1.3.1"},
5959
"2": {Name: "openssl", Version: "3.0.9"},
6060
"3": {Name: "meson", Version: "1.4.1"},
61-
"4": {Name: "ninja", Version: "1.13.1"},
61+
"4": {Name: "ninja", Version: "1.13.2"},
6262
"5": {Name: "openssl", Version: "3.0.9"}, // duplicate, should be removed
6363
}
6464

tests/testdata/other/conan/dependencies.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@
145145
"visible": true
146146
},
147147
"4": {
148-
"ref": "ninja/1.13.1",
149-
"require": "ninja/1.13.1",
148+
"ref": "ninja/1.13.2",
149+
"require": "ninja/1.13.2",
150150
"run": true,
151151
"libs": false,
152152
"skip": false,
@@ -965,7 +965,7 @@
965965
},
966966
"dependencies": {
967967
"4": {
968-
"ref": "ninja/1.13.1",
968+
"ref": "ninja/1.13.2",
969969
"require": "ninja/[>=1.10.2 <2]",
970970
"run": true,
971971
"libs": false,
@@ -985,7 +985,7 @@
985985
"test": false
986986
},
987987
"4": {
988-
"ref": "ninja/1.13.1#294f8721dbcde145674f7ba44994700e",
988+
"ref": "ninja/1.13.2#294f8721dbcde145674f7ba44994700e",
989989
"id": "4",
990990
"recipe": "Downloaded",
991991
"package_id": "3593751651824fb813502c69c971267624ced41a",
@@ -1016,7 +1016,7 @@
10161016
"win_bash_run": null,
10171017
"default_options": null,
10181018
"options_description": null,
1019-
"version": "1.13.1",
1019+
"version": "1.13.2",
10201020
"topics": [
10211021
"ninja",
10221022
"build"
@@ -1079,7 +1079,7 @@
10791079
}
10801080
},
10811081
"conf_info": {},
1082-
"label": "ninja/1.13.1",
1082+
"label": "ninja/1.13.2",
10831083
"info": {
10841084
"settings": {
10851085
"os": "Linux",
@@ -1090,9 +1090,9 @@
10901090
"vendor": false,
10911091
"conandata": {
10921092
"sources": {
1093-
"1.13.1": {
1093+
"1.13.2": {
10941094
"sha256": "f0055ad0369bf2e372955ba55128d000cfcc21777057806015b45e4accbebf23",
1095-
"url": "https://github.com/ninja-build/ninja/archive/v1.13.1.tar.gz"
1095+
"url": "https://github.com/ninja-build/ninja/archive/v1.13.2.tar.gz"
10961096
}
10971097
}
10981098
},
@@ -1106,7 +1106,7 @@
11061106
},
11071107
"overrides": {},
11081108
"resolved_ranges": {
1109-
"ninja/[>=1.10.2 <2]": "ninja/1.13.1"
1109+
"ninja/[>=1.10.2 <2]": "ninja/1.13.2"
11101110
},
11111111
"replaced_requires": {},
11121112
"error": null

utils/formats/sarifutils/sarifutils.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ func GetResultMsgText(result *sarif.Result) string {
577577
}
578578

579579
func GetResultRuleId(result *sarif.Result) string {
580-
if result.RuleID != nil {
580+
if result != nil && result.RuleID != nil {
581581
return *result.RuleID
582582
}
583583
return ""

utils/results/common.go

Lines changed: 45 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -476,41 +476,27 @@ func GetCveApplicabilityField(cveId string, applicabilityScanResults []*sarif.Ru
476476
return nil
477477
}
478478
applicability := formats.Applicability{}
479-
resultFound := false
480479
var applicabilityStatuses []jasutils.ApplicabilityStatus
481480
for _, applicabilityRun := range applicabilityScanResults {
481+
// Get applicability information from the rule
482482
if rule := sarifutils.GetRuleById(applicabilityRun, jasutils.CveToApplicabilityRuleId(cveId)); rule != nil {
483483
applicability.ScannerDescription = sarifutils.GetRuleFullDescription(rule)
484484
applicability.UndeterminedReason = sarifutils.GetRuleUndeterminedReason(rule)
485-
status := getApplicabilityStatusFromRule(rule)
486-
if status != "" {
485+
if status := getApplicabilityStatusFromRule(rule); status != jasutils.NotScanned {
487486
applicabilityStatuses = append(applicabilityStatuses, status)
488487
}
489488
}
490-
cveResults := sarifutils.GetResultsByRuleId(jasutils.CveToApplicabilityRuleId(cveId), applicabilityRun)
491-
if len(cveResults) == 0 {
492-
continue
493-
}
494-
resultFound = true
495-
for _, result := range cveResults {
496-
// Add new evidences from locations
489+
// Get applicability evidence from the results
490+
for _, result := range sarifutils.GetResultsByRuleId(jasutils.CveToApplicabilityRuleId(cveId), applicabilityRun) {
497491
for _, location := range result.Locations {
498492
if evidence := getEvidence(result, location, applicabilityRun.Invocations...); evidence != nil {
499493
applicability.Evidence = append(applicability.Evidence, *evidence)
500494
}
501495
}
502496
}
503497
}
504-
switch {
505-
case len(applicabilityStatuses) > 0:
506-
applicability.Status = string(GetFinalApplicabilityStatus(applicabilityStatuses))
507-
case !resultFound:
508-
applicability.Status = string(jasutils.ApplicabilityUndetermined)
509-
case len(applicability.Evidence) == 0:
510-
applicability.Status = string(jasutils.NotApplicable)
511-
default:
512-
applicability.Status = string(jasutils.Applicable)
513-
}
498+
// Calculate final applicability status
499+
applicability.Status = GetFinalApplicabilityStatus(true, applicabilityStatuses).String()
514500
return &applicability
515501
}
516502

@@ -551,16 +537,13 @@ func GetApplicableCveStatus(entitledForJas bool, applicabilityScanResults []*sar
551537
if !entitledForJas || len(applicabilityScanResults) == 0 {
552538
return jasutils.NotScanned
553539
}
554-
if len(cves) == 0 {
555-
return jasutils.NotCovered
556-
}
557540
var applicableStatuses []jasutils.ApplicabilityStatus
558541
for _, cve := range cves {
559542
if cve.Applicability != nil {
560543
applicableStatuses = append(applicableStatuses, jasutils.ApplicabilityStatus(cve.Applicability.Status))
561544
}
562545
}
563-
return GetFinalApplicabilityStatus(applicableStatuses)
546+
return GetFinalApplicabilityStatus(true, applicableStatuses)
564547
}
565548

566549
func getApplicabilityStatusFromRule(rule *sarif.ReportingDescriptor) jasutils.ApplicabilityStatus {
@@ -569,7 +552,7 @@ func getApplicabilityStatusFromRule(rule *sarif.ReportingDescriptor) jasutils.Ap
569552
if !ok {
570553
log.Debug(fmt.Sprintf("Failed to get applicability status from rule properties for rule_id %s", sarifutils.GetRuleId(rule)))
571554
}
572-
switch status {
555+
switch strings.ToLower(status) {
573556
case "not_covered":
574557
return jasutils.NotCovered
575558
case "undetermined":
@@ -632,45 +615,58 @@ func shouldDisqualifyEvidence(components map[string]services.Component, evidence
632615
return
633616
}
634617

635-
// If we don't get any statues it means the applicability scanner didn't run -> final value is not scanned
636-
// If at least one cve is applicable -> final value is applicable
637-
// Else if at least one cve is undetermined -> final value is undetermined
638-
// Else if at least one cve is missing context -> final value is missing context
639-
// Else if all cves are not covered -> final value is not covered
640-
// Else (case when all cves aren't applicable) -> final value is not applicable
641-
func GetFinalApplicabilityStatus(applicabilityStatuses []jasutils.ApplicabilityStatus) jasutils.ApplicabilityStatus {
642-
if len(applicabilityStatuses) == 0 {
618+
// If we don't get any statues (not scanned are ignored) it means the applicability -> scanner didn't run = not scanned, scanner run = not covered
619+
// If only one status -> final value is that status
620+
// Else If at least one status is applicable -> final value is applicable
621+
// Else if at least one status is undetermined -> final value is undetermined
622+
// Else if at least one status is missing context -> final value is missing context
623+
// Else if all statuses are not applicable -> final value is not applicable
624+
// Else (at least one status is not covered) -> final value is not covered
625+
func GetFinalApplicabilityStatus(hasContextualAnalysisRun bool, applicabilityStatuses []jasutils.ApplicabilityStatus) jasutils.ApplicabilityStatus {
626+
actualStatuses := []jasutils.ApplicabilityStatus{}
627+
for _, status := range applicabilityStatuses {
628+
if status != jasutils.NotScanned {
629+
actualStatuses = append(actualStatuses, status)
630+
}
631+
}
632+
if len(actualStatuses) == 0 {
633+
if hasContextualAnalysisRun {
634+
// Run exists but no statuses found
635+
return jasutils.NotCovered
636+
}
637+
// No runs so not scanned
643638
return jasutils.NotScanned
644639
}
640+
if len(actualStatuses) == 1 {
641+
// Only one status so return it directly
642+
return actualStatuses[0]
643+
}
645644
foundUndetermined := false
646645
foundMissingContext := false
647-
foundNotCovered := false
648-
for _, status := range applicabilityStatuses {
646+
allNotApplicable := true
647+
for _, status := range actualStatuses {
649648
if status == jasutils.Applicable {
649+
// Has at least one applicable status
650650
return jasutils.Applicable
651651
}
652-
if status == jasutils.ApplicabilityUndetermined {
653-
foundUndetermined = true
654-
}
655-
if status == jasutils.MissingContext {
656-
foundMissingContext = true
657-
}
658-
if status == jasutils.NotCovered {
659-
foundNotCovered = true
660-
}
661-
652+
foundUndetermined = foundUndetermined || (status == jasutils.ApplicabilityUndetermined)
653+
foundMissingContext = foundMissingContext || (status == jasutils.MissingContext)
654+
allNotApplicable = allNotApplicable && (status == jasutils.NotApplicable)
662655
}
663656
if foundUndetermined {
657+
// Has at least one undetermined status and no applicable status
664658
return jasutils.ApplicabilityUndetermined
665659
}
666660
if foundMissingContext {
661+
// Has at least one missing context status and no applicable or undetermined status
667662
return jasutils.MissingContext
668663
}
669-
if foundNotCovered {
670-
return jasutils.NotCovered
664+
if allNotApplicable {
665+
// All statuses are not applicable
666+
return jasutils.NotApplicable
671667
}
672-
673-
return jasutils.NotApplicable
668+
// At least one status is not covered (and no applicable, undetermined or missing context and not all are not applicable)
669+
return jasutils.NotCovered
674670
}
675671

676672
func GetJasResultApplicability(result *sarif.Result) *formats.Applicability {

0 commit comments

Comments
 (0)