mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
read relative etc/apk/repositories for alpine version when no OS provided (#1615)
Signed-off-by: Avi Deitcher <avi@deitcher.net>
This commit is contained in:
parent
5f90d03718
commit
01230aa766
@ -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())
|
||||
})
|
||||
}
|
||||
|
||||
@ -3,7 +3,9 @@ package apkdb
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -20,11 +22,15 @@ import (
|
||||
// integrity check
|
||||
var _ generic.Parser = parseApkDB
|
||||
|
||||
var (
|
||||
repoRegex = regexp.MustCompile(`(?m)^https://.*\.alpinelinux\.org/alpine/v([^/]+)/([a-zA-Z0-9_]+)$`)
|
||||
)
|
||||
|
||||
// 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) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
var apks []pkg.ApkMetadata
|
||||
@ -101,6 +107,19 @@ 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 {
|
||||
// find the repositories file from the relative directory of the DB file
|
||||
releases := findReleases(resolver, reader.Location.RealPath)
|
||||
|
||||
if len(releases) > 0 {
|
||||
r = &releases[0]
|
||||
}
|
||||
}
|
||||
|
||||
pkgs := make([]pkg.Package, 0, len(apks))
|
||||
for _, apk := range apks {
|
||||
@ -110,6 +129,58 @@ func parseApkDB(_ source.FileResolver, env *generic.Environment, reader source.L
|
||||
return pkgs, discoverPackageDependencies(pkgs), nil
|
||||
}
|
||||
|
||||
func findReleases(resolver source.FileResolver, dbPath string) []linux.Release {
|
||||
if resolver == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
reposLocation := path.Clean(path.Join(path.Dir(dbPath), "../../../etc/apk/repositories"))
|
||||
locations, err := resolver.FilesByPath(reposLocation)
|
||||
if err != nil {
|
||||
log.Tracef("unable to find APK repositories file %q: %+v", reposLocation, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(locations) == 0 {
|
||||
return nil
|
||||
}
|
||||
location := locations[0]
|
||||
|
||||
reposReader, err := resolver.FileContentsByLocation(location)
|
||||
if err != nil {
|
||||
log.Tracef("unable to fetch contents for APK repositories file %q: %+v", reposLocation, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return parseReleasesFromAPKRepository(source.LocationReadCloser{
|
||||
Location: location,
|
||||
ReadCloser: reposReader,
|
||||
})
|
||||
}
|
||||
|
||||
func parseReleasesFromAPKRepository(reader source.LocationReadCloser) []linux.Release {
|
||||
var releases []linux.Release
|
||||
|
||||
reposB, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
log.Tracef("unable to read APK repositories file %q: %+v", reader.Location.RealPath, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
parts := repoRegex.FindAllStringSubmatch(string(reposB), -1)
|
||||
for _, part := range parts {
|
||||
if len(part) >= 3 {
|
||||
releases = append(releases, linux.Release{
|
||||
Name: "Alpine Linux",
|
||||
ID: "alpine",
|
||||
VersionID: part[1],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return releases
|
||||
}
|
||||
|
||||
func parseApkField(line string) *apkField {
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
package apkdb
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@ -1186,3 +1188,49 @@ func Test_stripVersionSpecifier(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseReleasesFromAPKRepository(t *testing.T) {
|
||||
tests := []struct {
|
||||
repos string
|
||||
want []linux.Release
|
||||
desc string
|
||||
}{
|
||||
{
|
||||
"https://foo.alpinelinux.org/alpine/v3.14/main",
|
||||
[]linux.Release{
|
||||
{Name: "Alpine Linux", ID: "alpine", VersionID: "3.14"},
|
||||
},
|
||||
"single repo",
|
||||
},
|
||||
{
|
||||
`https://foo.alpinelinux.org/alpine/v3.14/main
|
||||
https://foo.alpinelinux.org/alpine/v3.14/community`,
|
||||
[]linux.Release{
|
||||
{Name: "Alpine Linux", ID: "alpine", VersionID: "3.14"},
|
||||
{Name: "Alpine Linux", ID: "alpine", VersionID: "3.14"},
|
||||
},
|
||||
"multiple repos",
|
||||
},
|
||||
{
|
||||
``,
|
||||
nil,
|
||||
"empty",
|
||||
},
|
||||
{
|
||||
`https://foo.bar.org/alpine/v3.14/main
|
||||
https://foo.them.org/alpine/v3.14/community`,
|
||||
nil,
|
||||
"invalid repos",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
reposReader := io.NopCloser(strings.NewReader(tt.repos))
|
||||
got := parseReleasesFromAPKRepository(source.LocationReadCloser{
|
||||
Location: source.NewLocation("test"),
|
||||
ReadCloser: reposReader,
|
||||
})
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user