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

     1  package debian
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  	"github.com/stretchr/testify/require"
     9  
    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 TestDpkgCataloger(t *testing.T) {
    16  	tests := []struct {
    17  		name     string
    18  		expected []pkg.Package
    19  	}{
    20  		{
    21  			name: "image-dpkg",
    22  			expected: []pkg.Package{
    23  				{
    24  					Name:    "libpam-runtime",
    25  					Version: "1.1.8-3.6",
    26  					FoundBy: "dpkg-db-cataloger",
    27  					Licenses: pkg.NewLicenseSet(
    28  						pkg.NewLicenseFromLocations("GPL-1", file.NewVirtualLocation("/usr/share/doc/libpam-runtime/copyright", "/usr/share/doc/libpam-runtime/copyright")),
    29  						pkg.NewLicenseFromLocations("GPL-2", file.NewVirtualLocation("/usr/share/doc/libpam-runtime/copyright", "/usr/share/doc/libpam-runtime/copyright")),
    30  						pkg.NewLicenseFromLocations("LGPL-2.1", file.NewVirtualLocation("/usr/share/doc/libpam-runtime/copyright", "/usr/share/doc/libpam-runtime/copyright")),
    31  					),
    32  					Locations: file.NewLocationSet(
    33  						file.NewVirtualLocation("/var/lib/dpkg/status", "/var/lib/dpkg/status"),
    34  						file.NewVirtualLocation("/var/lib/dpkg/info/libpam-runtime.md5sums", "/var/lib/dpkg/info/libpam-runtime.md5sums"),
    35  						file.NewVirtualLocation("/var/lib/dpkg/info/libpam-runtime.conffiles", "/var/lib/dpkg/info/libpam-runtime.conffiles"),
    36  						file.NewVirtualLocation("/usr/share/doc/libpam-runtime/copyright", "/usr/share/doc/libpam-runtime/copyright"),
    37  					),
    38  					Type: pkg.DebPkg,
    39  					Metadata: pkg.DpkgDBEntry{
    40  						Package:       "libpam-runtime",
    41  						Source:        "pam",
    42  						Version:       "1.1.8-3.6",
    43  						Architecture:  "all",
    44  						Maintainer:    "Steve Langasek <vorlon@debian.org>",
    45  						InstalledSize: 1016,
    46  						Description: `Runtime support for the PAM library
    47   Contains configuration files and  directories required for
    48   authentication  to work on Debian systems.  This package is required
    49   on almost all installations.`,
    50  						Depends: []string{
    51  							"debconf (>= 0.5) | debconf-2.0",
    52  							"debconf (>= 1.5.19) | cdebconf",
    53  							"libpam-modules (>= 1.0.1-6)",
    54  						},
    55  						Files: []pkg.DpkgFileRecord{
    56  							{
    57  								Path: "/etc/pam.conf",
    58  								Digest: &file.Digest{
    59  									Algorithm: "md5",
    60  									Value:     "87fc76f18e98ee7d3848f6b81b3391e5",
    61  								},
    62  								IsConfigFile: true,
    63  							},
    64  							{
    65  								Path: "/etc/pam.d/other",
    66  								Digest: &file.Digest{
    67  									Algorithm: "md5",
    68  									Value:     "31aa7f2181889ffb00b87df4126d1701",
    69  								},
    70  								IsConfigFile: true,
    71  							},
    72  							{Path: "/lib/x86_64-linux-gnu/libz.so.1.2.11", Digest: &file.Digest{
    73  								Algorithm: "md5",
    74  								Value:     "55f905631797551d4d936a34c7e73474",
    75  							}},
    76  							{Path: "/usr/share/doc/zlib1g/changelog.Debian.gz", Digest: &file.Digest{
    77  								Algorithm: "md5",
    78  								Value:     "cede84bda30d2380217f97753c8ccf3a",
    79  							}},
    80  							{Path: "/usr/share/doc/zlib1g/changelog.gz", Digest: &file.Digest{
    81  								Algorithm: "md5",
    82  								Value:     "f3c9dafa6da7992c47328b4464f6d122",
    83  							}},
    84  							{Path: "/usr/share/doc/zlib1g/copyright", Digest: &file.Digest{
    85  								Algorithm: "md5",
    86  								Value:     "a4fae96070439a5209a62ae5b8017ab2",
    87  							}},
    88  						},
    89  					},
    90  				},
    91  			},
    92  		},
    93  		{
    94  			name: "image-distroless-deb",
    95  			expected: []pkg.Package{
    96  				{
    97  					Name:    "libsqlite3-0",
    98  					Version: "3.34.1-3",
    99  					FoundBy: "dpkg-db-cataloger",
   100  					Licenses: pkg.NewLicenseSet(
   101  						pkg.NewLicenseFromLocations("public-domain", file.NewVirtualLocation("/usr/share/doc/libsqlite3-0/copyright", "/usr/share/doc/libsqlite3-0/copyright")),
   102  						pkg.NewLicenseFromLocations("GPL-2+", file.NewVirtualLocation("/usr/share/doc/libsqlite3-0/copyright", "/usr/share/doc/libsqlite3-0/copyright")),
   103  						pkg.NewLicenseFromLocations("GPL-2", file.NewVirtualLocation("/usr/share/doc/libsqlite3-0/copyright", "/usr/share/doc/libsqlite3-0/copyright")),
   104  					),
   105  					Locations: file.NewLocationSet(
   106  						file.NewVirtualLocation("/var/lib/dpkg/status.d/libsqlite3-0", "/var/lib/dpkg/status.d/libsqlite3-0"),
   107  						file.NewVirtualLocation("/var/lib/dpkg/status.d/libsqlite3-0.md5sums", "/var/lib/dpkg/status.d/libsqlite3-0.md5sums"),
   108  						file.NewVirtualLocation("/usr/share/doc/libsqlite3-0/copyright", "/usr/share/doc/libsqlite3-0/copyright"),
   109  					),
   110  					Type: pkg.DebPkg,
   111  					Metadata: pkg.DpkgDBEntry{
   112  						Package:       "libsqlite3-0",
   113  						Source:        "sqlite3",
   114  						Version:       "3.34.1-3",
   115  						Architecture:  "arm64",
   116  						Maintainer:    "Laszlo Boszormenyi (GCS) <gcs@debian.org>",
   117  						InstalledSize: 1490,
   118  						Description: `SQLite 3 shared library
   119   SQLite is a C library that implements an SQL database engine.
   120   Programs that link with the SQLite library can have SQL database
   121   access without running a separate RDBMS process.`,
   122  						Depends: []string{"libc6 (>= 2.29)"},
   123  						Files: []pkg.DpkgFileRecord{
   124  							{Path: "/usr/lib/aarch64-linux-gnu/libsqlite3.so.0.8.6", Digest: &file.Digest{
   125  								Algorithm: "md5",
   126  								Value:     "e11d70c96979a1328ae4e7e50542782b",
   127  							}},
   128  							{Path: "/usr/share/doc/libsqlite3-0/README.Debian", Digest: &file.Digest{
   129  								Algorithm: "md5",
   130  								Value:     "9d8facc2fa9d2df52f1c7cb4e5fa4741",
   131  							}},
   132  							{Path: "/usr/share/doc/libsqlite3-0/changelog.Debian.gz", Digest: &file.Digest{
   133  								Algorithm: "md5",
   134  								Value:     "a58942e742f5056be0595e6ba69a323f",
   135  							}},
   136  							{Path: "/usr/share/doc/libsqlite3-0/changelog.gz", Digest: &file.Digest{
   137  								Algorithm: "md5",
   138  								Value:     "52317be84c3ca44b7888c6921131e37d",
   139  							}},
   140  							{Path: "/usr/share/doc/libsqlite3-0/changelog.html.gz", Digest: &file.Digest{
   141  								Algorithm: "md5",
   142  								Value:     "a856310354e6c8768e85b39ae838dd0a",
   143  							}},
   144  							{Path: "/usr/share/doc/libsqlite3-0/copyright", Digest: &file.Digest{
   145  								Algorithm: "md5",
   146  								Value:     "be64db3e095486e5e105652c51199358",
   147  							}},
   148  						},
   149  					},
   150  				},
   151  			},
   152  		},
   153  	}
   154  
   155  	for _, tt := range tests {
   156  		t.Run(tt.name, func(t *testing.T) {
   157  			c := NewDBCataloger()
   158  			pkgtest.NewCatalogTester().
   159  				WithImageResolver(t, tt.name).
   160  				IgnoreLocationLayer(). // this fixture can be rebuilt, thus the layer ID will change
   161  				Expects(tt.expected, nil).
   162  				TestCataloger(t, c)
   163  		})
   164  	}
   165  }
   166  
   167  func Test_CatalogerRelationships(t *testing.T) {
   168  	tests := []struct {
   169  		name              string
   170  		fixture           string
   171  		wantRelationships map[string][]string
   172  	}{
   173  		{
   174  			name:    "relationships for coreutils",
   175  			fixture: "test-fixtures/var/lib/dpkg/status.d/coreutils-relationships",
   176  			wantRelationships: map[string][]string{
   177  				"coreutils":    {"libacl1", "libattr1", "libc6", "libgmp10", "libselinux1"},
   178  				"libacl1":      {"libc6"},
   179  				"libattr1":     {"libc6"},
   180  				"libc6":        {"libgcc-s1"},
   181  				"libgcc-s1":    {"gcc-12-base", "libc6"},
   182  				"libgmp10":     {"libc6"},
   183  				"libpcre2-8-0": {"libc6"},
   184  				"libselinux1":  {"libc6", "libpcre2-8-0"},
   185  			},
   186  		},
   187  		{
   188  			name:    "relationships from dpkg example docs",
   189  			fixture: "test-fixtures/var/lib/dpkg/status.d/doc-examples",
   190  			wantRelationships: map[string][]string{
   191  				"made-up-package-1": {"gnumach-dev", "hurd-dev", "kernel-headers-2.2.10"},
   192  				"made-up-package-2": {"liblua5.1-dev", "libluajit5.1-dev"},
   193  				"made-up-package-3": {"bar", "foo"},
   194  				// note that the "made-up-package-4" depends on "made-up-package-5" but not via the direct
   195  				// package name, but through the "provides" virtual package name "virtual-package-5".
   196  				"made-up-package-4": {"made-up-package-5"},
   197  				// note that though there is a "default-mta | mail-transport-agent | not-installed"
   198  				// dependency choice we raise up the packages that are installed for every choice.
   199  				// In this case that means that "default-mta" and "mail-transport-agent".
   200  				"mutt": {"default-mta", "libc6", "mail-transport-agent"},
   201  			},
   202  		},
   203  		{
   204  			name:    "relationships for libpam-runtime",
   205  			fixture: "test-fixtures/var/lib/dpkg/status.d/libpam-runtime",
   206  			wantRelationships: map[string][]string{
   207  				"libpam-runtime": {"cdebconf", "debconf-2.0", "debconf1", "debconf2", "libpam-modules"},
   208  			},
   209  		},
   210  	}
   211  	for _, tt := range tests {
   212  		t.Run(tt.name, func(t *testing.T) {
   213  			pkgs, relationships, err := NewDBCataloger().Catalog(context.Background(), file.NewMockResolverForPaths(tt.fixture))
   214  			require.NotEmpty(t, pkgs)
   215  			require.NotEmpty(t, relationships)
   216  			require.NoError(t, err)
   217  
   218  			if d := cmp.Diff(tt.wantRelationships, abstractRelationships(t, relationships)); d != "" {
   219  				t.Errorf("unexpected relationships (-want +got):\n%s", d)
   220  			}
   221  		})
   222  	}
   223  }
   224  
   225  func TestCataloger_Globs(t *testing.T) {
   226  	tests := []struct {
   227  		name     string
   228  		fixture  string
   229  		expected []string
   230  	}{
   231  		{
   232  			name:    "obtain db status files",
   233  			fixture: "test-fixtures/glob-paths",
   234  			expected: []string{
   235  				"var/lib/dpkg/status",
   236  				"var/lib/dpkg/status.d/pkg-1.0",
   237  				"usr/lib/opkg/status",
   238  				"usr/lib/opkg/info/pkg-1.0.control",
   239  			},
   240  		},
   241  	}
   242  
   243  	for _, test := range tests {
   244  		t.Run(test.name, func(t *testing.T) {
   245  			pkgtest.NewCatalogTester().
   246  				FromDirectory(t, test.fixture).
   247  				ExpectsResolverContentQueries(test.expected).
   248  				TestCataloger(t, NewDBCataloger())
   249  		})
   250  	}
   251  }