package image_test

import (
	"context"
	"errors"
	"testing"
	"time"

	v1 "github.com/google/go-containerregistry/pkg/v1"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"golang.org/x/xerrors"

	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
	"github.com/aquasecurity/trivy/pkg/fanal/artifact"
	image2 "github.com/aquasecurity/trivy/pkg/fanal/artifact/image"
	"github.com/aquasecurity/trivy/pkg/fanal/cache"
	"github.com/aquasecurity/trivy/pkg/fanal/image"
	"github.com/aquasecurity/trivy/pkg/fanal/types"

	_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/command/apk"
	_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/php/composer"
	_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/ruby/bundler"
	_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/licensing"
	_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alpine"
	_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/debian"
	_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/apk"
	_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/dpkg"
	_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/repo/apk"
	_ "github.com/aquasecurity/trivy/pkg/fanal/handler/misconf"
	_ "github.com/aquasecurity/trivy/pkg/fanal/handler/sysfile"
)

func TestArtifact_Inspect(t *testing.T) {
	tests := []struct {
		name                    string
		imagePath               string
		artifactOpt             artifact.Option
		missingBlobsExpectation cache.ArtifactCacheMissingBlobsExpectation
		putBlobExpectations     []cache.ArtifactCachePutBlobExpectation
		putArtifactExpectations []cache.ArtifactCachePutArtifactExpectation
		want                    types.ArtifactReference
		wantErr                 string
	}{
		{
			name:      "happy path",
			imagePath: "../../test/testdata/alpine-311.tar.gz",
			missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{
				Args: cache.ArtifactCacheMissingBlobsArgs{
					ArtifactID: "sha256:059741cfbdc039e88e337d621e57e03e99b0e0a75df32f2027ebef13f839af65",
					BlobIDs:    []string{"sha256:bb59015f49048b23e73873f72dc5d0f42b44c64890ba13662849e8e4f9c2f1b7"},
				},
				Returns: cache.ArtifactCacheMissingBlobsReturns{
					MissingArtifact: true,
					MissingBlobIDs:  []string{"sha256:bb59015f49048b23e73873f72dc5d0f42b44c64890ba13662849e8e4f9c2f1b7"},
				},
			},
			putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{
				{
					Args: cache.ArtifactCachePutBlobArgs{
						BlobID: "sha256:bb59015f49048b23e73873f72dc5d0f42b44c64890ba13662849e8e4f9c2f1b7",
						BlobInfo: types.BlobInfo{
							SchemaVersion: types.BlobJSONSchemaVersion,
							Digest:        "",
							DiffID:        "sha256:beee9f30bc1f711043e78d4a2be0668955d4b761d587d6f60c2c8dc081efb203",
							OS: &types.OS{
								Family: "alpine",
								Name:   "3.11.5",
							},
							Repository: &types.Repository{
								Family:  "alpine",
								Release: "3.11",
							},
							PackageInfos: []types.PackageInfo{
								{
									FilePath: "lib/apk/db/installed",
									Packages: []types.Package{
										{
											Name: "alpine-baselayout", Version: "3.2.0-r3",
											SrcName: "alpine-baselayout", SrcVersion: "3.2.0-r3",
											Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "alpine-keys", Version: "2.1-r2", SrcName: "alpine-keys",
											SrcVersion: "2.1-r2", Licenses: []string{"MIT"},
										},
										{
											Name: "apk-tools", Version: "2.10.4-r3", SrcName: "apk-tools",
											SrcVersion: "2.10.4-r3", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "busybox", Version: "1.31.1-r9", SrcName: "busybox",
											SrcVersion: "1.31.1-r9", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "ca-certificates-cacert", Version: "20191127-r1",
											SrcName: "ca-certificates", SrcVersion: "20191127-r1",
											Licenses: []string{"MPL-2.0", "GPL-2.0"},
										},
										{
											Name: "libc-utils", Version: "0.7.2-r0", SrcName: "libc-dev",
											SrcVersion: "0.7.2-r0", Licenses: []string{"BSD-3-Clause"},
										},
										{
											Name: "libcrypto1.1", Version: "1.1.1d-r3", SrcName: "openssl",
											SrcVersion: "1.1.1d-r3", Licenses: []string{"OpenSSL"},
										},
										{
											Name: "libssl1.1", Version: "1.1.1d-r3", SrcName: "openssl",
											SrcVersion: "1.1.1d-r3", Licenses: []string{"OpenSSL"},
										},
										{
											Name: "libtls-standalone", Version: "2.9.1-r0",
											SrcName: "libtls-standalone", SrcVersion: "2.9.1-r0",
											Licenses: []string{"ISC"},
										},
										{
											Name: "musl", Version: "1.1.24-r2", SrcName: "musl",
											SrcVersion: "1.1.24-r2", Licenses: []string{"MIT"},
										},
										{
											Name: "musl-utils", Version: "1.1.24-r2", SrcName: "musl",
											SrcVersion: "1.1.24-r2", Licenses: []string{"MIT", "BSD-3-Clause", "GPL-2.0"},
										},
										{
											Name: "scanelf", Version: "1.2.4-r0", SrcName: "pax-utils",
											SrcVersion: "1.2.4-r0", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "ssl_client", Version: "1.31.1-r9", SrcName: "busybox",
											SrcVersion: "1.31.1-r9", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "zlib", Version: "1.2.11-r3", SrcName: "zlib",
											SrcVersion: "1.2.11-r3", Licenses: []string{"Zlib"},
										},
									},
								},
							},
							Licenses: []types.LicenseFile{
								{
									Type:     "license-file",
									FilePath: "/etc/ssl/misc/CA.pl",
									Findings: []types.LicenseFinding{
										{
											Name:       "OpenSSL",
											Confidence: 1,
											Link:       "https://spdx.org/licenses/OpenSSL.html",
										},
									},
									Layer: types.Layer{
										Digest: "",
										DiffID: "",
									},
								},
								{
									Type:     "license-file",
									FilePath: "/etc/ssl/misc/tsget.pl",
									Findings: []types.LicenseFinding{
										{
											Name:       "OpenSSL",
											Confidence: 1,
											Link:       "https://spdx.org/licenses/OpenSSL.html",
										},
									},
									Layer: types.Layer{
										Digest: "",
										DiffID: "",
									},
								},
							},
							Applications:  []types.Application(nil),
							OpaqueDirs:    []string(nil),
							WhiteoutFiles: []string(nil),
						},
					},
					Returns: cache.ArtifactCachePutBlobReturns{},
				},
			},
			putArtifactExpectations: []cache.ArtifactCachePutArtifactExpectation{
				{
					Args: cache.ArtifactCachePutArtifactArgs{
						ArtifactID: "sha256:059741cfbdc039e88e337d621e57e03e99b0e0a75df32f2027ebef13f839af65",
						ArtifactInfo: types.ArtifactInfo{
							SchemaVersion: types.ArtifactJSONSchemaVersion,
							Architecture:  "amd64",
							Created:       time.Date(2020, 3, 23, 21, 19, 34, 196162891, time.UTC),
							DockerVersion: "18.09.7",
							OS:            "linux",
						},
					},
				},
			},
			want: types.ArtifactReference{
				Name:    "../../test/testdata/alpine-311.tar.gz",
				Type:    types.ArtifactContainerImage,
				ID:      "sha256:059741cfbdc039e88e337d621e57e03e99b0e0a75df32f2027ebef13f839af65",
				BlobIDs: []string{"sha256:bb59015f49048b23e73873f72dc5d0f42b44c64890ba13662849e8e4f9c2f1b7"},
				ImageMetadata: types.ImageMetadata{
					ID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
					DiffIDs: []string{
						"sha256:beee9f30bc1f711043e78d4a2be0668955d4b761d587d6f60c2c8dc081efb203",
					},
					ConfigFile: v1.ConfigFile{
						Architecture:  "amd64",
						Author:        "",
						Container:     "fb71ddde5f6411a82eb056a9190f0cc1c80d7f77a8509ee90a2054428edb0024",
						Created:       v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 196162891, time.UTC)},
						DockerVersion: "18.09.7",
						History: []v1.History{
							{
								Author:     "",
								Created:    v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 27725872, time.UTC)},
								CreatedBy:  "/bin/sh -c #(nop) ADD file:0c4555f363c2672e350001f1293e689875a3760afe7b3f9146886afe67121cba in / ",
								Comment:    "",
								EmptyLayer: false,
							},
							{
								Author:     "",
								Created:    v1.Time{Time: time.Date(2020, 3, 23, 21, 19, 34, 196162891, time.UTC)},
								CreatedBy:  "/bin/sh -c #(nop)  CMD [\"/bin/sh\"]",
								Comment:    "",
								EmptyLayer: true,
							},
						},
						OS: "linux",
						RootFS: v1.RootFS{
							Type: "layers", DiffIDs: []v1.Hash{
								{
									Algorithm: "sha256",
									Hex:       "beee9f30bc1f711043e78d4a2be0668955d4b761d587d6f60c2c8dc081efb203",
								},
							},
						},
						Config: v1.Config{
							Cmd:         []string{"/bin/sh"},
							Env:         []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
							Hostname:    "",
							Image:       "sha256:74df73bb19fbfc7fb5ab9a8234b3d98ee2fb92df5b824496679802685205ab8c",
							ArgsEscaped: true,
						},
					},
				},
			},
		},
		{
			name:      "happy path: include lock files",
			imagePath: "../../test/testdata/vuln-image.tar.gz",
			missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{
				Args: cache.ArtifactCacheMissingBlobsArgs{
					ArtifactID: "sha256:a646bb11d39c149d4aaf9b888233048e0848304e5abd75667ea6f21d540d800c",
					BlobIDs: []string{
						"sha256:6b7f14517d97567b8424123929188f4a807df7f6ba19cd6fdda4ffd1a2672115",
						"sha256:061feee2cc149279b3248dd65d62e8f93b66673751cee4ef9ff4c85d9becab1e",
						"sha256:585926dc8c241a62c6def47fe20d193cf95060609518bfdc109190f2e5593cb9",
						"sha256:47b1a45c2166e8a760f9c59efdaff94184d96ef278d61f466cece82e35b800b2",
					},
				},
				Returns: cache.ArtifactCacheMissingBlobsReturns{
					MissingBlobIDs: []string{
						"sha256:6b7f14517d97567b8424123929188f4a807df7f6ba19cd6fdda4ffd1a2672115",
						"sha256:061feee2cc149279b3248dd65d62e8f93b66673751cee4ef9ff4c85d9becab1e",
						"sha256:585926dc8c241a62c6def47fe20d193cf95060609518bfdc109190f2e5593cb9",
						"sha256:47b1a45c2166e8a760f9c59efdaff94184d96ef278d61f466cece82e35b800b2",
					},
				},
			},
			putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{
				{
					Args: cache.ArtifactCachePutBlobArgs{
						BlobID: "sha256:6b7f14517d97567b8424123929188f4a807df7f6ba19cd6fdda4ffd1a2672115",
						BlobInfo: types.BlobInfo{
							SchemaVersion: types.BlobJSONSchemaVersion,
							Digest:        "",
							DiffID:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
							OS: &types.OS{
								Family: "debian",
								Name:   "9.9",
							},
							PackageInfos: []types.PackageInfo{
								{
									FilePath: "var/lib/dpkg/status.d/base",
									Packages: []types.Package{
										{
											Name: "base-files", Version: "9.9+deb9u9", SrcName: "base-files",
											SrcVersion: "9.9+deb9u9",
										},
									},
								},
								{
									FilePath: "var/lib/dpkg/status.d/netbase",
									Packages: []types.Package{
										{Name: "netbase", Version: "5.4", SrcName: "netbase", SrcVersion: "5.4"},
									},
								},
								{
									FilePath: "var/lib/dpkg/status.d/tzdata",
									Packages: []types.Package{
										{
											Name: "tzdata", Version: "2019a-0+deb9u1", SrcName: "tzdata",
											SrcVersion: "2019a-0+deb9u1",
										},
									},
								},
							},
							Licenses: []types.LicenseFile{
								{
									Type:     types.LicenseTypeDpkg,
									FilePath: "usr/share/doc/base-files/copyright",
									Findings: []types.LicenseFinding{
										{Name: "GPL-3.0"},
									},
									PkgName: "base-files",
								},
								{
									Type:     types.LicenseTypeDpkg,
									FilePath: "usr/share/doc/ca-certificates/copyright",
									Findings: []types.LicenseFinding{
										{Name: "GPL-2.0"},
										{Name: "MPL-2.0"},
									},
									PkgName: "ca-certificates",
								},
								{
									Type:     types.LicenseTypeDpkg,
									FilePath: "usr/share/doc/netbase/copyright",
									Findings: []types.LicenseFinding{
										{Name: "GPL-2.0"},
									},
									PkgName: "netbase",
								},
							},
						},
					},
				},
				{
					Args: cache.ArtifactCachePutBlobArgs{
						BlobID: "sha256:061feee2cc149279b3248dd65d62e8f93b66673751cee4ef9ff4c85d9becab1e",
						BlobInfo: types.BlobInfo{
							SchemaVersion: types.BlobJSONSchemaVersion,
							Digest:        "",
							DiffID:        "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
							PackageInfos: []types.PackageInfo{
								{
									FilePath: "var/lib/dpkg/status.d/libc6",
									Packages: []types.Package{
										{
											Name: "libc6", Version: "2.24-11+deb9u4", SrcName: "glibc",
											SrcVersion: "2.24-11+deb9u4",
										},
									},
								},
								{
									FilePath: "var/lib/dpkg/status.d/libssl1",
									Packages: []types.Package{
										{
											Name: "libssl1.1", Version: "1.1.0k-1~deb9u1", SrcName: "openssl",
											SrcVersion: "1.1.0k-1~deb9u1",
										},
									},
								},
								{
									FilePath: "var/lib/dpkg/status.d/openssl",
									Packages: []types.Package{
										{
											Name: "openssl", Version: "1.1.0k-1~deb9u1", SrcName: "openssl",
											SrcVersion: "1.1.0k-1~deb9u1",
										},
									},
								},
							},
							Licenses: []types.LicenseFile{
								{
									Type:     types.LicenseTypeDpkg,
									FilePath: "usr/share/doc/libc6/copyright",
									Findings: []types.LicenseFinding{
										{Name: "LGPL-2.1"},
										{Name: "GPL-2.0"},
									},
									PkgName: "libc6",
								},
								{
									Type:     types.LicenseTypeDpkg,
									FilePath: "usr/share/doc/libssl1.1/copyright",
									Findings: []types.LicenseFinding{
										{Name: "OpenSSL"},
									},
									PkgName: "libssl1.1",
								},
								{
									Type:     types.LicenseTypeDpkg,
									FilePath: "usr/share/doc/openssl/copyright",
									Findings: []types.LicenseFinding{
										{Name: "OpenSSL"},
									},
									PkgName: "openssl",
								},
							},
						},
					},
				},
				{
					Args: cache.ArtifactCachePutBlobArgs{
						BlobID: "sha256:585926dc8c241a62c6def47fe20d193cf95060609518bfdc109190f2e5593cb9",
						BlobInfo: types.BlobInfo{
							SchemaVersion: types.BlobJSONSchemaVersion,
							Digest:        "",
							DiffID:        "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
							Applications: []types.Application{
								{
									Type: "composer", FilePath: "php-app/composer.lock",
									Libraries: []types.Package{
										{Name: "guzzlehttp/guzzle", Version: "6.2.0"},
										{Name: "guzzlehttp/promises", Version: "v1.3.1"},
										{Name: "guzzlehttp/psr7", Version: "1.5.2"},
										{Name: "laravel/installer", Version: "v2.0.1"},
										{Name: "pear/log", Version: "1.13.1"},
										{Name: "pear/pear_exception", Version: "v1.0.0"},
										{Name: "psr/http-message", Version: "1.0.1"},
										{Name: "ralouphie/getallheaders", Version: "2.0.5"},
										{Name: "symfony/console", Version: "v4.2.7"},
										{Name: "symfony/contracts", Version: "v1.0.2"},
										{Name: "symfony/filesystem", Version: "v4.2.7"},
										{Name: "symfony/polyfill-ctype", Version: "v1.11.0"},
										{Name: "symfony/polyfill-mbstring", Version: "v1.11.0"},
										{Name: "symfony/process", Version: "v4.2.7"},
									},
								},
							},
							OpaqueDirs: []string{"php-app/"},
						},
					},
				},
				{
					Args: cache.ArtifactCachePutBlobArgs{
						BlobID: "sha256:47b1a45c2166e8a760f9c59efdaff94184d96ef278d61f466cece82e35b800b2",
						BlobInfo: types.BlobInfo{
							SchemaVersion: types.BlobJSONSchemaVersion,
							Digest:        "",
							DiffID:        "sha256:a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566",
							Applications: []types.Application{
								{
									Type: types.Bundler, FilePath: "ruby-app/Gemfile.lock",
									Libraries: []types.Package{
										{Name: "actioncable", Version: "5.2.3"},
										{Name: "actionmailer", Version: "5.2.3"},
										{Name: "actionpack", Version: "5.2.3"},
										{Name: "actionview", Version: "5.2.3"},
										{Name: "activejob", Version: "5.2.3"},
										{Name: "activemodel", Version: "5.2.3"},
										{Name: "activerecord", Version: "5.2.3"},
										{Name: "activestorage", Version: "5.2.3"},
										{Name: "activesupport", Version: "5.2.3"},
										{Name: "arel", Version: "9.0.0"},
										{Name: "ast", Version: "2.4.0"},
										{Name: "builder", Version: "3.2.3"},
										{Name: "coderay", Version: "1.1.2"},
										{Name: "concurrent-ruby", Version: "1.1.5"},
										{Name: "crass", Version: "1.0.4"},
										{Name: "dotenv", Version: "2.7.2"},
										{Name: "erubi", Version: "1.8.0"},
										{Name: "faker", Version: "1.9.3"},
										{Name: "globalid", Version: "0.4.2"},
										{Name: "i18n", Version: "1.6.0"},
										{Name: "jaro_winkler", Version: "1.5.2"},
										{Name: "json", Version: "2.2.0"},
										{Name: "loofah", Version: "2.2.3"},
										{Name: "mail", Version: "2.7.1"},
										{Name: "marcel", Version: "0.3.3"},
										{Name: "method_source", Version: "0.9.2"},
										{Name: "mimemagic", Version: "0.3.3"},
										{Name: "mini_mime", Version: "1.0.1"},
										{Name: "mini_portile2", Version: "2.4.0"},
										{Name: "minitest", Version: "5.11.3"},
										{Name: "nio4r", Version: "2.3.1"},
										{Name: "nokogiri", Version: "1.10.3"},
										{Name: "parallel", Version: "1.17.0"},
										{Name: "parser", Version: "2.6.3.0"},
										{Name: "pry", Version: "0.12.2"},
										{Name: "psych", Version: "3.1.0"},
										{Name: "rack", Version: "2.0.7"},
										{Name: "rack-test", Version: "1.1.0"},
										{Name: "rails", Version: "5.2.0"},
										{Name: "rails-dom-testing", Version: "2.0.3"},
										{Name: "rails-html-sanitizer", Version: "1.0.3"},
										{Name: "railties", Version: "5.2.3"},
										{Name: "rainbow", Version: "3.0.0"},
										{Name: "rake", Version: "12.3.2"},
										{Name: "rubocop", Version: "0.67.2"},
										{Name: "ruby-progressbar", Version: "1.10.0"},
										{Name: "sprockets", Version: "3.7.2"},
										{Name: "sprockets-rails", Version: "3.2.1"},
										{Name: "thor", Version: "0.20.3"},
										{Name: "thread_safe", Version: "0.3.6"},
										{Name: "tzinfo", Version: "1.2.5"},
										{Name: "unicode-display_width", Version: "1.5.0"},
										{Name: "websocket-driver", Version: "0.7.0"},
										{Name: "websocket-extensions", Version: "0.1.3"},
									},
								},
							},
							OpaqueDirs: []string{
								"ruby-app/",
							},
						},
					},
				},
			},
			want: types.ArtifactReference{
				Name: "../../test/testdata/vuln-image.tar.gz",
				Type: types.ArtifactContainerImage,
				ID:   "sha256:a646bb11d39c149d4aaf9b888233048e0848304e5abd75667ea6f21d540d800c",
				BlobIDs: []string{
					"sha256:6b7f14517d97567b8424123929188f4a807df7f6ba19cd6fdda4ffd1a2672115",
					"sha256:061feee2cc149279b3248dd65d62e8f93b66673751cee4ef9ff4c85d9becab1e",
					"sha256:585926dc8c241a62c6def47fe20d193cf95060609518bfdc109190f2e5593cb9",
					"sha256:47b1a45c2166e8a760f9c59efdaff94184d96ef278d61f466cece82e35b800b2",
				},
				ImageMetadata: types.ImageMetadata{
					ID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4",
					DiffIDs: []string{
						"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
						"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
						"sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
						"sha256:a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566",
					},
					ConfigFile: v1.ConfigFile{
						Architecture:  "amd64",
						Author:        "",
						Created:       v1.Time{Time: time.Date(2020, 2, 16, 10, 38, 41, 114114788, time.UTC)},
						DockerVersion: "19.03.5",
						History: []v1.History{
							{
								Author:     "Bazel",
								Created:    v1.Time{Time: time.Date(1970, 01, 01, 0, 0, 0, 0, time.UTC)},
								CreatedBy:  "bazel build ...",
								EmptyLayer: false,
							},
							{
								Author:     "Bazel",
								Created:    v1.Time{Time: time.Date(1970, 01, 01, 0, 0, 0, 0, time.UTC)},
								CreatedBy:  "bazel build ...",
								EmptyLayer: false,
							},
							{
								Author:     "",
								Created:    v1.Time{Time: time.Date(2020, 2, 16, 10, 38, 40, 976530082, time.UTC)},
								CreatedBy:  "/bin/sh -c #(nop) COPY file:842584685f26edb24dc305d76894f51cfda2bad0c24a05e727f9d4905d184a70 in /php-app/composer.lock ",
								Comment:    "",
								EmptyLayer: false,
							},
							{
								Author:     "",
								Created:    v1.Time{Time: time.Date(2020, 2, 16, 10, 38, 41, 114114788, time.UTC)},
								CreatedBy:  "/bin/sh -c #(nop) COPY file:c6d0373d380252b91829a5bb3c81d5b1afa574c91cef7752d18170a231c31f6d in /ruby-app/Gemfile.lock ",
								Comment:    "",
								EmptyLayer: false,
							},
						},
						OS: "linux",
						RootFS: v1.RootFS{
							Type: "layers",
							DiffIDs: []v1.Hash{
								{
									Algorithm: "sha256",
									Hex:       "932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
								}, {
									Algorithm: "sha256",
									Hex:       "dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
								}, {
									Algorithm: "sha256",
									Hex:       "24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
								}, {
									Algorithm: "sha256",
									Hex:       "a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566",
								},
							},
						},
						Config: v1.Config{
							Env: []string{
								"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
								"SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt",
							},
							Image: "sha256:916390dcf84a1c7852e298f24fb5389a6e7801102086924e55eb08cd58d6a741",
						},
					},
				},
			},
		},
		{
			name:      "happy path: disable analyzers",
			imagePath: "../../test/testdata/vuln-image.tar.gz",
			artifactOpt: artifact.Option{
				DisabledAnalyzers: []analyzer.Type{
					analyzer.TypeDebian, analyzer.TypeDpkg, analyzer.TypeDpkgLicense, analyzer.TypeComposer,
					analyzer.TypeBundler, analyzer.TypeLicenseFile,
				},
			},
			missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{
				Args: cache.ArtifactCacheMissingBlobsArgs{
					ArtifactID: "sha256:a646bb11d39c149d4aaf9b888233048e0848304e5abd75667ea6f21d540d800c",
					BlobIDs: []string{
						"sha256:57ada28264043324e1f99eb3db63de1a7e3f27f1fa4dcbb1df2f76875b98b9c4",
						"sha256:64f08ed6c84283289beb64335f76a4c60a89f62c7937b8ea50fd8bfda304f0e2",
						"sha256:da802174ac83921ac629ec623f5f5ad530291fb2420102f6a213322cb257655c",
						"sha256:996fcbfcc2964d20456afd0de16747533693b1cbebb72a6d28823a134abf0f5f",
					},
				},
				Returns: cache.ArtifactCacheMissingBlobsReturns{
					MissingBlobIDs: []string{
						"sha256:57ada28264043324e1f99eb3db63de1a7e3f27f1fa4dcbb1df2f76875b98b9c4",
						"sha256:64f08ed6c84283289beb64335f76a4c60a89f62c7937b8ea50fd8bfda304f0e2",
						"sha256:da802174ac83921ac629ec623f5f5ad530291fb2420102f6a213322cb257655c",
						"sha256:996fcbfcc2964d20456afd0de16747533693b1cbebb72a6d28823a134abf0f5f",
					},
				},
			},
			putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{
				{
					Args: cache.ArtifactCachePutBlobArgs{
						BlobID: "sha256:57ada28264043324e1f99eb3db63de1a7e3f27f1fa4dcbb1df2f76875b98b9c4",
						BlobInfo: types.BlobInfo{
							SchemaVersion: types.BlobJSONSchemaVersion,
							Digest:        "",
							DiffID:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
						},
					},
				},
				{
					Args: cache.ArtifactCachePutBlobArgs{
						BlobID: "sha256:64f08ed6c84283289beb64335f76a4c60a89f62c7937b8ea50fd8bfda304f0e2",
						BlobInfo: types.BlobInfo{
							SchemaVersion: types.BlobJSONSchemaVersion,
							Digest:        "",
							DiffID:        "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
						},
					},
				},
				{
					Args: cache.ArtifactCachePutBlobArgs{
						BlobID: "sha256:da802174ac83921ac629ec623f5f5ad530291fb2420102f6a213322cb257655c",
						BlobInfo: types.BlobInfo{
							SchemaVersion: types.BlobJSONSchemaVersion,
							Digest:        "",
							DiffID:        "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
							OpaqueDirs:    []string{"php-app/"},
						},
					},
				},
				{
					Args: cache.ArtifactCachePutBlobArgs{
						BlobID: "sha256:996fcbfcc2964d20456afd0de16747533693b1cbebb72a6d28823a134abf0f5f",
						BlobInfo: types.BlobInfo{
							SchemaVersion: types.BlobJSONSchemaVersion,
							Digest:        "",
							DiffID:        "sha256:a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566",
							OpaqueDirs:    []string{"ruby-app/"},
						},
					},
				},
			},
			want: types.ArtifactReference{
				Name: "../../test/testdata/vuln-image.tar.gz",
				Type: types.ArtifactContainerImage,
				ID:   "sha256:a646bb11d39c149d4aaf9b888233048e0848304e5abd75667ea6f21d540d800c",
				BlobIDs: []string{
					"sha256:57ada28264043324e1f99eb3db63de1a7e3f27f1fa4dcbb1df2f76875b98b9c4",
					"sha256:64f08ed6c84283289beb64335f76a4c60a89f62c7937b8ea50fd8bfda304f0e2",
					"sha256:da802174ac83921ac629ec623f5f5ad530291fb2420102f6a213322cb257655c",
					"sha256:996fcbfcc2964d20456afd0de16747533693b1cbebb72a6d28823a134abf0f5f",
				},
				ImageMetadata: types.ImageMetadata{
					ID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4",
					DiffIDs: []string{
						"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
						"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
						"sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
						"sha256:a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566",
					},
					ConfigFile: v1.ConfigFile{
						Architecture:  "amd64",
						Author:        "",
						Created:       v1.Time{Time: time.Date(2020, 2, 16, 10, 38, 41, 114114788, time.UTC)},
						DockerVersion: "19.03.5",
						History: []v1.History{
							{
								Author:     "Bazel",
								Created:    v1.Time{Time: time.Date(1970, 01, 01, 0, 0, 0, 0, time.UTC)},
								CreatedBy:  "bazel build ...",
								Comment:    "",
								EmptyLayer: false,
							},
							{
								Author:     "Bazel",
								Created:    v1.Time{Time: time.Date(1970, 01, 01, 0, 0, 0, 0, time.UTC)},
								CreatedBy:  "bazel build ...",
								Comment:    "",
								EmptyLayer: false,
							},
							{
								Created:    v1.Time{Time: time.Date(2020, 2, 16, 10, 38, 40, 976530082, time.UTC)},
								CreatedBy:  "/bin/sh -c #(nop) COPY file:842584685f26edb24dc305d76894f51cfda2bad0c24a05e727f9d4905d184a70 in /php-app/composer.lock ",
								Comment:    "",
								EmptyLayer: false,
							},
							{
								Created:    v1.Time{Time: time.Date(2020, 2, 16, 10, 38, 41, 114114788, time.UTC)},
								CreatedBy:  "/bin/sh -c #(nop) COPY file:c6d0373d380252b91829a5bb3c81d5b1afa574c91cef7752d18170a231c31f6d in /ruby-app/Gemfile.lock ",
								Comment:    "",
								EmptyLayer: false,
							},
						},
						OS: "linux",
						RootFS: v1.RootFS{
							Type: "layers",
							DiffIDs: []v1.Hash{
								{
									Algorithm: "sha256",
									Hex:       "932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
								},
								{
									Algorithm: "sha256",
									Hex:       "dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
								},
								{
									Algorithm: "sha256",
									Hex:       "24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
								},
								{
									Algorithm: "sha256",
									Hex:       "a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566",
								},
							},
						},
						Config: v1.Config{
							Env: []string{
								"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
								"SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt",
							},
							Hostname: "",
							Image:    "sha256:916390dcf84a1c7852e298f24fb5389a6e7801102086924e55eb08cd58d6a741",
						},
					},
				},
			},
		},
		{
			name:      "sad path, MissingBlobs returns an error",
			imagePath: "../../test/testdata/alpine-311.tar.gz",
			missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{
				Args: cache.ArtifactCacheMissingBlobsArgs{
					ArtifactID: "sha256:059741cfbdc039e88e337d621e57e03e99b0e0a75df32f2027ebef13f839af65",
					BlobIDs:    []string{"sha256:bb59015f49048b23e73873f72dc5d0f42b44c64890ba13662849e8e4f9c2f1b7"},
				},
				Returns: cache.ArtifactCacheMissingBlobsReturns{
					Err: xerrors.New("MissingBlobs failed"),
				},
			},
			wantErr: "MissingBlobs failed",
		},
		{
			name:      "sad path, PutBlob returns an error",
			imagePath: "../../test/testdata/alpine-311.tar.gz",
			missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{
				Args: cache.ArtifactCacheMissingBlobsArgs{
					ArtifactID: "sha256:059741cfbdc039e88e337d621e57e03e99b0e0a75df32f2027ebef13f839af65",
					BlobIDs:    []string{"sha256:bb59015f49048b23e73873f72dc5d0f42b44c64890ba13662849e8e4f9c2f1b7"},
				},
				Returns: cache.ArtifactCacheMissingBlobsReturns{
					MissingBlobIDs: []string{"sha256:bb59015f49048b23e73873f72dc5d0f42b44c64890ba13662849e8e4f9c2f1b7"},
				},
			},
			putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{
				{
					Args: cache.ArtifactCachePutBlobArgs{
						BlobID: "sha256:bb59015f49048b23e73873f72dc5d0f42b44c64890ba13662849e8e4f9c2f1b7",
						BlobInfo: types.BlobInfo{
							SchemaVersion: types.BlobJSONSchemaVersion,
							Digest:        "",
							DiffID:        "sha256:beee9f30bc1f711043e78d4a2be0668955d4b761d587d6f60c2c8dc081efb203",
							OS: &types.OS{
								Family: "alpine",
								Name:   "3.11.5",
							},
							Repository: &types.Repository{
								Family:  "alpine",
								Release: "3.11",
							},
							PackageInfos: []types.PackageInfo{
								{
									FilePath: "lib/apk/db/installed",
									Packages: []types.Package{
										{
											Name: "alpine-baselayout", Version: "3.2.0-r3",
											SrcName: "alpine-baselayout", SrcVersion: "3.2.0-r3",
											Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "alpine-keys", Version: "2.1-r2", SrcName: "alpine-keys",
											SrcVersion: "2.1-r2", Licenses: []string{"MIT"},
										},
										{
											Name: "apk-tools", Version: "2.10.4-r3", SrcName: "apk-tools",
											SrcVersion: "2.10.4-r3", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "busybox", Version: "1.31.1-r9", SrcName: "busybox",
											SrcVersion: "1.31.1-r9", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "ca-certificates-cacert", Version: "20191127-r1",
											SrcName: "ca-certificates", SrcVersion: "20191127-r1",
											Licenses: []string{"MPL-2.0", "GPL-2.0"},
										},
										{
											Name: "libc-utils", Version: "0.7.2-r0", SrcName: "libc-dev",
											SrcVersion: "0.7.2-r0", Licenses: []string{"BSD-3-Clause"},
										},
										{
											Name: "libcrypto1.1", Version: "1.1.1d-r3", SrcName: "openssl",
											SrcVersion: "1.1.1d-r3", Licenses: []string{"OpenSSL"},
										},
										{
											Name: "libssl1.1", Version: "1.1.1d-r3", SrcName: "openssl",
											SrcVersion: "1.1.1d-r3", Licenses: []string{"OpenSSL"},
										},
										{
											Name: "libtls-standalone", Version: "2.9.1-r0",
											SrcName: "libtls-standalone", SrcVersion: "2.9.1-r0",
											Licenses: []string{"ISC"},
										},
										{
											Name: "musl", Version: "1.1.24-r2", SrcName: "musl",
											SrcVersion: "1.1.24-r2", Licenses: []string{"MIT"},
										},
										{
											Name: "musl-utils", Version: "1.1.24-r2", SrcName: "musl",
											SrcVersion: "1.1.24-r2", Licenses: []string{"MIT", "BSD-3-Clause", "GPL-2.0"},
										},
										{
											Name: "scanelf", Version: "1.2.4-r0", SrcName: "pax-utils",
											SrcVersion: "1.2.4-r0", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "ssl_client", Version: "1.31.1-r9", SrcName: "busybox",
											SrcVersion: "1.31.1-r9", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "zlib", Version: "1.2.11-r3", SrcName: "zlib",
											SrcVersion: "1.2.11-r3", Licenses: []string{"Zlib"},
										},
									},
								},
							},
							Licenses: []types.LicenseFile{
								{
									Type:     "license-file",
									FilePath: "/etc/ssl/misc/CA.pl",
									Findings: []types.LicenseFinding{
										{
											Name:       "OpenSSL",
											Confidence: 1,
											Link:       "https://spdx.org/licenses/OpenSSL.html",
										},
									},
								},
								{
									Type:     "license-file",
									FilePath: "/etc/ssl/misc/tsget.pl",
									Findings: []types.LicenseFinding{
										{
											Name:       "OpenSSL",
											Confidence: 1,
											Link:       "https://spdx.org/licenses/OpenSSL.html",
										},
									},
								},
							},
							Applications:  []types.Application(nil),
							OpaqueDirs:    []string(nil),
							WhiteoutFiles: []string(nil),
						},
					},
					Returns: cache.ArtifactCachePutBlobReturns{
						Err: errors.New("put layer failed"),
					},
				},
			},
			wantErr: "put layer failed",
		},
		{
			name:      "sad path, PutArtifact returns an error",
			imagePath: "../../test/testdata/alpine-311.tar.gz",
			missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{
				Args: cache.ArtifactCacheMissingBlobsArgs{
					ArtifactID: "sha256:059741cfbdc039e88e337d621e57e03e99b0e0a75df32f2027ebef13f839af65",
					BlobIDs:    []string{"sha256:bb59015f49048b23e73873f72dc5d0f42b44c64890ba13662849e8e4f9c2f1b7"},
				},
				Returns: cache.ArtifactCacheMissingBlobsReturns{
					MissingArtifact: true,
					MissingBlobIDs:  []string{"sha256:bb59015f49048b23e73873f72dc5d0f42b44c64890ba13662849e8e4f9c2f1b7"},
				},
			},
			putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{
				{
					Args: cache.ArtifactCachePutBlobArgs{
						BlobID: "sha256:bb59015f49048b23e73873f72dc5d0f42b44c64890ba13662849e8e4f9c2f1b7",
						BlobInfo: types.BlobInfo{
							SchemaVersion: types.BlobJSONSchemaVersion,
							Digest:        "",
							DiffID:        "sha256:beee9f30bc1f711043e78d4a2be0668955d4b761d587d6f60c2c8dc081efb203",
							OS: &types.OS{
								Family: "alpine",
								Name:   "3.11.5",
							},
							Repository: &types.Repository{
								Family:  "alpine",
								Release: "3.11",
							},
							PackageInfos: []types.PackageInfo{
								{
									FilePath: "lib/apk/db/installed",
									Packages: []types.Package{
										{
											Name: "alpine-baselayout", Version: "3.2.0-r3",
											SrcName: "alpine-baselayout", SrcVersion: "3.2.0-r3",
											Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "alpine-keys", Version: "2.1-r2", SrcName: "alpine-keys",
											SrcVersion: "2.1-r2", Licenses: []string{"MIT"},
										},
										{
											Name: "apk-tools", Version: "2.10.4-r3", SrcName: "apk-tools",
											SrcVersion: "2.10.4-r3", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "busybox", Version: "1.31.1-r9", SrcName: "busybox",
											SrcVersion: "1.31.1-r9", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "ca-certificates-cacert", Version: "20191127-r1",
											SrcName: "ca-certificates", SrcVersion: "20191127-r1",
											Licenses: []string{"MPL-2.0", "GPL-2.0"},
										},
										{
											Name: "libc-utils", Version: "0.7.2-r0", SrcName: "libc-dev",
											SrcVersion: "0.7.2-r0", Licenses: []string{"BSD-3-Clause"},
										},
										{
											Name: "libcrypto1.1", Version: "1.1.1d-r3", SrcName: "openssl",
											SrcVersion: "1.1.1d-r3", Licenses: []string{"OpenSSL"},
										},
										{
											Name: "libssl1.1", Version: "1.1.1d-r3", SrcName: "openssl",
											SrcVersion: "1.1.1d-r3", Licenses: []string{"OpenSSL"},
										},
										{
											Name: "libtls-standalone", Version: "2.9.1-r0",
											SrcName: "libtls-standalone", SrcVersion: "2.9.1-r0",
											Licenses: []string{"ISC"},
										},
										{
											Name: "musl", Version: "1.1.24-r2", SrcName: "musl",
											SrcVersion: "1.1.24-r2", Licenses: []string{"MIT"},
										},
										{
											Name: "musl-utils", Version: "1.1.24-r2", SrcName: "musl",
											SrcVersion: "1.1.24-r2", Licenses: []string{"MIT", "BSD-3-Clause", "GPL-2.0"},
										},
										{
											Name: "scanelf", Version: "1.2.4-r0", SrcName: "pax-utils",
											SrcVersion: "1.2.4-r0", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "ssl_client", Version: "1.31.1-r9", SrcName: "busybox",
											SrcVersion: "1.31.1-r9", Licenses: []string{"GPL-2.0"},
										},
										{
											Name: "zlib", Version: "1.2.11-r3", SrcName: "zlib",
											SrcVersion: "1.2.11-r3", Licenses: []string{"Zlib"},
										},
									},
								},
							},
							Licenses: []types.LicenseFile{
								{
									Type:     "license-file",
									FilePath: "/etc/ssl/misc/CA.pl",
									Findings: []types.LicenseFinding{
										{
											Name:       "OpenSSL",
											Confidence: 1,
											Link:       "https://spdx.org/licenses/OpenSSL.html",
										},
									},
								},
								{
									Type:     "license-file",
									FilePath: "/etc/ssl/misc/tsget.pl",
									Findings: []types.LicenseFinding{
										{
											Name:       "OpenSSL",
											Confidence: 1,
											Link:       "https://spdx.org/licenses/OpenSSL.html",
										},
									},
								},
							},
							Applications:  []types.Application(nil),
							OpaqueDirs:    []string(nil),
							WhiteoutFiles: []string(nil),
						},
					},
					Returns: cache.ArtifactCachePutBlobReturns{},
				},
			},
			putArtifactExpectations: []cache.ArtifactCachePutArtifactExpectation{
				{
					Args: cache.ArtifactCachePutArtifactArgs{
						ArtifactID: "sha256:059741cfbdc039e88e337d621e57e03e99b0e0a75df32f2027ebef13f839af65",
						ArtifactInfo: types.ArtifactInfo{
							SchemaVersion: types.ArtifactJSONSchemaVersion,
							Architecture:  "amd64",
							Created:       time.Date(2020, 3, 23, 21, 19, 34, 196162891, time.UTC),
							DockerVersion: "18.09.7",
							OS:            "linux",
						},
					},
					Returns: cache.ArtifactCachePutArtifactReturns{
						Err: errors.New("put artifact failed"),
					},
				},
			},
			wantErr: "put artifact failed",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mockCache := new(cache.MockArtifactCache)
			mockCache.ApplyMissingBlobsExpectation(tt.missingBlobsExpectation)
			mockCache.ApplyPutBlobExpectations(tt.putBlobExpectations)
			mockCache.ApplyPutArtifactExpectations(tt.putArtifactExpectations)

			img, err := image.NewArchiveImage(tt.imagePath)
			require.NoError(t, err)

			a, err := image2.NewArtifact(img, mockCache, tt.artifactOpt)
			require.NoError(t, err)

			got, err := a.Inspect(context.Background())
			if tt.wantErr != "" {
				require.NotNil(t, err)
				assert.Contains(t, err.Error(), tt.wantErr, tt.name)
			} else {
				require.NoError(t, err, tt.name)
			}
			assert.Equal(t, tt.want, got)
		})
	}
}
