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

     1  package aws
     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  // Amazon Linux keeps a consistent os-release file between
    16  // major releases.
    17  // All tested images on docker hub contained os-release file
    18  //
    19  // ScannerVersion increased to 2 when adding AL2023
    20  
    21  const (
    22  	scannerName    = "aws"
    23  	scannerVersion = "2"
    24  	scannerKind    = "distribution"
    25  )
    26  
    27  type awsRegex struct {
    28  	release Release
    29  	regexp  *regexp.Regexp
    30  }
    31  
    32  var awsRegexes = []awsRegex{
    33  	{
    34  		release: AmazonLinux1,
    35  		regexp:  regexp.MustCompile(`CPE_NAME="cpe:/o:amazon:linux:201.\.0[39]:ga"`),
    36  	},
    37  	{
    38  		release: AmazonLinux2,
    39  		regexp:  regexp.MustCompile(`CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"`),
    40  	},
    41  	{
    42  		release: AmazonLinux2023,
    43  		regexp:  regexp.MustCompile(`CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2023"`),
    44  	},
    45  }
    46  
    47  const osReleasePath = `etc/os-release`
    48  
    49  var (
    50  	_ indexer.DistributionScanner = (*DistributionScanner)(nil)
    51  	_ indexer.VersionedScanner    = (*DistributionScanner)(nil)
    52  )
    53  
    54  // DistributionScanner attempts to discover if a layer
    55  // displays characteristics of a AWS distribution
    56  type DistributionScanner struct{}
    57  
    58  // Name implements scanner.VersionedScanner.
    59  func (*DistributionScanner) Name() string { return scannerName }
    60  
    61  // Version implements scanner.VersionedScanner.
    62  func (*DistributionScanner) Version() string { return scannerVersion }
    63  
    64  // Kind implements scanner.VersionedScanner.
    65  func (*DistributionScanner) Kind() string { return scannerKind }
    66  
    67  // Scan will inspect the layer for an os-release or lsb-release file
    68  // and perform a regex match for keywords indicating the associated AWS release
    69  //
    70  // If neither file is found a (nil,nil) is returned.
    71  // If the files are found but all regexp fail to match an empty slice is returned.
    72  func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Distribution, error) {
    73  	defer trace.StartRegion(ctx, "Scanner.Scan").End()
    74  	ctx = zlog.ContextWithValues(ctx,
    75  		"component", "aws_dist_scanner",
    76  		"name", ds.Name(),
    77  		"version", ds.Version(),
    78  		"kind", ds.Kind(),
    79  		"layer", l.Hash.String())
    80  	zlog.Debug(ctx).Msg("start")
    81  	defer zlog.Debug(ctx).Msg("done")
    82  	files, err := l.Files(osReleasePath)
    83  	if err != nil {
    84  		zlog.Debug(ctx).Msg("didn't find an os-release")
    85  		return nil, nil
    86  	}
    87  	for _, buff := range files {
    88  		dist := ds.parse(buff)
    89  		if dist != nil {
    90  			return []*claircore.Distribution{dist}, nil
    91  		}
    92  	}
    93  	return []*claircore.Distribution{}, nil
    94  }
    95  
    96  // parse attempts to match all AWS release regexp and returns the associated
    97  // distribution if it exists.
    98  //
    99  // separated into its own method to aid testing.
   100  func (ds *DistributionScanner) parse(buff *bytes.Buffer) *claircore.Distribution {
   101  	for _, ur := range awsRegexes {
   102  		if ur.regexp.Match(buff.Bytes()) {
   103  			dist := releaseToDist(ur.release)
   104  			return dist
   105  		}
   106  	}
   107  	return nil
   108  }