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

     1  package redhat
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"path/filepath"
     9  	"strconv"
    10  	"strings"
    11  
    12  	bolt "go.etcd.io/bbolt"
    13  	"golang.org/x/text/cases"
    14  	"golang.org/x/text/language"
    15  	"golang.org/x/xerrors"
    16  
    17  	"github.com/khulnasoft-lab/tunnel-db/pkg/db"
    18  	"github.com/khulnasoft-lab/tunnel-db/pkg/types"
    19  	"github.com/khulnasoft-lab/tunnel-db/pkg/utils"
    20  	"github.com/khulnasoft-lab/tunnel-db/pkg/vulnsrc/vulnerability"
    21  )
    22  
    23  const (
    24  	vulnListDir = "vuln-list-redhat"
    25  	apiDir      = "api"
    26  
    27  	resourceURL = "https://access.redhat.com/security/cve/%s"
    28  )
    29  
    30  type VulnSrc struct {
    31  	dbc db.Operation
    32  }
    33  
    34  func NewVulnSrc() VulnSrc {
    35  	return VulnSrc{
    36  		dbc: db.Config{},
    37  	}
    38  }
    39  
    40  func (vs VulnSrc) Name() types.SourceID {
    41  	return vulnerability.RedHat
    42  }
    43  
    44  func (vs VulnSrc) Update(dir string) error {
    45  	rootDir := filepath.Join(dir, vulnListDir, apiDir)
    46  
    47  	var cves []RedhatCVE
    48  	err := utils.FileWalk(rootDir, func(r io.Reader, _ string) error {
    49  		content, err := io.ReadAll(r)
    50  		if err != nil {
    51  			return err
    52  		}
    53  		cve := RedhatCVE{}
    54  		if err = json.Unmarshal(content, &cve); err != nil {
    55  			return xerrors.Errorf("failed to decode RedHat JSON: %w", err)
    56  		}
    57  		switch cve.TempAffectedRelease.(type) {
    58  		case []interface{}:
    59  			var ar RedhatCVEAffectedReleaseArray
    60  			if err = json.Unmarshal(content, &ar); err != nil {
    61  				return xerrors.Errorf("unknown affected_release type: %w", err)
    62  			}
    63  			cve.AffectedRelease = ar.AffectedRelease
    64  		case map[string]interface{}:
    65  			var ar RedhatCVEAffectedReleaseObject
    66  			if err = json.Unmarshal(content, &ar); err != nil {
    67  				return xerrors.Errorf("unknown affected_release type: %w", err)
    68  			}
    69  			cve.AffectedRelease = []RedhatAffectedRelease{ar.AffectedRelease}
    70  		case nil:
    71  		default:
    72  			return xerrors.New("unknown affected_release type")
    73  		}
    74  
    75  		switch cve.TempPackageState.(type) {
    76  		case []interface{}:
    77  			var ps RedhatCVEPackageStateArray
    78  			if err = json.Unmarshal(content, &ps); err != nil {
    79  				return xerrors.Errorf("unknown package_state type: %w", err)
    80  			}
    81  			cve.PackageState = ps.PackageState
    82  		case map[string]interface{}:
    83  			var ps RedhatCVEPackageStateObject
    84  			if err = json.Unmarshal(content, &ps); err != nil {
    85  				return xerrors.Errorf("unknown package_state type: %w", err)
    86  			}
    87  			cve.PackageState = []RedhatPackageState{ps.PackageState}
    88  		case nil:
    89  		default:
    90  			return xerrors.New("unknown package_state type")
    91  		}
    92  		cves = append(cves, cve)
    93  		return nil
    94  	})
    95  	if err != nil {
    96  		return xerrors.Errorf("error in Red Hat walk: %w", err)
    97  	}
    98  
    99  	if err = vs.save(cves); err != nil {
   100  		return xerrors.Errorf("error in Red Hat save: %w", err)
   101  	}
   102  
   103  	return nil
   104  }
   105  
   106  func (vs VulnSrc) save(cves []RedhatCVE) error {
   107  	log.Println("Saving Red Hat DB")
   108  	err := vs.dbc.BatchUpdate(func(tx *bolt.Tx) error {
   109  		return vs.commit(tx, cves)
   110  	})
   111  	if err != nil {
   112  		return xerrors.Errorf("failed batch update: %w", err)
   113  	}
   114  	return nil
   115  }
   116  
   117  func (vs VulnSrc) commit(tx *bolt.Tx, cves []RedhatCVE) error {
   118  	for _, cve := range cves {
   119  		if err := vs.putVulnerabilityDetail(tx, cve); err != nil {
   120  			return err
   121  		}
   122  	}
   123  
   124  	return nil
   125  }
   126  
   127  func (vs VulnSrc) putVulnerabilityDetail(tx *bolt.Tx, cve RedhatCVE) error {
   128  	cvssScore, _ := strconv.ParseFloat(cve.Cvss.CvssBaseScore, 64)
   129  	cvss3Score, _ := strconv.ParseFloat(cve.Cvss3.Cvss3BaseScore, 64)
   130  	title := strings.TrimPrefix(strings.TrimSpace(cve.Bugzilla.Description), cve.Name)
   131  	references := append(cve.References, fmt.Sprintf(resourceURL, cve.Name))
   132  
   133  	vuln := types.VulnerabilityDetail{
   134  		CvssScore:    cvssScore,
   135  		CvssVector:   cve.Cvss.CvssScoringVector,
   136  		CvssScoreV3:  cvss3Score,
   137  		CvssVectorV3: cve.Cvss3.Cvss3ScoringVector,
   138  		Severity:     severityFromThreat(cve.ThreatSeverity),
   139  		References:   references,
   140  		Title:        strings.TrimSpace(title),
   141  		Description:  strings.TrimSpace(strings.Join(cve.Details, "")),
   142  	}
   143  	if err := vs.dbc.PutVulnerabilityDetail(tx, cve.Name, vulnerability.RedHat, vuln); err != nil {
   144  		return xerrors.Errorf("failed to save Red Hat vulnerability: %w", err)
   145  	}
   146  
   147  	// for optimization
   148  	if err := vs.dbc.PutVulnerabilityID(tx, cve.Name); err != nil {
   149  		return xerrors.Errorf("failed to save the vulnerability ID: %w", err)
   150  	}
   151  	return nil
   152  }
   153  
   154  func severityFromThreat(sev string) types.Severity {
   155  	severity := cases.Title(language.English).String(sev)
   156  	switch severity {
   157  	case "Low":
   158  		return types.SeverityLow
   159  	case "Moderate":
   160  		return types.SeverityMedium
   161  	case "Important":
   162  		return types.SeverityHigh
   163  	case "Critical":
   164  		return types.SeverityCritical
   165  	}
   166  	return types.SeverityUnknown
   167  }