github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/alpine/parse_apk_db_test.go (about)

     1  package alpine
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/anchore/syft/syft/file"
    16  	"github.com/anchore/syft/syft/linux"
    17  	"github.com/anchore/syft/syft/pkg"
    18  	"github.com/anchore/syft/syft/pkg/cataloger/generic"
    19  	"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
    20  )
    21  
    22  func TestExtraFileAttributes(t *testing.T) {
    23  	tests := []struct {
    24  		name     string
    25  		expected pkg.ApkDBEntry
    26  	}{
    27  		{
    28  			name: "test extra file attributes (checksum) are ignored",
    29  			expected: pkg.ApkDBEntry{
    30  				Files: []pkg.ApkFileRecord{
    31  					{
    32  						Path: "/usr",
    33  					},
    34  					{
    35  						Path: "/usr/lib",
    36  					},
    37  					{
    38  						Path: "/usr/lib/jvm",
    39  					},
    40  					{
    41  						Path: "/usr/lib/jvm/java-1.8-openjdk",
    42  					},
    43  					{
    44  						Path: "/usr/lib/jvm/java-1.8-openjdk/bin",
    45  					},
    46  					{
    47  						Path:        "/usr/lib/jvm/java-1.8-openjdk/bin/policytool",
    48  						OwnerUID:    "0",
    49  						OwnerGID:    "0",
    50  						Permissions: "755",
    51  						Digest: &file.Digest{
    52  							Algorithm: "'Q1'+base64(sha1)",
    53  							Value:     "Q1M0C9qfC/+kdRiOodeihG2GMRtkE=",
    54  						},
    55  					},
    56  				},
    57  			},
    58  		},
    59  	}
    60  
    61  	for _, test := range tests {
    62  		t.Run(test.name, func(t *testing.T) {
    63  			fixturePath := "test-fixtures/extra-file-attributes"
    64  			lrc := newLocationReadCloser(t, fixturePath)
    65  
    66  			pkgs, _, err := parseApkDB(context.Background(), nil, new(generic.Environment), lrc)
    67  			assert.NoError(t, err)
    68  			require.Len(t, pkgs, 1)
    69  			metadata := pkgs[0].Metadata.(pkg.ApkDBEntry)
    70  
    71  			if diff := cmp.Diff(test.expected.Files, metadata.Files); diff != "" {
    72  				t.Errorf("Files mismatch (-want +got):\n%s", diff)
    73  			}
    74  		})
    75  	}
    76  }
    77  
    78  func TestSinglePackageDetails(t *testing.T) {
    79  	ctx := context.TODO()
    80  	tests := []struct {
    81  		fixture  string
    82  		expected pkg.Package
    83  	}{
    84  		{
    85  			fixture: "test-fixtures/single",
    86  			expected: pkg.Package{
    87  				Name:    "musl-utils",
    88  				Version: "1.1.24-r2",
    89  				Licenses: pkg.NewLicenseSet(
    90  					pkg.NewLicenseWithContext(ctx, "MIT"),
    91  					pkg.NewLicenseWithContext(ctx, "BSD"),
    92  					pkg.NewLicenseWithContext(ctx, "GPL2+"),
    93  				),
    94  				Type: pkg.ApkPkg,
    95  				Metadata: pkg.ApkDBEntry{
    96  					Package:       "musl-utils",
    97  					OriginPackage: "musl",
    98  					Version:       "1.1.24-r2",
    99  					Description:   "the musl c library (libc) implementation",
   100  					Maintainer:    "Timo Teräs <timo.teras@iki.fi>",
   101  					Architecture:  "x86_64",
   102  					URL:           "https://musl.libc.org/",
   103  					Size:          37944,
   104  					InstalledSize: 151552,
   105  					Dependencies:  []string{"scanelf", "so:libc.musl-x86_64.so.1"},
   106  					Provides:      []string{"cmd:getconf", "cmd:getent", "cmd:iconv", "cmd:ldconfig", "cmd:ldd"},
   107  					Checksum:      "Q1bTtF5526tETKfL+lnigzIDvm+2o=",
   108  					GitCommit:     "4024cc3b29ad4c65544ad068b8f59172b5494306",
   109  					Files: []pkg.ApkFileRecord{
   110  						{
   111  							Path: "/sbin",
   112  						},
   113  						{
   114  							Path:        "/sbin/ldconfig",
   115  							OwnerUID:    "0",
   116  							OwnerGID:    "0",
   117  							Permissions: "755",
   118  							Digest: &file.Digest{
   119  								Algorithm: "'Q1'+base64(sha1)",
   120  								Value:     "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=",
   121  							},
   122  						},
   123  						{
   124  							Path: "/usr",
   125  						},
   126  						{
   127  							Path: "/usr/bin",
   128  						},
   129  						{
   130  							Path:        "/usr/bin/iconv",
   131  							OwnerUID:    "0",
   132  							OwnerGID:    "0",
   133  							Permissions: "755",
   134  							Digest: &file.Digest{
   135  								Algorithm: "'Q1'+base64(sha1)",
   136  								Value:     "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=",
   137  							},
   138  						},
   139  						{
   140  							Path:        "/usr/bin/ldd",
   141  							OwnerUID:    "0",
   142  							OwnerGID:    "0",
   143  							Permissions: "755",
   144  							Digest: &file.Digest{
   145  								Algorithm: "'Q1'+base64(sha1)",
   146  								Value:     "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=",
   147  							},
   148  						},
   149  						{
   150  							Path:        "/usr/bin/getconf",
   151  							OwnerUID:    "0",
   152  							OwnerGID:    "0",
   153  							Permissions: "755",
   154  							Digest: &file.Digest{
   155  								Algorithm: "'Q1'+base64(sha1)",
   156  								Value:     "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=",
   157  							},
   158  						},
   159  						{
   160  							Path:        "/usr/bin/getent",
   161  							OwnerUID:    "0",
   162  							OwnerGID:    "0",
   163  							Permissions: "755",
   164  							Digest: &file.Digest{
   165  								Algorithm: "'Q1'+base64(sha1)",
   166  								Value:     "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=",
   167  							},
   168  						},
   169  					},
   170  				},
   171  			},
   172  		},
   173  		{
   174  			fixture: "test-fixtures/empty-deps-and-provides",
   175  			expected: pkg.Package{
   176  				Name:    "alpine-baselayout-data",
   177  				Version: "3.4.0-r0",
   178  				Licenses: pkg.NewLicenseSet(
   179  					pkg.NewLicenseWithContext(ctx, "GPL-2.0-only"),
   180  				),
   181  				Type: pkg.ApkPkg,
   182  				Metadata: pkg.ApkDBEntry{
   183  					Package:       "alpine-baselayout-data",
   184  					OriginPackage: "alpine-baselayout",
   185  					Version:       "3.4.0-r0",
   186  					Description:   "Alpine base dir structure and init scripts",
   187  					Maintainer:    "Natanael Copa <ncopa@alpinelinux.org>",
   188  					Architecture:  "x86_64",
   189  					URL:           "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout",
   190  					Size:          11664,
   191  					InstalledSize: 77824,
   192  					Dependencies:  []string{},
   193  					Provides:      []string{},
   194  					Checksum:      "Q15ffjKT28lB7iSXjzpI/eDdYRCwM=",
   195  					GitCommit:     "bd965a7ebf7fd8f07d7a0cc0d7375bf3e4eb9b24",
   196  					Files: []pkg.ApkFileRecord{
   197  						{Path: "/etc"},
   198  						{Path: "/etc/fstab"},
   199  						{Path: "/etc/group"},
   200  						{Path: "/etc/hostname"},
   201  						{Path: "/etc/hosts"},
   202  						{Path: "/etc/inittab"},
   203  						{Path: "/etc/modules"},
   204  						{Path: "/etc/mtab", OwnerUID: "0", OwnerGID: "0", Permissions: "0777"},
   205  						{Path: "/etc/nsswitch.conf"},
   206  						{Path: "/etc/passwd"},
   207  						{Path: "/etc/profile"},
   208  						{Path: "/etc/protocols"},
   209  						{Path: "/etc/services"},
   210  						{Path: "/etc/shadow", OwnerUID: "0", OwnerGID: "148", Permissions: "0640"},
   211  						{Path: "/etc/shells"},
   212  						{Path: "/etc/sysctl.conf"},
   213  					},
   214  				},
   215  			},
   216  		},
   217  		{
   218  			fixture: "test-fixtures/base",
   219  			expected: pkg.Package{
   220  				Name:    "alpine-baselayout",
   221  				Version: "3.2.0-r6",
   222  				Licenses: pkg.NewLicenseSet(
   223  					pkg.NewLicenseWithContext(ctx, "GPL-2.0-only"),
   224  				),
   225  				Type: pkg.ApkPkg,
   226  				PURL: "",
   227  				Metadata: pkg.ApkDBEntry{
   228  					Package:       "alpine-baselayout",
   229  					OriginPackage: "alpine-baselayout",
   230  					Version:       "3.2.0-r6",
   231  					Description:   "Alpine base dir structure and init scripts",
   232  					Maintainer:    "Natanael Copa <ncopa@alpinelinux.org>",
   233  					Architecture:  "x86_64",
   234  					URL:           "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout",
   235  					Size:          19917,
   236  					InstalledSize: 409600,
   237  					Dependencies:  []string{"/bin/sh", "so:libc.musl-x86_64.so.1"},
   238  					Provides:      []string{"cmd:mkmntdirs"},
   239  					Checksum:      "Q1myMNfd7u5v5UTgNHeq1e31qTjZU=",
   240  					GitCommit:     "e1c51734fa96fa4bac92e9f14a474324c67916fc",
   241  					Files: []pkg.ApkFileRecord{
   242  						{
   243  							Path: "/dev",
   244  						},
   245  						{
   246  							Path: "/dev/pts",
   247  						},
   248  						{
   249  							Path: "/dev/shm",
   250  						},
   251  						{
   252  							Path: "/etc",
   253  						},
   254  						{
   255  							Path: "/etc/fstab",
   256  							Digest: &file.Digest{
   257  								Algorithm: "'Q1'+base64(sha1)",
   258  								Value:     "Q11Q7hNe8QpDS531guqCdrXBzoA/o=",
   259  							},
   260  						},
   261  						{
   262  							Path: "/etc/group",
   263  							Digest: &file.Digest{
   264  								Algorithm: "'Q1'+base64(sha1)",
   265  								Value:     "Q1oJ16xWudgKOrXIEquEDzlF2Lsm4=",
   266  							},
   267  						},
   268  						{
   269  							Path: "/etc/hostname",
   270  							Digest: &file.Digest{
   271  								Algorithm: "'Q1'+base64(sha1)",
   272  								Value:     "Q16nVwYVXP/tChvUPdukVD2ifXOmc=",
   273  							},
   274  						},
   275  						{
   276  							Path: "/etc/hosts",
   277  							Digest: &file.Digest{
   278  								Algorithm: "'Q1'+base64(sha1)",
   279  								Value:     "Q1BD6zJKZTRWyqGnPi4tSfd3krsMU=",
   280  							},
   281  						},
   282  						{
   283  							Path: "/etc/inittab",
   284  							Digest: &file.Digest{
   285  								Algorithm: "'Q1'+base64(sha1)",
   286  								Value:     "Q1TsthbhW7QzWRe1E/NKwTOuD4pHc=",
   287  							},
   288  						},
   289  						{
   290  							Path: "/etc/modules",
   291  							Digest: &file.Digest{
   292  								Algorithm: "'Q1'+base64(sha1)",
   293  								Value:     "Q1toogjUipHGcMgECgPJX64SwUT1M=",
   294  							},
   295  						},
   296  						{
   297  							Path: "/etc/motd",
   298  							Digest: &file.Digest{
   299  								Algorithm: "'Q1'+base64(sha1)",
   300  								Value:     "Q1XmduVVNURHQ27TvYp1Lr5TMtFcA=",
   301  							},
   302  						},
   303  						{
   304  							Path:        "/etc/mtab",
   305  							OwnerUID:    "0",
   306  							OwnerGID:    "0",
   307  							Permissions: "777",
   308  							Digest: &file.Digest{
   309  								Algorithm: "'Q1'+base64(sha1)",
   310  								Value:     "Q1kiljhXXH1LlQroHsEJIkPZg2eiw=",
   311  							},
   312  						},
   313  						{
   314  							Path: "/etc/passwd",
   315  							Digest: &file.Digest{
   316  								Algorithm: "'Q1'+base64(sha1)",
   317  								Value:     "Q1TchuuLUfur0izvfZQZxgN/LJhB8=",
   318  							},
   319  						},
   320  						{
   321  							Path: "/etc/profile",
   322  							Digest: &file.Digest{
   323  								Algorithm: "'Q1'+base64(sha1)",
   324  								Value:     "Q1KpFb8kl5LvwXWlY3e58FNsjrI34=",
   325  							},
   326  						},
   327  						{
   328  							Path: "/etc/protocols",
   329  							Digest: &file.Digest{
   330  								Algorithm: "'Q1'+base64(sha1)",
   331  								Value:     "Q13FqXUnvuOpMDrH/6rehxuYAEE34=",
   332  							},
   333  						},
   334  						{
   335  							Path: "/etc/services",
   336  							Digest: &file.Digest{
   337  								Algorithm: "'Q1'+base64(sha1)",
   338  								Value:     "Q1C6HJNgQvLWqt5VY+n7MZJ1rsDuY=",
   339  							},
   340  						},
   341  						{
   342  							Path:        "/etc/shadow",
   343  							OwnerUID:    "0",
   344  							OwnerGID:    "42",
   345  							Permissions: "640",
   346  							Digest: &file.Digest{
   347  								Algorithm: "'Q1'+base64(sha1)",
   348  								Value:     "Q1ltrPIAW2zHeDiajsex2Bdmq3uqA=",
   349  							},
   350  						},
   351  						{
   352  							Path: "/etc/shells",
   353  							Digest: &file.Digest{
   354  								Algorithm: "'Q1'+base64(sha1)",
   355  								Value:     "Q1ojm2YdpCJ6B/apGDaZ/Sdb2xJkA=",
   356  							},
   357  						},
   358  						{
   359  							Path: "/etc/sysctl.conf",
   360  							Digest: &file.Digest{
   361  								Algorithm: "'Q1'+base64(sha1)",
   362  								Value:     "Q14upz3tfnNxZkIEsUhWn7Xoiw96g=",
   363  							},
   364  						},
   365  						{
   366  							Path: "/etc/apk",
   367  						},
   368  						{
   369  							Path: "/etc/conf.d",
   370  						},
   371  						{
   372  							Path: "/etc/crontabs",
   373  						},
   374  						{
   375  							Path:        "/etc/crontabs/root",
   376  							OwnerUID:    "0",
   377  							OwnerGID:    "0",
   378  							Permissions: "600",
   379  							Digest: &file.Digest{
   380  								Algorithm: "'Q1'+base64(sha1)",
   381  								Value:     "Q1vfk1apUWI4yLJGhhNRd0kJixfvY=",
   382  							},
   383  						},
   384  						{
   385  							Path: "/etc/init.d",
   386  						},
   387  						{
   388  							Path: "/etc/modprobe.d",
   389  						},
   390  						{
   391  							Path: "/etc/modprobe.d/aliases.conf",
   392  							Digest: &file.Digest{
   393  								Algorithm: "'Q1'+base64(sha1)",
   394  								Value:     "Q1WUbh6TBYNVK7e4Y+uUvLs/7viqk=",
   395  							},
   396  						},
   397  						{
   398  							Path: "/etc/modprobe.d/blacklist.conf",
   399  							Digest: &file.Digest{
   400  								Algorithm: "'Q1'+base64(sha1)",
   401  								Value:     "Q1xxYGU6S6TLQvb7ervPrWWwAWqMg=",
   402  							},
   403  						},
   404  						{
   405  							Path: "/etc/modprobe.d/i386.conf",
   406  							Digest: &file.Digest{
   407  								Algorithm: "'Q1'+base64(sha1)",
   408  								Value:     "Q1pnay/njn6ol9cCssL7KiZZ8etlc=",
   409  							},
   410  						},
   411  						{
   412  							Path: "/etc/modprobe.d/kms.conf",
   413  							Digest: &file.Digest{
   414  								Algorithm: "'Q1'+base64(sha1)",
   415  								Value:     "Q1ynbLn3GYDpvajba/ldp1niayeog=",
   416  							},
   417  						},
   418  						{
   419  							Path: "/etc/modules-load.d",
   420  						},
   421  						{
   422  							Path: "/etc/network",
   423  						},
   424  						{
   425  							Path: "/etc/network/if-down.d",
   426  						},
   427  						{
   428  							Path: "/etc/network/if-post-down.d",
   429  						},
   430  						{
   431  							Path: "/etc/network/if-pre-up.d",
   432  						},
   433  						{
   434  							Path: "/etc/network/if-up.d",
   435  						},
   436  						{
   437  							Path: "/etc/opt",
   438  						},
   439  						{
   440  							Path: "/etc/periodic",
   441  						},
   442  						{
   443  							Path: "/etc/periodic/15min",
   444  						},
   445  						{
   446  							Path: "/etc/periodic/daily",
   447  						},
   448  						{
   449  							Path: "/etc/periodic/hourly",
   450  						},
   451  						{
   452  							Path: "/etc/periodic/monthly",
   453  						},
   454  						{
   455  							Path: "/etc/periodic/weekly",
   456  						},
   457  						{
   458  							Path: "/etc/profile.d",
   459  						},
   460  						{
   461  							Path: "/etc/profile.d/color_prompt",
   462  							Digest: &file.Digest{
   463  								Algorithm: "'Q1'+base64(sha1)",
   464  								Value:     "Q10wL23GuSCVfumMRgakabUI6EsSk=",
   465  							},
   466  						},
   467  						{
   468  							Path: "/etc/profile.d/locale",
   469  							Digest: &file.Digest{
   470  								Algorithm: "'Q1'+base64(sha1)",
   471  								Value:     "Q1R4bIEpnKxxOSrlnZy9AoawqZ5DU=",
   472  							},
   473  						},
   474  						{
   475  							Path: "/etc/sysctl.d",
   476  						},
   477  						{
   478  							Path: "/home",
   479  						},
   480  						{
   481  							Path: "/lib",
   482  						},
   483  						{
   484  							Path: "/lib/firmware",
   485  						},
   486  						{
   487  							Path: "/lib/mdev",
   488  						},
   489  						{
   490  							Path: "/lib/modules-load.d",
   491  						},
   492  						{
   493  							Path: "/lib/sysctl.d",
   494  						},
   495  						{
   496  							Path: "/lib/sysctl.d/00-alpine.conf",
   497  							Digest: &file.Digest{
   498  								Algorithm: "'Q1'+base64(sha1)",
   499  								Value:     "Q1HpElzW1xEgmKfERtTy7oommnq6c=",
   500  							},
   501  						},
   502  						{
   503  							Path: "/media",
   504  						},
   505  						{
   506  							Path: "/media/cdrom",
   507  						},
   508  						{
   509  							Path: "/media/floppy",
   510  						},
   511  						{
   512  							Path: "/media/usb",
   513  						},
   514  						{
   515  							Path: "/mnt",
   516  						},
   517  						{
   518  							Path: "/opt",
   519  						},
   520  						{
   521  							Path: "/proc",
   522  						},
   523  						{
   524  							Path:        "/root",
   525  							OwnerUID:    "0",
   526  							OwnerGID:    "0",
   527  							Permissions: "700",
   528  						},
   529  						{
   530  							Path: "/run",
   531  						},
   532  						{
   533  							Path: "/sbin",
   534  						},
   535  						{
   536  							Path:        "/sbin/mkmntdirs",
   537  							OwnerUID:    "0",
   538  							OwnerGID:    "0",
   539  							Permissions: "755",
   540  							Digest: &file.Digest{
   541  								Algorithm: "'Q1'+base64(sha1)",
   542  								Value:     "Q1YeuSmC7iDbEWrusPzA/zUQF6YSg=",
   543  							},
   544  						},
   545  						{
   546  							Path: "/srv",
   547  						},
   548  						{
   549  							Path: "/sys",
   550  						},
   551  						{
   552  							Path:        "/tmp",
   553  							OwnerUID:    "0",
   554  							OwnerGID:    "0",
   555  							Permissions: "1777",
   556  						},
   557  						{
   558  							Path: "/usr",
   559  						},
   560  						{
   561  							Path: "/usr/lib",
   562  						},
   563  						{
   564  							Path: "/usr/lib/modules-load.d",
   565  						},
   566  						{
   567  							Path: "/usr/local",
   568  						},
   569  						{
   570  							Path: "/usr/local/bin",
   571  						},
   572  						{
   573  							Path: "/usr/local/lib",
   574  						},
   575  						{
   576  							Path: "/usr/local/share",
   577  						},
   578  						{
   579  							Path: "/usr/sbin",
   580  						},
   581  						{
   582  							Path: "/usr/share",
   583  						},
   584  						{
   585  							Path: "/usr/share/man",
   586  						},
   587  						{
   588  							Path: "/usr/share/misc",
   589  						},
   590  						{
   591  							Path: "/var",
   592  						},
   593  						{
   594  							Path:        "/var/run",
   595  							OwnerUID:    "0",
   596  							OwnerGID:    "0",
   597  							Permissions: "777",
   598  							Digest: &file.Digest{
   599  								Algorithm: "'Q1'+base64(sha1)",
   600  								Value:     "Q11/SNZz/8cK2dSKK+cJpVrZIuF4Q=",
   601  							},
   602  						},
   603  						{
   604  							Path: "/var/cache",
   605  						},
   606  						{
   607  							Path: "/var/cache/misc",
   608  						},
   609  						{
   610  							Path:        "/var/empty",
   611  							OwnerUID:    "0",
   612  							OwnerGID:    "0",
   613  							Permissions: "555",
   614  						},
   615  						{
   616  							Path: "/var/lib",
   617  						},
   618  						{
   619  							Path: "/var/lib/misc",
   620  						},
   621  						{
   622  							Path: "/var/local",
   623  						},
   624  						{
   625  							Path: "/var/lock",
   626  						},
   627  						{
   628  							Path: "/var/lock/subsys",
   629  						},
   630  						{
   631  							Path: "/var/log",
   632  						},
   633  						{
   634  							Path: "/var/mail",
   635  						},
   636  						{
   637  							Path: "/var/opt",
   638  						},
   639  						{
   640  							Path: "/var/spool",
   641  						},
   642  						{
   643  							Path:        "/var/spool/mail",
   644  							OwnerUID:    "0",
   645  							OwnerGID:    "0",
   646  							Permissions: "777",
   647  							Digest: &file.Digest{
   648  								Algorithm: "'Q1'+base64(sha1)",
   649  								Value:     "Q1dzbdazYZA2nTzSIG3YyNw7d4Juc=",
   650  							},
   651  						},
   652  						{
   653  							Path: "/var/spool/cron",
   654  						},
   655  						{
   656  							Path:        "/var/spool/cron/crontabs",
   657  							OwnerUID:    "0",
   658  							OwnerGID:    "0",
   659  							Permissions: "777",
   660  							Digest: &file.Digest{
   661  								Algorithm: "'Q1'+base64(sha1)",
   662  								Value:     "Q1OFZt+ZMp7j0Gny0rqSKuWJyqYmA=",
   663  							},
   664  						},
   665  						{
   666  							Path:        "/var/tmp",
   667  							OwnerUID:    "0",
   668  							OwnerGID:    "0",
   669  							Permissions: "1777",
   670  						},
   671  					},
   672  				},
   673  			},
   674  		},
   675  	}
   676  
   677  	for _, test := range tests {
   678  		t.Run(test.fixture, func(t *testing.T) {
   679  			fixtureLocation := file.NewLocation(test.fixture)
   680  			test.expected.Locations = file.NewLocationSet(fixtureLocation)
   681  			licenses := test.expected.Licenses.ToSlice()
   682  			for i := range licenses {
   683  				licenses[i].Locations.Add(fixtureLocation)
   684  			}
   685  			test.expected.Licenses = pkg.NewLicenseSet(licenses...)
   686  			pkgtest.TestFileParser(t, test.fixture, parseApkDB, []pkg.Package{test.expected}, nil)
   687  		})
   688  	}
   689  }
   690  
   691  func Test_processChecksum(t *testing.T) {
   692  	tests := []struct {
   693  		name  string
   694  		value string
   695  		want  file.Digest
   696  	}{
   697  		{
   698  			name:  "md5",
   699  			value: "38870ede8700535d7382ff66a46fcc2f",
   700  			want: file.Digest{
   701  				Algorithm: "md5",
   702  				Value:     "38870ede8700535d7382ff66a46fcc2f",
   703  			},
   704  		},
   705  		{
   706  			name:  "sha1",
   707  			value: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=",
   708  			want: file.Digest{
   709  				Algorithm: "'Q1'+base64(sha1)",
   710  				Value:     "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=",
   711  			},
   712  		},
   713  	}
   714  
   715  	for _, test := range tests {
   716  		t.Run(test.name, func(t *testing.T) {
   717  			assert.Equal(t, &test.want, processChecksum(test.value))
   718  		})
   719  	}
   720  }
   721  
   722  func Test_parseApkDB_expectedPkgNames(t *testing.T) {
   723  	tests := []struct {
   724  		fixture      string
   725  		wantPkgNames []string
   726  		wantErr      assert.ErrorAssertionFunc
   727  	}{
   728  		{
   729  			fixture: "very-large-entries",
   730  			wantPkgNames: []string{
   731  				"ca-certificates-bundle",
   732  				"glibc-locale-posix",
   733  				"wolfi-baselayout",
   734  				"glibc",
   735  				"libcrypto3",
   736  				"libssl3",
   737  				"zlib",
   738  				"apk-tools",
   739  				"ncurses-terminfo-base",
   740  				"ncurses",
   741  				"bash",
   742  				"libcap",
   743  				"bubblewrap",
   744  				"busybox",
   745  				"libbrotlicommon1",
   746  				"libbrotlidec1",
   747  				"libnghttp2-14",
   748  				"libcurl4",
   749  				"curl",
   750  				"expat",
   751  				"libpcre2-8-0",
   752  				"git",
   753  				"binutils",
   754  				"libstdc++-dev",
   755  				"libgcc",
   756  				"libstdc++",
   757  				"gmp",
   758  				"isl",
   759  				"mpfr",
   760  				"mpc",
   761  				"gcc",
   762  				"linux-headers",
   763  				"glibc-dev",
   764  				"make",
   765  				"pkgconf",
   766  				"build-base",
   767  				"go",
   768  				"tree",
   769  				"sdk",
   770  			},
   771  			wantErr: assert.NoError,
   772  		},
   773  	}
   774  
   775  	for _, test := range tests {
   776  		t.Run(test.fixture, func(t *testing.T) {
   777  			fixturePath := filepath.Join("test-fixtures", test.fixture)
   778  			lrc := newLocationReadCloser(t, fixturePath)
   779  
   780  			pkgs, _, err := parseApkDB(context.Background(), nil, new(generic.Environment), lrc)
   781  			test.wantErr(t, err)
   782  
   783  			names := toPackageNames(pkgs)
   784  			if diff := cmp.Diff(test.wantPkgNames, names); diff != "" {
   785  				t.Errorf("Packages mismatch (-want +got):\n%s", diff)
   786  			}
   787  		})
   788  	}
   789  }
   790  
   791  func toPackageNames(pkgs []pkg.Package) []string {
   792  	names := make([]string, 0, len(pkgs))
   793  	for _, p := range pkgs {
   794  		names = append(names, p.Name)
   795  	}
   796  
   797  	return names
   798  }
   799  
   800  func newLocationReadCloser(t *testing.T, path string) file.LocationReadCloser {
   801  	f, err := os.Open(path)
   802  	require.NoError(t, err)
   803  	t.Cleanup(func() { f.Close() })
   804  
   805  	return file.NewLocationReadCloser(file.NewLocation(path), f)
   806  }
   807  
   808  func TestParseReleasesFromAPKRepository(t *testing.T) {
   809  	tests := []struct {
   810  		repos string
   811  		want  []linux.Release
   812  		desc  string
   813  	}{
   814  		{
   815  			"https://foo.alpinelinux.org/alpine/v3.14/main",
   816  			[]linux.Release{
   817  				{Name: "Alpine Linux", ID: "alpine", VersionID: "3.14"},
   818  			},
   819  			"single repo",
   820  		},
   821  		{
   822  			`https://foo.alpinelinux.org/alpine/v3.14/main
   823  https://foo.alpinelinux.org/alpine/v3.14/community`,
   824  			[]linux.Release{
   825  				{Name: "Alpine Linux", ID: "alpine", VersionID: "3.14"},
   826  				{Name: "Alpine Linux", ID: "alpine", VersionID: "3.14"},
   827  			},
   828  			"multiple repos",
   829  		},
   830  		{
   831  			``,
   832  			nil,
   833  			"empty",
   834  		},
   835  		{
   836  			`https://foo.bar.org/alpine/v3.14/main
   837  https://foo.them.org/alpine/v3.14/community`,
   838  			nil,
   839  			"invalid repos",
   840  		},
   841  	}
   842  	for _, tt := range tests {
   843  		t.Run(tt.desc, func(t *testing.T) {
   844  			reposReader := io.NopCloser(strings.NewReader(tt.repos))
   845  			got := parseReleasesFromAPKRepository(file.LocationReadCloser{
   846  				Location:   file.NewLocation("test"),
   847  				ReadCloser: reposReader,
   848  			})
   849  			assert.Equal(t, tt.want, got)
   850  		})
   851  	}
   852  }