From 7fe6b6dec546ef94bb4eccfa33714c0be850530c Mon Sep 17 00:00:00 2001 From: Avi Deitcher Date: Thu, 23 Feb 2023 21:43:14 +0200 Subject: [PATCH] read relative etc/apk/repositories for alpine version when no OS provided Signed-off-by: Avi Deitcher --- syft/pkg/cataloger/apkdb/cataloger_test.go | 1 + syft/pkg/cataloger/apkdb/parse_apk_db.go | 50 +++++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/syft/pkg/cataloger/apkdb/cataloger_test.go b/syft/pkg/cataloger/apkdb/cataloger_test.go index 5a29607913a6..9be92209bd60 100644 --- a/syft/pkg/cataloger/apkdb/cataloger_test.go +++ b/syft/pkg/cataloger/apkdb/cataloger_test.go @@ -24,6 +24,7 @@ func TestCataloger_Globs(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). ExpectsResolverContentQueries(test.expected). + IgnoreUnfulfilledPathResponses("etc/apk/repositories"). TestCataloger(t, NewApkdbCataloger()) }) } diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db.go b/syft/pkg/cataloger/apkdb/parse_apk_db.go index f83b7bb521f6..fa78e655f52d 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db.go @@ -3,7 +3,9 @@ package apkdb import ( "bufio" "fmt" + "io" "path" + "regexp" "strconv" "strings" @@ -20,11 +22,47 @@ import ( // integrity check var _ generic.Parser = parseApkDB +var ( + repoRegex = regexp.MustCompile(`(?m)^https://.*\.alpinelinux\.org/alpine/v([^\/]+)/([a-zA-Z0-9_]+)$`) + newlineSplitRegex = regexp.MustCompile(`[\r \s\n]+`) +) + +func init() { + newlineSplitRegex.Longest() +} + // parseApkDB parses packages from a given APK installed DB file. For more // information on specific fields, see https://wiki.alpinelinux.org/wiki/Apk_spec. // -//nolint:funlen -func parseApkDB(_ source.FileResolver, env *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { +//nolint:funlen,gocognit +func parseApkDB(resolver source.FileResolver, env *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + // find the repositories file from the the relative directory of the DB file + var repos []linux.Release + if resolver != nil { + reposLocation := path.Clean(path.Join(path.Dir(reader.Location.RealPath), "../../../etc/apk/repositories")) + paths, err := resolver.FilesByPath(reposLocation) + if err != nil { + log.Debugf("unable to find repositories file at %s: %w", err) + } + if err == nil && len(paths) > 0 { + reposContent, err := resolver.FileContentsByLocation(paths[0]) + if err == nil { + reposB, err := io.ReadAll(reposContent) + if err == nil { + parts := repoRegex.FindAllStringSubmatch(string(reposB), -1) + for _, part := range parts { + if len(part) >= 3 { + repos = append(repos, linux.Release{ + Name: "Alpine Linux", + ID: "alpine", + VersionID: part[1], + }) + } + } + } + } + } + } scanner := bufio.NewScanner(reader) var apks []pkg.ApkMetadata @@ -101,6 +139,14 @@ func parseApkDB(_ source.FileResolver, env *generic.Environment, reader source.L if env != nil { r = env.LinuxRelease } + // this is somewhat ugly, but better than completely failing when we can't find the release, + // e.g. embedded deeper in the tree, like containers or chroots. + // but we now have no way of handling different repository sources. On the other hand, + // we never could before this. At least now, we can handle some. + // This should get fixed with https://gitlab.alpinelinux.org/alpine/apk-tools/-/issues/10875 + if r == nil && len(repos) > 0 { + r = &repos[0] + } pkgs := make([]pkg.Package, 0, len(apks)) for _, apk := range apks {