github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/analyzer/imgconf/apk/apk_test.go (about)

     1  package apk
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"os"
     9  	"reflect"
    10  	"sort"
    11  	"testing"
    12  	"time"
    13  
    14  	v1 "github.com/google/go-containerregistry/pkg/v1"
    15  	"github.com/kylelemons/godebug/pretty"
    16  	"github.com/samber/lo"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  
    20  	"github.com/devseccon/trivy/pkg/fanal/analyzer"
    21  	"github.com/devseccon/trivy/pkg/fanal/types"
    22  )
    23  
    24  var (
    25  	oldAlpineConfig = &v1.ConfigFile{
    26  		Architecture: "amd64",
    27  		Container:    "f5b08762ace1af069127a337579acd51c415b919d736e6615b453a3c6fbf260d",
    28  		Created: v1.Time{
    29  			Time: time.Date(2018, time.October, 15, 21, 28, 53, 798628678, time.UTC),
    30  		},
    31  		DockerVersion: "17.06.2-ce",
    32  		History: []v1.History{
    33  			{
    34  				Created: v1.Time{
    35  					Time: time.Date(2018, time.September, 11, 22, 19, 38, 885299940, time.UTC),
    36  				},
    37  				CreatedBy: "/bin/sh -c #(nop) ADD file:49f9e47e678d868d5b023482aa8dded71276a241a665c4f8b55ca77269321b34 in / ",
    38  			},
    39  			{
    40  				Created: v1.Time{
    41  					Time: time.Date(2018, time.September, 11, 22, 19, 39, 58628442, time.UTC),
    42  				},
    43  				CreatedBy:  "/bin/sh -c #(nop)  CMD [\"/bin/sh\"]",
    44  				EmptyLayer: true,
    45  			},
    46  			{
    47  				Created: v1.Time{
    48  					Time: time.Date(2018, time.September, 12, 1, 26, 59, 951316015, time.UTC),
    49  				},
    50  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHPIZE_DEPS=autoconf \t\tdpkg-dev dpkg \t\tfile \t\tg++ \t\tgcc \t\tlibc-dev \t\tmake \t\tpkgconf \t\tre2c",
    51  				EmptyLayer: true,
    52  			},
    53  			{
    54  				Created: v1.Time{
    55  					Time: time.Date(2018, time.September, 12, 1, 27, 1, 470388635, time.UTC),
    56  				},
    57  				CreatedBy: "/bin/sh -c apk add --no-cache --virtual .persistent-deps \t\tca-certificates \t\tcurl \t\ttar \t\txz \t\tlibressl",
    58  			},
    59  			{
    60  				Created: v1.Time{
    61  					Time: time.Date(2018, time.September, 12, 1, 27, 2, 432381785, time.UTC),
    62  				},
    63  				CreatedBy: "/bin/sh -c set -x \t&& addgroup -g 82 -S www-data \t&& adduser -u 82 -D -S -G www-data www-data",
    64  			},
    65  			{
    66  				Created: v1.Time{
    67  					Time: time.Date(2018, time.September, 12, 1, 27, 2, 715120309, time.UTC),
    68  				},
    69  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_INI_DIR=/usr/local/etc/php",
    70  				EmptyLayer: true,
    71  			},
    72  			{
    73  				Created: v1.Time{
    74  					Time: time.Date(2018, time.September, 12, 1, 27, 3, 655421341, time.UTC),
    75  				},
    76  				CreatedBy: "/bin/sh -c mkdir -p $PHP_INI_DIR/conf.d",
    77  			},
    78  			{
    79  				Created: v1.Time{
    80  					Time: time.Date(2018, time.September, 12, 1, 27, 3, 931799562, time.UTC),
    81  				},
    82  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2",
    83  				EmptyLayer: true,
    84  			},
    85  			{
    86  				Created: v1.Time{
    87  					Time: time.Date(2018, time.September, 12, 1, 27, 4, 210945499, time.UTC),
    88  				},
    89  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2",
    90  				EmptyLayer: true,
    91  			},
    92  			{
    93  				Created: v1.Time{
    94  					Time: time.Date(2018, time.September, 12, 1, 27, 4, 523116501, time.UTC),
    95  				},
    96  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_LDFLAGS=-Wl,-O1 -Wl,--hash-style=both -pie",
    97  				EmptyLayer: true,
    98  			},
    99  			{
   100  				Created: v1.Time{
   101  					Time: time.Date(2018, time.September, 12, 1, 27, 4, 795176159, time.UTC),
   102  				},
   103  				CreatedBy:  "/bin/sh -c #(nop)  ENV GPG_KEYS=1729F83938DA44E27BA0F4D3DBDB397470D12172 B1B44D8F021E4E2D6021E995DC9FF8D3EE5AF27F",
   104  				EmptyLayer: true,
   105  			},
   106  			{
   107  				Created: v1.Time{
   108  					Time: time.Date(2018, time.October, 15, 19, 2, 18, 415761689, time.UTC),
   109  				},
   110  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_VERSION=7.2.11",
   111  				EmptyLayer: true,
   112  			},
   113  			{
   114  				Created: v1.Time{
   115  					Time: time.Date(2018, time.October, 15, 19, 2, 18, 599097853, time.UTC),
   116  				},
   117  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_URL=https://secure.php.net/get/php-7.2.11.tar.xz/from/this/mirror PHP_ASC_URL=https://secure.php.net/get/php-7.2.11.tar.xz.asc/from/this/mirror",
   118  				EmptyLayer: true,
   119  			},
   120  			{
   121  				Created: v1.Time{
   122  					Time: time.Date(2018, time.October, 15, 19, 2, 18, 782890412, time.UTC),
   123  				},
   124  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_SHA256=da1a705c0bc46410e330fc6baa967666c8cd2985378fb9707c01a8e33b01d985 PHP_MD5=",
   125  				EmptyLayer: true,
   126  			},
   127  			{
   128  				Created: v1.Time{
   129  					Time: time.Date(2018, time.October, 15, 19, 2, 22, 795846753, time.UTC),
   130  				},
   131  				CreatedBy: "/bin/sh -c set -xe; \t\tapk add --no-cache --virtual .fetch-deps \t\tgnupg \t\twget \t; \t\tmkdir -p /usr/src; \tcd /usr/src; \t\twget -O php.tar.xz \"$PHP_URL\"; \t\tif [ -n \"$PHP_SHA256\" ]; then \t\techo \"$PHP_SHA256 *php.tar.xz\" | sha256sum -c -; \tfi; \tif [ -n \"$PHP_MD5\" ]; then \t\techo \"$PHP_MD5 *php.tar.xz\" | md5sum -c -; \tfi; \t\tif [ -n \"$PHP_ASC_URL\" ]; then \t\twget -O php.tar.xz.asc \"$PHP_ASC_URL\"; \t\texport GNUPGHOME=\"$(mktemp -d)\"; \t\tfor key in $GPG_KEYS; do \t\t\tgpg --keyserver ha.pool.sks-keyservers.net --recv-keys \"$key\"; \t\tdone; \t\tgpg --batch --verify php.tar.xz.asc php.tar.xz; \t\tcommand -v gpgconf > /dev/null && gpgconf --kill all; \t\trm -rf \"$GNUPGHOME\"; \tfi; \t\tapk del .fetch-deps",
   132  			},
   133  			{
   134  				Created: v1.Time{
   135  					Time: time.Date(2018, time.October, 15, 19, 2, 23, 71406376, time.UTC),
   136  				},
   137  				CreatedBy: "/bin/sh -c #(nop) COPY file:207c686e3fed4f71f8a7b245d8dcae9c9048d276a326d82b553c12a90af0c0ca in /usr/local/bin/ ",
   138  			},
   139  			{
   140  				Created: v1.Time{
   141  					Time: time.Date(2018, time.October, 15, 19, 7, 13, 93396680, time.UTC),
   142  				},
   143  				CreatedBy: "/bin/sh -c set -xe \t&& apk add --no-cache --virtual .build-deps \t\t$PHPIZE_DEPS \t\tcoreutils \t\tcurl-dev \t\tlibedit-dev \t\tlibressl-dev \t\tlibsodium-dev \t\tlibxml2-dev \t\tsqlite-dev \t\t&& export CFLAGS=\"$PHP_CFLAGS\" \t\tCPPFLAGS=\"$PHP_CPPFLAGS\" \t\tLDFLAGS=\"$PHP_LDFLAGS\" \t&& docker-php-source extract \t&& cd /usr/src/php \t&& gnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\" \t&& ./configure \t\t--build=\"$gnuArch\" \t\t--with-config-file-path=\"$PHP_INI_DIR\" \t\t--with-config-file-scan-dir=\"$PHP_INI_DIR/conf.d\" \t\t\t\t--enable-option-checking=fatal \t\t\t\t--with-mhash \t\t\t\t--enable-ftp \t\t--enable-mbstring \t\t--enable-mysqlnd \t\t--with-sodium=shared \t\t\t\t--with-curl \t\t--with-libedit \t\t--with-openssl \t\t--with-zlib \t\t\t\t$(test \"$gnuArch\" = 's390x-linux-gnu' && echo '--without-pcre-jit') \t\t\t\t$PHP_EXTRA_CONFIGURE_ARGS \t&& make -j \"$(nproc)\" \t&& make install \t&& { find /usr/local/bin /usr/local/sbin -type f -perm +0111 -exec strip --strip-all '{}' + || true; } \t&& make clean \t\t&& cp -v php.ini-* \"$PHP_INI_DIR/\" \t\t&& cd / \t&& docker-php-source delete \t\t&& runDeps=\"$( \t\tscanelf --needed --nobanner --format '%n#p' --recursive /usr/local \t\t\t| tr ',' '\\n' \t\t\t| sort -u \t\t\t| awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' \t)\" \t&& apk add --no-cache --virtual .php-rundeps $runDeps \t\t&& apk del .build-deps \t\t&& pecl update-channels \t&& rm -rf /tmp/pear ~/.pearrc",
   144  			},
   145  			{
   146  				Created: v1.Time{
   147  					Time: time.Date(2018, time.October, 15, 19, 7, 13, 722586262, time.UTC),
   148  				},
   149  				CreatedBy: "/bin/sh -c #(nop) COPY multi:2cdcedabcf5a3b9ae610fab7848e94bc2f64b4d85710d55fd6f79e44dacf73d8 in /usr/local/bin/ ",
   150  			},
   151  			{
   152  				Created: v1.Time{
   153  					Time: time.Date(2018, time.October, 15, 19, 7, 14, 618087104, time.UTC),
   154  				},
   155  				CreatedBy: "/bin/sh -c docker-php-ext-enable sodium",
   156  			},
   157  			{
   158  				Created: v1.Time{
   159  					Time: time.Date(2018, time.October, 15, 19, 7, 14, 826981756, time.UTC),
   160  				},
   161  				CreatedBy:  "/bin/sh -c #(nop)  ENTRYPOINT [\"docker-php-entrypoint\"]",
   162  				EmptyLayer: true,
   163  			},
   164  			{
   165  				Created: v1.Time{
   166  					Time: time.Date(2018, time.October, 15, 19, 7, 15, 10831572, time.UTC),
   167  				},
   168  				CreatedBy:  "/bin/sh -c #(nop)  CMD [\"php\" \"-a\"]",
   169  				EmptyLayer: true,
   170  			},
   171  			{
   172  				Created: v1.Time{
   173  					Time: time.Date(2018, time.October, 15, 21, 28, 21, 919735971, time.UTC),
   174  				},
   175  				CreatedBy: "/bin/sh -c apk --no-cache add git subversion openssh mercurial tini bash patch",
   176  			},
   177  			{
   178  				Created: v1.Time{
   179  					Time: time.Date(2018, time.October, 15, 21, 28, 22, 611763893, time.UTC),
   180  				},
   181  				CreatedBy: "/bin/sh -c echo \"memory_limit=-1\" > \"$PHP_INI_DIR/conf.d/memory-limit.ini\"  && echo \"date.timezone=${PHP_TIMEZONE:-UTC}\" > \"$PHP_INI_DIR/conf.d/date_timezone.ini\"",
   182  			},
   183  			{
   184  				Created: v1.Time{
   185  					Time: time.Date(2018, time.October, 15, 21, 28, 50, 224278478, time.UTC),
   186  				},
   187  				CreatedBy: "/bin/sh -c apk add --no-cache --virtual .build-deps zlib-dev  && docker-php-ext-install zip  && runDeps=\"$(     scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions     | tr ',' '\\n'     | sort -u     | awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }'     )\"  && apk add --virtual .composer-phpext-rundeps $runDeps  && apk del .build-deps",
   188  			},
   189  			{
   190  				Created: v1.Time{
   191  					Time: time.Date(2018, time.October, 15, 21, 28, 50, 503010161, time.UTC),
   192  				},
   193  				CreatedBy:  "/bin/sh -c #(nop)  ENV COMPOSER_ALLOW_SUPERUSER=1",
   194  				EmptyLayer: true,
   195  			},
   196  			{
   197  				Created: v1.Time{
   198  					Time: time.Date(2018, time.October, 15, 21, 28, 50, 775378559, time.UTC),
   199  				},
   200  				CreatedBy:  "/bin/sh -c #(nop)  ENV COMPOSER_HOME=/tmp",
   201  				EmptyLayer: true,
   202  			},
   203  			{
   204  				Created: v1.Time{
   205  					time.Date(2018, time.October, 15, 21, 28, 51, 35012363, time.UTC),
   206  				},
   207  				CreatedBy:  "/bin/sh -c #(nop)  ENV COMPOSER_VERSION=1.7.2",
   208  				EmptyLayer: true,
   209  			},
   210  			{
   211  				Created: v1.Time{
   212  					Time: time.Date(2018, time.October, 15, 21, 28, 52, 491402624, time.UTC),
   213  				},
   214  				CreatedBy: "/bin/sh -c curl --silent --fail --location --retry 3 --output /tmp/installer.php --url https://raw.githubusercontent.com/composer/getcomposer.org/b107d959a5924af895807021fcef4ffec5a76aa9/web/installer  && php -r \"     \\$signature = '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061';     \\$hash = hash('SHA384', file_get_contents('/tmp/installer.php'));     if (!hash_equals(\\$signature, \\$hash)) {         unlink('/tmp/installer.php');         echo 'Integrity check failed, installer is either corrupt or worse.' . PHP_EOL;         exit(1);     }\"  && php /tmp/installer.php --no-ansi --install-dir=/usr/bin --filename=composer --version=${COMPOSER_VERSION}  && composer --ansi --version --no-interaction  && rm -rf /tmp/* /tmp/.htaccess",
   215  			},
   216  			{
   217  				Created: v1.Time{
   218  					Time: time.Date(2018, time.October, 15, 21, 28, 52, 948859545, time.UTC),
   219  				},
   220  				CreatedBy: "/bin/sh -c #(nop) COPY file:295943a303e8f27de4302b6aa3687bce4b1d1392335efaaab9ecd37bec5ab4c5 in /docker-entrypoint.sh ",
   221  			},
   222  			{
   223  				Created: v1.Time{
   224  					Time: time.Date(2018, time.October, 15, 21, 28, 53, 295399872, time.UTC),
   225  				},
   226  				CreatedBy: "/bin/sh -c #(nop) WORKDIR /app",
   227  			},
   228  			{
   229  				Created: v1.Time{
   230  					Time: time.Date(2018, time.October, 15, 21, 28, 53, 582920705, time.UTC),
   231  				},
   232  				CreatedBy:  "/bin/sh -c #(nop)  ENTRYPOINT [\"/bin/sh\" \"/docker-entrypoint.sh\"]",
   233  				EmptyLayer: true,
   234  			},
   235  			{
   236  				Created: v1.Time{
   237  					time.Date(2018, time.October, 15, 21, 28, 53, 798628678, time.UTC),
   238  				},
   239  				CreatedBy:  "/bin/sh -c #(nop)  CMD [\"composer\"]",
   240  				EmptyLayer: true,
   241  			},
   242  		},
   243  		OS: "linux",
   244  		RootFS: v1.RootFS{
   245  			Type: "layers",
   246  			DiffIDs: []v1.Hash{
   247  				{
   248  					Algorithm: "sha256",
   249  					Hex:       "ebf12965380b39889c99a9c02e82ba465f887b45975b6e389d42e9e6a3857888",
   250  				},
   251  				{
   252  					Algorithm: "sha256",
   253  					Hex:       "0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
   254  				},
   255  				{
   256  					Algorithm: "sha256",
   257  					Hex:       "9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
   258  				},
   259  				{
   260  					Algorithm: "sha256",
   261  					Hex:       "dc00fbef458ad3204bbb548e2d766813f593d857b845a940a0de76aed94c94d1",
   262  				},
   263  				{
   264  					Algorithm: "sha256",
   265  					Hex:       "5cb2a5009179b1e78ecfef81a19756328bb266456cf9a9dbbcf9af8b83b735f0",
   266  				},
   267  				{
   268  					Algorithm: "sha256",
   269  					Hex:       "9bdb2c849099a99c8ab35f6fd7469c623635e8f4479a0a5a3df61e22bae509f6",
   270  				},
   271  				{
   272  					Algorithm: "sha256",
   273  					Hex:       "6408527580eade39c2692dbb6b0f6a9321448d06ea1c2eef06bb7f37da9c5013",
   274  				},
   275  				{
   276  					Algorithm: "sha256",
   277  					Hex:       "83abef706f5ae199af65d1c13d737d0eb36219f0d18e36c6d8ff06159df39a63",
   278  				},
   279  				{
   280  					Algorithm: "sha256",
   281  					Hex:       "c03283c257abd289a30b4f5e9e1345da0e9bfdc6ca398ee7e8fac6d2c1456227",
   282  				},
   283  				{
   284  					Algorithm: "sha256",
   285  					Hex:       "2da3602d664dd3f71fae83cbc566d4e80b432c6ee8bb4efd94c8e85122f503d4",
   286  				},
   287  				{
   288  					Algorithm: "sha256",
   289  					Hex:       "82c59ac8ee582542648e634ca5aff9a464c68ff8a054f105a58689fb52209e34",
   290  				},
   291  				{
   292  					Algorithm: "sha256",
   293  					Hex:       "2f4a5c9187c249834ebc28783bd3c65bdcbacaa8baa6620ddaa27846dd3ef708",
   294  				},
   295  				{
   296  					Algorithm: "sha256",
   297  					Hex:       "6ca56f561e677ae06c3bc87a70792642d671a4416becb9a101577c1a6e090e36",
   298  				},
   299  				{
   300  					Algorithm: "sha256",
   301  					Hex:       "154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
   302  				},
   303  				{
   304  					Algorithm: "sha256",
   305  					Hex:       "b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
   306  				},
   307  			},
   308  		},
   309  		Config: v1.Config{
   310  			Cmd: []string{"composer"},
   311  			Entrypoint: []string{
   312  				"/bin/sh",
   313  				"/docker-entrypoint.sh",
   314  			},
   315  			Env: []string{
   316  				"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
   317  				"PHPIZE_DEPS=autoconf \t\tdpkg-dev dpkg \t\tfile \t\tg++ \t\tgcc \t\tlibc-dev \t\tmake \t\tpkgconf \t\tre2c",
   318  				"PHP_INI_DIR=/usr/local/etc/php",
   319  				"PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2",
   320  				"PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2",
   321  				"PHP_LDFLAGS=-Wl,-O1 -Wl,--hash-style=both -pie",
   322  				"GPG_KEYS=1729F83938DA44E27BA0F4D3DBDB397470D12172 B1B44D8F021E4E2D6021E995DC9FF8D3EE5AF27F",
   323  				"PHP_VERSION=7.2.11",
   324  				"PHP_URL=https://secure.php.net/get/php-7.2.11.tar.xz/from/this/mirror",
   325  				"PHP_ASC_URL=https://secure.php.net/get/php-7.2.11.tar.xz.asc/from/this/mirror",
   326  				"PHP_SHA256=da1a705c0bc46410e330fc6baa967666c8cd2985378fb9707c01a8e33b01d985",
   327  				"PHP_MD5=",
   328  				"COMPOSER_ALLOW_SUPERUSER=1",
   329  				"COMPOSER_HOME=/tmp",
   330  				"COMPOSER_VERSION=1.7.2",
   331  			},
   332  			Image:       "sha256:ad8c55ed62ca1f439bd600c7251de347926ca901ab7f52a93d8fba743ef397c6",
   333  			WorkingDir:  "/app",
   334  			ArgsEscaped: true,
   335  		},
   336  	}
   337  
   338  	alpineConfig = &v1.ConfigFile{
   339  		Architecture:  "amd64",
   340  		Container:     "47d9d33b3d5abb0316dba1a0bfcbc12a6fa88d98ad30170c41d30718003de82e",
   341  		Created:       v1.Time{Time: time.Date(2019, time.May, 11, 5, 10, 20, 331457195, time.UTC)},
   342  		DockerVersion: "18.06.1-ce",
   343  		History: []v1.History{
   344  			{
   345  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 0, 7, 3, 358250803, time.UTC)},
   346  				CreatedBy: "/bin/sh -c #(nop) ADD file:a86aea1f3a7d68f6ae03397b99ea77f2e9ee901c5c59e59f76f93adbb4035913 in / ",
   347  			},
   348  			{
   349  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 0, 7, 3, 510395965, time.UTC)},
   350  				CreatedBy:  "/bin/sh -c #(nop)  CMD [\"/bin/sh\"]",
   351  				EmptyLayer: true,
   352  			},
   353  			{
   354  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 43, 80069360, time.UTC)},
   355  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHPIZE_DEPS=autoconf \t\tdpkg-dev dpkg \t\tfile \t\tg++ \t\tgcc \t\tlibc-dev \t\tmake \t\tpkgconf \t\tre2c",
   356  				EmptyLayer: true,
   357  			},
   358  			{
   359  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 44, 655269947, time.UTC)},
   360  				CreatedBy: "/bin/sh -c apk add --no-cache \t\tca-certificates \t\tcurl \t\ttar \t\txz \t\topenssl",
   361  			},
   362  			{
   363  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 45, 787769041, time.UTC)},
   364  				CreatedBy: "/bin/sh -c set -x \t&& addgroup -g 82 -S www-data \t&& adduser -u 82 -D -S -G www-data www-data",
   365  			},
   366  			{
   367  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 46, 47800659, time.UTC)},
   368  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_INI_DIR=/usr/local/etc/php",
   369  				EmptyLayer: true,
   370  			},
   371  			{
   372  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 47, 131691293, time.UTC)},
   373  				CreatedBy: "/bin/sh -c set -eux; \tmkdir -p \"$PHP_INI_DIR/conf.d\"; \t[ ! -d /var/www/html ]; \tmkdir -p /var/www/html; \tchown www-data:www-data /var/www/html; \tchmod 777 /var/www/html",
   374  			},
   375  			{
   376  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 47, 360137598, time.UTC)},
   377  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2",
   378  				EmptyLayer: true,
   379  			},
   380  			{
   381  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 47, 624002469, time.UTC)},
   382  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2",
   383  				EmptyLayer: true,
   384  			},
   385  			{
   386  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 47, 823552655, time.UTC)},
   387  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_LDFLAGS=-Wl,-O1 -Wl,--hash-style=both -pie",
   388  				EmptyLayer: true,
   389  			},
   390  			{
   391  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 48, 90975339, time.UTC)},
   392  				CreatedBy:  "/bin/sh -c #(nop)  ENV GPG_KEYS=CBAF69F173A0FEA4B537F470D66C9593118BCCB6 F38252826ACD957EF380D39F2F7956BC5DA04B5D",
   393  				EmptyLayer: true,
   394  			},
   395  			{
   396  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 48, 311134986, time.UTC)},
   397  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_VERSION=7.3.5",
   398  				EmptyLayer: true,
   399  			},
   400  			{
   401  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 48, 546724822, time.UTC)},
   402  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_URL=https://www.php.net/get/php-7.3.5.tar.xz/from/this/mirror PHP_ASC_URL=https://www.php.net/get/php-7.3.5.tar.xz.asc/from/this/mirror",
   403  				EmptyLayer: true,
   404  			},
   405  			{
   406  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 48, 787069773, time.UTC)},
   407  				CreatedBy:  "/bin/sh -c #(nop)  ENV PHP_SHA256=e1011838a46fd4a195c8453b333916622d7ff5bce4aca2d9d99afac142db2472 PHP_MD5=",
   408  				EmptyLayer: true,
   409  			},
   410  			{
   411  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 54, 588915046, time.UTC)},
   412  				CreatedBy: "/bin/sh -c set -xe; \t\tapk add --no-cache --virtual .fetch-deps \t\tgnupg \t\twget \t; \t\tmkdir -p /usr/src; \tcd /usr/src; \t\twget -O php.tar.xz \"$PHP_URL\"; \t\tif [ -n \"$PHP_SHA256\" ]; then \t\techo \"$PHP_SHA256 *php.tar.xz\" | sha256sum -c -; \tfi; \tif [ -n \"$PHP_MD5\" ]; then \t\techo \"$PHP_MD5 *php.tar.xz\" | md5sum -c -; \tfi; \t\tif [ -n \"$PHP_ASC_URL\" ]; then \t\twget -O php.tar.xz.asc \"$PHP_ASC_URL\"; \t\texport GNUPGHOME=\"$(mktemp -d)\"; \t\tfor key in $GPG_KEYS; do \t\t\tgpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys \"$key\"; \t\tdone; \t\tgpg --batch --verify php.tar.xz.asc php.tar.xz; \t\tcommand -v gpgconf > /dev/null && gpgconf --kill all; \t\trm -rf \"$GNUPGHOME\"; \tfi; \t\tapk del --no-network .fetch-deps",
   413  			},
   414  			{
   415  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 3, 4, 54, 868883630, time.UTC)},
   416  				CreatedBy: "/bin/sh -c #(nop) COPY file:ce57c04b70896f77cc11eb2766417d8a1240fcffe5bba92179ec78c458844110 in /usr/local/bin/ ",
   417  			},
   418  			{
   419  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 3, 12, 28, 585346378, time.UTC)},
   420  				CreatedBy: "/bin/sh -c set -xe \t&& apk add --no-cache --virtual .build-deps \t\t$PHPIZE_DEPS \t\targon2-dev \t\tcoreutils \t\tcurl-dev \t\tlibedit-dev \t\tlibsodium-dev \t\tlibxml2-dev \t\topenssl-dev \t\tsqlite-dev \t\t&& export CFLAGS=\"$PHP_CFLAGS\" \t\tCPPFLAGS=\"$PHP_CPPFLAGS\" \t\tLDFLAGS=\"$PHP_LDFLAGS\" \t&& docker-php-source extract \t&& cd /usr/src/php \t&& gnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\" \t&& ./configure \t\t--build=\"$gnuArch\" \t\t--with-config-file-path=\"$PHP_INI_DIR\" \t\t--with-config-file-scan-dir=\"$PHP_INI_DIR/conf.d\" \t\t\t\t--enable-option-checking=fatal \t\t\t\t--with-mhash \t\t\t\t--enable-ftp \t\t--enable-mbstring \t\t--enable-mysqlnd \t\t--with-password-argon2 \t\t--with-sodium=shared \t\t\t\t--with-curl \t\t--with-libedit \t\t--with-openssl \t\t--with-zlib \t\t\t\t$(test \"$gnuArch\" = 's390x-linux-gnu' && echo '--without-pcre-jit') \t\t\t\t$PHP_EXTRA_CONFIGURE_ARGS \t&& make -j \"$(nproc)\" \t&& find -type f -name '*.a' -delete \t&& make install \t&& { find /usr/local/bin /usr/local/sbin -type f -perm +0111 -exec strip --strip-all '{}' + || true; } \t&& make clean \t\t&& cp -v php.ini-* \"$PHP_INI_DIR/\" \t\t&& cd / \t&& docker-php-source delete \t\t&& runDeps=\"$( \t\tscanelf --needed --nobanner --format '%n#p' --recursive /usr/local \t\t\t| tr ',' '\\n' \t\t\t| sort -u \t\t\t| awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' \t)\" \t&& apk add --no-cache $runDeps \t\t&& apk del --no-network .build-deps \t\t&& pecl update-channels \t&& rm -rf /tmp/pear ~/.pearrc",
   421  			},
   422  			{
   423  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 3, 12, 29, 98563791, time.UTC)},
   424  				CreatedBy: "/bin/sh -c #(nop) COPY multi:03970f7b3773444b9f7f244f89d3ceeb4253ac6599f0ba0a4c0306c5bf7d1b9b in /usr/local/bin/ ",
   425  			},
   426  			{
   427  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 3, 12, 30, 99974579, time.UTC)},
   428  				CreatedBy: "/bin/sh -c docker-php-ext-enable sodium",
   429  			},
   430  			{
   431  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 3, 12, 30, 266754534, time.UTC)},
   432  				CreatedBy:  "/bin/sh -c #(nop)  ENTRYPOINT [\"docker-php-entrypoint\"]",
   433  				EmptyLayer: true,
   434  			},
   435  			{
   436  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 3, 12, 30, 414982715, time.UTC)},
   437  				CreatedBy:  "/bin/sh -c #(nop)  CMD [\"php\" \"-a\"]",
   438  				EmptyLayer: true,
   439  			},
   440  			{
   441  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 5, 10, 12, 574223281, time.UTC)},
   442  				CreatedBy: "/bin/sh -c apk add --no-cache --virtual .composer-rundeps git subversion openssh mercurial tini bash patch make zip unzip coreutils  && apk add --no-cache --virtual .build-deps zlib-dev libzip-dev  && docker-php-ext-configure zip --with-libzip  && docker-php-ext-install -j$(getconf _NPROCESSORS_ONLN) zip opcache  && runDeps=\"$(     scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions       | tr ',' '\\n'       | sort -u       | awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }'     )\"  && apk add --no-cache --virtual .composer-phpext-rundeps $runDeps  && apk del .build-deps  && printf \"# composer php cli ini settings\\ndate.timezone=UTC\\nmemory_limit=-1\\nopcache.enable_cli=1\\n\" > $PHP_INI_DIR/php-cli.ini",
   443  			},
   444  			{
   445  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 5, 10, 12, 831274473, time.UTC)},
   446  				CreatedBy:  "/bin/sh -c #(nop)  ENV COMPOSER_ALLOW_SUPERUSER=1",
   447  				EmptyLayer: true,
   448  			},
   449  			{
   450  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 5, 10, 13, 3330711, time.UTC)},
   451  				CreatedBy:  "/bin/sh -c #(nop)  ENV COMPOSER_HOME=/tmp",
   452  				EmptyLayer: true,
   453  			},
   454  			{
   455  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 5, 10, 18, 503381656, time.UTC)},
   456  				CreatedBy:  "/bin/sh -c #(nop)  ENV COMPOSER_VERSION=1.7.3",
   457  				EmptyLayer: true,
   458  			},
   459  			{
   460  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 5, 10, 19, 619504049, time.UTC)},
   461  				CreatedBy: "/bin/sh -c curl --silent --fail --location --retry 3 --output /tmp/installer.php --url https://raw.githubusercontent.com/composer/getcomposer.org/cb19f2aa3aeaa2006c0cd69a7ef011eb31463067/web/installer  && php -r \"     \\$signature = '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5';     \\$hash = hash('sha384', file_get_contents('/tmp/installer.php'));     if (!hash_equals(\\$signature, \\$hash)) {       unlink('/tmp/installer.php');       echo 'Integrity check failed, installer is either corrupt or worse.' . PHP_EOL;       exit(1);     }\"  && php /tmp/installer.php --no-ansi --install-dir=/usr/bin --filename=composer --version=${COMPOSER_VERSION}  && composer --ansi --version --no-interaction  && rm -f /tmp/installer.php",
   462  			},
   463  			{
   464  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 5, 10, 19, 803213107, time.UTC)},
   465  				CreatedBy: "/bin/sh -c #(nop) COPY file:0bcb2d1c76549e38469db832f5bcfcb4c538b26748a9d4246cc64f35a23280d0 in /docker-entrypoint.sh ",
   466  			},
   467  			{
   468  				Created:   v1.Time{Time: time.Date(2019, time.May, 11, 5, 10, 19, 987396089, time.UTC)},
   469  				CreatedBy: "/bin/sh -c #(nop) WORKDIR /app",
   470  			},
   471  			{
   472  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 5, 10, 20, 159217819, time.UTC)},
   473  				CreatedBy:  "/bin/sh -c #(nop)  ENTRYPOINT [\"/bin/sh\" \"/docker-entrypoint.sh\"]",
   474  				EmptyLayer: true,
   475  			},
   476  			{
   477  				Created:    v1.Time{Time: time.Date(2019, time.May, 11, 5, 10, 20, 331457195, time.UTC)},
   478  				CreatedBy:  "/bin/sh -c #(nop)  CMD [\"composer\"]",
   479  				EmptyLayer: true,
   480  			},
   481  		},
   482  		OS: "linux",
   483  		RootFS: v1.RootFS{
   484  			Type: "layers",
   485  			DiffIDs: []v1.Hash{
   486  				{
   487  					Algorithm: "sha256",
   488  					Hex:       "f1b5933fe4b5f49bbe8258745cf396afe07e625bdab3168e364daf7c956b6b81",
   489  				},
   490  				{
   491  					Algorithm: "sha256",
   492  					Hex:       "3575e617b5f4845d72ac357ea1712be9037c1f73e8893fa4a5b887be964f8f59",
   493  				},
   494  				{
   495  					Algorithm: "sha256",
   496  					Hex:       "414e112bbb2c35bef0e76708e87a68b521a011a1941fe6d062e30da800c69d1f",
   497  				},
   498  				{
   499  					Algorithm: "sha256",
   500  					Hex:       "21f626200b4c7decb2150402d3b801a886ef9dab022d11478eb3240b2a1bb175",
   501  				},
   502  				{
   503  					Algorithm: "sha256",
   504  					Hex:       "64a9089492da43bf6f8f3b3b45aafee7d71f1dfd6464477e27b43b4dbe1da341",
   505  				},
   506  				{
   507  					Algorithm: "sha256",
   508  					Hex:       "c60e74b6df1608ee7a080978a9f5eddce48dd4d7366b65a5ec00c6e96deabfae",
   509  				},
   510  				{
   511  					Algorithm: "sha256",
   512  					Hex:       "489ab25ac6f9d77b5868493bfccc72bcbfaa85d8f393cdd21f3a6cb6e0256c15",
   513  				},
   514  				{
   515  					Algorithm: "sha256",
   516  					Hex:       "5a8c7d3402d369f0f5838b74da5c2bd3eaa64c6bbd8d8e11d7ec0affb074c276",
   517  				},
   518  				{
   519  					Algorithm: "sha256",
   520  					Hex:       "fe6bde799f85946dbed35f5f614532d68a9f8b62f3f42ae9164740c3d0a6296a",
   521  				},
   522  				{
   523  					Algorithm: "sha256",
   524  					Hex:       "40dd29f574f814717669b34efc4ae527a3af0829a2cccb9ec4f077a8cb2766cc",
   525  				},
   526  				{
   527  					Algorithm: "sha256",
   528  					Hex:       "0d5d3c0e6691d3c6d24dc782de33d64d490226c503414da0df93b8f605f93da5",
   529  				},
   530  				{
   531  					Algorithm: "sha256",
   532  					Hex:       "41467c77644ee108b8ef3e89db7f235ebb720ed4a4041bf746d7342193e6bc7d",
   533  				},
   534  				{
   535  					Algorithm: "sha256",
   536  					Hex:       "6a64ec219cdeecfe63aac5b7f43fb3cb6651c6b1a02ebbde6deeabf8a7e3b345",
   537  				},
   538  			},
   539  		},
   540  		Config: v1.Config{
   541  			Cmd: []string{"composer"},
   542  			Entrypoint: []string{
   543  				"/bin/sh",
   544  				"/docker-entrypoint.sh",
   545  			},
   546  			Env: []string{
   547  				"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
   548  				"PHPIZE_DEPS=autoconf \t\tdpkg-dev dpkg \t\tfile \t\tg++ \t\tgcc \t\tlibc-dev \t\tmake \t\tpkgconf \t\tre2c",
   549  				"PHP_INI_DIR=/usr/local/etc/php",
   550  				"PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2",
   551  				"PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2",
   552  				"PHP_LDFLAGS=-Wl,-O1 -Wl,--hash-style=both -pie",
   553  				"GPG_KEYS=CBAF69F173A0FEA4B537F470D66C9593118BCCB6 F38252826ACD957EF380D39F2F7956BC5DA04B5D",
   554  				"PHP_VERSION=7.3.5",
   555  				"PHP_URL=https://www.php.net/get/php-7.3.5.tar.xz/from/this/mirror",
   556  				"PHP_ASC_URL=https://www.php.net/get/php-7.3.5.tar.xz.asc/from/this/mirror",
   557  				"PHP_SHA256=e1011838a46fd4a195c8453b333916622d7ff5bce4aca2d9d99afac142db2472",
   558  				"PHP_MD5=",
   559  				"COMPOSER_ALLOW_SUPERUSER=1",
   560  				"COMPOSER_HOME=/tmp",
   561  				"COMPOSER_VERSION=1.7.3",
   562  			},
   563  			Image:       "sha256:45a1f30c00e614b0d90bb2a24affba0a304ff27660ad4717987fefe067cadec8",
   564  			WorkingDir:  "/app",
   565  			ArgsEscaped: true,
   566  		},
   567  	}
   568  
   569  	wantPkgs = []types.Package{
   570  		{
   571  			Name:    "acl",
   572  			Version: "2.2.52-r5",
   573  		},
   574  		{
   575  			Name:    "apr",
   576  			Version: "1.6.5-r0",
   577  		},
   578  		{
   579  			Name:    "apr-util",
   580  			Version: "1.6.1-r5",
   581  		},
   582  		{
   583  			Name:    "argon2",
   584  			Version: "20171227-r1",
   585  		},
   586  		{
   587  			Name:    "argon2-dev",
   588  			Version: "20171227-r1",
   589  		},
   590  		{
   591  			Name:    "argon2-libs",
   592  			Version: "20171227-r1",
   593  		},
   594  		{
   595  			Name:    "attr",
   596  			Version: "2.4.47-r7",
   597  		},
   598  		{
   599  			Name:    "autoconf",
   600  			Version: "2.69-r2",
   601  		},
   602  		{
   603  			Name:    "bash",
   604  			Version: "4.4.19-r1",
   605  		},
   606  		{
   607  			Name:    "binutils",
   608  			Version: "2.31.1-r2",
   609  		},
   610  		{
   611  			Name:    "busybox",
   612  			Version: "1.29.3-r10",
   613  		},
   614  		{
   615  			Name:    "bzip2",
   616  			Version: "1.0.6-r6",
   617  		},
   618  		{
   619  			Name:    "ca-certificates",
   620  			Version: "20190108-r0",
   621  		},
   622  		{
   623  			Name:    "coreutils",
   624  			Version: "8.30-r0",
   625  		},
   626  		{
   627  			Name:    "curl",
   628  			Version: "7.64.0-r1",
   629  		},
   630  		{
   631  			Name:    "curl-dev",
   632  			Version: "7.64.0-r1",
   633  		},
   634  		{
   635  			Name:    "cyrus-sasl",
   636  			Version: "2.1.27-r1",
   637  		},
   638  		{
   639  			Name:    "db",
   640  			Version: "5.3.28-r1",
   641  		},
   642  		{
   643  			Name:    "dpkg",
   644  			Version: "1.19.2-r0",
   645  		},
   646  		{
   647  			Name:    "dpkg-dev",
   648  			Version: "1.19.2-r0",
   649  		},
   650  		{
   651  			Name:    "expat",
   652  			Version: "2.2.6-r0",
   653  		},
   654  		{
   655  			Name:    "file",
   656  			Version: "5.36-r0",
   657  		},
   658  		{
   659  			Name:    "g++",
   660  			Version: "8.3.0-r0",
   661  		},
   662  		{
   663  			Name:    "gcc",
   664  			Version: "8.3.0-r0",
   665  		},
   666  		{
   667  			Name:    "gdbm",
   668  			Version: "1.13-r1",
   669  		},
   670  		{
   671  			Name:    "git",
   672  			Version: "2.20.1-r0",
   673  		},
   674  		{
   675  			Name:    "gmp",
   676  			Version: "6.1.2-r1",
   677  		},
   678  		{
   679  			Name:    "gnupg",
   680  			Version: "2.2.12-r0",
   681  		},
   682  		{
   683  			Name:    "gnutls",
   684  			Version: "3.6.7-r0",
   685  		},
   686  		{
   687  			Name:    "isl",
   688  			Version: "0.18-r0",
   689  		},
   690  		{
   691  			Name:    "libacl",
   692  			Version: "2.2.52-r5",
   693  		},
   694  		{
   695  			Name:    "libassuan",
   696  			Version: "2.5.1-r0",
   697  		},
   698  		{
   699  			Name:    "libatomic",
   700  			Version: "8.3.0-r0",
   701  		},
   702  		{
   703  			Name:    "libattr",
   704  			Version: "2.4.47-r7",
   705  		},
   706  		{
   707  			Name:    "libbz2",
   708  			Version: "1.0.6-r6",
   709  		},
   710  		{
   711  			Name:    "libc-dev",
   712  			Version: "0.7.1-r0",
   713  		},
   714  		{
   715  			Name:    "libcap",
   716  			Version: "2.26-r0",
   717  		},
   718  		{
   719  			Name:    "libcrypto1.1",
   720  			Version: "1.1.1b-r1",
   721  		},
   722  		{
   723  			Name:    "libcurl",
   724  			Version: "7.64.0-r1",
   725  		},
   726  		{
   727  			Name:    "libedit",
   728  			Version: "20181209.3.1-r0",
   729  		},
   730  		{
   731  			Name:    "libedit-dev",
   732  			Version: "20181209.3.1-r0",
   733  		},
   734  		{
   735  			Name:    "libffi",
   736  			Version: "3.2.1-r6",
   737  		},
   738  		{
   739  			Name:    "libgcc",
   740  			Version: "8.3.0-r0",
   741  		},
   742  		{
   743  			Name:    "libgcrypt",
   744  			Version: "1.8.4-r0",
   745  		},
   746  		{
   747  			Name:    "libgomp",
   748  			Version: "8.3.0-r0",
   749  		},
   750  		{
   751  			Name:    "libgpg-error",
   752  			Version: "1.33-r0",
   753  		},
   754  		{
   755  			Name:    "libksba",
   756  			Version: "1.3.5-r0",
   757  		},
   758  		{
   759  			Name:    "libldap",
   760  			Version: "2.4.47-r2",
   761  		},
   762  		{
   763  			Name:    "libmagic",
   764  			Version: "5.36-r0",
   765  		},
   766  		{
   767  			Name:    "libsasl",
   768  			Version: "2.1.27-r1",
   769  		},
   770  		{
   771  			Name:    "libsodium",
   772  			Version: "1.0.16-r0",
   773  		},
   774  		{
   775  			Name:    "libsodium-dev",
   776  			Version: "1.0.16-r0",
   777  		},
   778  		{
   779  			Name:    "libssh2",
   780  			Version: "1.8.2-r0",
   781  		},
   782  		{
   783  			Name:    "libssh2-dev",
   784  			Version: "1.8.2-r0",
   785  		},
   786  		{
   787  			Name:    "libssl1.1",
   788  			Version: "1.1.1b-r1",
   789  		},
   790  		{
   791  			Name:    "libstdc++",
   792  			Version: "8.3.0-r0",
   793  		},
   794  		{
   795  			Name:    "libtasn1",
   796  			Version: "4.13-r0",
   797  		},
   798  		{
   799  			Name:    "libunistring",
   800  			Version: "0.9.10-r0",
   801  		},
   802  		{
   803  			Name:    "libuuid",
   804  			Version: "2.33-r0",
   805  		},
   806  		{
   807  			Name:    "libxml2",
   808  			Version: "2.9.9-r1",
   809  		},
   810  		{
   811  			Name:    "libxml2-dev",
   812  			Version: "2.9.9-r1",
   813  		},
   814  		{
   815  			Name:    "lz4",
   816  			Version: "1.8.3-r2",
   817  		},
   818  		{
   819  			Name:    "lz4-libs",
   820  			Version: "1.8.3-r2",
   821  		},
   822  		{
   823  			Name:    "m4",
   824  			Version: "1.4.18-r1",
   825  		},
   826  		{
   827  			Name:    "make",
   828  			Version: "4.2.1-r2",
   829  		},
   830  		{
   831  			Name:    "mercurial",
   832  			Version: "4.9.1-r0",
   833  		},
   834  		{
   835  			Name:    "mpc1",
   836  			Version: "1.0.3-r1",
   837  		},
   838  		{
   839  			Name:    "mpfr3",
   840  			Version: "3.1.5-r1",
   841  		},
   842  		{
   843  			Name:    "musl",
   844  			Version: "1.1.20-r4",
   845  		},
   846  		{
   847  			Name:    "musl-dev",
   848  			Version: "1.1.20-r4",
   849  		},
   850  		{
   851  			Name:    "ncurses",
   852  			Version: "6.1_p20190105-r0",
   853  		},
   854  		{
   855  			Name:    "ncurses-dev",
   856  			Version: "6.1_p20190105-r0",
   857  		},
   858  		{
   859  			Name:    "ncurses-libs",
   860  			Version: "6.1_p20190105-r0",
   861  		},
   862  		{
   863  			Name:    "ncurses-terminfo",
   864  			Version: "6.1_p20190105-r0",
   865  		},
   866  		{
   867  			Name:    "ncurses-terminfo-base",
   868  			Version: "6.1_p20190105-r0",
   869  		},
   870  		{
   871  			Name:    "nettle",
   872  			Version: "3.4.1-r0",
   873  		},
   874  		{
   875  			Name:    "nghttp2",
   876  			Version: "1.35.1-r0",
   877  		},
   878  		{
   879  			Name:    "nghttp2-dev",
   880  			Version: "1.35.1-r0",
   881  		},
   882  		{
   883  			Name:    "nghttp2-libs",
   884  			Version: "1.35.1-r0",
   885  		},
   886  		{
   887  			Name:    "npth",
   888  			Version: "1.6-r0",
   889  		},
   890  		{
   891  			Name:    "openldap",
   892  			Version: "2.4.47-r2",
   893  		},
   894  		{
   895  			Name:    "openssh",
   896  			Version: "7.9_p1-r5",
   897  		},
   898  		{
   899  			Name:    "openssh-client",
   900  			Version: "7.9_p1-r5",
   901  		},
   902  		{
   903  			Name:    "openssh-keygen",
   904  			Version: "7.9_p1-r5",
   905  		},
   906  		{
   907  			Name:    "openssh-server",
   908  			Version: "7.9_p1-r5",
   909  		},
   910  		{
   911  			Name:    "openssh-server-common",
   912  			Version: "7.9_p1-r5",
   913  		},
   914  		{
   915  			Name:    "openssh-sftp-server",
   916  			Version: "7.9_p1-r5",
   917  		},
   918  		{
   919  			Name:    "openssl",
   920  			Version: "1.1.1b-r1",
   921  		},
   922  		{
   923  			Name:    "openssl-dev",
   924  			Version: "1.1.1b-r1",
   925  		},
   926  		{
   927  			Name:    "p11-kit",
   928  			Version: "0.23.14-r0",
   929  		},
   930  		{
   931  			Name:    "patch",
   932  			Version: "2.7.6-r4",
   933  		},
   934  		{
   935  			Name:    "pcre2",
   936  			Version: "10.32-r1",
   937  		},
   938  		{
   939  			Name:    "perl",
   940  			Version: "5.26.3-r0",
   941  		},
   942  		{
   943  			Name:    "pinentry",
   944  			Version: "1.1.0-r0",
   945  		},
   946  		{
   947  			Name:    "pkgconf",
   948  			Version: "1.6.0-r0",
   949  		},
   950  		{
   951  			Name:    "python2",
   952  			Version: "2.7.16-r1",
   953  		},
   954  		{
   955  			Name:    "re2c",
   956  			Version: "1.1.1-r0",
   957  		},
   958  		{
   959  			Name:    "readline",
   960  			Version: "7.0.003-r1",
   961  		},
   962  		{
   963  			Name:    "serf",
   964  			Version: "1.3.9-r5",
   965  		},
   966  		{
   967  			Name:    "sqlite",
   968  			Version: "3.26.0-r3",
   969  		},
   970  		{
   971  			Name:    "sqlite-dev",
   972  			Version: "3.26.0-r3",
   973  		},
   974  		{
   975  			Name:    "sqlite-libs",
   976  			Version: "3.26.0-r3",
   977  		},
   978  		{
   979  			Name:    "subversion",
   980  			Version: "1.11.1-r0",
   981  		},
   982  		{
   983  			Name:    "subversion-libs",
   984  			Version: "1.11.1-r0",
   985  		},
   986  		{
   987  			Name:    "tar",
   988  			Version: "1.32-r0",
   989  		},
   990  		{
   991  			Name:    "unzip",
   992  			Version: "6.0-r4",
   993  		},
   994  		{
   995  			Name:    "util-linux",
   996  			Version: "2.33-r0",
   997  		},
   998  		{
   999  			Name:    "wget",
  1000  			Version: "1.20.3-r0",
  1001  		},
  1002  		{
  1003  			Name:    "xz",
  1004  			Version: "5.2.4-r0",
  1005  		},
  1006  		{
  1007  			Name:    "xz-libs",
  1008  			Version: "5.2.4-r0",
  1009  		},
  1010  		{
  1011  			Name:    "zip",
  1012  			Version: "3.0-r7",
  1013  		},
  1014  		{
  1015  			Name:    "zlib",
  1016  			Version: "1.2.11-r1",
  1017  		},
  1018  		{
  1019  			Name:    "zlib-dev",
  1020  			Version: "1.2.11-r1",
  1021  		},
  1022  	}
  1023  )
  1024  
  1025  func TestAnalyze(t *testing.T) {
  1026  	testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
  1027  		content, err := os.ReadFile("testdata/history_v3.9.json")
  1028  		if err != nil {
  1029  			http.Error(res, err.Error(), http.StatusInternalServerError)
  1030  			return
  1031  		}
  1032  		res.WriteHeader(http.StatusOK)
  1033  		res.Write(content)
  1034  		return
  1035  	}))
  1036  	defer testServer.Close()
  1037  
  1038  	type args struct {
  1039  		targetOS types.OS
  1040  		config   *v1.ConfigFile
  1041  	}
  1042  	var tests = map[string]struct {
  1043  		args                args
  1044  		apkIndexArchivePath string
  1045  		want                types.Packages
  1046  	}{
  1047  		"old": {
  1048  			args: args{
  1049  				targetOS: types.OS{
  1050  					Family: "alpine",
  1051  					Name:   "3.9.1",
  1052  				},
  1053  				config: oldAlpineConfig,
  1054  			},
  1055  			apkIndexArchivePath: "file://testdata/history_v%s.json",
  1056  			want:                nil,
  1057  		},
  1058  		"new": {
  1059  			args: args{
  1060  				targetOS: types.OS{
  1061  					Family: "alpine",
  1062  					Name:   "3.9.1",
  1063  				},
  1064  				config: alpineConfig,
  1065  			},
  1066  			apkIndexArchivePath: "file://testdata/history_v%s.json",
  1067  			want:                wantPkgs,
  1068  		},
  1069  		"https": {
  1070  			args: args{
  1071  				targetOS: types.OS{
  1072  					Family: "alpine",
  1073  					Name:   "",
  1074  				},
  1075  				config: alpineConfig,
  1076  			},
  1077  			apkIndexArchivePath: testServer.URL + "%v",
  1078  			want:                wantPkgs,
  1079  		},
  1080  	}
  1081  	for testName, v := range tests {
  1082  		t.Run(testName, func(t *testing.T) {
  1083  			t.Setenv(envApkIndexArchiveURL, v.apkIndexArchivePath)
  1084  			a, err := newAlpineCmdAnalyzer(analyzer.ConfigAnalyzerOptions{})
  1085  			require.NoError(t, err)
  1086  			result, err := a.Analyze(context.Background(), analyzer.ConfigAnalysisInput{
  1087  				OS:     v.args.targetOS,
  1088  				Config: v.args.config,
  1089  			})
  1090  			require.NoError(t, err)
  1091  
  1092  			got := lo.FromPtr(result)
  1093  			sort.Sort(got.HistoryPackages)
  1094  			assert.Equal(t, v.want, got.HistoryPackages)
  1095  		})
  1096  	}
  1097  }
  1098  
  1099  func TestParseCommand(t *testing.T) {
  1100  	var tests = map[string]struct {
  1101  		command  string
  1102  		envs     map[string]string
  1103  		expected []string
  1104  	}{
  1105  		"no package": {
  1106  			command:  "/bin/sh -c #(nop) ADD file:49f9e47e678d868d5b023482aa8dded71276a241a665c4f8b55ca77269321b34 in / ",
  1107  			envs:     nil,
  1108  			expected: nil,
  1109  		},
  1110  		"no-cache": {
  1111  			command: "/bin/sh -c apk add --no-cache --virtual .persistent-deps \t\tca-certificates \t\tcurl \t\ttar \t\txz \t\tlibressl",
  1112  			envs:    nil,
  1113  			expected: []string{
  1114  				"ca-certificates",
  1115  				"curl",
  1116  				"tar",
  1117  				"xz",
  1118  				"libressl",
  1119  			},
  1120  		},
  1121  		// TODO: support $runDeps
  1122  		"joined by &&": {
  1123  			command:  `/bin/sh -c apk add --no-cache --virtual .build-deps zlib-dev  && docker-php-ext-install zip  && runDeps=\"$(     scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions     | tr ',' '\\n'     | sort -u     | awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }'     )\"  && apk add --virtual .composer-phpext-rundeps $runDeps  && apk del .build-deps`,
  1124  			envs:     nil,
  1125  			expected: []string{"zlib-dev"},
  1126  		},
  1127  		"joined by ;": {
  1128  			command: "/bin/sh -c set -xe; \t\tapk add --no-cache --virtual .fetch-deps \t\tgnupg \t\twget \t; \t\tmkdir -p /usr/src; \tcd /usr/src; \t\twget -O php.tar.xz \"$PHP_URL\"; \t\tif [ -n \"$PHP_SHA256\" ]; then \t\techo \"$PHP_SHA256 *php.tar.xz\" | sha256sum -c -; \tfi; \tif [ -n \"$PHP_MD5\" ]; then \t\techo \"$PHP_MD5 *php.tar.xz\" | md5sum -c -; \tfi; \t\tif [ -n \"$PHP_ASC_URL\" ]; then \t\twget -O php.tar.xz.asc \"$PHP_ASC_URL\"; \t\texport GNUPGHOME=\"$(mktemp -d)\"; \t\tfor key in $GPG_KEYS; do \t\t\tgpg --keyserver ha.pool.sks-keyservers.net --recv-keys \"$key\"; \t\tdone; \t\tgpg --batch --verify php.tar.xz.asc php.tar.xz; \t\tcommand -v gpgconf > /dev/null && gpgconf --kill all; \t\trm -rf \"$GNUPGHOME\"; \tfi; \t\tapk del .fetch-deps",
  1129  			envs:    nil,
  1130  			expected: []string{
  1131  				"gnupg",
  1132  				"wget",
  1133  			},
  1134  		},
  1135  		"ENV": {
  1136  			command: "/bin/sh -c set -xe \t&& apk add --no-cache --virtual .build-deps \t\t$PHPIZE_DEPS \t\tcoreutils \t\tcurl-dev \t\tlibedit-dev \t\tlibressl-dev \t\tlibsodium-dev \t\tlibxml2-dev \t\tsqlite-dev",
  1137  			envs: map[string]string{
  1138  				"$PHPIZE_DEPS": "autoconf \t\tdpkg-dev dpkg \t\tfile \t\tg++ \t\tgcc \t\tlibc-dev \t\tmake \t\tpkgconf \t\tre2c",
  1139  			},
  1140  			expected: []string{
  1141  				"autoconf",
  1142  				"dpkg-dev",
  1143  				"dpkg",
  1144  				"file",
  1145  				"g++",
  1146  				"gcc",
  1147  				"libc-dev",
  1148  				"make",
  1149  				"pkgconf",
  1150  				"re2c",
  1151  				"coreutils",
  1152  				"curl-dev",
  1153  				"libedit-dev",
  1154  				"libressl-dev",
  1155  				"libsodium-dev",
  1156  				"libxml2-dev",
  1157  				"sqlite-dev",
  1158  			},
  1159  		},
  1160  	}
  1161  	analyzer := alpineCmdAnalyzer{}
  1162  	for testName, v := range tests {
  1163  		actual := analyzer.parseCommand(v.command, v.envs)
  1164  		assert.Equal(t, v.expected, actual, "[%s]\n%s", testName, pretty.Compare(v.expected, actual))
  1165  	}
  1166  }
  1167  
  1168  func TestResolveDependency(t *testing.T) {
  1169  	var tests = map[string]struct {
  1170  		pkgName             string
  1171  		apkIndexArchivePath string
  1172  		expected            map[string]struct{}
  1173  	}{
  1174  		"low": {
  1175  			pkgName:             "libblkid",
  1176  			apkIndexArchivePath: "testdata/history_v3.9.json",
  1177  			expected: map[string]struct{}{
  1178  				"libblkid": {},
  1179  				"libuuid":  {},
  1180  				"musl":     {},
  1181  			},
  1182  		},
  1183  		"medium": {
  1184  			pkgName:             "libgcab",
  1185  			apkIndexArchivePath: "testdata/history_v3.9.json",
  1186  			expected: map[string]struct{}{
  1187  				"busybox":  {},
  1188  				"libblkid": {},
  1189  				"libuuid":  {},
  1190  				"musl":     {},
  1191  				"libmount": {},
  1192  				"pcre":     {},
  1193  				"glib":     {},
  1194  				"libgcab":  {},
  1195  				"libintl":  {},
  1196  				"zlib":     {},
  1197  				"libffi":   {},
  1198  			},
  1199  		},
  1200  		"high": {
  1201  			pkgName:             "postgresql",
  1202  			apkIndexArchivePath: "testdata/history_v3.9.json",
  1203  			expected: map[string]struct{}{
  1204  				"busybox":               {},
  1205  				"ncurses-terminfo-base": {},
  1206  				"ncurses-terminfo":      {},
  1207  				"libedit":               {},
  1208  				"db":                    {},
  1209  				"libsasl":               {},
  1210  				"libldap":               {},
  1211  				"libpq":                 {},
  1212  				"postgresql-client":     {},
  1213  				"tzdata":                {},
  1214  				"libxml2":               {},
  1215  				"postgresql":            {},
  1216  				"musl":                  {},
  1217  				"libcrypto1.1":          {},
  1218  				"libssl1.1":             {},
  1219  				"ncurses-libs":          {},
  1220  				"zlib":                  {},
  1221  			},
  1222  		},
  1223  		"package alias": {
  1224  			pkgName:             "sqlite-dev",
  1225  			apkIndexArchivePath: "testdata/history_v3.9.json",
  1226  			expected: map[string]struct{}{
  1227  				"sqlite-dev":  {},
  1228  				"sqlite-libs": {},
  1229  				"pkgconf":     {}, // pkgconfig => pkgconf
  1230  				"musl":        {},
  1231  			},
  1232  		},
  1233  		"circular dependencies": {
  1234  			pkgName:             "nodejs",
  1235  			apkIndexArchivePath: "testdata/history_v3.7.json",
  1236  			expected: map[string]struct{}{
  1237  				"busybox":               {},
  1238  				"c-ares":                {},
  1239  				"ca-certificates":       {},
  1240  				"http-parser":           {},
  1241  				"libcrypto1.0":          {},
  1242  				"libgcc":                {},
  1243  				"libressl2.6-libcrypto": {},
  1244  				"libssl1.0":             {},
  1245  				"libstdc++":             {},
  1246  				"libuv":                 {},
  1247  				"musl":                  {},
  1248  				"nodejs":                {},
  1249  				"nodejs-npm":            {},
  1250  				"zlib":                  {},
  1251  			},
  1252  		},
  1253  	}
  1254  	analyzer := alpineCmdAnalyzer{}
  1255  	for testName, v := range tests {
  1256  		f, err := os.Open(v.apkIndexArchivePath)
  1257  		if err != nil {
  1258  			t.Fatalf("unexpected error: %s", err)
  1259  		}
  1260  		apkIndexArchive := &apkIndex{}
  1261  		if err = json.NewDecoder(f).Decode(&apkIndexArchive); err != nil {
  1262  			t.Fatalf("unexpected error: %s", err)
  1263  		}
  1264  		circularDependencyCheck := map[string]struct{}{}
  1265  		pkgs := analyzer.resolveDependency(apkIndexArchive, v.pkgName, circularDependencyCheck)
  1266  		actual := map[string]struct{}{}
  1267  		for _, pkg := range pkgs {
  1268  			actual[pkg] = struct{}{}
  1269  		}
  1270  		if !reflect.DeepEqual(v.expected, actual) {
  1271  			t.Errorf("[%s]\n%s", testName, pretty.Compare(v.expected, actual))
  1272  		}
  1273  	}
  1274  }
  1275  
  1276  func TestGuessVersion(t *testing.T) {
  1277  	var tests = map[string]struct {
  1278  		apkIndexArchive *apkIndex
  1279  		pkgs            []string
  1280  		createdAt       time.Time
  1281  		expected        []types.Package
  1282  	}{
  1283  		"normal": {
  1284  			apkIndexArchive: &apkIndex{
  1285  				Package: map[string]archive{
  1286  					"busybox": {
  1287  						Versions: map[string]int{
  1288  							"1.24.2-r0": 100,
  1289  							"1.24.2-r1": 200,
  1290  							"1.24.2-r2": 300,
  1291  						},
  1292  					},
  1293  				},
  1294  			},
  1295  			pkgs:      []string{"busybox"},
  1296  			createdAt: time.Unix(200, 0),
  1297  			expected: []types.Package{
  1298  				{
  1299  					Name:    "busybox",
  1300  					Version: "1.24.2-r1",
  1301  				},
  1302  			},
  1303  		},
  1304  		"unmatched version": {
  1305  			apkIndexArchive: &apkIndex{
  1306  				Package: map[string]archive{
  1307  					"busybox": {
  1308  						Versions: map[string]int{
  1309  							"1.24.2-r0": 100,
  1310  							"1.24.2-r1": 200,
  1311  							"1.24.2-r2": 300,
  1312  						},
  1313  					},
  1314  				},
  1315  			},
  1316  			pkgs:      []string{"busybox"},
  1317  			createdAt: time.Unix(50, 0),
  1318  			expected:  nil,
  1319  		},
  1320  		"unmatched package": {
  1321  			apkIndexArchive: &apkIndex{
  1322  				Package: map[string]archive{
  1323  					"busybox": {
  1324  						Versions: map[string]int{
  1325  							"1.24.2-r0": 100,
  1326  							"1.24.2-r1": 200,
  1327  							"1.24.2-r2": 300,
  1328  						},
  1329  					},
  1330  				},
  1331  			},
  1332  			pkgs: []string{
  1333  				"busybox",
  1334  				"openssl",
  1335  			},
  1336  			createdAt: time.Unix(200, 0),
  1337  			expected: []types.Package{
  1338  				{
  1339  					Name:    "busybox",
  1340  					Version: "1.24.2-r1",
  1341  				},
  1342  			},
  1343  		},
  1344  		"origin": {
  1345  			apkIndexArchive: &apkIndex{
  1346  				Package: map[string]archive{
  1347  					"sqlite-dev": {
  1348  						Versions: map[string]int{
  1349  							"3.26.0-r0": 100,
  1350  							"3.26.0-r1": 200,
  1351  							"3.26.0-r2": 300,
  1352  							"3.26.0-r3": 400,
  1353  						},
  1354  						Origin: "sqlite",
  1355  					},
  1356  				},
  1357  			},
  1358  			pkgs:      []string{"sqlite-dev"},
  1359  			createdAt: time.Unix(500, 0),
  1360  			expected: []types.Package{
  1361  				{
  1362  					Name:    "sqlite-dev",
  1363  					Version: "3.26.0-r3",
  1364  				},
  1365  				{
  1366  					Name:    "sqlite",
  1367  					Version: "3.26.0-r3",
  1368  				},
  1369  			},
  1370  		},
  1371  	}
  1372  	analyzer := alpineCmdAnalyzer{}
  1373  	for testName, v := range tests {
  1374  		actual := analyzer.guessVersion(v.apkIndexArchive, v.pkgs, v.createdAt)
  1375  		if !reflect.DeepEqual(v.expected, actual) {
  1376  			t.Errorf("[%s]\n%s", testName, pretty.Compare(v.expected, actual))
  1377  		}
  1378  	}
  1379  }