github.com/google/osv-scalibr@v0.4.1/guidedremediation/internal/remediation/match.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 remediation 16 17 import ( 18 "math" 19 "slices" 20 21 "github.com/google/osv-scalibr/guidedremediation/internal/resolution" 22 "github.com/google/osv-scalibr/guidedremediation/internal/severity" 23 "github.com/google/osv-scalibr/guidedremediation/internal/vulns" 24 "github.com/google/osv-scalibr/guidedremediation/options" 25 osvpb "github.com/ossf/osv-schema/bindings/go/osvschema" 26 ) 27 28 // MatchVuln checks whether a found vulnerability should be considered according to the remediation options. 29 func MatchVuln(opts options.RemediationOptions, v resolution.Vulnerability) bool { 30 if matchID(v, opts.IgnoreVulns) { 31 return false 32 } 33 34 if !opts.DevDeps && v.DevOnly { 35 return false 36 } 37 38 return matchSeverity(v, opts.MinSeverity) && matchDepth(v, opts.MaxDepth) 39 } 40 41 func matchID(v resolution.Vulnerability, ids []string) bool { 42 if slices.Contains(ids, v.OSV.Id) { 43 return true 44 } 45 46 for _, id := range v.OSV.Aliases { 47 if slices.Contains(ids, id) { 48 return true 49 } 50 } 51 52 return false 53 } 54 55 func matchSeverity(v resolution.Vulnerability, minSeverity float64) bool { 56 maxScore := -1.0 57 severities := v.OSV.Severity 58 if len(severities) == 0 { 59 // There are no top-level severity, see if there are individual affected[].severity field. 60 severities = []*osvpb.Severity{} 61 for _, sg := range v.Subgraphs { 62 inv := vulns.VKToPackage(sg.Nodes[sg.Dependency].Version) 63 // Make and match a dummy OSV record per affected[] entry to determine which applies. 64 for _, affected := range v.OSV.Affected { 65 if vulns.IsAffected(&osvpb.Vulnerability{Affected: []*osvpb.Affected{affected}}, inv) { 66 severities = append(severities, affected.Severity...) 67 break 68 } 69 } 70 } 71 } 72 73 for _, sev := range severities { 74 if score, err := severity.CalculateScore(sev); err == nil { // skip errors 75 maxScore = max(maxScore, score) 76 } 77 } 78 79 // CVSS scores are meant to only be to 1 decimal place 80 // and we want to avoid something being falsely rejected/included due to floating point precision. 81 // Multiply and round to only consider relevant parts of the score. 82 return math.Round(10*maxScore) >= math.Round(10*minSeverity) || 83 maxScore < 0 // Always include vulns with unknown severities 84 } 85 86 func matchDepth(v resolution.Vulnerability, maxDepth int) bool { 87 if maxDepth <= 0 { 88 return true 89 } 90 91 for _, sg := range v.Subgraphs { 92 if sg.Nodes[0].Distance <= maxDepth { 93 return true 94 } 95 } 96 97 return false 98 }