mirror of
https://github.com/anchore/syft.git
synced 2026-11-05 08:56:41 +01:00
Add abstraction for adding relationships from package cataloger results (#2853)
* add internal dependency resolver Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * refactor dependency relationship resolution to common object Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * replace cataloger decorator with generic processor Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * refactor resolver to be a single function Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * use common dependency specifier for debian Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * use common dependency specifier for arch Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * use common dependency specifier for alpine Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * allow for generic pkg and rel assertions in testpkg helper Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * do not allow for empty results Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * move stable deduplicate comment Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * remove relationship resolver type Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
@@ -6,10 +6,12 @@ package alpine
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/dependency"
|
||||
)
|
||||
|
||||
// NewDBCataloger returns a new cataloger object initialized for Alpine package DB flat-file stores.
|
||||
func NewDBCataloger() pkg.Cataloger {
|
||||
return generic.NewCataloger("apk-db-cataloger").
|
||||
WithParserByGlobs(parseApkDB, pkg.ApkDBGlob)
|
||||
WithParserByGlobs(parseApkDB, pkg.ApkDBGlob).
|
||||
WithProcessors(dependency.Processor(dbEntryDependencySpecifier))
|
||||
}
|
||||
|
||||
@@ -3,9 +3,239 @@ package alpine
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
func TestApkDBCataloger(t *testing.T) {
|
||||
dbLocation := file.NewLocation("lib/apk/db/installed")
|
||||
|
||||
bashPkg := pkg.Package{
|
||||
Name: "bash",
|
||||
Version: "5.2.21-r0",
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL-3.0-or-later", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "bash",
|
||||
OriginPackage: "bash",
|
||||
Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
|
||||
Version: "5.2.21-r0",
|
||||
Architecture: "x86_64",
|
||||
URL: "https://www.gnu.org/software/bash/bash.html",
|
||||
Description: "The GNU Bourne Again shell",
|
||||
Size: 448728,
|
||||
InstalledSize: 1396736,
|
||||
Dependencies: []string{
|
||||
"/bin/sh", "so:libc.musl-x86_64.so.1", "so:libreadline.so.8",
|
||||
},
|
||||
Provides: []string{
|
||||
"cmd:bash=5.2.21-r0",
|
||||
},
|
||||
// note: files not provided and not under test
|
||||
},
|
||||
}
|
||||
|
||||
busyboxBinshPkg := pkg.Package{
|
||||
Name: "busybox-binsh",
|
||||
Version: "1.36.1-r15",
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL-2.0-only", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "busybox-binsh",
|
||||
OriginPackage: "busybox",
|
||||
Maintainer: "Sören Tempel <soeren+alpine@soeren-tempel.net>",
|
||||
Version: "1.36.1-r15",
|
||||
Architecture: "x86_64",
|
||||
URL: "https://busybox.net/",
|
||||
Description: "busybox ash /bin/sh",
|
||||
Size: 1543,
|
||||
InstalledSize: 8192,
|
||||
Dependencies: []string{
|
||||
"busybox=1.36.1-r15",
|
||||
},
|
||||
Provides: []string{
|
||||
"/bin/sh", "cmd:sh=1.36.1-r15",
|
||||
},
|
||||
// note: files not provided and not under test
|
||||
},
|
||||
}
|
||||
|
||||
muslPkg := pkg.Package{
|
||||
Name: "musl",
|
||||
Version: "1.2.4_git20230717-r4",
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "musl",
|
||||
OriginPackage: "musl",
|
||||
Maintainer: "Timo Teräs <timo.teras@iki.fi>",
|
||||
Version: "1.2.4_git20230717-r4",
|
||||
Architecture: "x86_64",
|
||||
URL: "https://musl.libc.org/",
|
||||
Description: "the musl c library (libc) implementation",
|
||||
Size: 407278,
|
||||
InstalledSize: 667648,
|
||||
Dependencies: []string{},
|
||||
Provides: []string{
|
||||
"so:libc.musl-x86_64.so.1=1",
|
||||
},
|
||||
// note: files not provided and not under test
|
||||
},
|
||||
}
|
||||
|
||||
readlinePkg := pkg.Package{
|
||||
Name: "readline",
|
||||
Version: "8.2.1-r2",
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL-2.0-or-later", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "readline",
|
||||
OriginPackage: "readline",
|
||||
Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
|
||||
Version: "8.2.1-r2",
|
||||
Architecture: "x86_64",
|
||||
URL: "https://tiswww.cwru.edu/php/chet/readline/rltop.html",
|
||||
Description: "GNU readline library",
|
||||
Size: 119878,
|
||||
InstalledSize: 303104,
|
||||
Dependencies: []string{
|
||||
"so:libc.musl-x86_64.so.1", "so:libncursesw.so.6",
|
||||
},
|
||||
Provides: []string{
|
||||
"so:libreadline.so.8=8.2",
|
||||
},
|
||||
// note: files not provided and not under test
|
||||
},
|
||||
}
|
||||
|
||||
expectedPkgs := []pkg.Package{
|
||||
bashPkg,
|
||||
busyboxBinshPkg,
|
||||
muslPkg,
|
||||
readlinePkg,
|
||||
}
|
||||
|
||||
// # apk info --depends bash
|
||||
// bash-5.2.21-r0 depends on:
|
||||
// /bin/sh
|
||||
// so:libc.musl-x86_64.so.1
|
||||
// so:libreadline.so.8
|
||||
//
|
||||
// # apk info --who-owns /bin/sh
|
||||
// /bin/sh is owned by busybox-binsh-1.36.1-r15
|
||||
//
|
||||
// # find / | grep musl
|
||||
// /lib/ld-musl-x86_64.so.1
|
||||
// /lib/libc.musl-x86_64.so.1
|
||||
//
|
||||
// # apk info --who-owns '/lib/libc.musl-x86_64.so.1'
|
||||
// /lib/libc.musl-x86_64.so.1 is owned by musl-1.2.4_git20230717-r4
|
||||
//
|
||||
// # find / | grep libreadline
|
||||
// /usr/lib/libreadline.so.8.2
|
||||
// /usr/lib/libreadline.so.8
|
||||
//
|
||||
// # apk info --who-owns '/usr/lib/libreadline.so.8'
|
||||
// /usr/lib/libreadline.so.8 is owned by readline-8.2.1-r2
|
||||
|
||||
expectedRelationships := []artifact.Relationship{
|
||||
{
|
||||
From: busyboxBinshPkg,
|
||||
To: bashPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
{
|
||||
From: readlinePkg,
|
||||
To: bashPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
{
|
||||
From: muslPkg,
|
||||
To: readlinePkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
{
|
||||
From: muslPkg,
|
||||
To: bashPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
}
|
||||
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, "test-fixtures/multiple-1").
|
||||
WithCompareOptions(cmpopts.IgnoreFields(pkg.ApkDBEntry{}, "Files", "GitCommit", "Checksum")).
|
||||
Expects(expectedPkgs, expectedRelationships).
|
||||
TestCataloger(t, NewDBCataloger())
|
||||
|
||||
}
|
||||
|
||||
func TestCatalogerDependencyTree(t *testing.T) {
|
||||
assertion := func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||
expected := map[string][]string{
|
||||
"alpine-baselayout": {"busybox", "alpine-baselayout-data", "musl"},
|
||||
"apk-tools": {"ca-certificates-bundle", "musl", "libcrypto1.1", "libssl1.1", "zlib"},
|
||||
"busybox": {"musl"},
|
||||
"libc-utils": {"musl-utils"},
|
||||
"libcrypto1.1": {"musl"},
|
||||
"libssl1.1": {"musl", "libcrypto1.1"},
|
||||
"musl-utils": {"scanelf", "musl"},
|
||||
"scanelf": {"musl"},
|
||||
"ssl_client": {"musl", "libcrypto1.1", "libssl1.1"},
|
||||
"zlib": {"musl"},
|
||||
}
|
||||
pkgsByID := make(map[artifact.ID]pkg.Package)
|
||||
for _, p := range pkgs {
|
||||
p.SetID()
|
||||
pkgsByID[p.ID()] = p
|
||||
}
|
||||
|
||||
actualDependencies := make(map[string][]string)
|
||||
|
||||
for _, r := range relationships {
|
||||
switch r.Type {
|
||||
case artifact.DependencyOfRelationship:
|
||||
to := pkgsByID[r.To.ID()]
|
||||
from := pkgsByID[r.From.ID()]
|
||||
actualDependencies[to.Name] = append(actualDependencies[to.Name], from.Name)
|
||||
default:
|
||||
t.Fatalf("unexpected relationship type: %+v", r.Type)
|
||||
}
|
||||
}
|
||||
|
||||
if d := cmp.Diff(expected, actualDependencies); d != "" {
|
||||
t.Fail()
|
||||
t.Log(d)
|
||||
}
|
||||
}
|
||||
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, "test-fixtures/multiple-2").
|
||||
ExpectsAssertion(assertion).
|
||||
TestCataloger(t, NewDBCataloger())
|
||||
|
||||
}
|
||||
|
||||
func TestCataloger_Globs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package alpine
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/dependency"
|
||||
)
|
||||
|
||||
var _ dependency.Specifier = dbEntryDependencySpecifier
|
||||
|
||||
func dbEntryDependencySpecifier(p pkg.Package) dependency.Specification {
|
||||
meta, ok := p.Metadata.(pkg.ApkDBEntry)
|
||||
if !ok {
|
||||
log.Tracef("cataloger failed to extract apk metadata for package %+v", p.Name)
|
||||
return dependency.Specification{}
|
||||
}
|
||||
|
||||
provides := []string{p.Name}
|
||||
provides = append(provides, stripVersionSpecifiers(meta.Provides)...)
|
||||
|
||||
return dependency.Specification{
|
||||
Provides: provides,
|
||||
Requires: stripVersionSpecifiers(meta.Dependencies),
|
||||
}
|
||||
}
|
||||
|
||||
func stripVersionSpecifiers(given []string) []string {
|
||||
var keys []string
|
||||
for _, key := range given {
|
||||
key = stripVersionSpecifier(key)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func stripVersionSpecifier(s string) string {
|
||||
// examples:
|
||||
// musl>=1 --> musl
|
||||
// cmd:scanelf=1.3.4-r0 --> cmd:scanelf
|
||||
|
||||
return strings.TrimSpace(internal.SplitAny(s, "<>=")[0])
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package alpine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/dependency"
|
||||
)
|
||||
|
||||
func Test_dbEntryDependencySpecifier(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
p pkg.Package
|
||||
want dependency.Specification
|
||||
}{
|
||||
{
|
||||
name: "keeps given values + package name",
|
||||
p: pkg.Package{
|
||||
Name: "package-c",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Provides: []string{"a-thing"},
|
||||
Dependencies: []string{"b-thing"},
|
||||
},
|
||||
},
|
||||
want: dependency.Specification{
|
||||
Provides: []string{"package-c", "a-thing"},
|
||||
Requires: []string{"b-thing"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "strip version specifiers",
|
||||
p: pkg.Package{
|
||||
Name: "package-a",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Provides: []string{"so:libc.musl-x86_64.so.1=1"},
|
||||
Dependencies: []string{"so:libc.musl-x86_64.so.2=2"},
|
||||
},
|
||||
},
|
||||
want: dependency.Specification{
|
||||
Provides: []string{"package-a", "so:libc.musl-x86_64.so.1"},
|
||||
Requires: []string{"so:libc.musl-x86_64.so.2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty dependency data entries",
|
||||
p: pkg.Package{
|
||||
Name: "package-a",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Provides: []string{""},
|
||||
Dependencies: []string{""},
|
||||
},
|
||||
},
|
||||
want: dependency.Specification{
|
||||
Provides: []string{"package-a"},
|
||||
Requires: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, dbEntryDependencySpecifier(tt.p))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_stripVersionSpecifier(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
version string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "empty expression",
|
||||
version: "",
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "no expression",
|
||||
version: "cmd:foo",
|
||||
want: "cmd:foo",
|
||||
},
|
||||
{
|
||||
name: "=",
|
||||
version: "cmd:scanelf=1.3.4-r0",
|
||||
want: "cmd:scanelf",
|
||||
},
|
||||
{
|
||||
name: ">=",
|
||||
version: "cmd:scanelf>=1.3.4-r0",
|
||||
want: "cmd:scanelf",
|
||||
},
|
||||
{
|
||||
name: "<",
|
||||
version: "cmd:scanelf<1.3.4-r0",
|
||||
want: "cmd:scanelf",
|
||||
},
|
||||
{
|
||||
name: "ignores file paths",
|
||||
version: "/bin/sh",
|
||||
want: "/bin/sh",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, stripVersionSpecifier(tt.version))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -131,7 +131,7 @@ func parseApkDB(_ context.Context, resolver file.Resolver, env *generic.Environm
|
||||
pkgs = append(pkgs, newPackage(apk, r, reader.Location))
|
||||
}
|
||||
|
||||
return pkgs, discoverPackageDependencies(pkgs), nil
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func findReleases(resolver file.Resolver, dbPath string) []linux.Release {
|
||||
@@ -386,57 +386,3 @@ func processChecksum(value string) *file.Digest {
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func discoverPackageDependencies(pkgs []pkg.Package) (relationships []artifact.Relationship) {
|
||||
// map["provides" string] -> packages that provide the "p" key
|
||||
lookup := make(map[string][]pkg.Package)
|
||||
// read "Provides" (p) and add as keys for lookup keys as well as package names
|
||||
for _, p := range pkgs {
|
||||
apkg, ok := p.Metadata.(pkg.ApkDBEntry)
|
||||
if !ok {
|
||||
log.Warnf("cataloger failed to extract apk 'provides' metadata for package %+v", p.Name)
|
||||
continue
|
||||
}
|
||||
lookup[p.Name] = append(lookup[p.Name], p)
|
||||
for _, provides := range apkg.Provides {
|
||||
k := stripVersionSpecifier(provides)
|
||||
lookup[k] = append(lookup[k], p)
|
||||
}
|
||||
}
|
||||
|
||||
// read "Pull Dependencies" (D) and match with keys
|
||||
for _, p := range pkgs {
|
||||
apkg, ok := p.Metadata.(pkg.ApkDBEntry)
|
||||
if !ok {
|
||||
log.Warnf("cataloger failed to extract apk dependency metadata for package %+v", p.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, depSpecifier := range apkg.Dependencies {
|
||||
// use the lookup to find what pkg we depend on
|
||||
dep := stripVersionSpecifier(depSpecifier)
|
||||
for _, depPkg := range lookup[dep] {
|
||||
// this is a pkg that package "p" depends on... make a relationship
|
||||
relationships = append(relationships, artifact.Relationship{
|
||||
From: depPkg,
|
||||
To: p,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return relationships
|
||||
}
|
||||
|
||||
func stripVersionSpecifier(s string) string {
|
||||
// examples:
|
||||
// musl>=1 --> musl
|
||||
// cmd:scanelf=1.3.4-r0 --> cmd:scanelf
|
||||
|
||||
items := internal.SplitAny(s, "<>=")
|
||||
if len(items) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
return items[0]
|
||||
}
|
||||
|
||||
@@ -9,11 +9,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -689,144 +687,6 @@ func TestSinglePackageDetails(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiplePackages(t *testing.T) {
|
||||
fixture := "test-fixtures/multiple"
|
||||
location := file.NewLocation(fixture)
|
||||
fixtureLocationSet := file.NewLocationSet(location)
|
||||
expectedPkgs := []pkg.Package{
|
||||
{
|
||||
Name: "libc-utils",
|
||||
Version: "0.7.2-r0",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MPL-2.0 AND MIT", location),
|
||||
),
|
||||
Type: pkg.ApkPkg,
|
||||
PURL: "pkg:apk/alpine/libc-utils@0.7.2-r0?arch=x86_64&upstream=libc-dev&distro=alpine-3.12",
|
||||
Locations: fixtureLocationSet,
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "libc-utils",
|
||||
OriginPackage: "libc-dev",
|
||||
Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
|
||||
Version: "0.7.2-r0",
|
||||
Architecture: "x86_64",
|
||||
URL: "http://alpinelinux.org",
|
||||
Description: "Meta package to pull in correct libc",
|
||||
Size: 1175,
|
||||
InstalledSize: 4096,
|
||||
Checksum: "Q1p78yvTLG094tHE1+dToJGbmYzQE=",
|
||||
GitCommit: "97b1c2842faa3bfa30f5811ffbf16d5ff9f1a479",
|
||||
Dependencies: []string{"musl-utils"},
|
||||
Provides: []string{},
|
||||
Files: []pkg.ApkFileRecord{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "musl-utils",
|
||||
Version: "1.1.24-r2",
|
||||
Type: pkg.ApkPkg,
|
||||
PURL: "pkg:apk/alpine/musl-utils@1.1.24-r2?arch=x86_64&upstream=musl&distro=alpine-3.12",
|
||||
Locations: fixtureLocationSet,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", location),
|
||||
pkg.NewLicenseFromLocations("BSD", location),
|
||||
pkg.NewLicenseFromLocations("GPL2+", location),
|
||||
),
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "musl-utils",
|
||||
OriginPackage: "musl",
|
||||
Version: "1.1.24-r2",
|
||||
Description: "the musl c library (libc) implementation",
|
||||
Maintainer: "Timo Teräs <timo.teras@iki.fi>",
|
||||
Architecture: "x86_64",
|
||||
URL: "https://musl.libc.org/",
|
||||
Size: 37944,
|
||||
InstalledSize: 151552,
|
||||
GitCommit: "4024cc3b29ad4c65544ad068b8f59172b5494306",
|
||||
Dependencies: []string{"scanelf", "so:libc.musl-x86_64.so.1"},
|
||||
Provides: []string{"cmd:getconf", "cmd:getent", "cmd:iconv", "cmd:ldconfig", "cmd:ldd"},
|
||||
Checksum: "Q1bTtF5526tETKfL+lnigzIDvm+2o=",
|
||||
Files: []pkg.ApkFileRecord{
|
||||
{
|
||||
Path: "/sbin",
|
||||
},
|
||||
{
|
||||
Path: "/sbin/ldconfig",
|
||||
OwnerUID: "0",
|
||||
OwnerGID: "0",
|
||||
Permissions: "755",
|
||||
Digest: &file.Digest{
|
||||
Algorithm: "'Q1'+base64(sha1)",
|
||||
Value: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=",
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/usr",
|
||||
},
|
||||
{
|
||||
Path: "/usr/bin",
|
||||
},
|
||||
{
|
||||
Path: "/usr/bin/iconv",
|
||||
OwnerUID: "0",
|
||||
OwnerGID: "0",
|
||||
Permissions: "755",
|
||||
Digest: &file.Digest{
|
||||
Algorithm: "'Q1'+base64(sha1)",
|
||||
Value: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=",
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/usr/bin/ldd",
|
||||
OwnerUID: "0",
|
||||
OwnerGID: "0",
|
||||
Permissions: "755",
|
||||
Digest: &file.Digest{
|
||||
Algorithm: "'Q1'+base64(sha1)",
|
||||
Value: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=",
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/usr/bin/getconf",
|
||||
OwnerUID: "0",
|
||||
OwnerGID: "0",
|
||||
Permissions: "755",
|
||||
Digest: &file.Digest{
|
||||
Algorithm: "'Q1'+base64(sha1)",
|
||||
Value: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=",
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/usr/bin/getent",
|
||||
OwnerUID: "0",
|
||||
OwnerGID: "0",
|
||||
Permissions: "755",
|
||||
Digest: &file.Digest{
|
||||
Algorithm: "'Q1'+base64(sha1)",
|
||||
Value: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expectedRelationships := []artifact.Relationship{
|
||||
{
|
||||
From: expectedPkgs[1], // musl-utils
|
||||
To: expectedPkgs[0], // libc-utils
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
Data: nil,
|
||||
},
|
||||
}
|
||||
|
||||
env := generic.Environment{LinuxRelease: &linux.Release{
|
||||
ID: "alpine",
|
||||
VersionID: "3.12",
|
||||
}}
|
||||
|
||||
pkgtest.TestFileParserWithEnv(t, fixture, parseApkDB, &env, expectedPkgs, expectedRelationships)
|
||||
}
|
||||
|
||||
func Test_processChecksum(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -858,237 +718,6 @@ func Test_processChecksum(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_discoverPackageDependencies(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
genFn func() ([]pkg.Package, []artifact.Relationship)
|
||||
}{
|
||||
{
|
||||
name: "has no dependency",
|
||||
genFn: func() ([]pkg.Package, []artifact.Relationship) {
|
||||
a := pkg.Package{
|
||||
Name: "package-a",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Provides: []string{"a-thing"},
|
||||
},
|
||||
}
|
||||
a.SetID()
|
||||
b := pkg.Package{
|
||||
Name: "package-b",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Provides: []string{"b-thing"},
|
||||
},
|
||||
}
|
||||
b.SetID()
|
||||
|
||||
return []pkg.Package{a, b}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "has 1 dependency",
|
||||
genFn: func() ([]pkg.Package, []artifact.Relationship) {
|
||||
a := pkg.Package{
|
||||
Name: "package-a",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Dependencies: []string{"b-thing"},
|
||||
},
|
||||
}
|
||||
a.SetID()
|
||||
b := pkg.Package{
|
||||
Name: "package-b",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Provides: []string{"b-thing"},
|
||||
},
|
||||
}
|
||||
b.SetID()
|
||||
|
||||
return []pkg.Package{a, b}, []artifact.Relationship{
|
||||
{
|
||||
From: b,
|
||||
To: a,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "strip version specifiers",
|
||||
genFn: func() ([]pkg.Package, []artifact.Relationship) {
|
||||
a := pkg.Package{
|
||||
Name: "package-a",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Dependencies: []string{"so:libc.musl-x86_64.so.1"},
|
||||
},
|
||||
}
|
||||
a.SetID()
|
||||
b := pkg.Package{
|
||||
Name: "package-b",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Provides: []string{"so:libc.musl-x86_64.so.1=1"},
|
||||
},
|
||||
}
|
||||
b.SetID()
|
||||
|
||||
return []pkg.Package{a, b}, []artifact.Relationship{
|
||||
{
|
||||
From: b,
|
||||
To: a,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "strip version specifiers with empty provides value",
|
||||
genFn: func() ([]pkg.Package, []artifact.Relationship) {
|
||||
a := pkg.Package{
|
||||
Name: "package-a",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Dependencies: []string{"so:libc.musl-x86_64.so.1"},
|
||||
},
|
||||
}
|
||||
a.SetID()
|
||||
b := pkg.Package{
|
||||
Name: "package-b",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Provides: []string{""},
|
||||
},
|
||||
}
|
||||
b.SetID()
|
||||
|
||||
return []pkg.Package{a, b}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "depends on package name",
|
||||
genFn: func() ([]pkg.Package, []artifact.Relationship) {
|
||||
a := pkg.Package{
|
||||
Name: "package-a",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Dependencies: []string{"musl>=1.2"},
|
||||
},
|
||||
}
|
||||
a.SetID()
|
||||
b := pkg.Package{
|
||||
Name: "musl",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Provides: []string{"so:libc.musl-x86_64.so.1=1"},
|
||||
},
|
||||
}
|
||||
b.SetID()
|
||||
|
||||
return []pkg.Package{a, b}, []artifact.Relationship{
|
||||
{
|
||||
From: b,
|
||||
To: a,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "depends on package file",
|
||||
genFn: func() ([]pkg.Package, []artifact.Relationship) {
|
||||
a := pkg.Package{
|
||||
Name: "alpine-baselayout",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Dependencies: []string{"/bin/sh"},
|
||||
},
|
||||
}
|
||||
a.SetID()
|
||||
b := pkg.Package{
|
||||
Name: "busybox",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Provides: []string{"/bin/sh"},
|
||||
},
|
||||
}
|
||||
b.SetID()
|
||||
|
||||
return []pkg.Package{a, b}, []artifact.Relationship{
|
||||
{
|
||||
From: b,
|
||||
To: a,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
pkgs, wantRelationships := test.genFn()
|
||||
gotRelationships := discoverPackageDependencies(pkgs)
|
||||
d := cmp.Diff(wantRelationships, gotRelationships, cmpopts.IgnoreUnexported(pkg.Package{}, file.LocationSet{}, pkg.LicenseSet{}))
|
||||
if d != "" {
|
||||
t.Fail()
|
||||
t.Log(d)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackageDbDependenciesByParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
fixture string
|
||||
expected map[string][]string
|
||||
}{
|
||||
{
|
||||
fixture: "test-fixtures/installed",
|
||||
expected: map[string][]string{
|
||||
"alpine-baselayout": {"alpine-baselayout-data", "busybox", "musl"},
|
||||
"apk-tools": {"musl", "ca-certificates-bundle", "musl", "libcrypto1.1", "libssl1.1", "zlib"},
|
||||
"busybox": {"musl"},
|
||||
"libc-utils": {"musl-utils"},
|
||||
"libcrypto1.1": {"musl"},
|
||||
"libssl1.1": {"musl", "libcrypto1.1"},
|
||||
"musl-utils": {"scanelf", "musl"},
|
||||
"scanelf": {"musl"},
|
||||
"ssl_client": {"musl", "libcrypto1.1", "libssl1.1"},
|
||||
"zlib": {"musl"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixture, func(t *testing.T) {
|
||||
f, err := os.Open(test.fixture)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { require.NoError(t, f.Close()) })
|
||||
|
||||
pkgs, relationships, err := parseApkDB(context.Background(), nil, nil, file.LocationReadCloser{
|
||||
Location: file.NewLocation(test.fixture),
|
||||
ReadCloser: f,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
pkgsByID := make(map[artifact.ID]pkg.Package)
|
||||
for _, p := range pkgs {
|
||||
p.SetID()
|
||||
pkgsByID[p.ID()] = p
|
||||
}
|
||||
|
||||
actualDependencies := make(map[string][]string)
|
||||
|
||||
for _, r := range relationships {
|
||||
switch r.Type {
|
||||
case artifact.DependencyOfRelationship:
|
||||
to := pkgsByID[r.To.ID()]
|
||||
from := pkgsByID[r.From.ID()]
|
||||
actualDependencies[to.Name] = append(actualDependencies[to.Name], from.Name)
|
||||
default:
|
||||
t.Fatalf("unexpected relationship type: %+v", r.Type)
|
||||
}
|
||||
}
|
||||
|
||||
if d := cmp.Diff(test.expected, actualDependencies); d != "" {
|
||||
t.Fail()
|
||||
t.Log(d)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseApkDB_expectedPkgNames(t *testing.T) {
|
||||
tests := []struct {
|
||||
fixture string
|
||||
@@ -1175,45 +804,6 @@ func newLocationReadCloser(t *testing.T, path string) file.LocationReadCloser {
|
||||
return file.NewLocationReadCloser(file.NewLocation(path), f)
|
||||
}
|
||||
|
||||
func Test_stripVersionSpecifier(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
version string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "empty expression",
|
||||
version: "",
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "no expression",
|
||||
version: "cmd:foo",
|
||||
want: "cmd:foo",
|
||||
},
|
||||
{
|
||||
name: "=",
|
||||
version: "cmd:scanelf=1.3.4-r0",
|
||||
want: "cmd:scanelf",
|
||||
},
|
||||
{
|
||||
name: ">=",
|
||||
version: "cmd:scanelf>=1.3.4-r0",
|
||||
want: "cmd:scanelf",
|
||||
},
|
||||
{
|
||||
name: "<",
|
||||
version: "cmd:scanelf<1.3.4-r0",
|
||||
want: "cmd:scanelf",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, stripVersionSpecifier(tt.version))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseReleasesFromAPKRepository(t *testing.T) {
|
||||
tests := []struct {
|
||||
repos string
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
C:Q1p78yvTLG094tHE1+dToJGbmYzQE=
|
||||
P:libc-utils
|
||||
V:0.7.2-r0
|
||||
A:x86_64
|
||||
S:1175
|
||||
I:4096
|
||||
T:Meta package to pull in correct libc
|
||||
U:http://alpinelinux.org
|
||||
L:MPL-2.0 AND MIT
|
||||
o:libc-dev
|
||||
m:Natanael Copa <ncopa@alpinelinux.org>
|
||||
t:1575749004
|
||||
c:97b1c2842faa3bfa30f5811ffbf16d5ff9f1a479
|
||||
D:musl-utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
C:Q1bTtF5526tETKfL+lnigzIDvm+2o=
|
||||
P:musl-utils
|
||||
V:1.1.24-r2
|
||||
A:x86_64
|
||||
S:37944
|
||||
I:151552
|
||||
T:the musl c library (libc) implementation
|
||||
U:https://musl.libc.org/
|
||||
L:MIT BSD GPL2+
|
||||
o:musl
|
||||
m:Timo Teräs <timo.teras@iki.fi>
|
||||
t:1584790550
|
||||
c:4024cc3b29ad4c65544ad068b8f59172b5494306
|
||||
D:scanelf so:libc.musl-x86_64.so.1
|
||||
p:cmd:getconf cmd:getent cmd:iconv cmd:ldconfig cmd:ldd
|
||||
r:libiconv
|
||||
F:sbin
|
||||
R:ldconfig
|
||||
a:0:0:755
|
||||
Z:Q1Kja2+POZKxEkUOZqwSjC6kmaED4=
|
||||
F:usr
|
||||
F:usr/bin
|
||||
R:iconv
|
||||
a:0:0:755
|
||||
Z:Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=
|
||||
R:ldd
|
||||
a:0:0:755
|
||||
Z:Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=
|
||||
R:getconf
|
||||
a:0:0:755
|
||||
Z:Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=
|
||||
R:getent
|
||||
a:0:0:755
|
||||
Z:Q1eR2Dz/WylabgbWMTkd2+hGmEya4=
|
||||
@@ -0,0 +1,78 @@
|
||||
P:bash
|
||||
V:5.2.21-r0
|
||||
A:x86_64
|
||||
S:448728
|
||||
I:1396736
|
||||
T:The GNU Bourne Again shell
|
||||
U:https://www.gnu.org/software/bash/bash.html
|
||||
L:GPL-3.0-or-later
|
||||
o:bash
|
||||
m:Natanael Copa <ncopa@alpinelinux.org>
|
||||
t:1701073495
|
||||
c:6a9559d98850225ba80771901ef1abda91cb29aa
|
||||
D:/bin/sh so:libc.musl-x86_64.so.1 so:libreadline.so.8
|
||||
p:cmd:bash=5.2.21-r0
|
||||
|
||||
P:busybox-binsh
|
||||
V:1.36.1-r15
|
||||
A:x86_64
|
||||
S:1543
|
||||
I:8192
|
||||
T:busybox ash /bin/sh
|
||||
U:https://busybox.net/
|
||||
L:GPL-2.0-only
|
||||
o:busybox
|
||||
m:Sören Tempel <soeren+alpine@soeren-tempel.net>
|
||||
t:1699383189
|
||||
c:d1b6f274f29076967826e0ecf6ebcaa5d360272f
|
||||
k:100
|
||||
D:busybox=1.36.1-r15
|
||||
p:/bin/sh cmd:sh=1.36.1-r15
|
||||
r:busybox-initscripts
|
||||
F:bin
|
||||
R:sh
|
||||
a:0:0:777
|
||||
Z:Q1pcfTfDNEbNKQc2s1tia7da05M8Q=
|
||||
|
||||
P:musl
|
||||
V:1.2.4_git20230717-r4
|
||||
A:x86_64
|
||||
S:407278
|
||||
I:667648
|
||||
T:the musl c library (libc) implementation
|
||||
U:https://musl.libc.org/
|
||||
L:MIT
|
||||
o:musl
|
||||
m:Timo Teräs <timo.teras@iki.fi>
|
||||
t:1699271358
|
||||
c:ca7f2ab5e88794e4e654b40776f8a92256f50639
|
||||
p:so:libc.musl-x86_64.so.1=1
|
||||
F:lib
|
||||
R:ld-musl-x86_64.so.1
|
||||
a:0:0:755
|
||||
Z:Q1+zEJiG53Cxy7DkV5oZQqeWnzybY=
|
||||
R:libc.musl-x86_64.so.1
|
||||
a:0:0:777
|
||||
Z:Q17yJ3JFNypA4mxhJJr0ou6CzsJVI=
|
||||
|
||||
P:readline
|
||||
V:8.2.1-r2
|
||||
A:x86_64
|
||||
S:119878
|
||||
I:303104
|
||||
T:GNU readline library
|
||||
U:https://tiswww.cwru.edu/php/chet/readline/rltop.html
|
||||
L:GPL-2.0-or-later
|
||||
o:readline
|
||||
m:Natanael Copa <ncopa@alpinelinux.org>
|
||||
t:1684120357
|
||||
c:33283848034c9885d984c8e8697c645c57324938
|
||||
D:so:libc.musl-x86_64.so.1 so:libncursesw.so.6
|
||||
p:so:libreadline.so.8=8.2
|
||||
F:etc
|
||||
R:inputrc
|
||||
Z:Q1ilcgkuEseXEH6iMo9UNjLn1pPfg=
|
||||
F:usr
|
||||
F:usr/lib
|
||||
R:libreadline.so.8
|
||||
a:0:0:777
|
||||
Reference in New Issue
Block a user