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

     1  package ubuntu
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"path/filepath"
     9  
    10  	bolt "go.etcd.io/bbolt"
    11  	"golang.org/x/xerrors"
    12  
    13  	"github.com/khulnasoft-lab/tunnel-db/pkg/db"
    14  	"github.com/khulnasoft-lab/tunnel-db/pkg/types"
    15  	"github.com/khulnasoft-lab/tunnel-db/pkg/utils"
    16  	"github.com/khulnasoft-lab/tunnel-db/pkg/utils/strings"
    17  	"github.com/khulnasoft-lab/tunnel-db/pkg/vulnsrc/vulnerability"
    18  )
    19  
    20  const (
    21  	ubuntuDir      = "ubuntu"
    22  	platformFormat = "ubuntu %s"
    23  )
    24  
    25  var (
    26  	targetStatuses        = []string{"needed", "deferred", "released"}
    27  	UbuntuReleasesMapping = map[string]string{
    28  		"precise": "12.04",
    29  		"quantal": "12.10",
    30  		"raring":  "13.04",
    31  		"saucy":   "13.10",
    32  		"trusty":  "14.04",
    33  		"utopic":  "14.10",
    34  		"vivid":   "15.04",
    35  		"wily":    "15.10",
    36  		"xenial":  "16.04",
    37  		"yakkety": "16.10",
    38  		"zesty":   "17.04",
    39  		"artful":  "17.10",
    40  		"bionic":  "18.04",
    41  		"cosmic":  "18.10",
    42  		"disco":   "19.04",
    43  		"eoan":    "19.10",
    44  		"focal":   "20.04",
    45  		"groovy":  "20.10",
    46  		"hirsute": "21.04",
    47  		"impish":  "21.10",
    48  		"jammy":   "22.04",
    49  		"kinetic": "22.10",
    50  		"lunar":   "23.04",
    51  		"mantic":  "23.10",
    52  		// ESM versions:
    53  		"precise/esm":      "12.04-ESM",
    54  		"trusty/esm":       "14.04-ESM",
    55  		"esm-infra/xenial": "16.04-ESM",
    56  	}
    57  
    58  	source = types.DataSource{
    59  		ID:   vulnerability.Ubuntu,
    60  		Name: "Ubuntu CVE Tracker",
    61  		URL:  "https://git.launchpad.net/ubuntu-cve-tracker",
    62  	}
    63  )
    64  
    65  type Option func(src *VulnSrc)
    66  
    67  func WithCustomPut(put db.CustomPut) Option {
    68  	return func(src *VulnSrc) {
    69  		src.put = put
    70  	}
    71  }
    72  
    73  type VulnSrc struct {
    74  	put db.CustomPut
    75  	dbc db.Operation
    76  }
    77  
    78  func NewVulnSrc(opts ...Option) VulnSrc {
    79  	src := VulnSrc{
    80  		put: defaultPut,
    81  		dbc: db.Config{},
    82  	}
    83  
    84  	for _, o := range opts {
    85  		o(&src)
    86  	}
    87  
    88  	return src
    89  }
    90  
    91  func (vs VulnSrc) Name() types.SourceID {
    92  	return source.ID
    93  }
    94  
    95  func (vs VulnSrc) Update(dir string) error {
    96  	rootDir := filepath.Join(dir, "vuln-list", ubuntuDir)
    97  	var cves []UbuntuCVE
    98  	err := utils.FileWalk(rootDir, func(r io.Reader, path string) error {
    99  		var cve UbuntuCVE
   100  		if err := json.NewDecoder(r).Decode(&cve); err != nil {
   101  			return xerrors.Errorf("failed to decode Ubuntu JSON: %w", err)
   102  		}
   103  		cves = append(cves, cve)
   104  		return nil
   105  	})
   106  	if err != nil {
   107  		return xerrors.Errorf("error in Ubuntu walk: %w", err)
   108  	}
   109  
   110  	if err = vs.save(cves); err != nil {
   111  		return xerrors.Errorf("error in Ubuntu save: %w", err)
   112  	}
   113  
   114  	return nil
   115  }
   116  
   117  func (vs VulnSrc) save(cves []UbuntuCVE) error {
   118  	log.Println("Saving Ubuntu DB")
   119  	err := vs.dbc.BatchUpdate(func(tx *bolt.Tx) error {
   120  		err := vs.commit(tx, cves)
   121  		if err != nil {
   122  			return err
   123  		}
   124  		return nil
   125  	})
   126  	if err != nil {
   127  		return xerrors.Errorf("error in batch update: %w", err)
   128  	}
   129  	return nil
   130  }
   131  
   132  func (vs VulnSrc) commit(tx *bolt.Tx, cves []UbuntuCVE) error {
   133  	for _, cve := range cves {
   134  		if err := vs.put(vs.dbc, tx, cve); err != nil {
   135  			return xerrors.Errorf("put error: %w", err)
   136  		}
   137  	}
   138  	return nil
   139  }
   140  
   141  func (vs VulnSrc) Get(release string, pkgName string) ([]types.Advisory, error) {
   142  	bucket := fmt.Sprintf(platformFormat, release)
   143  	advisories, err := vs.dbc.GetAdvisories(bucket, pkgName)
   144  	if err != nil {
   145  		return nil, xerrors.Errorf("failed to get Ubuntu advisories: %w", err)
   146  	}
   147  	return advisories, nil
   148  }
   149  
   150  func defaultPut(dbc db.Operation, tx *bolt.Tx, advisory interface{}) error {
   151  	cve, ok := advisory.(UbuntuCVE)
   152  	if !ok {
   153  		return xerrors.New("unknown type")
   154  	}
   155  
   156  	for packageName, patch := range cve.Patches {
   157  		pkgName := string(packageName)
   158  		for release, status := range patch {
   159  			if !strings.InSlice(status.Status, targetStatuses) {
   160  				continue
   161  			}
   162  			osVersion, ok := UbuntuReleasesMapping[string(release)]
   163  			if !ok {
   164  				continue
   165  			}
   166  			platformName := fmt.Sprintf(platformFormat, osVersion)
   167  			if err := dbc.PutDataSource(tx, platformName, source); err != nil {
   168  				return xerrors.Errorf("failed to put data source: %w", err)
   169  			}
   170  
   171  			adv := types.Advisory{}
   172  			if status.Status == "released" {
   173  				adv.FixedVersion = status.Note
   174  			}
   175  			if err := dbc.PutAdvisoryDetail(tx, cve.Candidate, pkgName, []string{platformName}, adv); err != nil {
   176  				return xerrors.Errorf("failed to save Ubuntu advisory: %w", err)
   177  			}
   178  
   179  			vuln := types.VulnerabilityDetail{
   180  				Severity:    SeverityFromPriority(cve.Priority),
   181  				References:  cve.References,
   182  				Description: cve.Description,
   183  			}
   184  			if err := dbc.PutVulnerabilityDetail(tx, cve.Candidate, source.ID, vuln); err != nil {
   185  				return xerrors.Errorf("failed to save Ubuntu vulnerability: %w", err)
   186  			}
   187  
   188  			// for optimization
   189  			if err := dbc.PutVulnerabilityID(tx, cve.Candidate); err != nil {
   190  				return xerrors.Errorf("failed to save the vulnerability ID: %w", err)
   191  			}
   192  		}
   193  	}
   194  
   195  	return nil
   196  }
   197  
   198  // SeverityFromPriority converts Ubuntu priority into Tunnel severity
   199  func SeverityFromPriority(priority string) types.Severity {
   200  	switch priority {
   201  	case "untriaged":
   202  		return types.SeverityUnknown
   203  	case "negligible", "low":
   204  		return types.SeverityLow
   205  	case "medium":
   206  		return types.SeverityMedium
   207  	case "high":
   208  		return types.SeverityHigh
   209  	case "critical":
   210  		return types.SeverityCritical
   211  	default:
   212  		return types.SeverityUnknown
   213  	}
   214  }