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