github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/applier/docker_test.go (about)

     1  package applier_test
     2  
     3  import (
     4  	"sort"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  
     9  	"github.com/devseccon/trivy/pkg/fanal/applier"
    10  	"github.com/devseccon/trivy/pkg/fanal/types"
    11  )
    12  
    13  func TestApplyLayers(t *testing.T) {
    14  	tests := []struct {
    15  		name        string
    16  		inputLayers []types.BlobInfo
    17  		want        types.ArtifactDetail
    18  	}{
    19  		{
    20  			name: "happy path",
    21  			inputLayers: []types.BlobInfo{
    22  				{
    23  					SchemaVersion: 1,
    24  					Digest:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
    25  					DiffID:        "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
    26  					OS: types.OS{
    27  						Family: "alpine",
    28  						Name:   "3.10",
    29  					},
    30  					PackageInfos: []types.PackageInfo{
    31  						{
    32  							FilePath: "lib/apk/db/installed",
    33  							Packages: types.Packages{
    34  								{
    35  									Name:    "openssl",
    36  									Version: "1.2.3",
    37  									Release: "4.5.6",
    38  								},
    39  							},
    40  						},
    41  					},
    42  					Applications: []types.Application{
    43  						{
    44  							Type:     types.Bundler,
    45  							FilePath: "app/Gemfile.lock",
    46  							Libraries: types.Packages{
    47  								{
    48  									Name:    "gemlibrary1",
    49  									Version: "1.2.3",
    50  								},
    51  							},
    52  						},
    53  						{
    54  							Type:     types.Composer,
    55  							FilePath: "app/composer.lock",
    56  							Libraries: types.Packages{
    57  								{
    58  									Name:    "phplibrary1",
    59  									Version: "6.6.6",
    60  								},
    61  							},
    62  						},
    63  						{
    64  							Type:     types.GemSpec,
    65  							FilePath: "usr/local/bundle/specifications/gon-6.3.2.gemspec",
    66  							Libraries: types.Packages{
    67  								{
    68  									Name:     "gon",
    69  									Version:  "6.3.2",
    70  									FilePath: "usr/local/bundle/specifications/gon-6.3.2.gemspec",
    71  								},
    72  							},
    73  						},
    74  					},
    75  				},
    76  				{
    77  					SchemaVersion: 1,
    78  					Digest:        "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
    79  					DiffID:        "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
    80  					PackageInfos: []types.PackageInfo{
    81  						{
    82  							FilePath: "lib/apk/db/installed",
    83  							Packages: types.Packages{
    84  								{
    85  									Name:    "openssl",
    86  									Version: "1.2.3",
    87  									Release: "4.5.6",
    88  								},
    89  								{
    90  									// added
    91  									Name:    "musl",
    92  									Version: "1.2.4",
    93  									Release: "4.5.7",
    94  								},
    95  							},
    96  						},
    97  					},
    98  					WhiteoutFiles: []string{"app/composer.lock"},
    99  				},
   100  				{
   101  					SchemaVersion: 1,
   102  					Digest:        "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
   103  					DiffID:        "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   104  					PackageInfos: []types.PackageInfo{
   105  						{
   106  							FilePath: "lib/apk/db/installed",
   107  							Packages: types.Packages{
   108  								{
   109  									Name:    "openssl",
   110  									Version: "1.2.3",
   111  									Release: "4.5.6",
   112  								},
   113  								{
   114  									Name:    "musl",
   115  									Version: "1.2.4",
   116  									Release: "4.5.8", // updated
   117  								},
   118  							},
   119  						},
   120  					},
   121  					Applications: []types.Application{
   122  						{
   123  							Type:     types.GemSpec,
   124  							FilePath: "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
   125  							Libraries: types.Packages{
   126  								{
   127  									Name:     "activesupport",
   128  									Version:  "6.0.2.1",
   129  									FilePath: "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
   130  								},
   131  							},
   132  						},
   133  					},
   134  				},
   135  			},
   136  			want: types.ArtifactDetail{
   137  				OS: types.OS{
   138  					Family: "alpine",
   139  					Name:   "3.10",
   140  				},
   141  				Packages: types.Packages{
   142  					{
   143  						Name:    "musl",
   144  						Version: "1.2.4",
   145  						Release: "4.5.8",
   146  						Layer: types.Layer{
   147  							Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
   148  							DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   149  						},
   150  					},
   151  					{
   152  						Name:    "openssl",
   153  						Version: "1.2.3",
   154  						Release: "4.5.6",
   155  						Layer: types.Layer{
   156  							Digest: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   157  							DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   158  						},
   159  					},
   160  				},
   161  				Applications: []types.Application{
   162  					{
   163  						Type: types.GemSpec,
   164  						Libraries: types.Packages{
   165  							{
   166  								Name:     "activesupport",
   167  								Version:  "6.0.2.1",
   168  								FilePath: "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
   169  								Layer: types.Layer{
   170  									Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
   171  									DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   172  								},
   173  							},
   174  							{
   175  								Name:     "gon",
   176  								Version:  "6.3.2",
   177  								FilePath: "usr/local/bundle/specifications/gon-6.3.2.gemspec",
   178  								Layer: types.Layer{
   179  									Digest: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   180  									DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   181  								},
   182  							},
   183  						},
   184  					},
   185  					{
   186  						Type:     types.Bundler,
   187  						FilePath: "app/Gemfile.lock",
   188  						Libraries: types.Packages{
   189  							{
   190  								Name:    "gemlibrary1",
   191  								Version: "1.2.3",
   192  								Layer: types.Layer{
   193  									Digest: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   194  									DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   195  								},
   196  							},
   197  						},
   198  					},
   199  				},
   200  			},
   201  		},
   202  		{
   203  			name: "happy path with digests in libs/packages (as for SBOM)",
   204  			inputLayers: []types.BlobInfo{
   205  				{
   206  					SchemaVersion: 2,
   207  					OS: types.OS{
   208  						Family: "debian",
   209  						Name:   "11.8",
   210  					},
   211  					PackageInfos: []types.PackageInfo{
   212  						{
   213  							Packages: types.Packages{
   214  								{
   215  									ID:         "adduser@3.118+deb11u1",
   216  									Name:       "adduser",
   217  									Version:    "3.118+deb11u1",
   218  									Arch:       "all",
   219  									SrcName:    "adduser",
   220  									SrcVersion: "3.118+deb11u1",
   221  									Ref:        "pkg:deb/debian/adduser@3.118%2Bdeb11u1?arch=all&distro=debian-11.8",
   222  									Layer: types.Layer{
   223  										Digest: "sha256:e67fdae3559346105027c63e7fb032bba57e62b1fe9f2da23e6fdfb56384e00b",
   224  										DiffID: "sha256:633f5bf471f7595b236a21e62dc60beef321db45916363a02ad5af02d794d497",
   225  									},
   226  								},
   227  							},
   228  						},
   229  					},
   230  					Applications: []types.Application{
   231  						{
   232  							Type: types.PythonPkg,
   233  							Libraries: types.Packages{
   234  								{
   235  									Name:    "pip",
   236  									Version: "23.0.1",
   237  									Layer: types.Layer{
   238  										DiffID: "sha256:1def056a3160854c9395aa76282dd62172ec08c18a5fa03bb7d50a777c15ba99",
   239  									},
   240  									FilePath: "usr/local/lib/python3.9/site-packages/pip-23.0.1.dist-info/METADATA",
   241  								},
   242  							},
   243  						},
   244  					},
   245  				},
   246  			},
   247  			want: types.ArtifactDetail{
   248  				OS: types.OS{
   249  					Family: "debian",
   250  					Name:   "11.8",
   251  				},
   252  				Packages: types.Packages{
   253  					{
   254  						ID:         "adduser@3.118+deb11u1",
   255  						Name:       "adduser",
   256  						Version:    "3.118+deb11u1",
   257  						Arch:       "all",
   258  						SrcName:    "adduser",
   259  						SrcVersion: "3.118+deb11u1",
   260  						Ref:        "pkg:deb/debian/adduser@3.118%2Bdeb11u1?arch=all&distro=debian-11.8",
   261  						Layer: types.Layer{
   262  							Digest: "sha256:e67fdae3559346105027c63e7fb032bba57e62b1fe9f2da23e6fdfb56384e00b",
   263  							DiffID: "sha256:633f5bf471f7595b236a21e62dc60beef321db45916363a02ad5af02d794d497",
   264  						},
   265  					},
   266  				},
   267  				Applications: []types.Application{
   268  					{
   269  						Type: types.PythonPkg,
   270  						Libraries: types.Packages{
   271  							{
   272  								Name:     "pip",
   273  								Version:  "23.0.1",
   274  								FilePath: "usr/local/lib/python3.9/site-packages/pip-23.0.1.dist-info/METADATA",
   275  								Layer: types.Layer{
   276  									DiffID: "sha256:1def056a3160854c9395aa76282dd62172ec08c18a5fa03bb7d50a777c15ba99",
   277  								},
   278  							},
   279  						},
   280  					},
   281  				},
   282  			},
   283  		},
   284  		{
   285  			name: "happy path with merging ubuntu version and ESM",
   286  			inputLayers: []types.BlobInfo{
   287  				{
   288  					SchemaVersion: 1,
   289  					Digest:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   290  					DiffID:        "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   291  					OS: types.OS{
   292  						Family:   "ubuntu",
   293  						Extended: true,
   294  					},
   295  				},
   296  				{
   297  					SchemaVersion: 1,
   298  					Digest:        "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   299  					DiffID:        "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   300  					OS: types.OS{
   301  						Family: "ubuntu",
   302  						Name:   "16.04",
   303  					},
   304  				},
   305  			},
   306  			want: types.ArtifactDetail{
   307  				OS: types.OS{
   308  					Family:   "ubuntu",
   309  					Name:     "16.04",
   310  					Extended: true,
   311  				},
   312  			},
   313  		},
   314  		{
   315  			name: "happy path with removed and updated lockfile",
   316  			inputLayers: []types.BlobInfo{
   317  				{
   318  					SchemaVersion: 1,
   319  					Digest:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   320  					DiffID:        "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   321  					OS: types.OS{
   322  						Family: "alpine",
   323  						Name:   "3.10",
   324  					},
   325  					Applications: []types.Application{
   326  						{
   327  							Type:     types.Bundler,
   328  							FilePath: "app/Gemfile.lock",
   329  							Libraries: types.Packages{
   330  								{
   331  									Name:    "rails",
   332  									Version: "5.0.0",
   333  								},
   334  								{
   335  									Name:    "rack",
   336  									Version: "4.0.0",
   337  								},
   338  							},
   339  						},
   340  						{
   341  							Type:     types.Composer,
   342  							FilePath: "app/composer.lock",
   343  							Libraries: types.Packages{
   344  								{
   345  									Name:    "phplibrary1",
   346  									Version: "6.6.6",
   347  								},
   348  							},
   349  						},
   350  						{
   351  							Type:     types.GemSpec,
   352  							FilePath: "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
   353  							Libraries: types.Packages{
   354  								{
   355  									Name:     "activesupport",
   356  									Version:  "6.0.2.1",
   357  									FilePath: "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
   358  								},
   359  							},
   360  						},
   361  					},
   362  				},
   363  				{
   364  					SchemaVersion: 1,
   365  					Digest:        "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   366  					DiffID:        "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   367  					Applications: []types.Application{
   368  						{
   369  							Type:     types.Bundler,
   370  							FilePath: "app/Gemfile.lock",
   371  							Libraries: types.Packages{
   372  								{
   373  									Name:    "rails",
   374  									Version: "6.0.0",
   375  								},
   376  								{
   377  									Name:    "rack",
   378  									Version: "4.0.0",
   379  								},
   380  							},
   381  						},
   382  						{
   383  							Type:     "composer",
   384  							FilePath: "app/composer2.lock",
   385  							Libraries: types.Packages{
   386  								{
   387  									Name:    "phplibrary1",
   388  									Version: "6.6.6",
   389  								},
   390  							},
   391  						},
   392  					},
   393  					WhiteoutFiles: []string{
   394  						"app/composer.lock",
   395  						"var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
   396  					},
   397  				},
   398  			},
   399  			want: types.ArtifactDetail{
   400  				OS: types.OS{
   401  					Family: "alpine",
   402  					Name:   "3.10",
   403  				},
   404  				Applications: []types.Application{
   405  					{
   406  						Type:     types.Bundler,
   407  						FilePath: "app/Gemfile.lock",
   408  						Libraries: types.Packages{
   409  							{
   410  								Name:    "rack",
   411  								Version: "4.0.0",
   412  								Layer: types.Layer{
   413  									Digest: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   414  									DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   415  								},
   416  							},
   417  							{
   418  								Name:    "rails",
   419  								Version: "6.0.0",
   420  								Layer: types.Layer{
   421  									Digest: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   422  									DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   423  								},
   424  							},
   425  						},
   426  					},
   427  					{
   428  						Type:     types.Composer,
   429  						FilePath: "app/composer2.lock",
   430  						Libraries: types.Packages{
   431  							{
   432  								Name:    "phplibrary1",
   433  								Version: "6.6.6",
   434  								Layer: types.Layer{
   435  									Digest: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   436  									DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   437  								},
   438  							},
   439  						},
   440  					},
   441  				},
   442  			},
   443  		},
   444  		{
   445  			name: "happy path with removed and updated secret",
   446  			inputLayers: []types.BlobInfo{
   447  				{
   448  					SchemaVersion: 2,
   449  					Digest:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   450  					DiffID:        "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   451  					CreatedBy:     "Line_1",
   452  					Secrets: []types.Secret{
   453  						{
   454  							FilePath: "usr/secret.txt",
   455  							Findings: []types.SecretFinding{
   456  								{
   457  									RuleID:    "aws-access-key-id",
   458  									Category:  "AWS",
   459  									Severity:  "CRITICAL",
   460  									Title:     "AWS Access Key ID",
   461  									StartLine: 1,
   462  									EndLine:   1,
   463  									Match:     "AWS_ACCESS_KEY_ID=********************",
   464  									Code: types.Code{
   465  										Lines: []types.Line{
   466  											{
   467  												Number:      1,
   468  												Content:     "AWS_ACCESS_KEY_ID=********************",
   469  												IsCause:     true,
   470  												Highlighted: "AWS_ACCESS_KEY_ID=********************",
   471  												FirstCause:  true,
   472  												LastCause:   true,
   473  											},
   474  										},
   475  									},
   476  								},
   477  							},
   478  						},
   479  					},
   480  				},
   481  				{
   482  					SchemaVersion: 2,
   483  					Digest:        "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   484  					DiffID:        "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   485  					CreatedBy:     "Line_2",
   486  					Secrets: []types.Secret{
   487  						{
   488  							FilePath: "usr/secret.txt",
   489  							Findings: []types.SecretFinding{
   490  								{
   491  									RuleID:    "github-pat",
   492  									Category:  "GitHub",
   493  									Severity:  "CRITICAL",
   494  									Title:     "GitHub Personal Access Token",
   495  									StartLine: 1,
   496  									EndLine:   1,
   497  									Match:     "GITHUB_PAT=****************************************",
   498  									Code: types.Code{
   499  										Lines: []types.Line{
   500  											{
   501  												Number:      1,
   502  												Content:     "GITHUB_PAT=****************************************",
   503  												IsCause:     true,
   504  												Highlighted: "GITHUB_PAT=****************************************",
   505  												FirstCause:  true,
   506  												LastCause:   true,
   507  											},
   508  										},
   509  									},
   510  								},
   511  								{
   512  									RuleID:    "aws-access-key-id",
   513  									Category:  "AWS",
   514  									Severity:  "CRITICAL",
   515  									Title:     "AWS Access Key ID",
   516  									StartLine: 2,
   517  									EndLine:   2,
   518  									Match:     "AWS_ACCESS_KEY_ID=********************",
   519  									Code: types.Code{
   520  										Lines: []types.Line{
   521  											{
   522  												Number:      1,
   523  												Content:     "AWS_ACCESS_KEY_ID=********************",
   524  												IsCause:     true,
   525  												Highlighted: "AWS_ACCESS_KEY_ID=********************",
   526  												FirstCause:  true,
   527  												LastCause:   true,
   528  											},
   529  										},
   530  									},
   531  								},
   532  							},
   533  						},
   534  					},
   535  				},
   536  				{
   537  					SchemaVersion: 2,
   538  					Digest:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   539  					DiffID:        "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   540  					CreatedBy:     "Line_3",
   541  					WhiteoutFiles: []string{
   542  						"usr/secret.txt",
   543  					},
   544  				},
   545  			},
   546  			want: types.ArtifactDetail{
   547  				Secrets: []types.Secret{
   548  					{
   549  						FilePath: "usr/secret.txt",
   550  						Findings: []types.SecretFinding{
   551  							{
   552  								RuleID:    "github-pat",
   553  								Category:  "GitHub",
   554  								Severity:  "CRITICAL",
   555  								Title:     "GitHub Personal Access Token",
   556  								StartLine: 1,
   557  								EndLine:   1,
   558  								Match:     "GITHUB_PAT=****************************************",
   559  								Layer: types.Layer{
   560  									Digest:    "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   561  									DiffID:    "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   562  									CreatedBy: "Line_2",
   563  								},
   564  								Code: types.Code{
   565  									Lines: []types.Line{
   566  										{
   567  											Number:      1,
   568  											Content:     "GITHUB_PAT=****************************************",
   569  											IsCause:     true,
   570  											Highlighted: "GITHUB_PAT=****************************************",
   571  											FirstCause:  true,
   572  											LastCause:   true,
   573  										},
   574  									},
   575  								},
   576  							},
   577  							{
   578  								RuleID:    "aws-access-key-id",
   579  								Category:  "AWS",
   580  								Severity:  "CRITICAL",
   581  								Title:     "AWS Access Key ID",
   582  								StartLine: 2,
   583  								EndLine:   2,
   584  								Match:     "AWS_ACCESS_KEY_ID=********************",
   585  								Layer: types.Layer{
   586  									Digest:    "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   587  									DiffID:    "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   588  									CreatedBy: "Line_2",
   589  								},
   590  								Code: types.Code{
   591  									Lines: []types.Line{
   592  										{
   593  											Number:      1,
   594  											Content:     "AWS_ACCESS_KEY_ID=********************",
   595  											IsCause:     true,
   596  											Highlighted: "AWS_ACCESS_KEY_ID=********************",
   597  											FirstCause:  true,
   598  											LastCause:   true,
   599  										},
   600  									},
   601  								},
   602  							},
   603  						},
   604  					},
   605  				},
   606  			},
   607  		},
   608  		{
   609  			name: "happy path with status.d and opaque dirs without the trailing slash",
   610  			inputLayers: []types.BlobInfo{
   611  				{
   612  					SchemaVersion: 1,
   613  					Digest:        "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   614  					DiffID:        "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   615  					OS: types.OS{
   616  						Family: "debian",
   617  						Name:   "8",
   618  					},
   619  					PackageInfos: []types.PackageInfo{
   620  						{
   621  							FilePath: "var/lib/dpkg/status.d/openssl",
   622  							Packages: types.Packages{
   623  								{
   624  									Name:    "openssl",
   625  									Version: "1.2.3",
   626  									Release: "4.5.6",
   627  								},
   628  							},
   629  						},
   630  					},
   631  					Applications: []types.Application{
   632  						{
   633  							Type:     "composer",
   634  							FilePath: "app/composer.lock",
   635  							Libraries: types.Packages{
   636  								{
   637  									Name:    "phplibrary1",
   638  									Version: "6.6.6",
   639  								},
   640  							},
   641  						},
   642  					},
   643  					Licenses: []types.LicenseFile{
   644  						{
   645  							Type:     types.LicenseTypeDpkg,
   646  							FilePath: "usr/share/doc/openssl/copyright",
   647  							Findings: []types.LicenseFinding{
   648  								{Name: "OpenSSL"},
   649  							},
   650  							PkgName: "openssl",
   651  						},
   652  					},
   653  				},
   654  				{
   655  					SchemaVersion: 1,
   656  					Digest:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   657  					DiffID:        "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   658  					PackageInfos: []types.PackageInfo{
   659  						{
   660  							FilePath: "var/lib/dpkg/status.d/libc",
   661  							Packages: types.Packages{
   662  								{
   663  									Name:    "libc",
   664  									Version: "1.2.4",
   665  									Release: "4.5.7",
   666  								},
   667  							},
   668  						},
   669  					},
   670  					Licenses: []types.LicenseFile{
   671  						{
   672  							Type:     types.LicenseTypeDpkg,
   673  							FilePath: "usr/share/doc/libc/copyright",
   674  							Findings: []types.LicenseFinding{
   675  								{Name: "GPL-2"},
   676  							},
   677  							PkgName: "libc",
   678  						},
   679  					},
   680  					OpaqueDirs: []string{"app"},
   681  				},
   682  			},
   683  			want: types.ArtifactDetail{
   684  				OS: types.OS{
   685  					Family: "debian",
   686  					Name:   "8",
   687  				},
   688  				Packages: types.Packages{
   689  					{
   690  						Name:     "libc",
   691  						Version:  "1.2.4",
   692  						Release:  "4.5.7",
   693  						Licenses: []string{"GPL-2"},
   694  						Layer: types.Layer{
   695  							Digest: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   696  							DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   697  						},
   698  					},
   699  					{
   700  						Name:     "openssl",
   701  						Version:  "1.2.3",
   702  						Release:  "4.5.6",
   703  						Licenses: []string{"OpenSSL"},
   704  						Layer: types.Layer{
   705  							Digest: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   706  							DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   707  						},
   708  					},
   709  				},
   710  			},
   711  		},
   712  		{
   713  			name: "happy path, opaque dirs with the trailing slash",
   714  			inputLayers: []types.BlobInfo{
   715  				{
   716  					SchemaVersion: 1,
   717  					Digest:        "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   718  					DiffID:        "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   719  					Applications: []types.Application{
   720  						{
   721  							Type:     "composer",
   722  							FilePath: "app/composer.lock",
   723  							Libraries: types.Packages{
   724  								{
   725  									Name:    "phplibrary1",
   726  									Version: "6.6.6",
   727  								},
   728  							},
   729  						},
   730  					},
   731  				},
   732  				{
   733  					SchemaVersion: 1,
   734  					Digest:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   735  					DiffID:        "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   736  					OpaqueDirs:    []string{"app/"},
   737  				},
   738  			},
   739  			want: types.ArtifactDetail{},
   740  		},
   741  		{
   742  			name: "happy path with Red Hat content sets",
   743  			inputLayers: []types.BlobInfo{
   744  				{
   745  					SchemaVersion: 1,
   746  					Digest:        "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   747  					DiffID:        "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   748  					OS: types.OS{
   749  						Family: "redhat",
   750  						Name:   "8",
   751  					},
   752  					PackageInfos: []types.PackageInfo{
   753  						{
   754  							FilePath: "var/lib/rpm/Packages",
   755  							Packages: types.Packages{
   756  								{
   757  									Name:    "openssl",
   758  									Version: "1.2.3",
   759  									Release: "4",
   760  								},
   761  							},
   762  						},
   763  					},
   764  				},
   765  				{
   766  					SchemaVersion: 1,
   767  					Digest:        "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   768  					DiffID:        "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   769  					BuildInfo: &types.BuildInfo{
   770  						ContentSets: []string{
   771  							"rhel-8-for-x86_64-baseos-rpms",
   772  							"rhel-8-for-x86_64-appstream-rpms",
   773  						},
   774  					},
   775  					PackageInfos: []types.PackageInfo{
   776  						{
   777  							FilePath: "var/lib/rpm/Packages",
   778  							Packages: types.Packages{
   779  								{
   780  									Name:    "openssl",
   781  									Version: "1.2.3",
   782  									Release: "4",
   783  								},
   784  								{
   785  									Name:    "libc",
   786  									Version: "1.2.4",
   787  									Release: "5",
   788  								},
   789  							},
   790  						},
   791  					},
   792  				},
   793  				{
   794  					SchemaVersion: 1,
   795  					Digest:        "sha256:a64e5f34c33ed4c5121498e721e24d95dae2c9599bee4aa6d07850702b401406",
   796  					DiffID:        "sha256:0abd3f2c73de6f02e033f410590111f9339b9500dc07270234f283f2d9a2694b",
   797  					BuildInfo: &types.BuildInfo{
   798  						Nvr:  "3scale-amp-apicast-gateway-container-1.11-1",
   799  						Arch: "x86_64",
   800  					},
   801  				},
   802  				{
   803  					SchemaVersion: 1,
   804  					Digest:        "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
   805  					DiffID:        "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   806  					PackageInfos: []types.PackageInfo{
   807  						{
   808  							FilePath: "var/lib/rpm/Packages",
   809  							Packages: types.Packages{
   810  								{
   811  									Name:    "openssl",
   812  									Version: "1.2.3",
   813  									Release: "4",
   814  								},
   815  								{
   816  									Name:    "libc",
   817  									Version: "1.2.4",
   818  									Release: "5",
   819  								},
   820  								{
   821  									Name:    "bash",
   822  									Version: "5.6.7",
   823  									Release: "8",
   824  								},
   825  							},
   826  						},
   827  					},
   828  				},
   829  			},
   830  			want: types.ArtifactDetail{
   831  				OS: types.OS{
   832  					Family: "redhat",
   833  					Name:   "8",
   834  				},
   835  				Packages: types.Packages{
   836  					{
   837  						Name:    "bash",
   838  						Version: "5.6.7",
   839  						Release: "8",
   840  						Layer: types.Layer{
   841  							Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
   842  							DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   843  						},
   844  						BuildInfo: &types.BuildInfo{
   845  							Nvr:  "3scale-amp-apicast-gateway-container-1.11-1",
   846  							Arch: "x86_64",
   847  						},
   848  					},
   849  					{
   850  						Name:    "libc",
   851  						Version: "1.2.4",
   852  						Release: "5",
   853  						Layer: types.Layer{
   854  							Digest: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
   855  							DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
   856  						},
   857  						BuildInfo: &types.BuildInfo{
   858  							ContentSets: []string{
   859  								"rhel-8-for-x86_64-baseos-rpms",
   860  								"rhel-8-for-x86_64-appstream-rpms",
   861  							},
   862  						},
   863  					},
   864  					{
   865  						Name:    "openssl",
   866  						Version: "1.2.3",
   867  						Release: "4",
   868  						Layer: types.Layer{
   869  							Digest: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
   870  							DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819",
   871  						},
   872  						BuildInfo: &types.BuildInfo{
   873  							ContentSets: []string{
   874  								"rhel-8-for-x86_64-baseos-rpms",
   875  								"rhel-8-for-x86_64-appstream-rpms",
   876  							},
   877  						},
   878  					},
   879  				},
   880  			},
   881  		},
   882  	}
   883  
   884  	for _, tt := range tests {
   885  		t.Run(tt.name, func(t *testing.T) {
   886  			got := applier.ApplyLayers(tt.inputLayers)
   887  			sort.Sort(got.Packages)
   888  			sort.Slice(got.Applications, func(i, j int) bool {
   889  				return got.Applications[i].FilePath < got.Applications[j].FilePath
   890  			})
   891  			for _, app := range got.Applications {
   892  				sort.Sort(app.Libraries)
   893  			}
   894  			assert.Equal(t, tt.want, got, tt.name)
   895  		})
   896  	}
   897  }