github.com/khulnasoft-lab/tunnel-db@v0.0.0-20231117205118-74e1113bd007/pkg/vulnsrc/vulnerability/vulnerability.go (about)

     1  package vulnerability
     2  
     3  import (
     4  	"sort"
     5  	"strings"
     6  
     7  	"github.com/khulnasoft-lab/tunnel-db/pkg/db"
     8  	"github.com/khulnasoft-lab/tunnel-db/pkg/log"
     9  	"github.com/khulnasoft-lab/tunnel-db/pkg/types"
    10  )
    11  
    12  const (
    13  	rejectVulnerability = "** REJECT **"
    14  )
    15  
    16  var (
    17  	sources = []types.SourceID{NVD, RedHat, Debian, Ubuntu, Alpine, Amazon, OracleOVAL, SuseCVRF, Photon,
    18  		ArchLinux, Alma, Rocky, CBLMariner, RubySec, PhpSecurityAdvisories, NodejsSecurityWg, GHSA, GLAD, OSV, K8sVulnDB,
    19  	}
    20  )
    21  
    22  type Vulnerability struct {
    23  	dbc db.Operation
    24  }
    25  
    26  func New(dbc db.Operation) Vulnerability {
    27  	return Vulnerability{dbc: dbc}
    28  }
    29  
    30  func (v Vulnerability) GetDetails(vulnID string) map[types.SourceID]types.VulnerabilityDetail {
    31  	details, err := v.dbc.GetVulnerabilityDetail(vulnID)
    32  	if err != nil {
    33  		log.Logger.Warnf("Failed to get vulnerability detail: %s", err)
    34  		return nil
    35  	} else if len(details) == 0 {
    36  		return nil
    37  	}
    38  	return details
    39  }
    40  
    41  func (Vulnerability) IsRejected(details map[types.SourceID]types.VulnerabilityDetail) bool {
    42  	return getRejectedStatus(details)
    43  }
    44  
    45  func (Vulnerability) Normalize(details map[types.SourceID]types.VulnerabilityDetail) types.Vulnerability {
    46  	return types.Vulnerability{
    47  		Title:            getTitle(details),
    48  		Description:      getDescription(details),
    49  		Severity:         getSeverity(details).String(), // TODO: We have to keep this key until we deprecate
    50  		CweIDs:           getCweIDs(details),
    51  		VendorSeverity:   getVendorSeverity(details),
    52  		CVSS:             getCVSS(details),
    53  		References:       getReferences(details),
    54  		PublishedDate:    details[NVD].PublishedDate,
    55  		LastModifiedDate: details[NVD].LastModifiedDate,
    56  	}
    57  }
    58  
    59  func getCVSS(details map[types.SourceID]types.VulnerabilityDetail) types.VendorCVSS {
    60  	vc := make(types.VendorCVSS)
    61  	for vendor, detail := range details {
    62  		if (detail.CvssVector == "" || detail.CvssScore == 0) && (detail.CvssVectorV3 == "" || detail.CvssScoreV3 == 0) {
    63  			continue
    64  		}
    65  		vc[vendor] = types.CVSS{
    66  			V2Vector: detail.CvssVector,
    67  			V3Vector: detail.CvssVectorV3,
    68  			V2Score:  detail.CvssScore,
    69  			V3Score:  detail.CvssScoreV3,
    70  		}
    71  	}
    72  	return vc
    73  }
    74  
    75  func getVendorSeverity(details map[types.SourceID]types.VulnerabilityDetail) types.VendorSeverity {
    76  	vs := make(types.VendorSeverity)
    77  	for vendor, detail := range details {
    78  		switch {
    79  		case detail.SeverityV3 != types.SeverityUnknown:
    80  			vs[vendor] = detail.SeverityV3
    81  		case detail.Severity != types.SeverityUnknown:
    82  			vs[vendor] = detail.Severity
    83  		case detail.CvssScoreV3 > 0:
    84  			vs[vendor] = scoreToSeverity(detail.CvssScoreV3)
    85  		case detail.CvssScore > 0:
    86  			vs[vendor] = scoreToSeverity(detail.CvssScore)
    87  		}
    88  	}
    89  	return vs
    90  }
    91  
    92  func getSeverity(details map[types.SourceID]types.VulnerabilityDetail) types.Severity {
    93  	for _, source := range sources {
    94  		switch d, ok := details[source]; {
    95  		case !ok:
    96  			continue
    97  		case d.CvssScoreV3 > 0:
    98  			return scoreToSeverity(d.CvssScoreV3)
    99  		case d.CvssScore > 0:
   100  			return scoreToSeverity(d.CvssScore)
   101  		case d.SeverityV3 != 0:
   102  			return d.SeverityV3
   103  		case d.Severity != 0:
   104  			return d.Severity
   105  		}
   106  	}
   107  	return types.SeverityUnknown
   108  }
   109  
   110  func getTitle(details map[types.SourceID]types.VulnerabilityDetail) string {
   111  	for _, source := range sources {
   112  		d, ok := details[source]
   113  		if !ok {
   114  			continue
   115  		}
   116  		if d.Title != "" {
   117  			return d.Title
   118  		}
   119  	}
   120  	return ""
   121  }
   122  
   123  func getDescription(details map[types.SourceID]types.VulnerabilityDetail) string {
   124  	for _, source := range sources {
   125  		d, ok := details[source]
   126  		if !ok {
   127  			continue
   128  		}
   129  		if d.Description != "" {
   130  			return d.Description
   131  		}
   132  	}
   133  	return ""
   134  }
   135  
   136  func getCweIDs(details map[types.SourceID]types.VulnerabilityDetail) []string {
   137  	for _, source := range sources {
   138  		d, ok := details[source]
   139  		if !ok {
   140  			continue
   141  		}
   142  		if len(d.CweIDs) != 0 {
   143  			return d.CweIDs
   144  		}
   145  	}
   146  	return nil
   147  }
   148  
   149  func getReferences(details map[types.SourceID]types.VulnerabilityDetail) []string {
   150  	references := map[string]struct{}{}
   151  	for _, source := range sources {
   152  		// Amazon contains unrelated references
   153  		if source == Amazon {
   154  			continue
   155  		}
   156  		d, ok := details[source]
   157  		if !ok {
   158  			continue
   159  		}
   160  		for _, ref := range d.References {
   161  			// e.g. "\nhttps://curl.haxx.se/docs/CVE-2019-5481.html\n    "
   162  			ref = strings.TrimSpace(ref)
   163  			for _, r := range strings.Split(ref, "\n") {
   164  				references[r] = struct{}{}
   165  			}
   166  		}
   167  	}
   168  	var refs []string
   169  	for ref := range references {
   170  		refs = append(refs, ref)
   171  	}
   172  	sort.Slice(refs, func(i, j int) bool {
   173  		return refs[i] < refs[j]
   174  	})
   175  	return refs
   176  }
   177  
   178  func getRejectedStatus(details map[types.SourceID]types.VulnerabilityDetail) bool {
   179  	for _, source := range sources {
   180  		d, ok := details[source]
   181  		if !ok {
   182  			continue
   183  		}
   184  		if strings.Contains(d.Description, rejectVulnerability) {
   185  			return true
   186  		}
   187  	}
   188  	return false
   189  }
   190  
   191  func scoreToSeverity(score float64) types.Severity {
   192  	switch {
   193  	case score >= 9.0:
   194  		return types.SeverityCritical
   195  	case score >= 7.0:
   196  		return types.SeverityHigh
   197  	case score >= 4.0:
   198  		return types.SeverityMedium
   199  	case score > 0.0:
   200  		return types.SeverityLow
   201  	default:
   202  		return types.SeverityUnknown
   203  	}
   204  }
   205  
   206  func NormalizePkgName(ecosystem types.Ecosystem, pkgName string) string {
   207  	if ecosystem == Pip {
   208  		// from https://www.python.org/dev/peps/pep-0426/#name
   209  		// All comparisons of distribution names MUST be case insensitive,
   210  		// and MUST consider hyphens and underscores to be equivalent.
   211  		pkgName = strings.ToLower(pkgName)
   212  		pkgName = strings.ReplaceAll(pkgName, "_", "-")
   213  	} else if ecosystem == Swift {
   214  		// Swift uses `https://github.com/<author>/<package>.git format
   215  		// But part Swift advisories doesn't `https://` prefix or `.git` suffix
   216  		// e.g. https://github.com/github/advisory-database/blob/76f65b0d0fdac39c8b0e834ab03562b5f80d5b27/advisories/github-reviewed/2023/06/GHSA-qvxg-wjxc-r4gg/GHSA-qvxg-wjxc-r4gg.json#L21
   217  		// https://github.com/github/advisory-database/blob/76f65b0d0fdac39c8b0e834ab03562b5f80d5b27/advisories/github-reviewed/2023/07/GHSA-jq43-q8mx-r7mq/GHSA-jq43-q8mx-r7mq.json#L21
   218  		// Remove them to fit the same format
   219  		pkgName = strings.TrimPrefix(pkgName, "https://")
   220  		pkgName = strings.TrimSuffix(pkgName, ".git")
   221  	} else if ecosystem != NuGet && ecosystem != Go && ecosystem != Cocoapods {
   222  		// Nuget and Cocoapods are case-sensitive, Go has case-sensitive packages
   223  		pkgName = strings.ToLower(pkgName)
   224  	}
   225  	return pkgName
   226  }