github.com/quay/claircore@v1.5.28/rhel/parser.go (about)

     1  package rhel
     2  
     3  import (
     4  	"context"
     5  	"encoding/xml"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/quay/goval-parser/oval"
    10  	"github.com/quay/zlog"
    11  
    12  	"github.com/quay/claircore"
    13  	"github.com/quay/claircore/internal/xmlutil"
    14  	"github.com/quay/claircore/pkg/ovalutil"
    15  	"github.com/quay/claircore/rhel/internal/common"
    16  	"github.com/quay/claircore/toolkit/types/cpe"
    17  )
    18  
    19  // Parse implements [driver.Updater].
    20  //
    21  // Parse treats the data inside the provided io.ReadCloser as Red Hat
    22  // flavored OVAL XML. The distribution associated with vulnerabilities
    23  // is configured via the Updater. The repository associated with
    24  // vulnerabilies is based on the affected CPE list.
    25  func (u *Updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vulnerability, error) {
    26  	ctx = zlog.ContextWithValues(ctx, "component", "rhel/Updater.Parse")
    27  	zlog.Info(ctx).Msg("starting parse")
    28  	defer r.Close()
    29  	root := oval.Root{}
    30  	dec := xml.NewDecoder(r)
    31  	dec.CharsetReader = xmlutil.CharsetReader
    32  	if err := dec.Decode(&root); err != nil {
    33  		return nil, fmt.Errorf("rhel: unable to decode OVAL document: %w", err)
    34  	}
    35  	zlog.Debug(ctx).Msg("xml decoded")
    36  	protoVulns := func(def oval.Definition) ([]*claircore.Vulnerability, error) {
    37  		vs := []*claircore.Vulnerability{}
    38  
    39  		defType, err := ovalutil.GetDefinitionType(def)
    40  		if err != nil {
    41  			return nil, err
    42  		}
    43  		// Red Hat OVAL data include information about vulnerabilities,
    44  		// that actually don't affect the package in any way. Storing them
    45  		// would increase number of records in DB without adding any value.
    46  		if isSkippableDefinitionType(defType, u.ignoreUnpatched) {
    47  			return vs, nil
    48  		}
    49  
    50  		for _, affected := range def.Advisory.AffectedCPEList {
    51  			// Work around having empty entries. This seems to be some issue
    52  			// with the tool used to produce the database but only seems to
    53  			// appear sometimes, like RHSA-2018:3140 in the rhel-7-alt database.
    54  			if affected == "" {
    55  				continue
    56  			}
    57  
    58  			wfn, err := cpe.Unbind(affected)
    59  			if err != nil {
    60  				return nil, err
    61  			}
    62  			v := &claircore.Vulnerability{
    63  				Updater:            u.Name(),
    64  				Name:               def.Title,
    65  				Description:        def.Description,
    66  				Issued:             def.Advisory.Issued.Date,
    67  				Links:              ovalutil.Links(def),
    68  				Severity:           def.Advisory.Severity,
    69  				NormalizedSeverity: common.NormalizeSeverity(def.Advisory.Severity),
    70  				Repo: &claircore.Repository{
    71  					Name: affected,
    72  					CPE:  wfn,
    73  					Key:  repositoryKey,
    74  				},
    75  				Dist: u.dist,
    76  			}
    77  			vs = append(vs, v)
    78  		}
    79  		return vs, nil
    80  	}
    81  	vulns, err := ovalutil.RPMDefsToVulns(ctx, &root, protoVulns)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	return vulns, nil
    86  }
    87  
    88  func isSkippableDefinitionType(defType ovalutil.DefinitionType, ignoreUnpatched bool) bool {
    89  	return defType == ovalutil.UnaffectedDefinition ||
    90  		defType == ovalutil.NoneDefinition ||
    91  		(ignoreUnpatched && defType == ovalutil.CVEDefinition)
    92  }