github.com/google/osv-scalibr@v0.4.1/binary/proto/vex_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 proto_test 16 17 import ( 18 "errors" 19 "testing" 20 21 "github.com/google/go-cmp/cmp" 22 "github.com/google/osv-scalibr/binary/proto" 23 "github.com/google/osv-scalibr/inventory/vex" 24 "google.golang.org/protobuf/testing/protocmp" 25 26 spb "github.com/google/osv-scalibr/binary/proto/scan_result_go_proto" 27 ) 28 29 // --- Struct to Proto 30 31 func TestPackageVEXToProto(t *testing.T) { 32 testCases := []struct { 33 desc string 34 v *vex.PackageExploitabilitySignal 35 want *spb.PackageExploitabilitySignal 36 wantErr error 37 }{ 38 { 39 desc: "nil", 40 v: nil, 41 want: nil, 42 }, 43 { 44 desc: "matches_specific_vulns", 45 v: &vex.PackageExploitabilitySignal{ 46 Plugin: "some-plugin", 47 Justification: vex.ComponentNotPresent, 48 VulnIdentifiers: []string{ 49 "CVE-1234", 50 "CVE-5678", 51 }, 52 }, 53 want: &spb.PackageExploitabilitySignal{ 54 Plugin: "some-plugin", 55 Justification: spb.VexJustification_COMPONENT_NOT_PRESENT, 56 VulnFilter: &spb.PackageExploitabilitySignal_VulnIdentifiers{ 57 VulnIdentifiers: &spb.VulnIdentifiers{ 58 Identifiers: []string{ 59 "CVE-1234", 60 "CVE-5678", 61 }, 62 }, 63 }, 64 }, 65 }, 66 { 67 desc: "matches_all_vulns", 68 v: &vex.PackageExploitabilitySignal{ 69 Plugin: "some-plugin", 70 Justification: vex.ComponentNotPresent, 71 MatchesAllVulns: true, 72 }, 73 want: &spb.PackageExploitabilitySignal{ 74 Plugin: "some-plugin", 75 Justification: spb.VexJustification_COMPONENT_NOT_PRESENT, 76 VulnFilter: &spb.PackageExploitabilitySignal_MatchesAllVulns{ 77 MatchesAllVulns: true, 78 }, 79 }, 80 }, 81 { 82 desc: "both_vuln_identifiers_and_matches_all_vulns_set", 83 v: &vex.PackageExploitabilitySignal{ 84 Plugin: "some-plugin", 85 Justification: vex.ComponentNotPresent, 86 VulnIdentifiers: []string{"CVE-1234"}, 87 MatchesAllVulns: true, 88 }, 89 wantErr: proto.ErrVulnIdentifiersAndMatchsAllSet, 90 }, 91 } 92 93 for _, tc := range testCases { 94 t.Run(tc.desc, func(t *testing.T) { 95 got, err := proto.PackageVEXToProto(tc.v) 96 if !errors.Is(err, tc.wantErr) { 97 t.Errorf("PackageVEXToProto(%v) returned error %v, want error %v", tc.v, err, tc.wantErr) 98 } 99 if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { 100 t.Fatalf("PackageVEXToProto(%v) returned diff (-want +got):\n%s", tc.v, diff) 101 } 102 103 // No need to test the reverse conversion if the result is nil. 104 if got == nil && tc.v != nil { 105 return 106 } 107 108 // Test the reverse conversion for completeness. 109 gotPB, err := proto.PackageVEXToStruct(got) 110 if err != nil { 111 t.Fatalf("PackageVEXToStruct(%v) returned error %v, want nil", got, err) 112 } 113 if diff := cmp.Diff(tc.v, gotPB, protocmp.Transform()); diff != "" { 114 t.Fatalf("PackageVEXToStruct(%v) returned diff (-want +got):\n%s", got, diff) 115 } 116 }) 117 } 118 } 119 120 func TestFindingVEXToProto(t *testing.T) { 121 testCases := []struct { 122 desc string 123 v *vex.FindingExploitabilitySignal 124 want *spb.FindingExploitabilitySignal 125 }{ 126 { 127 desc: "nil", 128 v: nil, 129 want: nil, 130 }, 131 { 132 desc: "success", 133 v: &vex.FindingExploitabilitySignal{ 134 Plugin: "some-plugin", 135 Justification: vex.ComponentNotPresent, 136 }, 137 want: &spb.FindingExploitabilitySignal{ 138 Plugin: "some-plugin", 139 Justification: spb.VexJustification_COMPONENT_NOT_PRESENT, 140 }, 141 }, 142 } 143 144 for _, tc := range testCases { 145 t.Run(tc.desc, func(t *testing.T) { 146 got := proto.FindingVEXToProto(tc.v) 147 if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { 148 t.Fatalf("FindingVEXToProto(%v) returned diff (-want +got):\n%s", tc.v, diff) 149 } 150 151 // Test the reverse conversion for completeness. 152 gotPB := proto.FindingVEXToStruct(got) 153 if diff := cmp.Diff(tc.v, gotPB, protocmp.Transform()); diff != "" { 154 t.Fatalf("FindingVEXToStruct(%v) returned diff (-want +got):\n%s", got, diff) 155 } 156 }) 157 } 158 } 159 160 // --- Proto to Struct 161 162 func TestPackageVEXToStruct(t *testing.T) { 163 testCases := []struct { 164 desc string 165 v *spb.PackageExploitabilitySignal 166 want *vex.PackageExploitabilitySignal 167 wantErr error 168 }{ 169 { 170 desc: "nil", 171 v: nil, 172 want: nil, 173 }, 174 { 175 desc: "matches_specific_vulns", 176 v: &spb.PackageExploitabilitySignal{ 177 Plugin: "some-plugin", 178 Justification: spb.VexJustification_COMPONENT_NOT_PRESENT, 179 VulnFilter: &spb.PackageExploitabilitySignal_VulnIdentifiers{ 180 VulnIdentifiers: &spb.VulnIdentifiers{ 181 Identifiers: []string{ 182 "CVE-1234", 183 "CVE-5678", 184 }, 185 }, 186 }, 187 }, 188 want: &vex.PackageExploitabilitySignal{ 189 Plugin: "some-plugin", 190 Justification: vex.ComponentNotPresent, 191 VulnIdentifiers: []string{ 192 "CVE-1234", 193 "CVE-5678", 194 }, 195 }, 196 }, 197 { 198 desc: "matches_all_vulns", 199 v: &spb.PackageExploitabilitySignal{ 200 Plugin: "some-plugin", 201 Justification: spb.VexJustification_COMPONENT_NOT_PRESENT, 202 VulnFilter: &spb.PackageExploitabilitySignal_MatchesAllVulns{ 203 MatchesAllVulns: true, 204 }, 205 }, 206 want: &vex.PackageExploitabilitySignal{ 207 Plugin: "some-plugin", 208 Justification: vex.ComponentNotPresent, 209 MatchesAllVulns: true, 210 }, 211 }, 212 } 213 214 for _, tc := range testCases { 215 t.Run(tc.desc, func(t *testing.T) { 216 got, err := proto.PackageVEXToStruct(tc.v) 217 if !errors.Is(err, tc.wantErr) { 218 t.Errorf("PackageVEXToStruct(%v) returned error %v, want error %v", tc.v, err, tc.wantErr) 219 } 220 if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { 221 t.Fatalf("PackageVEXToStruct(%v) returned diff (-want +got):\n%s", tc.v, diff) 222 } 223 224 // No need to test the reverse conversion if the result is nil. 225 if got == nil && tc.v != nil { 226 return 227 } 228 229 // Test the reverse conversion for completeness. 230 gotPB, err := proto.PackageVEXToProto(got) 231 if err != nil { 232 t.Fatalf("PackageVEXToProto(%v) returned error %v, want nil", got, err) 233 } 234 if diff := cmp.Diff(tc.v, gotPB, protocmp.Transform()); diff != "" { 235 t.Fatalf("PackageVEXToProto(%v) returned diff (-want +got):\n%s", got, diff) 236 } 237 }) 238 } 239 } 240 241 func TestFindingVEXToStruct(t *testing.T) { 242 testCases := []struct { 243 desc string 244 v *spb.FindingExploitabilitySignal 245 want *vex.FindingExploitabilitySignal 246 }{ 247 { 248 desc: "nil", 249 v: nil, 250 want: nil, 251 }, 252 { 253 desc: "success", 254 v: &spb.FindingExploitabilitySignal{ 255 Plugin: "some-plugin", 256 Justification: spb.VexJustification_COMPONENT_NOT_PRESENT, 257 }, 258 want: &vex.FindingExploitabilitySignal{ 259 Plugin: "some-plugin", 260 Justification: vex.ComponentNotPresent, 261 }, 262 }, 263 } 264 265 for _, tc := range testCases { 266 t.Run(tc.desc, func(t *testing.T) { 267 got := proto.FindingVEXToStruct(tc.v) 268 if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { 269 t.Fatalf("FindingVEXToStruct(%v) returned diff (-want +got):\n%s", tc.v, diff) 270 } 271 272 // Test the reverse conversion for completeness. 273 gotPB := proto.FindingVEXToProto(got) 274 if diff := cmp.Diff(tc.v, gotPB, protocmp.Transform()); diff != "" { 275 t.Fatalf("FindingVEXToProto(%v) returned diff (-want +got):\n%s", got, diff) 276 } 277 }) 278 } 279 }