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

     1  package oracleoval
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	bolt "go.etcd.io/bbolt"
    12  	"golang.org/x/xerrors"
    13  
    14  	version "github.com/khulnasoft-lab/go-rpm-version"
    15  	"github.com/khulnasoft-lab/tunnel-db/pkg/db"
    16  	"github.com/khulnasoft-lab/tunnel-db/pkg/types"
    17  	"github.com/khulnasoft-lab/tunnel-db/pkg/utils"
    18  	ustrings "github.com/khulnasoft-lab/tunnel-db/pkg/utils/strings"
    19  	"github.com/khulnasoft-lab/tunnel-db/pkg/vulnsrc/vulnerability"
    20  )
    21  
    22  var (
    23  	// cat /etc/os-release ORACLE_BUGZILLA_PRODUCT="Oracle Linux 8"
    24  	platformFormat  = "Oracle Linux %s"
    25  	targetPlatforms = []string{"Oracle Linux 5", "Oracle Linux 6", "Oracle Linux 7", "Oracle Linux 8", "Oracle Linux 9"}
    26  	oracleDir       = filepath.Join("oval", "oracle")
    27  
    28  	source = types.DataSource{
    29  		ID:   vulnerability.OracleOVAL,
    30  		Name: "Oracle Linux OVAL definitions",
    31  		URL:  "https://linux.oracle.com/security/oval/",
    32  	}
    33  )
    34  
    35  type PutInput struct {
    36  	VulnID     string                             // CVE-ID or ELSA-ID
    37  	Vuln       types.VulnerabilityDetail          // vulnerability detail such as CVSS and description
    38  	Advisories map[AffectedPackage]types.Advisory // pkg => advisory
    39  	OVAL       OracleOVAL                         // for extensibility, not used in tunnel-db
    40  }
    41  
    42  type DB interface {
    43  	db.Operation
    44  	Put(*bolt.Tx, PutInput) error
    45  	Get(release, pkgName string) ([]types.Advisory, error)
    46  }
    47  
    48  type VulnSrc struct {
    49  	DB // Those who want to customize Tunnel DB can override put/get methods.
    50  }
    51  
    52  type Oracle struct {
    53  	db.Operation
    54  }
    55  
    56  func NewVulnSrc() *VulnSrc {
    57  	return &VulnSrc{
    58  		DB: &Oracle{Operation: db.Config{}},
    59  	}
    60  }
    61  
    62  func (vs *VulnSrc) Name() types.SourceID {
    63  	return source.ID
    64  }
    65  
    66  func (vs *VulnSrc) Update(dir string) error {
    67  	rootDir := filepath.Join(dir, "vuln-list", oracleDir)
    68  	ovals, err := vs.parse(rootDir)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	if err = vs.put(ovals); err != nil {
    73  		return xerrors.Errorf("error in Oracle Linux OVAL save: %w", err)
    74  	}
    75  
    76  	return nil
    77  }
    78  
    79  // Parse parses all the advisories from Alma Linux.
    80  // It is exported for those who want to customize tunnel-db.
    81  func (vs *VulnSrc) parse(rootDir string) ([]OracleOVAL, error) {
    82  	var ovals []OracleOVAL
    83  	err := utils.FileWalk(rootDir, func(r io.Reader, path string) error {
    84  		var oval OracleOVAL
    85  		if err := json.NewDecoder(r).Decode(&oval); err != nil {
    86  			return xerrors.Errorf("failed to decode Oracle Linux OVAL JSON: %w", err)
    87  		}
    88  		ovals = append(ovals, oval)
    89  		return nil
    90  	})
    91  	if err != nil {
    92  		return nil, xerrors.Errorf("error in Oracle Linux OVAL walk: %w", err)
    93  	}
    94  
    95  	return ovals, nil
    96  }
    97  
    98  func (vs *VulnSrc) put(ovals []OracleOVAL) error {
    99  	log.Println("Saving Oracle Linux OVAL")
   100  
   101  	err := vs.BatchUpdate(func(tx *bolt.Tx) error {
   102  		return vs.commit(tx, ovals)
   103  	})
   104  	if err != nil {
   105  		return xerrors.Errorf("error in batch update: %w", err)
   106  	}
   107  
   108  	return nil
   109  
   110  }
   111  
   112  func (vs *VulnSrc) commit(tx *bolt.Tx, ovals []OracleOVAL) error {
   113  	for _, oval := range ovals {
   114  		elsaID := strings.Split(oval.Title, ":")[0]
   115  
   116  		var vulnIDs []string
   117  		for _, cve := range oval.Cves {
   118  			vulnIDs = append(vulnIDs, cve.ID)
   119  		}
   120  		if len(vulnIDs) == 0 {
   121  			vulnIDs = append(vulnIDs, elsaID)
   122  		}
   123  
   124  		advisories := map[AffectedPackage]types.Advisory{}
   125  		affectedPkgs := walkOracle(oval.Criteria, "", []AffectedPackage{})
   126  		for _, affectedPkg := range affectedPkgs {
   127  			if affectedPkg.Package.Name == "" {
   128  				continue
   129  			}
   130  
   131  			platformName := affectedPkg.PlatformName()
   132  			if !ustrings.InSlice(platformName, targetPlatforms) {
   133  				continue
   134  			}
   135  
   136  			if err := vs.PutDataSource(tx, platformName, source); err != nil {
   137  				return xerrors.Errorf("failed to put data source: %w", err)
   138  			}
   139  
   140  			advisories[affectedPkg] = types.Advisory{
   141  				FixedVersion: affectedPkg.Package.FixedVersion,
   142  			}
   143  		}
   144  
   145  		var references []string
   146  		for _, ref := range oval.References {
   147  			references = append(references, ref.URI)
   148  		}
   149  
   150  		for _, vulnID := range vulnIDs {
   151  			vuln := types.VulnerabilityDetail{
   152  				Description: oval.Description,
   153  				References:  referencesFromContains(references, []string{elsaID, vulnID}),
   154  				Title:       oval.Title,
   155  				Severity:    severityFromThreat(oval.Severity),
   156  			}
   157  
   158  			err := vs.Put(tx, PutInput{
   159  				VulnID:     vulnID,
   160  				Vuln:       vuln,
   161  				Advisories: advisories,
   162  				OVAL:       oval,
   163  			})
   164  			if err != nil {
   165  				return xerrors.Errorf("db put error: %w", err)
   166  			}
   167  		}
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  func (o *Oracle) Put(tx *bolt.Tx, input PutInput) error {
   174  	if err := o.PutVulnerabilityDetail(tx, input.VulnID, source.ID, input.Vuln); err != nil {
   175  		return xerrors.Errorf("failed to save Oracle Linux OVAL vulnerability: %w", err)
   176  	}
   177  
   178  	// for optimization
   179  	if err := o.PutVulnerabilityID(tx, input.VulnID); err != nil {
   180  		return xerrors.Errorf("failed to save %s: %w", input.VulnID, err)
   181  	}
   182  
   183  	for pkg, advisory := range input.Advisories {
   184  		platformName := pkg.PlatformName()
   185  		if err := o.PutAdvisoryDetail(tx, input.VulnID, pkg.Package.Name, []string{platformName}, advisory); err != nil {
   186  			return xerrors.Errorf("failed to save Oracle Linux advisory: %w", err)
   187  		}
   188  	}
   189  	return nil
   190  }
   191  
   192  func (o *Oracle) Get(release string, pkgName string) ([]types.Advisory, error) {
   193  	bucket := fmt.Sprintf(platformFormat, release)
   194  	advisories, err := o.GetAdvisories(bucket, pkgName)
   195  	if err != nil {
   196  		return nil, xerrors.Errorf("failed to get Oracle Linux advisories: %w", err)
   197  	}
   198  	return advisories, nil
   199  }
   200  
   201  func walkOracle(cri Criteria, osVer string, pkgs []AffectedPackage) []AffectedPackage {
   202  	for _, c := range cri.Criterions {
   203  		if strings.HasPrefix(c.Comment, "Oracle Linux ") &&
   204  			strings.HasSuffix(c.Comment, " is installed") {
   205  			osVer = strings.TrimSuffix(strings.TrimPrefix(c.Comment, "Oracle Linux "), " is installed")
   206  		}
   207  		ss := strings.Split(c.Comment, " is earlier than ")
   208  		if len(ss) != 2 {
   209  			continue
   210  		}
   211  
   212  		pkgs = append(pkgs, AffectedPackage{
   213  			OSVer: osVer,
   214  			Package: Package{
   215  				Name:         ss[0],
   216  				FixedVersion: version.NewVersion(ss[1]).String(),
   217  			},
   218  		})
   219  	}
   220  
   221  	for _, c := range cri.Criterias {
   222  		pkgs = walkOracle(c, osVer, pkgs)
   223  	}
   224  	return pkgs
   225  }
   226  
   227  func referencesFromContains(sources []string, matches []string) []string {
   228  	var references []string
   229  	for _, s := range sources {
   230  		for _, m := range matches {
   231  			if strings.Contains(s, m) {
   232  				references = append(references, s)
   233  			}
   234  		}
   235  	}
   236  	return ustrings.Unique(references)
   237  }
   238  
   239  func severityFromThreat(sev string) types.Severity {
   240  	switch sev {
   241  	case "LOW":
   242  		return types.SeverityLow
   243  	case "MODERATE":
   244  		return types.SeverityMedium
   245  	case "IMPORTANT":
   246  		return types.SeverityHigh
   247  	case "CRITICAL":
   248  		return types.SeverityCritical
   249  	}
   250  	return types.SeverityUnknown
   251  }