github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/analyzer/repo/apk/apk.go (about)

     1  package apk
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"os"
     7  	"regexp"
     8  
     9  	"golang.org/x/exp/slices"
    10  	"golang.org/x/xerrors"
    11  
    12  	ver "github.com/aquasecurity/go-version/pkg/version"
    13  	"github.com/devseccon/trivy/pkg/fanal/analyzer"
    14  	"github.com/devseccon/trivy/pkg/fanal/types"
    15  )
    16  
    17  func init() {
    18  	analyzer.RegisterAnalyzer(&apkRepoAnalyzer{})
    19  }
    20  
    21  const version = 1
    22  const edgeVersion = "edge"
    23  
    24  var (
    25  	requiredFiles  = []string{"etc/apk/repositories"}
    26  	urlParseRegexp = regexp.MustCompile(`(https*|ftp)://[0-9A-Za-z.-]+/([A-Za-z]+)/v?([0-9A-Za-z_.-]+)/`)
    27  )
    28  
    29  type apkRepoAnalyzer struct{}
    30  
    31  func (a apkRepoAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
    32  	scanner := bufio.NewScanner(input.Content)
    33  	var osFamily types.OSType
    34  	var repoVer string
    35  	for scanner.Scan() {
    36  		line := scanner.Text()
    37  
    38  		m := urlParseRegexp.FindStringSubmatch(line)
    39  		if len(m) != 4 {
    40  			continue
    41  		}
    42  
    43  		newOSFamily := types.OSType(m[2])
    44  		newVersion := m[3]
    45  
    46  		// Find OS Family
    47  		if osFamily != "" && osFamily != newOSFamily {
    48  			return nil, xerrors.Errorf("mixing different distributions in etc/apk/repositories: %s != %s", osFamily, newOSFamily)
    49  		}
    50  		osFamily = newOSFamily
    51  
    52  		// Find max Release version
    53  		switch {
    54  		case repoVer == "":
    55  			repoVer = newVersion
    56  		case repoVer == edgeVersion || newVersion == edgeVersion:
    57  			repoVer = edgeVersion
    58  		default:
    59  			oldVer, err := ver.Parse(repoVer)
    60  			if err != nil {
    61  				continue
    62  			}
    63  			newVer, err := ver.Parse(newVersion)
    64  			if err != nil {
    65  				continue
    66  			}
    67  
    68  			// Take the maximum version in apk repositories
    69  			if newVer.GreaterThan(oldVer) {
    70  				repoVer = newVersion
    71  			}
    72  		}
    73  	}
    74  
    75  	// Currently, we support only Alpine Linux in apk repositories.
    76  	if osFamily != types.Alpine || repoVer == "" {
    77  		return nil, nil
    78  	}
    79  
    80  	return &analyzer.AnalysisResult{
    81  		Repository: &types.Repository{
    82  			Family:  osFamily,
    83  			Release: repoVer,
    84  		},
    85  	}, nil
    86  }
    87  
    88  func (a apkRepoAnalyzer) Required(filePath string, _ os.FileInfo) bool {
    89  	return slices.Contains(requiredFiles, filePath)
    90  }
    91  
    92  func (a apkRepoAnalyzer) Type() analyzer.Type {
    93  	return analyzer.TypeApkRepo
    94  }
    95  
    96  func (a apkRepoAnalyzer) Version() int {
    97  	return version
    98  }