github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/vulnerability/vulnerability.go (about)

     1  package vulnerability
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/google/wire"
     7  
     8  	"github.com/aquasecurity/trivy-db/pkg/db"
     9  	dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
    10  	"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
    11  	"github.com/devseccon/trivy/pkg/log"
    12  	"github.com/devseccon/trivy/pkg/types"
    13  )
    14  
    15  var (
    16  	primaryURLPrefixes = map[dbTypes.SourceID][]string{
    17  		vulnerability.Debian: {
    18  			"http://www.debian.org",
    19  			"https://www.debian.org",
    20  		},
    21  		vulnerability.Ubuntu: {
    22  			"http://www.ubuntu.com",
    23  			"https://usn.ubuntu.com",
    24  		},
    25  		vulnerability.RedHat: {"https://access.redhat.com"},
    26  		vulnerability.SuseCVRF: {
    27  			"http://lists.opensuse.org",
    28  			"https://lists.opensuse.org",
    29  		},
    30  		vulnerability.OracleOVAL: {
    31  			"http://linux.oracle.com/errata",
    32  			"https://linux.oracle.com/errata",
    33  		},
    34  		vulnerability.NodejsSecurityWg: {
    35  			"https://www.npmjs.com",
    36  			"https://hackerone.com",
    37  		},
    38  		vulnerability.RubySec: {"https://groups.google.com"},
    39  	}
    40  )
    41  
    42  // SuperSet binds the dependencies
    43  var SuperSet = wire.NewSet(
    44  	wire.Struct(new(db.Config)),
    45  	wire.Bind(new(db.Operation), new(db.Config)),
    46  	NewClient,
    47  )
    48  
    49  // Client manipulates vulnerabilities
    50  type Client struct {
    51  	dbc db.Operation
    52  }
    53  
    54  // NewClient is the factory method for Client
    55  func NewClient(dbc db.Operation) Client {
    56  	return Client{dbc: dbc}
    57  }
    58  
    59  // FillInfo fills extra info in vulnerability objects
    60  func (c Client) FillInfo(vulns []types.DetectedVulnerability) {
    61  	for i := range vulns {
    62  		// Add the vulnerability status
    63  		// Some vendors such as Red Hat have their own vulnerability status, and we use it.
    64  		// Otherwise, we put "fixed" or "affected" according to the fixed version.
    65  		if vulns[i].FixedVersion != "" {
    66  			vulns[i].Status = dbTypes.StatusFixed
    67  		} else if vulns[i].Status == dbTypes.StatusUnknown {
    68  			vulns[i].Status = dbTypes.StatusAffected
    69  		}
    70  
    71  		// Get the vulnerability detail
    72  		vulnID := vulns[i].VulnerabilityID
    73  		vuln, err := c.dbc.GetVulnerability(vulnID)
    74  		if err != nil {
    75  			log.Logger.Warnf("Error while getting vulnerability details: %s", err)
    76  			continue
    77  		}
    78  
    79  		// Detect the data source
    80  		var source dbTypes.SourceID
    81  		if vulns[i].DataSource != nil {
    82  			source = vulns[i].DataSource.ID
    83  		}
    84  
    85  		// Select the severity according to the detected source.
    86  		severity, severitySource := c.getVendorSeverity(vulnID, &vuln, source)
    87  
    88  		// The vendor might provide package-specific severity like Debian.
    89  		// For example, CVE-2015-2328 in Debian has "unimportant" for mongodb and "low" for pcre3.
    90  		// In that case, we keep the severity as is.
    91  		if vulns[i].SeveritySource != "" {
    92  			severity = vulns[i].Severity
    93  			severitySource = vulns[i].SeveritySource
    94  
    95  			// Store package-specific severity in vendor severities
    96  			if vuln.VendorSeverity == nil {
    97  				vuln.VendorSeverity = make(dbTypes.VendorSeverity)
    98  			}
    99  			s, _ := dbTypes.NewSeverity(severity) // skip error handling because `SeverityUnknown` will be returned in case of error
   100  			vuln.VendorSeverity[severitySource] = s
   101  		}
   102  
   103  		// Add the vulnerability detail
   104  		vulns[i].Vulnerability = vuln
   105  
   106  		vulns[i].Severity = severity
   107  		vulns[i].SeveritySource = severitySource
   108  		vulns[i].PrimaryURL = c.getPrimaryURL(vulnID, vuln.References, source)
   109  	}
   110  }
   111  
   112  func (c Client) getVendorSeverity(vulnID string, vuln *dbTypes.Vulnerability, source dbTypes.SourceID) (string, dbTypes.SourceID) {
   113  	if vs, ok := vuln.VendorSeverity[source]; ok {
   114  		return vs.String(), source
   115  	}
   116  
   117  	// use severity from GitHub for all GHSA-xxx vulnerabilities
   118  	if strings.HasPrefix(vulnID, "GHSA-") {
   119  		if vs, ok := vuln.VendorSeverity[vulnerability.GHSA]; ok {
   120  			return vs.String(), vulnerability.GHSA
   121  		}
   122  	}
   123  
   124  	// Try NVD as a fallback if it exists
   125  	if vs, ok := vuln.VendorSeverity[vulnerability.NVD]; ok {
   126  		return vs.String(), vulnerability.NVD
   127  	}
   128  
   129  	if vuln.Severity == "" {
   130  		return dbTypes.SeverityUnknown.String(), ""
   131  	}
   132  
   133  	return vuln.Severity, ""
   134  }
   135  
   136  func (c Client) getPrimaryURL(vulnID string, refs []string, source dbTypes.SourceID) string {
   137  	switch {
   138  	case strings.HasPrefix(vulnID, "CVE-"):
   139  		return "https://avd.aquasec.com/nvd/" + strings.ToLower(vulnID)
   140  	case strings.HasPrefix(vulnID, "RUSTSEC-"):
   141  		return "https://osv.dev/vulnerability/" + vulnID
   142  	case strings.HasPrefix(vulnID, "GHSA-"):
   143  		return "https://github.com/advisories/" + vulnID
   144  	case strings.HasPrefix(vulnID, "TEMP-"):
   145  		return "https://security-tracker.debian.org/tracker/" + vulnID
   146  	}
   147  
   148  	prefixes := primaryURLPrefixes[source]
   149  	for _, pre := range prefixes {
   150  		for _, ref := range refs {
   151  			if strings.HasPrefix(ref, pre) {
   152  				return ref
   153  			}
   154  		}
   155  	}
   156  	return ""
   157  }