github.com/quay/claircore@v1.5.28/ruby/matcher.go (about)

     1  package ruby
     2  
     3  import (
     4  	"context"
     5  	"net/url"
     6  
     7  	"github.com/quay/zlog"
     8  
     9  	"github.com/quay/claircore"
    10  	"github.com/quay/claircore/libvuln/driver"
    11  )
    12  
    13  var _ driver.Matcher = (*Matcher)(nil)
    14  
    15  // Matcher attempts to correlate discovered ruby packages with reported
    16  // vulnerabilities.
    17  type Matcher struct{}
    18  
    19  // Name implements driver.Matcher.
    20  func (*Matcher) Name() string { return "ruby-gem" }
    21  
    22  // Filter implements driver.Matcher.
    23  func (*Matcher) Filter(record *claircore.IndexRecord) bool {
    24  	return record.Repository != nil && record.Repository.Name == repository
    25  }
    26  
    27  // Query implements driver.Matcher.
    28  func (*Matcher) Query() []driver.MatchConstraint {
    29  	return []driver.MatchConstraint{driver.RepositoryName}
    30  }
    31  
    32  // Vulnerable implements driver.Matcher.
    33  func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, vuln *claircore.Vulnerability) (bool, error) {
    34  	// TODO(ross): This is a common pattern for OSV vulnerabilities. This should be moved into
    35  	// a common place for all OSV vulnerability matchers.
    36  
    37  	if vuln.FixedInVersion == "" {
    38  		return true, nil
    39  	}
    40  
    41  	// Parse the package first. If it cannot be parsed, it cannot properly be analyzed for vulnerabilities.
    42  	rv, err := NewVersion(record.Package.Version)
    43  	if err != nil {
    44  		zlog.Warn(ctx).
    45  			Str("package", record.Package.Name).
    46  			Str("version", record.Package.Version).
    47  			Msg("unable to parse ruby gem package version")
    48  		return false, err
    49  	}
    50  
    51  	decodedVersions, err := url.ParseQuery(vuln.FixedInVersion)
    52  	if err != nil {
    53  		return false, err
    54  	}
    55  
    56  	introduced := decodedVersions.Get("introduced")
    57  	// If there is an introduced version, check if the package's version is lower.
    58  	if introduced != "" {
    59  		iv, err := NewVersion(introduced)
    60  		if err != nil {
    61  			zlog.Warn(ctx).
    62  				Str("package", vuln.Package.Name).
    63  				Str("version", introduced).
    64  				Msg("unable to parse ruby gem introduced version")
    65  			return false, err
    66  		}
    67  		// If the package's version is less than the introduced version, it's not vulnerable.
    68  		if rv.Compare(iv) < 0 {
    69  			return false, nil
    70  		}
    71  	}
    72  
    73  	fixedVersion := decodedVersions.Get("fixed")
    74  	lastAffected := decodedVersions.Get("lastAffected")
    75  	switch {
    76  	case fixedVersion != "":
    77  		fv, err := NewVersion(fixedVersion)
    78  		if err != nil {
    79  			zlog.Warn(ctx).
    80  				Str("package", vuln.Package.Name).
    81  				Str("version", fixedVersion).
    82  				Msg("unable to parse ruby gem fixed version")
    83  			return false, err
    84  		}
    85  		// The package is affected if its version is less than the fixed version.
    86  		return rv.Compare(fv) < 0, nil
    87  	case lastAffected != "":
    88  		la, err := NewVersion(lastAffected)
    89  		if err != nil {
    90  			zlog.Warn(ctx).
    91  				Str("package", vuln.Package.Name).
    92  				Str("version", lastAffected).
    93  				Msg("unable to parse ruby gem last_affected version")
    94  			return false, err
    95  		}
    96  		// The package is affected if its version is less than or equal to the last affected version.
    97  		return rv.Compare(la) <= 0, nil
    98  	}
    99  
   100  	// Just say the package is vulnerable, by default.
   101  	return true, nil
   102  }