github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/alpine/cataloger_test.go (about)

     1  package alpine
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/google/go-cmp/cmp"
     7  	"github.com/google/go-cmp/cmp/cmpopts"
     8  
     9  	"github.com/anchore/syft/syft/artifact"
    10  	"github.com/anchore/syft/syft/file"
    11  	"github.com/anchore/syft/syft/pkg"
    12  	"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
    13  )
    14  
    15  func TestApkDBCataloger(t *testing.T) {
    16  	dbLocation := file.NewLocation("lib/apk/db/installed")
    17  
    18  	bashPkg := pkg.Package{
    19  		Name:    "bash",
    20  		Version: "5.2.21-r0",
    21  		Type:    pkg.ApkPkg,
    22  		FoundBy: "apk-db-cataloger",
    23  		Licenses: pkg.NewLicenseSet(
    24  			pkg.NewLicenseFromLocations("GPL-3.0-or-later", dbLocation),
    25  		),
    26  		Locations: file.NewLocationSet(dbLocation),
    27  		Metadata: pkg.ApkDBEntry{
    28  			Package:       "bash",
    29  			OriginPackage: "bash",
    30  			Maintainer:    "Natanael Copa <ncopa@alpinelinux.org>",
    31  			Version:       "5.2.21-r0",
    32  			Architecture:  "x86_64",
    33  			URL:           "https://www.gnu.org/software/bash/bash.html",
    34  			Description:   "The GNU Bourne Again shell",
    35  			Size:          448728,
    36  			InstalledSize: 1396736,
    37  			Dependencies: []string{
    38  				"/bin/sh", "so:libc.musl-x86_64.so.1", "so:libreadline.so.8",
    39  			},
    40  			Provides: []string{
    41  				"cmd:bash=5.2.21-r0",
    42  			},
    43  			// note: files not provided and not under test
    44  		},
    45  	}
    46  
    47  	busyboxBinshPkg := pkg.Package{
    48  		Name:    "busybox-binsh",
    49  		Version: "1.36.1-r15",
    50  		Type:    pkg.ApkPkg,
    51  		FoundBy: "apk-db-cataloger",
    52  		Licenses: pkg.NewLicenseSet(
    53  			pkg.NewLicenseFromLocations("GPL-2.0-only", dbLocation),
    54  		),
    55  		Locations: file.NewLocationSet(dbLocation),
    56  		Metadata: pkg.ApkDBEntry{
    57  			Package:       "busybox-binsh",
    58  			OriginPackage: "busybox",
    59  			Maintainer:    "Sören Tempel <soeren+alpine@soeren-tempel.net>",
    60  			Version:       "1.36.1-r15",
    61  			Architecture:  "x86_64",
    62  			URL:           "https://busybox.net/",
    63  			Description:   "busybox ash /bin/sh",
    64  			Size:          1543,
    65  			InstalledSize: 8192,
    66  			Dependencies: []string{
    67  				"busybox=1.36.1-r15",
    68  			},
    69  			Provides: []string{
    70  				"/bin/sh", "cmd:sh=1.36.1-r15",
    71  			},
    72  			// note: files not provided and not under test
    73  		},
    74  	}
    75  
    76  	muslPkg := pkg.Package{
    77  		Name:    "musl",
    78  		Version: "1.2.4_git20230717-r4",
    79  		Type:    pkg.ApkPkg,
    80  		FoundBy: "apk-db-cataloger",
    81  		Licenses: pkg.NewLicenseSet(
    82  			pkg.NewLicenseFromLocations("MIT", dbLocation),
    83  		),
    84  		Locations: file.NewLocationSet(dbLocation),
    85  		Metadata: pkg.ApkDBEntry{
    86  			Package:       "musl",
    87  			OriginPackage: "musl",
    88  			Maintainer:    "Timo Teräs <timo.teras@iki.fi>",
    89  			Version:       "1.2.4_git20230717-r4",
    90  			Architecture:  "x86_64",
    91  			URL:           "https://musl.libc.org/",
    92  			Description:   "the musl c library (libc) implementation",
    93  			Size:          407278,
    94  			InstalledSize: 667648,
    95  			Dependencies:  []string{},
    96  			Provides: []string{
    97  				"so:libc.musl-x86_64.so.1=1",
    98  			},
    99  			// note: files not provided and not under test
   100  		},
   101  	}
   102  
   103  	readlinePkg := pkg.Package{
   104  		Name:    "readline",
   105  		Version: "8.2.1-r2",
   106  		Type:    pkg.ApkPkg,
   107  		FoundBy: "apk-db-cataloger",
   108  		Licenses: pkg.NewLicenseSet(
   109  			pkg.NewLicenseFromLocations("GPL-2.0-or-later", dbLocation),
   110  		),
   111  		Locations: file.NewLocationSet(dbLocation),
   112  		Metadata: pkg.ApkDBEntry{
   113  			Package:       "readline",
   114  			OriginPackage: "readline",
   115  			Maintainer:    "Natanael Copa <ncopa@alpinelinux.org>",
   116  			Version:       "8.2.1-r2",
   117  			Architecture:  "x86_64",
   118  			URL:           "https://tiswww.cwru.edu/php/chet/readline/rltop.html",
   119  			Description:   "GNU readline library",
   120  			Size:          119878,
   121  			InstalledSize: 303104,
   122  			Dependencies: []string{
   123  				"so:libc.musl-x86_64.so.1", "so:libncursesw.so.6",
   124  			},
   125  			Provides: []string{
   126  				"so:libreadline.so.8=8.2",
   127  			},
   128  			// note: files not provided and not under test
   129  		},
   130  	}
   131  
   132  	expectedPkgs := []pkg.Package{
   133  		bashPkg,
   134  		busyboxBinshPkg,
   135  		muslPkg,
   136  		readlinePkg,
   137  	}
   138  
   139  	// # apk info --depends bash
   140  	//   bash-5.2.21-r0 depends on:
   141  	//   /bin/sh
   142  	//   so:libc.musl-x86_64.so.1
   143  	//   so:libreadline.so.8
   144  	//
   145  	// # apk info --who-owns /bin/sh
   146  	//   /bin/sh is owned by busybox-binsh-1.36.1-r15
   147  	//
   148  	// # find / | grep musl
   149  	//   /lib/ld-musl-x86_64.so.1
   150  	//   /lib/libc.musl-x86_64.so.1
   151  	//
   152  	// # apk info --who-owns '/lib/libc.musl-x86_64.so.1'
   153  	//   /lib/libc.musl-x86_64.so.1 is owned by musl-1.2.4_git20230717-r4
   154  	//
   155  	// # find / | grep libreadline
   156  	//   /usr/lib/libreadline.so.8.2
   157  	//   /usr/lib/libreadline.so.8
   158  	//
   159  	// # apk info --who-owns '/usr/lib/libreadline.so.8'
   160  	//   /usr/lib/libreadline.so.8 is owned by readline-8.2.1-r2
   161  
   162  	expectedRelationships := []artifact.Relationship{
   163  		{
   164  			From: busyboxBinshPkg,
   165  			To:   bashPkg,
   166  			Type: artifact.DependencyOfRelationship,
   167  		},
   168  		{
   169  			From: readlinePkg,
   170  			To:   bashPkg,
   171  			Type: artifact.DependencyOfRelationship,
   172  		},
   173  		{
   174  			From: muslPkg,
   175  			To:   readlinePkg,
   176  			Type: artifact.DependencyOfRelationship,
   177  		},
   178  		{
   179  			From: muslPkg,
   180  			To:   bashPkg,
   181  			Type: artifact.DependencyOfRelationship,
   182  		},
   183  	}
   184  
   185  	pkgtest.NewCatalogTester().
   186  		FromDirectory(t, "test-fixtures/multiple-1").
   187  		WithCompareOptions(cmpopts.IgnoreFields(pkg.ApkDBEntry{}, "Files", "GitCommit", "Checksum")).
   188  		Expects(expectedPkgs, expectedRelationships).
   189  		TestCataloger(t, NewDBCataloger())
   190  
   191  }
   192  
   193  func TestCatalogerDependencyTree(t *testing.T) {
   194  	assertion := func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
   195  		expected := map[string][]string{
   196  			"alpine-baselayout": {"busybox", "alpine-baselayout-data", "musl"},
   197  			"apk-tools":         {"ca-certificates-bundle", "musl", "libcrypto1.1", "libssl1.1", "zlib"},
   198  			"busybox":           {"musl"},
   199  			"libc-utils":        {"musl-utils"},
   200  			"libcrypto1.1":      {"musl"},
   201  			"libssl1.1":         {"musl", "libcrypto1.1"},
   202  			"musl-utils":        {"scanelf", "musl"},
   203  			"scanelf":           {"musl"},
   204  			"ssl_client":        {"musl", "libcrypto1.1", "libssl1.1"},
   205  			"zlib":              {"musl"},
   206  		}
   207  		pkgsByID := make(map[artifact.ID]pkg.Package)
   208  		for _, p := range pkgs {
   209  			p.SetID()
   210  			pkgsByID[p.ID()] = p
   211  		}
   212  
   213  		actualDependencies := make(map[string][]string)
   214  
   215  		for _, r := range relationships {
   216  			switch r.Type {
   217  			case artifact.DependencyOfRelationship:
   218  				to := pkgsByID[r.To.ID()]
   219  				from := pkgsByID[r.From.ID()]
   220  				actualDependencies[to.Name] = append(actualDependencies[to.Name], from.Name)
   221  			default:
   222  				t.Fatalf("unexpected relationship type: %+v", r.Type)
   223  			}
   224  		}
   225  
   226  		if d := cmp.Diff(expected, actualDependencies); d != "" {
   227  			t.Fail()
   228  			t.Log(d)
   229  		}
   230  	}
   231  
   232  	pkgtest.NewCatalogTester().
   233  		FromDirectory(t, "test-fixtures/multiple-2").
   234  		ExpectsAssertion(assertion).
   235  		TestCataloger(t, NewDBCataloger())
   236  
   237  }
   238  
   239  func TestCataloger_Globs(t *testing.T) {
   240  	tests := []struct {
   241  		name     string
   242  		fixture  string
   243  		expected []string
   244  	}{
   245  		{
   246  			name:     "obtain DB files",
   247  			fixture:  "test-fixtures/glob-paths",
   248  			expected: []string{"lib/apk/db/installed"},
   249  		},
   250  	}
   251  
   252  	for _, test := range tests {
   253  		t.Run(test.name, func(t *testing.T) {
   254  			pkgtest.NewCatalogTester().
   255  				FromDirectory(t, test.fixture).
   256  				ExpectsResolverContentQueries(test.expected).
   257  				IgnoreUnfulfilledPathResponses("etc/apk/repositories").
   258  				TestCataloger(t, NewDBCataloger())
   259  		})
   260  	}
   261  }