github.com/quay/claircore@v1.5.28/suse/distributionscanner.go (about)

     1  package suse
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"regexp"
     7  	"runtime/trace"
     8  
     9  	"github.com/quay/zlog"
    10  
    11  	"github.com/quay/claircore"
    12  	"github.com/quay/claircore/indexer"
    13  )
    14  
    15  // Suse Enterprise Server has service pack releases however their security database files are bundled together
    16  // by major version. for example `SUSE Linux Enterprise Server 15 (all Service Packs) - suse.linux.enterprise.server.15.xml`
    17  // we choose to normalize detected distributions into major releases and parse vulnerabilities by major release versions.
    18  //
    19  // Suse Leap has well defined sub releases and their sec db's match up fine.
    20  
    21  const (
    22  	scannerName    = "suse"
    23  	scannerVersion = "v0.0.1"
    24  	scannerKind    = "distribution"
    25  )
    26  
    27  const (
    28  	osReleasePath   = `etc/os-release`
    29  	suseReleasePath = `etc/SuSE-release`
    30  )
    31  
    32  type suseRegex struct {
    33  	release Release
    34  	regexp  *regexp.Regexp
    35  }
    36  
    37  var suseRegexes = []suseRegex{
    38  	{
    39  		release: EnterpriseServer15,
    40  		// regex for /etc/issue
    41  		regexp: regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 15`),
    42  	},
    43  	{
    44  		release: EnterpriseServer12,
    45  		regexp:  regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 12`),
    46  	},
    47  	{
    48  		release: EnterpriseServer11,
    49  		regexp:  regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 11`),
    50  	},
    51  	{
    52  		release: Leap151,
    53  		regexp:  regexp.MustCompile(`(?i)openSUSE Leap 15.1`),
    54  	},
    55  	{
    56  		release: Leap150,
    57  		regexp:  regexp.MustCompile(`(?i)openSUSE Leap 15.0`),
    58  	},
    59  	{
    60  		release: Leap423,
    61  		regexp:  regexp.MustCompile(`(?i)openSUSE Leap 42.3`),
    62  	},
    63  }
    64  
    65  var (
    66  	_ indexer.DistributionScanner = (*DistributionScanner)(nil)
    67  	_ indexer.VersionedScanner    = (*DistributionScanner)(nil)
    68  )
    69  
    70  // DistributionScanner attempts to discover if a layer
    71  // displays characteristics of a Suse distribution
    72  type DistributionScanner struct{}
    73  
    74  // Name implements scanner.VersionedScanner.
    75  func (*DistributionScanner) Name() string { return scannerName }
    76  
    77  // Version implements scanner.VersionedScanner.
    78  func (*DistributionScanner) Version() string { return scannerVersion }
    79  
    80  // Kind implements scanner.VersionedScanner.
    81  func (*DistributionScanner) Kind() string { return scannerKind }
    82  
    83  // Scan will inspect the layer for an os-release or lsb-release file
    84  // and perform a regex match for keywords indicating the associated Suse release
    85  //
    86  // If neither file is found a (nil,nil) is returned.
    87  // If the files are found but all regexp fail to match an empty slice is returned.
    88  func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Distribution, error) {
    89  	defer trace.StartRegion(ctx, "Scanner.Scan").End()
    90  	ctx = zlog.ContextWithValues(ctx,
    91  		"component", "suse/DistributionScanner.Scan",
    92  		"version", ds.Version(),
    93  		"layer", l.Hash.String())
    94  	zlog.Debug(ctx).Msg("start")
    95  	defer zlog.Debug(ctx).Msg("done")
    96  	files, err := l.Files(osReleasePath, suseReleasePath)
    97  	if err != nil {
    98  		zlog.Debug(ctx).Msg("didn't find an os-release or SuSE-release")
    99  		return nil, nil
   100  	}
   101  	for _, buff := range files {
   102  		dist := ds.parse(buff)
   103  		if dist != nil {
   104  			return []*claircore.Distribution{dist}, nil
   105  		}
   106  	}
   107  	return []*claircore.Distribution{}, nil
   108  }
   109  
   110  // parse attempts to match all SUSE release regexp and returns the associated
   111  // distribution if it exists.
   112  //
   113  // separated into its own method to aid testing.
   114  func (ds *DistributionScanner) parse(buff *bytes.Buffer) *claircore.Distribution {
   115  	for _, ur := range suseRegexes {
   116  		if ur.regexp.Match(buff.Bytes()) {
   117  			return releaseToDist(ur.release)
   118  		}
   119  	}
   120  	return nil
   121  }