package image_test

import (
	"math/rand"
	"testing"
	"time"

	"github.com/docker/go-units"
	v1 "github.com/google/go-containerregistry/pkg/v1"
	"github.com/google/go-containerregistry/pkg/v1/random"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/aquasecurity/trivy/internal/cachetest"
	"github.com/aquasecurity/trivy/internal/testutil"
	"github.com/aquasecurity/trivy/pkg/cache"
	"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/image"
	"github.com/aquasecurity/trivy/pkg/fanal/types"

	_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all"
	_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/imgconf/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/analyzer/secret"
	_ "github.com/aquasecurity/trivy/pkg/fanal/handler/sysfile"
)

// Common blob IDs used across multiple test cases to reduce duplication
const (
	alpineBaseLayerID     = "sha256:6c42077a82b21707f581759b12a99cc9a593ce35a0d7be4c19c01eb48bd5ba33"
	alpineBaseLayerDiffID = "sha256:beee9f30bc1f711043e78d4a2be0668955d4b761d587d6f60c2c8dc081efb203"
	alpineArtifactID      = "sha256:3c709d2a158be3a97051e10cd0e30f047225cb9505101feb3fadcd395c2e0408"
	composerImageID       = "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72"
)

func TestArtifact_Inspect(t *testing.T) {
	alpinePkgs := types.Packages{
		{
			ID:         "alpine-baselayout@3.2.0-r3",
			Name:       "alpine-baselayout",
			Version:    "3.2.0-r3",
			SrcName:    "alpine-baselayout",
			SrcVersion: "3.2.0-r3",
			Licenses:   []string{"GPL-2.0-only"},
			Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
			Digest:     "sha1:8f373f5b329c3aaf136eb30c63a387661ee0f3d0",
			DependsOn: []string{
				"busybox@1.31.1-r9",
				"musl@1.1.24-r2",
			},
			Arch: "x86_64",
			InstalledFiles: []string{
				"etc/hosts",
				"etc/sysctl.conf",
				"etc/group",
				"etc/protocols",
				"etc/fstab",
				"etc/mtab",
				"etc/profile",
				"etc/shells",
				"etc/motd",
				"etc/inittab",
				"etc/hostname",
				"etc/modules",
				"etc/services",
				"etc/shadow",
				"etc/passwd",
				"etc/profile.d/locale",
				"etc/profile.d/color_prompt",
				"etc/sysctl.d/00-alpine.conf",
				"etc/modprobe.d/i386.conf",
				"etc/modprobe.d/blacklist.conf",
				"etc/modprobe.d/aliases.conf",
				"etc/modprobe.d/kms.conf",
				"etc/crontabs/root",
				"sbin/mkmntdirs",
				"var/run",
				"var/spool/mail",
				"var/spool/cron/crontabs",
			},
		},
		{
			ID:         "alpine-keys@2.1-r2",
			Name:       "alpine-keys",
			Version:    "2.1-r2",
			SrcName:    "alpine-keys",
			SrcVersion: "2.1-r2",
			Licenses:   []string{"MIT"},
			Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
			Arch:       "x86_64",
			Digest:     "sha1:64929f85b7f8b4adbb664d905410312936b79d9b",
			InstalledFiles: []string{
				"etc/apk/keys/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub",
				"etc/apk/keys/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub",
				"etc/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub",
				"usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub",
				"usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub",
				"usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub",
				"usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58cbb476.rsa.pub",
				"usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub",
				"usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub",
				"usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58e4f17d.rsa.pub",
				"usr/share/apk/keys/aarch64/alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub",
				"usr/share/apk/keys/ppc64le/alpine-devel@lists.alpinelinux.org-58cbb476.rsa.pub",
				"usr/share/apk/keys/x86/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub",
				"usr/share/apk/keys/x86/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub",
				"usr/share/apk/keys/s390x/alpine-devel@lists.alpinelinux.org-58e4f17d.rsa.pub",
				"usr/share/apk/keys/armhf/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub",
				"usr/share/apk/keys/x86_64/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub",
				"usr/share/apk/keys/x86_64/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub",
			},
		},
		{
			ID:         "apk-tools@2.10.4-r3",
			Name:       "apk-tools",
			Version:    "2.10.4-r3",
			SrcName:    "apk-tools",
			SrcVersion: "2.10.4-r3",
			Licenses:   []string{"GPL-2.0-only"},
			Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
			Digest:     "sha1:b15ad0c90e4493dfdc948d6b90a8e020da8936ef",
			DependsOn: []string{
				"libcrypto1.1@1.1.1d-r3",
				"libssl1.1@1.1.1d-r3",
				"musl@1.1.24-r2",
				"zlib@1.2.11-r3",
			},
			Arch: "x86_64",
			InstalledFiles: []string{
				"sbin/apk",
			},
		},
		{
			ID:         "busybox@1.31.1-r9",
			Name:       "busybox",
			Version:    "1.31.1-r9",
			SrcName:    "busybox",
			SrcVersion: "1.31.1-r9",
			Licenses:   []string{"GPL-2.0-only"},
			Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
			Digest:     "sha1:a457703d71654811ea28d8d27a5cfc49ece27b34",
			DependsOn: []string{
				"musl@1.1.24-r2",
			},
			Arch: "x86_64",
			InstalledFiles: []string{
				"bin/busybox",
				"bin/sh",
				"etc/securetty",
				"etc/udhcpd.conf",
				"etc/logrotate.d/acpid",
				"etc/network/if-up.d/dad",
				"usr/share/udhcpc/default.script",
			},
		},
		{
			ID:         "ca-certificates-cacert@20191127-r1",
			Name:       "ca-certificates-cacert",
			Version:    "20191127-r1",
			SrcName:    "ca-certificates",
			SrcVersion: "20191127-r1",
			Licenses: []string{
				"MPL-2.0",
				"GPL-2.0-or-later",
			},
			Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
			Arch:       "x86_64",
			Digest:     "sha1:3aeb8a90d7179d2a187782e980a964494e08c5fb",
			InstalledFiles: []string{
				"etc/ssl/cert.pem",
			},
		},
		{
			ID:         "libc-utils@0.7.2-r0",
			Name:       "libc-utils",
			Version:    "0.7.2-r0",
			SrcName:    "libc-dev",
			SrcVersion: "0.7.2-r0",
			Licenses:   []string{"BSD-3-Clause"},
			Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
			Digest:     "sha1:a7bf32bd32c6d3de2d1c4d7e753a0919b998cd01",
			DependsOn: []string{
				"musl-utils@1.1.24-r2",
			},
			Arch: "x86_64",
		},
		{
			ID:         "libcrypto1.1@1.1.1d-r3",
			Name:       "libcrypto1.1",
			Version:    "1.1.1d-r3",
			SrcName:    "openssl",
			SrcVersion: "1.1.1d-r3",
			Licenses:   []string{"OpenSSL"},
			Maintainer: "Timo Teras <timo.teras@iki.fi>",
			Digest:     "sha1:dd8fb9a3cce7b2bcf954271da62fb85dac2b106a",
			DependsOn: []string{
				"musl@1.1.24-r2",
			},
			Arch: "x86_64",
			InstalledFiles: []string{
				"etc/ssl/openssl.cnf.dist",
				"etc/ssl/ct_log_list.cnf",
				"etc/ssl/ct_log_list.cnf.dist",
				"etc/ssl/openssl.cnf",
				"etc/ssl/misc/CA.pl",
				"etc/ssl/misc/tsget.pl",
				"etc/ssl/misc/tsget",
				"lib/libcrypto.so.1.1",
				"usr/lib/libcrypto.so.1.1",
				"usr/lib/engines-1.1/capi.so",
				"usr/lib/engines-1.1/padlock.so",
				"usr/lib/engines-1.1/afalg.so",
			},
		},
		{
			ID:         "libssl1.1@1.1.1d-r3",
			Name:       "libssl1.1",
			Version:    "1.1.1d-r3",
			SrcName:    "openssl",
			SrcVersion: "1.1.1d-r3",
			Licenses:   []string{"OpenSSL"},
			Maintainer: "Timo Teras <timo.teras@iki.fi>",
			Digest:     "sha1:938d46e41b3e56b339a3aeb2d02fad3d75728f35",
			DependsOn: []string{
				"libcrypto1.1@1.1.1d-r3",
				"musl@1.1.24-r2",
			},
			Arch: "x86_64",
			InstalledFiles: []string{
				"lib/libssl.so.1.1",
				"usr/lib/libssl.so.1.1",
			},
		},
		{
			ID:         "libtls-standalone@2.9.1-r0",
			Name:       "libtls-standalone",
			Version:    "2.9.1-r0",
			SrcName:    "libtls-standalone",
			SrcVersion: "2.9.1-r0",
			Licenses:   []string{"ISC"},
			Digest:     "sha1:b2e5627a56378ea6eeb962a8f33722df9393c1c5",
			DependsOn: []string{
				"ca-certificates-cacert@20191127-r1",
				"libcrypto1.1@1.1.1d-r3",
				"libssl1.1@1.1.1d-r3",
				"musl@1.1.24-r2",
			},
			Arch: "x86_64",
			InstalledFiles: []string{
				"usr/lib/libtls-standalone.so.1.0.0",
				"usr/lib/libtls-standalone.so.1",
			},
		},
		{
			ID:         "musl@1.1.24-r2",
			Name:       "musl",
			Version:    "1.1.24-r2",
			SrcName:    "musl",
			SrcVersion: "1.1.24-r2",
			Licenses:   []string{"MIT"},
			Maintainer: "Timo Teräs <timo.teras@iki.fi>",
			Arch:       "x86_64",
			Digest:     "sha1:cb2316a189ebee5282c4a9bd98794cc2477a74c6",
			InstalledFiles: []string{
				"lib/libc.musl-x86_64.so.1",
				"lib/ld-musl-x86_64.so.1",
			},
		},
		{
			ID:         "musl-utils@1.1.24-r2",
			Name:       "musl-utils",
			Version:    "1.1.24-r2",
			SrcName:    "musl",
			SrcVersion: "1.1.24-r2",
			Licenses: []string{
				"MIT",
				"BSD-3-Clause",
				"GPL-2.0-or-later",
			},
			Maintainer: "Timo Teräs <timo.teras@iki.fi>",
			Digest:     "sha1:6d3b45e79dbab444ca7cbfa59e2833203be6fb6a",
			DependsOn: []string{
				"musl@1.1.24-r2",
				"scanelf@1.2.4-r0",
			},
			Arch: "x86_64",
			InstalledFiles: []string{
				"sbin/ldconfig",
				"usr/bin/iconv",
				"usr/bin/ldd",
				"usr/bin/getconf",
				"usr/bin/getent",
			},
		},
		{
			ID:         "scanelf@1.2.4-r0",
			Name:       "scanelf",
			Version:    "1.2.4-r0",
			SrcName:    "pax-utils",
			SrcVersion: "1.2.4-r0",
			Licenses:   []string{"GPL-2.0-only"},
			Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
			Digest:     "sha1:d6147beb32bff803b5d9f83a3bec7ab319087185",
			DependsOn: []string{
				"musl@1.1.24-r2",
			},
			Arch: "x86_64",
			InstalledFiles: []string{
				"usr/bin/scanelf",
			},
		},
		{
			ID:         "ssl_client@1.31.1-r9",
			Name:       "ssl_client",
			Version:    "1.31.1-r9",
			SrcName:    "busybox",
			SrcVersion: "1.31.1-r9",
			Licenses:   []string{"GPL-2.0-only"},
			Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
			Digest:     "sha1:3b685152af320120ae8941c740d3376b54e43c10",
			DependsOn: []string{
				"libtls-standalone@2.9.1-r0",
				"musl@1.1.24-r2",
			},
			Arch: "x86_64",
			InstalledFiles: []string{
				"usr/bin/ssl_client",
			},
		},
		{
			ID:         "zlib@1.2.11-r3",
			Name:       "zlib",
			Version:    "1.2.11-r3",
			SrcName:    "zlib",
			SrcVersion: "1.2.11-r3",
			Licenses:   []string{"Zlib"},
			Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
			Digest:     "sha1:acca078ee8baa93e005f57b2fae359c1efd443cd",
			DependsOn: []string{
				"musl@1.1.24-r2",
			},
			Arch: "x86_64",
			InstalledFiles: []string{
				"lib/libz.so.1.2.11",
				"lib/libz.so.1",
			},
		},
	}

	tests := []struct {
		name         string
		imagePath    string
		artifactOpt  artifact.Option
		setupCache   func(t *testing.T) cache.Cache
		wantArtifact cachetest.WantArtifact
		wantBlobs    []cachetest.WantBlob
		want         artifact.Reference
		wantErr      string
	}{
		{
			name:      "happy path",
			imagePath: "../../test/testdata/alpine-311.tar.gz",
			artifactOpt: artifact.Option{
				LicenseScannerOption: analyzer.LicenseScannerOption{Full: true},
				ImageOption:          types.ImageOptions{MaxImageSize: units.GB},
			},
			wantBlobs: []cachetest.WantBlob{
				{
					ID: alpineBaseLayerID,
					BlobInfo: types.BlobInfo{
						SchemaVersion: types.BlobJSONSchemaVersion,
						Size:          5861888,
						Digest:        "",
						DiffID:        alpineBaseLayerDiffID,
						CreatedBy:     "ADD file:0c4555f363c2672e350001f1293e689875a3760afe7b3f9146886afe67121cba in / ",
						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: alpinePkgs,
							},
						},
						Licenses: []types.LicenseFile{
							{
								Type:     "header",
								FilePath: "etc/ssl/misc/CA.pl",
								Findings: []types.LicenseFinding{
									{
										Name:       "OpenSSL",
										Confidence: 1,
										Link:       "https://spdx.org/licenses/OpenSSL.html",
									},
								},
							},
							{
								Type:     "header",
								FilePath: "etc/ssl/misc/tsget.pl",
								Findings: []types.LicenseFinding{
									{
										Name:       "OpenSSL",
										Confidence: 1,
										Link:       "https://spdx.org/licenses/OpenSSL.html",
									},
								},
							},
						},
					},
				},
			},
			wantArtifact: cachetest.WantArtifact{
				ID: alpineArtifactID,
				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: artifact.Reference{
				Name:    "../../test/testdata/alpine-311.tar.gz",
				Type:    types.TypeContainerImage,
				ID:      alpineArtifactID,
				BlobIDs: []string{alpineBaseLayerID},
				ImageMetadata: artifact.ImageMetadata{
					ID: composerImageID,
					DiffIDs: []string{
						alpineBaseLayerDiffID,
					},
					RepoTags:    []string{"alpine:3.11"},
					RepoDigests: nil,
					Reference:   testutil.MustParseReference(t, "alpine:3.11"),
					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",
			artifactOpt: artifact.Option{
				LicenseScannerOption: analyzer.LicenseScannerOption{Full: true},
			},
			setupCache: func(t *testing.T) cache.Cache {
				c := cache.NewMemoryCache()
				require.NoError(t, c.PutArtifact(t.Context(), "sha256:0bebf0773ffd87baa7c64fbdbdf79a24ae125e3f99a8adebe52d1ccbe6bed16b", types.ArtifactInfo{
					SchemaVersion: types.ArtifactJSONSchemaVersion,
				}))
				return c
			},
			wantArtifact: cachetest.WantArtifact{
				ID: "sha256:0bebf0773ffd87baa7c64fbdbdf79a24ae125e3f99a8adebe52d1ccbe6bed16b",
				ArtifactInfo: types.ArtifactInfo{
					SchemaVersion: types.ArtifactJSONSchemaVersion,
				},
			},
			wantBlobs: []cachetest.WantBlob{
				{
					ID: "sha256:75a461ca76eecc6cea981889d69aa1c2dd78c436108be8be1bbc29295520c7d4",
					BlobInfo: types.BlobInfo{
						SchemaVersion: types.BlobJSONSchemaVersion,
						Size:          3061760,
						Digest:        "",
						DiffID:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
						CreatedBy:     "bazel build ...",
						OS: types.OS{
							Family: "debian",
							Name:   "9.9",
						},
						PackageInfos: []types.PackageInfo{
							{
								FilePath: "var/lib/dpkg/status.d/base",
								Packages: types.Packages{
									{
										ID:         "base-files@9.9+deb9u9",
										Name:       "base-files",
										Version:    "9.9+deb9u9",
										SrcName:    "base-files",
										SrcVersion: "9.9+deb9u9",
										Maintainer: "Santiago Vila <sanvila@debian.org>",
										Arch:       "amd64",
									},
								},
							},
							{
								FilePath: "var/lib/dpkg/status.d/netbase",
								Packages: types.Packages{
									{
										ID:         "netbase@5.4",
										Name:       "netbase",
										Version:    "5.4",
										SrcName:    "netbase",
										SrcVersion: "5.4",
										Maintainer: "Marco d'Itri <md@linux.it>",
										Arch:       "all",
									},
								},
							},
							{
								FilePath: "var/lib/dpkg/status.d/tzdata",
								Packages: types.Packages{
									{
										ID:         "tzdata@2019a-0+deb9u1",
										Name:       "tzdata",
										Version:    "2019a",
										SrcName:    "tzdata",
										Release:    "0+deb9u1",
										SrcVersion: "2019a",
										SrcRelease: "0+deb9u1",
										Maintainer: "GNU Libc Maintainers <debian-glibc@lists.debian.org>",
										Arch:       "all",
									},
								},
							},
						},
						Licenses: []types.LicenseFile{
							{
								Type:     types.LicenseTypeDpkg,
								FilePath: "usr/share/doc/base-files/copyright",
								Findings: []types.LicenseFinding{
									{Name: "GPL-2.0-or-later"},
								},
								PkgName: "base-files",
							},
							{
								Type:     types.LicenseTypeDpkg,
								FilePath: "usr/share/doc/ca-certificates/copyright",
								Findings: []types.LicenseFinding{
									{Name: "GPL-2.0-or-later"},
									{Name: "GPL-2.0-only"},
									{Name: "MPL-2.0"},
								},
								PkgName: "ca-certificates",
							},
							{
								Type:     types.LicenseTypeDpkg,
								FilePath: "usr/share/doc/netbase/copyright",
								Findings: []types.LicenseFinding{
									{Name: "GPL-2.0-only"},
								},
								PkgName: "netbase",
							},
						},
					},
				},
				{
					ID: "sha256:81afc1747d0fdec7a606c27570313634ae331fab6f13566b23d0f6b3e498c050",
					BlobInfo: types.BlobInfo{
						SchemaVersion: types.BlobJSONSchemaVersion,
						Size:          15441920,
						Digest:        "",
						DiffID:        "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
						CreatedBy:     "bazel build ...",
						PackageInfos: []types.PackageInfo{
							{
								FilePath: "var/lib/dpkg/status.d/libc6",
								Packages: types.Packages{
									{
										ID:         "libc6@2.24-11+deb9u4",
										Name:       "libc6",
										Version:    "2.24",
										Release:    "11+deb9u4",
										SrcName:    "glibc",
										SrcVersion: "2.24",
										SrcRelease: "11+deb9u4",
										Maintainer: "GNU Libc Maintainers <debian-glibc@lists.debian.org>",
										Arch:       "amd64",
									},
								},
							},
							{
								FilePath: "var/lib/dpkg/status.d/libssl1",
								Packages: types.Packages{
									{
										ID:         "libssl1.1@1.1.0k-1~deb9u1",
										Name:       "libssl1.1",
										Version:    "1.1.0k",
										SrcName:    "openssl",
										Release:    "1~deb9u1",
										SrcVersion: "1.1.0k",
										SrcRelease: "1~deb9u1",
										Maintainer: "Debian OpenSSL Team <pkg-openssl-devel@lists.alioth.debian.org>",
										Arch:       "amd64",
									},
								},
							},
							{
								FilePath: "var/lib/dpkg/status.d/openssl",
								Packages: types.Packages{
									{
										ID:         "openssl@1.1.0k-1~deb9u1",
										Name:       "openssl",
										Version:    "1.1.0k",
										SrcName:    "openssl",
										Release:    "1~deb9u1",
										SrcVersion: "1.1.0k",
										SrcRelease: "1~deb9u1",
										Maintainer: "Debian OpenSSL Team <pkg-openssl-devel@lists.alioth.debian.org>",
										Arch:       "amd64",
									},
								},
							},
						},
						Licenses: []types.LicenseFile{
							{
								Type:     types.LicenseTypeDpkg,
								FilePath: "usr/share/doc/libc6/copyright",
								Findings: []types.LicenseFinding{
									{Name: "LGPL-2.1-only"},
									{Name: "GPL-2.0-only"},
								},
								PkgName: "libc6",
							},
							{
								Type:     types.LicenseTypeDpkg,
								FilePath: "usr/share/doc/libssl1.1/copyright",
								Findings: []types.LicenseFinding{
									{
										Name:       "OpenSSL",
										Confidence: 0.9960474308300395,
										Link:       "https://spdx.org/licenses/OpenSSL.html",
									},
								},
								PkgName: "libssl1.1",
							},
							{
								Type:     types.LicenseTypeDpkg,
								FilePath: "usr/share/doc/openssl/copyright",
								Findings: []types.LicenseFinding{
									{
										Name:       "OpenSSL",
										Confidence: 0.9960474308300395,
										Link:       "https://spdx.org/licenses/OpenSSL.html",
									},
								},
								PkgName: "openssl",
							},
						},
					},
				},
				{
					ID: "sha256:0778c3e388c54f736a3d6e74ed390a91fdb42c6809f8fb743d4f72acb41a5d6d",
					BlobInfo: types.BlobInfo{
						SchemaVersion: types.BlobJSONSchemaVersion,
						Size:          29696,
						Digest:        "",
						DiffID:        "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
						CreatedBy:     "COPY file:842584685f26edb24dc305d76894f51cfda2bad0c24a05e727f9d4905d184a70 in /php-app/composer.lock ",
						OpaqueDirs:    []string{"php-app/"},
						Applications: []types.Application{
							{
								Type:     "composer",
								FilePath: "php-app/composer.lock",
								Packages: types.Packages{
									{
										ID:       "guzzlehttp/guzzle@6.2.0",
										Name:     "guzzlehttp/guzzle",
										Version:  "6.2.0",
										Licenses: []string{"MIT"},
										DependsOn: []string{
											"guzzlehttp/promises@v1.3.1",
											"guzzlehttp/psr7@1.5.2",
										},
										Locations: []types.Location{
											{
												StartLine: 9,
												EndLine:   73,
											},
										},
									},
									{
										ID:       "guzzlehttp/promises@v1.3.1",
										Name:     "guzzlehttp/promises",
										Version:  "v1.3.1",
										Licenses: []string{"MIT"},
										Locations: []types.Location{
											{
												StartLine: 74,
												EndLine:   124,
											},
										},
									},
									{
										ID:       "guzzlehttp/psr7@1.5.2",
										Name:     "guzzlehttp/psr7",
										Version:  "1.5.2",
										Licenses: []string{"MIT"},
										DependsOn: []string{
											"psr/http-message@1.0.1",
											"ralouphie/getallheaders@2.0.5",
										},
										Locations: []types.Location{
											{
												StartLine: 125,
												EndLine:   191,
											},
										},
									},
									{
										ID:       "laravel/installer@v2.0.1",
										Name:     "laravel/installer",
										Version:  "v2.0.1",
										Licenses: []string{"MIT"},
										DependsOn: []string{
											"guzzlehttp/guzzle@6.2.0",
											"symfony/console@v4.2.7",
											"symfony/filesystem@v4.2.7",
											"symfony/process@v4.2.7",
										},
										Locations: []types.Location{
											{
												StartLine: 192,
												EndLine:   237,
											},
										},
									},
									{
										ID:        "pear/log@1.13.1",
										Name:      "pear/log",
										Version:   "1.13.1",
										Licenses:  []string{"MIT"},
										DependsOn: []string{"pear/pear_exception@v1.0.0"},
										Locations: []types.Location{
											{
												StartLine: 238,
												EndLine:   290,
											},
										},
									},
									{
										ID:       "pear/pear_exception@v1.0.0",
										Name:     "pear/pear_exception",
										Version:  "v1.0.0",
										Licenses: []string{"BSD-2-Clause"},
										Locations: []types.Location{
											{
												StartLine: 291,
												EndLine:   345,
											},
										},
									},
									{
										ID:       "psr/http-message@1.0.1",
										Name:     "psr/http-message",
										Version:  "1.0.1",
										Licenses: []string{"MIT"},
										Locations: []types.Location{
											{
												StartLine: 346,
												EndLine:   395,
											},
										},
									},
									{
										ID:       "ralouphie/getallheaders@2.0.5",
										Name:     "ralouphie/getallheaders",
										Version:  "2.0.5",
										Licenses: []string{"MIT"},
										Locations: []types.Location{
											{
												StartLine: 396,
												EndLine:   435,
											},
										},
									},
									{
										ID:       "symfony/console@v4.2.7",
										Name:     "symfony/console",
										Version:  "v4.2.7",
										Licenses: []string{"MIT"},
										DependsOn: []string{
											"symfony/contracts@v1.0.2",
											"symfony/polyfill-mbstring@v1.11.0",
										},
										Locations: []types.Location{
											{
												StartLine: 436,
												EndLine:   507,
											},
										},
									},
									{
										ID:       "symfony/contracts@v1.0.2",
										Name:     "symfony/contracts",
										Version:  "v1.0.2",
										Licenses: []string{"MIT"},
										Locations: []types.Location{
											{
												StartLine: 508,
												EndLine:   575,
											},
										},
									},
									{
										ID:        "symfony/filesystem@v4.2.7",
										Name:      "symfony/filesystem",
										Version:   "v4.2.7",
										Licenses:  []string{"MIT"},
										DependsOn: []string{"symfony/polyfill-ctype@v1.11.0"},
										Locations: []types.Location{
											{
												StartLine: 576,
												EndLine:   625,
											},
										},
									},
									{
										ID:       "symfony/polyfill-ctype@v1.11.0",
										Name:     "symfony/polyfill-ctype",
										Version:  "v1.11.0",
										Licenses: []string{"MIT"},
										Locations: []types.Location{
											{
												StartLine: 626,
												EndLine:   683,
											},
										},
									},
									{
										ID:       "symfony/polyfill-mbstring@v1.11.0",
										Name:     "symfony/polyfill-mbstring",
										Version:  "v1.11.0",
										Licenses: []string{"MIT"},
										Locations: []types.Location{
											{
												StartLine: 684,
												EndLine:   742,
											},
										},
									},
									{
										ID:       "symfony/process@v4.2.7",
										Name:     "symfony/process",
										Version:  "v4.2.7",
										Licenses: []string{"MIT"},
										Locations: []types.Location{
											{
												StartLine: 743,
												EndLine:   791,
											},
										},
									},
								},
							},
						},
					},
				},
				{
					ID: "sha256:5a3e3f25fdc97a14d69d99c63dd640cd2d38af5b987b7a95084cce3d835970fb",
					BlobInfo: types.BlobInfo{
						SchemaVersion: types.BlobJSONSchemaVersion,
						Size:          6656,
						Digest:        "",
						DiffID:        "sha256:a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566",
						CreatedBy:     "COPY file:c6d0373d380252b91829a5bb3c81d5b1afa574c91cef7752d18170a231c31f6d in /ruby-app/Gemfile.lock ",
						OpaqueDirs: []string{
							"ruby-app/",
						},
						Applications: []types.Application{
							{
								Type:     "bundler",
								FilePath: "ruby-app/Gemfile.lock",
								Packages: types.Packages{
									{
										ID:           "dotenv@2.7.2",
										Name:         "dotenv",
										Version:      "2.7.2",
										Indirect:     false,
										Relationship: types.RelationshipDirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 51,
												EndLine:   51,
											},
										},
									},
									{
										ID:           "faker@1.9.3",
										Name:         "faker",
										Version:      "1.9.3",
										Indirect:     false,
										Relationship: types.RelationshipDirect,
										DependsOn:    []string{"i18n@1.6.0"},
										Locations: []types.Location{
											{
												StartLine: 53,
												EndLine:   53,
											},
										},
									},
									{
										ID:           "json@2.2.0",
										Name:         "json",
										Version:      "2.2.0",
										Indirect:     false,
										Relationship: types.RelationshipDirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 60,
												EndLine:   60,
											},
										},
									},
									{
										ID:           "pry@0.12.2",
										Name:         "pry",
										Version:      "0.12.2",
										Indirect:     false,
										Relationship: types.RelationshipDirect,
										DependsOn: []string{
											"coderay@1.1.2",
											"method_source@0.9.2",
										},
										Locations: []types.Location{
											{
												StartLine: 79,
												EndLine:   79,
											},
										},
									},
									{
										ID:           "rails@5.2.0",
										Name:         "rails",
										Version:      "5.2.0",
										Indirect:     false,
										Relationship: types.RelationshipDirect,
										DependsOn: []string{
											"actioncable@5.2.3",
											"actionmailer@5.2.3",
											"actionpack@5.2.3",
											"actionview@5.2.3",
											"activejob@5.2.3",
											"activemodel@5.2.3",
											"activerecord@5.2.3",
											"activestorage@5.2.3",
											"activesupport@5.2.3",
											"railties@5.2.3",
											"sprockets-rails@3.2.1",
										},
										Locations: []types.Location{
											{
												StartLine: 86,
												EndLine:   86,
											},
										},
									},
									{
										ID:           "rubocop@0.67.2",
										Name:         "rubocop",
										Version:      "0.67.2",
										Indirect:     false,
										Relationship: types.RelationshipDirect,
										DependsOn: []string{
											"jaro_winkler@1.5.2",
											"parallel@1.17.0",
											"parser@2.6.3.0",
											"psych@3.1.0",
											"rainbow@3.0.0",
											"ruby-progressbar@1.10.0",
											"unicode-display_width@1.5.0",
										},
										Locations: []types.Location{
											{
												StartLine: 112,
												EndLine:   112,
											},
										},
									},
									{
										ID:           "actioncable@5.2.3",
										Name:         "actioncable",
										Version:      "5.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"actionpack@5.2.3",
											"nio4r@2.3.1",
											"websocket-driver@0.7.0",
										},
										Locations: []types.Location{
											{
												StartLine: 4,
												EndLine:   4,
											},
										},
									},
									{
										ID:           "actionmailer@5.2.3",
										Name:         "actionmailer",
										Version:      "5.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"actionpack@5.2.3",
											"actionview@5.2.3",
											"activejob@5.2.3",
											"mail@2.7.1",
											"rails-dom-testing@2.0.3",
										},
										Locations: []types.Location{
											{
												StartLine: 8,
												EndLine:   8,
											},
										},
									},
									{
										ID:           "actionpack@5.2.3",
										Name:         "actionpack",
										Version:      "5.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"actionview@5.2.3",
											"activesupport@5.2.3",
											"rack@2.0.7",
											"rack-test@1.1.0",
											"rails-dom-testing@2.0.3",
											"rails-html-sanitizer@1.0.3",
										},
										Locations: []types.Location{
											{
												StartLine: 14,
												EndLine:   14,
											},
										},
									},
									{
										ID:           "actionview@5.2.3",
										Name:         "actionview",
										Version:      "5.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"activesupport@5.2.3",
											"builder@3.2.3",
											"erubi@1.8.0",
											"rails-dom-testing@2.0.3",
											"rails-html-sanitizer@1.0.3",
										},
										Locations: []types.Location{
											{
												StartLine: 21,
												EndLine:   21,
											},
										},
									},
									{
										ID:           "activejob@5.2.3",
										Name:         "activejob",
										Version:      "5.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"activesupport@5.2.3",
											"globalid@0.4.2",
										},
										Locations: []types.Location{
											{
												StartLine: 27,
												EndLine:   27,
											},
										},
									},
									{
										ID:           "activemodel@5.2.3",
										Name:         "activemodel",
										Version:      "5.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string{"activesupport@5.2.3"},
										Locations: []types.Location{
											{
												StartLine: 30,
												EndLine:   30,
											},
										},
									},
									{
										ID:           "activerecord@5.2.3",
										Name:         "activerecord",
										Version:      "5.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"activemodel@5.2.3",
											"activesupport@5.2.3",
											"arel@9.0.0",
										},
										Locations: []types.Location{
											{
												StartLine: 32,
												EndLine:   32,
											},
										},
									},
									{
										ID:           "activestorage@5.2.3",
										Name:         "activestorage",
										Version:      "5.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"actionpack@5.2.3",
											"activerecord@5.2.3",
											"marcel@0.3.3",
										},
										Locations: []types.Location{
											{
												StartLine: 36,
												EndLine:   36,
											},
										},
									},
									{
										ID:           "activesupport@5.2.3",
										Name:         "activesupport",
										Version:      "5.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"concurrent-ruby@1.1.5",
											"i18n@1.6.0",
											"minitest@5.11.3",
											"tzinfo@1.2.5",
										},
										Locations: []types.Location{
											{
												StartLine: 40,
												EndLine:   40,
											},
										},
									},
									{
										ID:           "arel@9.0.0",
										Name:         "arel",
										Version:      "9.0.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 45,
												EndLine:   45,
											},
										},
									},
									{
										ID:           "ast@2.4.0",
										Name:         "ast",
										Version:      "2.4.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 46,
												EndLine:   46,
											},
										},
									},
									{
										ID:           "builder@3.2.3",
										Name:         "builder",
										Version:      "3.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 47,
												EndLine:   47,
											},
										},
									},
									{
										ID:           "coderay@1.1.2",
										Name:         "coderay",
										Version:      "1.1.2",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 48,
												EndLine:   48,
											},
										},
									},
									{
										ID:           "concurrent-ruby@1.1.5",
										Name:         "concurrent-ruby",
										Version:      "1.1.5",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 49,
												EndLine:   49,
											},
										},
									},
									{
										ID:           "crass@1.0.4",
										Name:         "crass",
										Version:      "1.0.4",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 50,
												EndLine:   50,
											},
										},
									},
									{
										ID:           "erubi@1.8.0",
										Name:         "erubi",
										Version:      "1.8.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 52,
												EndLine:   52,
											},
										},
									},
									{
										ID:           "globalid@0.4.2",
										Name:         "globalid",
										Version:      "0.4.2",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string{"activesupport@5.2.3"},
										Locations: []types.Location{
											{
												StartLine: 55,
												EndLine:   55,
											},
										},
									},
									{
										ID:           "i18n@1.6.0",
										Name:         "i18n",
										Version:      "1.6.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string{"concurrent-ruby@1.1.5"},
										Locations: []types.Location{
											{
												StartLine: 57,
												EndLine:   57,
											},
										},
									},
									{
										ID:           "jaro_winkler@1.5.2",
										Name:         "jaro_winkler",
										Version:      "1.5.2",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 59,
												EndLine:   59,
											},
										},
									},
									{
										ID:           "loofah@2.2.3",
										Name:         "loofah",
										Version:      "2.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"crass@1.0.4",
											"nokogiri@1.10.3",
										},
										Locations: []types.Location{
											{
												StartLine: 61,
												EndLine:   61,
											},
										},
									},
									{
										ID:           "mail@2.7.1",
										Name:         "mail",
										Version:      "2.7.1",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string{"mini_mime@1.0.1"},
										Locations: []types.Location{
											{
												StartLine: 64,
												EndLine:   64,
											},
										},
									},
									{
										ID:           "marcel@0.3.3",
										Name:         "marcel",
										Version:      "0.3.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string{"mimemagic@0.3.3"},
										Locations: []types.Location{
											{
												StartLine: 66,
												EndLine:   66,
											},
										},
									},
									{
										ID:           "method_source@0.9.2",
										Name:         "method_source",
										Version:      "0.9.2",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 68,
												EndLine:   68,
											},
										},
									},
									{
										ID:           "mimemagic@0.3.3",
										Name:         "mimemagic",
										Version:      "0.3.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 69,
												EndLine:   69,
											},
										},
									},
									{
										ID:           "mini_mime@1.0.1",
										Name:         "mini_mime",
										Version:      "1.0.1",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 70,
												EndLine:   70,
											},
										},
									},
									{
										ID:           "mini_portile2@2.4.0",
										Name:         "mini_portile2",
										Version:      "2.4.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 71,
												EndLine:   71,
											},
										},
									},
									{
										ID:           "minitest@5.11.3",
										Name:         "minitest",
										Version:      "5.11.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 72,
												EndLine:   72,
											},
										},
									},
									{
										ID:           "nio4r@2.3.1",
										Name:         "nio4r",
										Version:      "2.3.1",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 73,
												EndLine:   73,
											},
										},
									},
									{
										ID:           "nokogiri@1.10.3",
										Name:         "nokogiri",
										Version:      "1.10.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string{"mini_portile2@2.4.0"},
										Locations: []types.Location{
											{
												StartLine: 74,
												EndLine:   74,
											},
										},
									},
									{
										ID:           "parallel@1.17.0",
										Name:         "parallel",
										Version:      "1.17.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 76,
												EndLine:   76,
											},
										},
									},
									{
										ID:           "parser@2.6.3.0",
										Name:         "parser",
										Version:      "2.6.3.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string{"ast@2.4.0"},
										Locations: []types.Location{
											{
												StartLine: 77,
												EndLine:   77,
											},
										},
									},
									{
										ID:           "psych@3.1.0",
										Name:         "psych",
										Version:      "3.1.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 82,
												EndLine:   82,
											},
										},
									},
									{
										ID:           "rack@2.0.7",
										Name:         "rack",
										Version:      "2.0.7",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 83,
												EndLine:   83,
											},
										},
									},
									{
										ID:           "rack-test@1.1.0",
										Name:         "rack-test",
										Version:      "1.1.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string{"rack@2.0.7"},
										Locations: []types.Location{
											{
												StartLine: 84,
												EndLine:   84,
											},
										},
									},
									{
										ID:           "rails-dom-testing@2.0.3",
										Name:         "rails-dom-testing",
										Version:      "2.0.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"activesupport@5.2.3",
											"nokogiri@1.10.3",
										},
										Locations: []types.Location{
											{
												StartLine: 99,
												EndLine:   99,
											},
										},
									},
									{
										ID:           "rails-html-sanitizer@1.0.3",
										Name:         "rails-html-sanitizer",
										Version:      "1.0.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string{"loofah@2.2.3"},
										Locations: []types.Location{
											{
												StartLine: 102,
												EndLine:   102,
											},
										},
									},
									{
										ID:           "railties@5.2.3",
										Name:         "railties",
										Version:      "5.2.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"actionpack@5.2.3",
											"activesupport@5.2.3",
											"method_source@0.9.2",
											"rake@12.3.2",
											"thor@0.20.3",
										},
										Locations: []types.Location{
											{
												StartLine: 104,
												EndLine:   104,
											},
										},
									},
									{
										ID:           "rainbow@3.0.0",
										Name:         "rainbow",
										Version:      "3.0.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 110,
												EndLine:   110,
											},
										},
									},
									{
										ID:           "rake@12.3.2",
										Name:         "rake",
										Version:      "12.3.2",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 111,
												EndLine:   111,
											},
										},
									},
									{
										ID:           "ruby-progressbar@1.10.0",
										Name:         "ruby-progressbar",
										Version:      "1.10.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 120,
												EndLine:   120,
											},
										},
									},
									{
										ID:           "sprockets@3.7.2",
										Name:         "sprockets",
										Version:      "3.7.2",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"concurrent-ruby@1.1.5",
											"rack@2.0.7",
										},
										Locations: []types.Location{
											{
												StartLine: 121,
												EndLine:   121,
											},
										},
									},
									{
										ID:           "sprockets-rails@3.2.1",
										Name:         "sprockets-rails",
										Version:      "3.2.1",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn: []string{
											"actionpack@5.2.3",
											"activesupport@5.2.3",
											"sprockets@3.7.2",
										},
										Locations: []types.Location{
											{
												StartLine: 124,
												EndLine:   124,
											},
										},
									},
									{
										ID:           "thor@0.20.3",
										Name:         "thor",
										Version:      "0.20.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 128,
												EndLine:   128,
											},
										},
									},
									{
										ID:           "thread_safe@0.3.6",
										Name:         "thread_safe",
										Version:      "0.3.6",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 129,
												EndLine:   129,
											},
										},
									},
									{
										ID:           "tzinfo@1.2.5",
										Name:         "tzinfo",
										Version:      "1.2.5",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string{"thread_safe@0.3.6"},
										Locations: []types.Location{
											{
												StartLine: 130,
												EndLine:   130,
											},
										},
									},
									{
										ID:           "unicode-display_width@1.5.0",
										Name:         "unicode-display_width",
										Version:      "1.5.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 132,
												EndLine:   132,
											},
										},
									},
									{
										ID:           "websocket-driver@0.7.0",
										Name:         "websocket-driver",
										Version:      "0.7.0",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string{"websocket-extensions@0.1.3"},
										Locations: []types.Location{
											{
												StartLine: 133,
												EndLine:   133,
											},
										},
									},
									{
										ID:           "websocket-extensions@0.1.3",
										Name:         "websocket-extensions",
										Version:      "0.1.3",
										Indirect:     true,
										Relationship: types.RelationshipIndirect,
										DependsOn:    []string(nil),
										Locations: []types.Location{
											{
												StartLine: 135,
												EndLine:   135,
											},
										},
									},
								},
							},
						},
					},
				},
			},
			want: artifact.Reference{
				Name: "../../test/testdata/vuln-image.tar.gz",
				Type: types.TypeContainerImage,
				ID:   "sha256:0bebf0773ffd87baa7c64fbdbdf79a24ae125e3f99a8adebe52d1ccbe6bed16b",
				BlobIDs: []string{
					"sha256:75a461ca76eecc6cea981889d69aa1c2dd78c436108be8be1bbc29295520c7d4",
					"sha256:81afc1747d0fdec7a606c27570313634ae331fab6f13566b23d0f6b3e498c050",
					"sha256:0778c3e388c54f736a3d6e74ed390a91fdb42c6809f8fb743d4f72acb41a5d6d",
					"sha256:5a3e3f25fdc97a14d69d99c63dd640cd2d38af5b987b7a95084cce3d835970fb",
				},
				ImageMetadata: artifact.ImageMetadata{
					ID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4",
					DiffIDs: []string{
						"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
						"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
						"sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
						"sha256:a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566",
					},
					RepoTags:    []string{"vuln-image:latest"},
					RepoDigests: nil,
					Reference:   testutil.MustParseReference(t, "vuln-image:latest"),
					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, 1, 1, 0, 0, 0, 0, time.UTC)},
								CreatedBy:  "bazel build ...",
								EmptyLayer: false,
							},
							{
								Author:     "Bazel",
								Created:    v1.Time{Time: time.Date(1970, 1, 1, 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,
				},
				LicenseScannerOption: analyzer.LicenseScannerOption{Full: true},
			},
			setupCache: func(t *testing.T) cache.Cache {
				c := cache.NewMemoryCache()
				require.NoError(t, c.PutArtifact(t.Context(), "sha256:0bebf0773ffd87baa7c64fbdbdf79a24ae125e3f99a8adebe52d1ccbe6bed16b", types.ArtifactInfo{
					SchemaVersion: types.ArtifactJSONSchemaVersion,
				}))
				return c
			},
			wantArtifact: cachetest.WantArtifact{
				ID: "sha256:0bebf0773ffd87baa7c64fbdbdf79a24ae125e3f99a8adebe52d1ccbe6bed16b",
				ArtifactInfo: types.ArtifactInfo{
					SchemaVersion: types.ArtifactJSONSchemaVersion,
				},
			},
			wantBlobs: []cachetest.WantBlob{
				{
					ID: "sha256:a83985cade3970577a9af328db9c88c0bf15cad40f7d2cf6d76e83882bc8146d",
					BlobInfo: types.BlobInfo{
						SchemaVersion: types.BlobJSONSchemaVersion,
						Size:          3061760,
						Digest:        "",
						DiffID:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
						CreatedBy:     "bazel build ...",
					},
				},
				{
					ID: "sha256:b109622c2d106193db505762f1f3e78cf0035a69e559caf07c305c92ddb89356",
					BlobInfo: types.BlobInfo{
						SchemaVersion: types.BlobJSONSchemaVersion,
						Size:          15441920,
						Digest:        "",
						DiffID:        "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
						CreatedBy:     "bazel build ...",
					},
				},
				{
					ID: "sha256:115f689385cb66077c338c52f2c9d6f3018a18c89be7fe7d23f1645422d7d59d",
					BlobInfo: types.BlobInfo{
						SchemaVersion: types.BlobJSONSchemaVersion,
						Size:          29696,
						Digest:        "",
						DiffID:        "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
						CreatedBy:     "COPY file:842584685f26edb24dc305d76894f51cfda2bad0c24a05e727f9d4905d184a70 in /php-app/composer.lock ",
						OpaqueDirs:    []string{"php-app/"},
					},
				},
				{
					ID: "sha256:60129d309cd4f16d69262106d6074f37c6d37f6c9089a9710ec96ae067716636",
					BlobInfo: types.BlobInfo{
						SchemaVersion: types.BlobJSONSchemaVersion,
						Size:          6656,
						Digest:        "",
						DiffID:        "sha256:a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566",
						CreatedBy:     "COPY file:c6d0373d380252b91829a5bb3c81d5b1afa574c91cef7752d18170a231c31f6d in /ruby-app/Gemfile.lock ",
						OpaqueDirs:    []string{"ruby-app/"},
					},
				},
			},
			want: artifact.Reference{
				Name: "../../test/testdata/vuln-image.tar.gz",
				Type: types.TypeContainerImage,
				ID:   "sha256:0bebf0773ffd87baa7c64fbdbdf79a24ae125e3f99a8adebe52d1ccbe6bed16b",
				BlobIDs: []string{
					"sha256:a83985cade3970577a9af328db9c88c0bf15cad40f7d2cf6d76e83882bc8146d",
					"sha256:b109622c2d106193db505762f1f3e78cf0035a69e559caf07c305c92ddb89356",
					"sha256:115f689385cb66077c338c52f2c9d6f3018a18c89be7fe7d23f1645422d7d59d",
					"sha256:60129d309cd4f16d69262106d6074f37c6d37f6c9089a9710ec96ae067716636",
				},
				ImageMetadata: artifact.ImageMetadata{
					ID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4",
					DiffIDs: []string{
						"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
						"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
						"sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
						"sha256:a4595c43a874856bf95f3bfc4fbf78bbaa04c92c726276d4f64193a47ced0566",
					},
					RepoTags:    []string{"vuln-image:latest"},
					RepoDigests: nil,
					Reference:   testutil.MustParseReference(t, "vuln-image:latest"),
					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, 1, 1, 0, 0, 0, 0, time.UTC)},
								CreatedBy:  "bazel build ...",
								Comment:    "",
								EmptyLayer: false,
							},
							{
								Author:     "Bazel",
								Created:    v1.Time{Time: time.Date(1970, 1, 1, 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",
			setupCache: func(_ *testing.T) cache.Cache {
				return cachetest.NewErrorCache(cachetest.ErrorCacheOptions{
					MissingBlobs: true,
				})
			},
			wantErr: "MissingBlobs failed",
		},
		{
			name:      "sad path, PutBlob returns an error",
			imagePath: "../../test/testdata/alpine-311.tar.gz",
			setupCache: func(_ *testing.T) cache.Cache {
				return cachetest.NewErrorCache(cachetest.ErrorCacheOptions{
					PutBlob: true,
				})
			},
			wantErr: "PutBlob failed",
		},
		{
			name:      "sad path, PutBlob returns an error with multiple layers",
			imagePath: "../../test/testdata/vuln-image.tar.gz",
			setupCache: func(_ *testing.T) cache.Cache {
				return cachetest.NewErrorCache(cachetest.ErrorCacheOptions{
					PutBlob: true,
				})
			},
			wantErr: "PutBlob failed",
		},
		{
			name:      "sad path, PutArtifact returns an error",
			imagePath: "../../test/testdata/alpine-311.tar.gz",
			setupCache: func(_ *testing.T) cache.Cache {
				return cachetest.NewErrorCache(cachetest.ErrorCacheOptions{
					PutArtifact: true,
				})
			},
			wantErr: "PutArtifact failed",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			c := cachetest.NewCache(t, tt.setupCache)

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

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

			got, err := a.Inspect(t.Context())
			if tt.wantErr != "" {
				assert.ErrorContains(t, err, tt.wantErr, tt.name)
				return
			}
			defer a.Clean(got)

			require.NoError(t, err, tt.name)
			assert.Equal(t, tt.want, got)

			cachetest.AssertArtifact(t, c, tt.wantArtifact)
			cachetest.AssertBlobs(t, c, tt.wantBlobs)
		})
	}
}

func TestArtifact_InspectWithMaxImageSize(t *testing.T) {
	randomImage, err := random.Image(1000, 2, random.WithSource(rand.NewSource(0)))
	require.NoError(t, err)

	img := &fakeImage{Image: randomImage}
	c := cachetest.NewCache(t, nil)

	tests := []struct {
		name        string
		artifactOpt artifact.Option
		wantErr     string
	}{
		{
			name: "compressed image size is larger than the maximum",
			artifactOpt: artifact.Option{
				ImageOption: types.ImageOptions{MaxImageSize: units.KB * 1},
			},
			wantErr: "compressed image size 2.44kB exceeds maximum allowed size 1kB",
		},
		{
			name: "uncompressed layers size is larger than the maximum",
			artifactOpt: artifact.Option{
				ImageOption: types.ImageOptions{MaxImageSize: units.KB * 3},
			},
			wantErr: "uncompressed layers size 5.12kB exceeds maximum allowed size 3kB",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			artifact, err := image2.NewArtifact(img, c, tt.artifactOpt)
			require.NoError(t, err)

			_, err = artifact.Inspect(t.Context())
			require.ErrorContains(t, err, tt.wantErr)
		})
	}
}
