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