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().
|
pkgtest.NewCatalogTester().
|
||||||
FromDirectory(t, test.fixture).
|
FromDirectory(t, test.fixture).
|
||||||
ExpectsResolverContentQueries(test.expected).
|
ExpectsResolverContentQueries(test.expected).
|
||||||
|
IgnoreUnfulfilledPathResponses("etc/apk/repositories").
|
||||||
TestCataloger(t, NewApkdbCataloger())
|
TestCataloger(t, NewApkdbCataloger())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,9 @@ package apkdb
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -20,11 +22,15 @@ import (
|
|||||||
// integrity check
|
// integrity check
|
||||||
var _ generic.Parser = parseApkDB
|
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
|
// parseApkDB parses packages from a given APK installed DB file. For more
|
||||||
// information on specific fields, see https://wiki.alpinelinux.org/wiki/Apk_spec.
|
// information on specific fields, see https://wiki.alpinelinux.org/wiki/Apk_spec.
|
||||||
//
|
//
|
||||||
//nolint:funlen
|
//nolint:funlen,gocognit
|
||||||
func parseApkDB(_ source.FileResolver, env *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
func parseApkDB(resolver source.FileResolver, env *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
scanner := bufio.NewScanner(reader)
|
scanner := bufio.NewScanner(reader)
|
||||||
|
|
||||||
var apks []pkg.ApkMetadata
|
var apks []pkg.ApkMetadata
|
||||||
@ -101,6 +107,19 @@ func parseApkDB(_ source.FileResolver, env *generic.Environment, reader source.L
|
|||||||
if env != nil {
|
if env != nil {
|
||||||
r = env.LinuxRelease
|
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))
|
pkgs := make([]pkg.Package, 0, len(apks))
|
||||||
for _, apk := range apks {
|
for _, apk := range apks {
|
||||||
@ -110,6 +129,58 @@ func parseApkDB(_ source.FileResolver, env *generic.Environment, reader source.L
|
|||||||
return pkgs, discoverPackageDependencies(pkgs), nil
|
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 {
|
func parseApkField(line string) *apkField {
|
||||||
parts := strings.SplitN(line, ":", 2)
|
parts := strings.SplitN(line, ":", 2)
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
package apkdb
|
package apkdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"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