github.com/google/osv-scalibr@v0.4.1/guidedremediation/internal/severity/severity.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 severity implements severity calculation for OSV records. 16 package severity 17 18 import ( 19 "fmt" 20 "strings" 21 22 osvpb "github.com/ossf/osv-schema/bindings/go/osvschema" 23 gocvss20 "github.com/pandatix/go-cvss/20" 24 gocvss30 "github.com/pandatix/go-cvss/30" 25 gocvss31 "github.com/pandatix/go-cvss/31" 26 gocvss40 "github.com/pandatix/go-cvss/40" 27 ) 28 29 // CalculateScoreAndRating returns the numeric score and rating for the given severity field. 30 // i.e. returns the CVSS Score (0.0 - 10.0) and the rating (e.g. "CRITICAL") 31 // 32 // returns (-1.0, "UNKNOWN", nil) if the severity is the empty struct. 33 // returns (-1.0, "", error) if severity type or score is invalid. 34 func CalculateScoreAndRating(severity *osvpb.Severity) (float64, string, error) { 35 if severity == nil || severity.Score == "" { 36 return -1.0, "UNKNOWN", nil 37 } 38 39 switch severity.Type { 40 case osvpb.Severity_CVSS_V2: 41 vec, err := gocvss20.ParseVector(severity.Score) 42 if err != nil { 43 return -1.0, "", err 44 } 45 score := vec.BaseScore() 46 // CVSS 2.0 does not have a rating. Use the CVSS 3.0 rating instead. 47 rating, err := gocvss30.Rating(score) 48 if err != nil { 49 rating = "UNKNOWN" 50 } 51 return score, rating, nil 52 case osvpb.Severity_CVSS_V3: 53 switch { 54 case strings.HasPrefix(severity.Score, "CVSS:3.0/"): 55 vec, err := gocvss30.ParseVector(severity.Score) 56 if err != nil { 57 return -1.0, "", err 58 } 59 score := vec.BaseScore() 60 rating, err := gocvss30.Rating(score) 61 if err != nil { 62 rating = "UNKNOWN" 63 } 64 return score, rating, nil 65 case strings.HasPrefix(severity.Score, "CVSS:3.1/"): 66 vec, err := gocvss31.ParseVector(severity.Score) 67 if err != nil { 68 return -1.0, "", err 69 } 70 score := vec.BaseScore() 71 rating, err := gocvss31.Rating(score) 72 if err != nil { 73 rating = "UNKNOWN" 74 } 75 return score, rating, nil 76 default: 77 return -1.0, "", fmt.Errorf("unsupported CVSS_V3 version: %s", severity.Score) 78 } 79 case osvpb.Severity_CVSS_V4: 80 vec, err := gocvss40.ParseVector(severity.Score) 81 if err != nil { 82 return -1.0, "", err 83 } 84 score := vec.Score() 85 rating, err := gocvss40.Rating(score) 86 if err != nil { 87 rating = "UNKNOWN" 88 } 89 return score, rating, nil 90 91 default: 92 return -1.0, "", fmt.Errorf("unsupported severity type: %s", severity.Type) 93 } 94 } 95 96 // CalculateScore returns the numeric score for the given severity field. 97 // i.e. returns the CVSS Score (0.0 - 10.0) 98 // 99 // returns (-1.0, nil) if the severity is the empty struct. 100 // returns (-1.0, error) if severity type or score is invalid. 101 func CalculateScore(severity *osvpb.Severity) (float64, error) { 102 score, _, err := CalculateScoreAndRating(severity) 103 return score, err 104 }