github.com/google/osv-scalibr@v0.4.1/detector/detectorrunner/detectorrunner_test.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package detectorrunner_test 16 17 import ( 18 "errors" 19 "testing" 20 21 "github.com/google/go-cmp/cmp" 22 "github.com/google/go-cmp/cmp/cmpopts" 23 "github.com/google/osv-scalibr/detector" 24 "github.com/google/osv-scalibr/detector/detectorrunner" 25 "github.com/google/osv-scalibr/extractor" 26 scalibrfs "github.com/google/osv-scalibr/fs" 27 "github.com/google/osv-scalibr/inventory" 28 "github.com/google/osv-scalibr/packageindex" 29 "github.com/google/osv-scalibr/plugin" 30 "github.com/google/osv-scalibr/stats" 31 fd "github.com/google/osv-scalibr/testing/fakedetector" 32 osvpb "github.com/ossf/osv-schema/bindings/go/osvschema" 33 "google.golang.org/protobuf/testing/protocmp" 34 ) 35 36 func TestRun(t *testing.T) { 37 finding1 := &inventory.GenericFinding{ 38 Adv: &inventory.GenericFindingAdvisory{ 39 ID: &inventory.AdvisoryID{ 40 Publisher: "CVE", 41 Reference: "CVE-1234", 42 }, 43 Sev: inventory.SeverityMedium, 44 }, 45 } 46 identicalFinding1 := &inventory.GenericFinding{ 47 Adv: &inventory.GenericFindingAdvisory{ 48 ID: &inventory.AdvisoryID{ 49 Publisher: "CVE", 50 Reference: "CVE-1234", 51 }, 52 Sev: inventory.SeverityMedium, 53 }, 54 } 55 finding2 := &inventory.GenericFinding{ 56 Adv: &inventory.GenericFindingAdvisory{ 57 ID: &inventory.AdvisoryID{ 58 Publisher: "CVE", 59 Reference: "CVE-5678", 60 }, 61 }, 62 } 63 findingNoAdvisory := &inventory.GenericFinding{} 64 findingNoAdvisoryID := &inventory.GenericFinding{Adv: &inventory.GenericFindingAdvisory{}} 65 packageVuln := &inventory.PackageVuln{ 66 Vulnerability: &osvpb.Vulnerability{Id: "CVE-9012"}, 67 } 68 det1 := fd.New().WithName("det1").WithVersion(1) 69 det2 := fd.New().WithName("det2").WithVersion(2) 70 success := &plugin.ScanStatus{Status: plugin.ScanStatusSucceeded} 71 72 testCases := []struct { 73 desc string 74 det []detector.Detector 75 wantFindings inventory.Finding 76 wantStatus []*plugin.Status 77 wantErr error 78 }{ 79 { 80 desc: "Plugins_successful", 81 det: []detector.Detector{ 82 det1.WithGenericFinding(finding1), 83 det2.WithGenericFinding(finding2), 84 }, 85 wantFindings: inventory.Finding{ 86 GenericFindings: []*inventory.GenericFinding{ 87 withDetectorName(finding1, "det1"), 88 withDetectorName(finding2, "det2"), 89 }, 90 }, 91 wantStatus: []*plugin.Status{ 92 {Name: "det1", Version: 1, Status: success}, 93 {Name: "det2", Version: 2, Status: success}, 94 }, 95 }, 96 { 97 desc: "One_plugin_failed", 98 det: []detector.Detector{ 99 det1.WithGenericFinding(finding1), 100 det2.WithErr(errors.New("detection failed")), 101 }, 102 wantFindings: inventory.Finding{ 103 GenericFindings: []*inventory.GenericFinding{withDetectorName(finding1, "det1")}, 104 }, 105 wantStatus: []*plugin.Status{ 106 {Name: "det1", Version: 1, Status: success}, 107 {Name: "det2", Version: 2, Status: &plugin.ScanStatus{ 108 Status: plugin.ScanStatusFailed, FailureReason: "detection failed", 109 }}, 110 }, 111 }, 112 { 113 desc: "Duplicate_findings_with_identical_advisories", 114 det: []detector.Detector{ 115 det1.WithGenericFinding(finding1), 116 det2.WithGenericFinding(identicalFinding1), 117 }, 118 wantFindings: inventory.Finding{GenericFindings: []*inventory.GenericFinding{ 119 withDetectorName(finding1, "det1"), 120 withDetectorName(finding1, "det2"), 121 }}, 122 wantStatus: []*plugin.Status{ 123 {Name: "det1", Version: 1, Status: success}, 124 {Name: "det2", Version: 2, Status: success}, 125 }, 126 }, 127 { 128 desc: "Duplicate_findings_with_different_advisories", 129 det: []detector.Detector{ 130 det1.WithGenericFinding(finding1), 131 det2.WithGenericFinding(&inventory.GenericFinding{ 132 Adv: &inventory.GenericFindingAdvisory{ID: finding1.Adv.ID, Title: "different title"}, 133 }), 134 }, 135 wantFindings: inventory.Finding{}, 136 wantStatus: []*plugin.Status{ 137 {Name: "det1", Version: 1, Status: success}, 138 {Name: "det2", Version: 2, Status: success}, 139 }, 140 wantErr: cmpopts.AnyError, 141 }, 142 { 143 desc: "Error_when_Advisory_is_not_set", 144 det: []detector.Detector{ 145 det1.WithGenericFinding(findingNoAdvisory), 146 }, 147 wantFindings: inventory.Finding{}, 148 wantStatus: []*plugin.Status{ 149 {Name: "det1", Version: 1, Status: success}, 150 }, 151 wantErr: cmpopts.AnyError, 152 }, 153 { 154 desc: "Error_when_Advisory_ID_is_not_set", 155 det: []detector.Detector{ 156 det1.WithGenericFinding(findingNoAdvisoryID), 157 }, 158 wantFindings: inventory.Finding{}, 159 wantStatus: []*plugin.Status{ 160 {Name: "det1", Version: 1, Status: success}, 161 }, 162 wantErr: cmpopts.AnyError, 163 }, 164 { 165 desc: "Package_and_generic_vulns", 166 det: []detector.Detector{ 167 det1.WithGenericFinding(finding1), 168 det2.WithPackageVuln(packageVuln), 169 }, 170 wantFindings: inventory.Finding{ 171 GenericFindings: []*inventory.GenericFinding{withDetectorName(finding1, "det1")}, 172 PackageVulns: []*inventory.PackageVuln{pkgVulnWithDetectorName(packageVuln, "det2")}, 173 }, 174 wantStatus: []*plugin.Status{ 175 {Name: "det1", Version: 1, Status: success}, 176 {Name: "det2", Version: 2, Status: success}, 177 }, 178 }, 179 } 180 181 for _, tc := range testCases { 182 t.Run(tc.desc, func(t *testing.T) { 183 px, _ := packageindex.New([]*extractor.Package{}) 184 tmp := t.TempDir() 185 gotFindings, gotStatus, err := detectorrunner.Run( 186 t.Context(), stats.NoopCollector{}, tc.det, scalibrfs.RealFSScanRoot(tmp), px, 187 ) 188 if diff := cmp.Diff(tc.wantErr, err, cmpopts.EquateErrors()); diff != "" { 189 t.Errorf("detectorrunner.Run(%v): unexpected error (-want +got):\n%s", tc.det, diff) 190 } 191 if diff := cmp.Diff(tc.wantFindings, gotFindings, protocmp.Transform()); diff != "" { 192 t.Errorf("detectorrunner.Run(%v): unexpected findings (-want +got):\n%s", tc.det, diff) 193 } 194 if diff := cmp.Diff(tc.wantStatus, gotStatus); diff != "" { 195 t.Errorf("detectorrunner.Run(%v): unexpected status (-want +got):\n%s", tc.det, diff) 196 } 197 }) 198 } 199 } 200 201 func withDetectorName(f *inventory.GenericFinding, det string) *inventory.GenericFinding { 202 c := *f 203 c.Plugins = []string{det} 204 return &c 205 } 206 207 func pkgVulnWithDetectorName(v *inventory.PackageVuln, det string) *inventory.PackageVuln { 208 c := *v 209 c.Plugins = []string{det} 210 return &c 211 }