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  }