github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/artifact/sbom/sbom_test.go (about)

     1  package sbom_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"path/filepath"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/devseccon/trivy/pkg/fanal/artifact"
    14  	"github.com/devseccon/trivy/pkg/fanal/artifact/sbom"
    15  	"github.com/devseccon/trivy/pkg/fanal/cache"
    16  	"github.com/devseccon/trivy/pkg/fanal/types"
    17  )
    18  
    19  func TestArtifact_Inspect(t *testing.T) {
    20  	tests := []struct {
    21  		name               string
    22  		filePath           string
    23  		putBlobExpectation cache.ArtifactCachePutBlobExpectation
    24  		want               types.ArtifactReference
    25  		wantErr            []string
    26  	}{
    27  		{
    28  			name:     "happy path",
    29  			filePath: filepath.Join("testdata", "bom.json"),
    30  			putBlobExpectation: cache.ArtifactCachePutBlobExpectation{
    31  				Args: cache.ArtifactCachePutBlobArgs{
    32  					BlobID: "sha256:3dca5f9082ac4e9669b5e461ae54ffe70db4ea275a09506014b17e012687e855",
    33  					BlobInfo: types.BlobInfo{
    34  						SchemaVersion: types.BlobJSONSchemaVersion,
    35  						OS: types.OS{
    36  							Family: "alpine",
    37  							Name:   "3.16.0",
    38  						},
    39  						PackageInfos: []types.PackageInfo{
    40  							{
    41  								Packages: types.Packages{
    42  									{
    43  										Name:       "musl",
    44  										Version:    "1.2.3-r0",
    45  										SrcName:    "musl",
    46  										SrcVersion: "1.2.3-r0",
    47  										Licenses:   []string{"MIT"},
    48  										Ref:        "pkg:apk/alpine/musl@1.2.3-r0?distro=3.16.0",
    49  										Layer: types.Layer{
    50  											DiffID: "sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3",
    51  										},
    52  									},
    53  								},
    54  							},
    55  						},
    56  						Applications: []types.Application{
    57  							{
    58  								Type:     "composer",
    59  								FilePath: "app/composer/composer.lock",
    60  								Libraries: types.Packages{
    61  									{
    62  										Name:    "pear/log",
    63  										Version: "1.13.1",
    64  										Ref:     "pkg:composer/pear/log@1.13.1",
    65  										Layer: types.Layer{
    66  											DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
    67  										},
    68  									},
    69  									{
    70  
    71  										Name:    "pear/pear_exception",
    72  										Version: "v1.0.0",
    73  										Ref:     "pkg:composer/pear/pear_exception@v1.0.0",
    74  										Layer: types.Layer{
    75  											DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
    76  										},
    77  									},
    78  								},
    79  							},
    80  							{
    81  								Type:     "gobinary",
    82  								FilePath: "app/gobinary/gobinary",
    83  								Libraries: types.Packages{
    84  									{
    85  										Name:    "github.com/package-url/packageurl-go",
    86  										Version: "v0.1.1-0.20220203205134-d70459300c8a",
    87  										Ref:     "pkg:golang/github.com/package-url/packageurl-go@v0.1.1-0.20220203205134-d70459300c8a",
    88  										Layer: types.Layer{
    89  											DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
    90  										},
    91  									},
    92  								},
    93  							},
    94  							{
    95  								Type:     "jar",
    96  								FilePath: "",
    97  								Libraries: types.Packages{
    98  									{
    99  										Name:    "org.codehaus.mojo:child-project",
   100  										Ref:     "pkg:maven/org.codehaus.mojo/child-project@1.0?file_path=app%2Fmaven%2Ftarget%2Fchild-project-1.0.jar",
   101  										Version: "1.0",
   102  										Layer: types.Layer{
   103  											DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
   104  										},
   105  										FilePath: "app/maven/target/child-project-1.0.jar",
   106  									},
   107  								},
   108  							},
   109  							{
   110  								Type:     "node-pkg",
   111  								FilePath: "",
   112  								Libraries: types.Packages{
   113  									{
   114  										Name:     "bootstrap",
   115  										Version:  "5.0.2",
   116  										Ref:      "pkg:npm/bootstrap@5.0.2?file_path=app%2Fapp%2Fpackage.json",
   117  										Licenses: []string{"MIT"},
   118  										Layer: types.Layer{
   119  											DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
   120  										},
   121  										FilePath: "app/app/package.json",
   122  									},
   123  								},
   124  							},
   125  						},
   126  					},
   127  				},
   128  				Returns: cache.ArtifactCachePutBlobReturns{},
   129  			},
   130  			want: types.ArtifactReference{
   131  				Name: filepath.Join("testdata", "bom.json"),
   132  				Type: types.ArtifactCycloneDX,
   133  				ID:   "sha256:3dca5f9082ac4e9669b5e461ae54ffe70db4ea275a09506014b17e012687e855",
   134  				BlobIDs: []string{
   135  					"sha256:3dca5f9082ac4e9669b5e461ae54ffe70db4ea275a09506014b17e012687e855",
   136  				},
   137  			},
   138  		},
   139  		{
   140  			name:     "happy path for sbom attestation",
   141  			filePath: filepath.Join("testdata", "sbom.cdx.intoto.jsonl"),
   142  			putBlobExpectation: cache.ArtifactCachePutBlobExpectation{
   143  				Args: cache.ArtifactCachePutBlobArgs{
   144  					BlobID: "sha256:3dca5f9082ac4e9669b5e461ae54ffe70db4ea275a09506014b17e012687e855",
   145  					BlobInfo: types.BlobInfo{
   146  						SchemaVersion: types.BlobJSONSchemaVersion,
   147  						OS: types.OS{
   148  							Family: "alpine",
   149  							Name:   "3.16.0",
   150  						},
   151  						PackageInfos: []types.PackageInfo{
   152  							{
   153  								Packages: types.Packages{
   154  									{
   155  										Name:       "musl",
   156  										Version:    "1.2.3-r0",
   157  										SrcName:    "musl",
   158  										SrcVersion: "1.2.3-r0",
   159  										Licenses:   []string{"MIT"},
   160  										Ref:        "pkg:apk/alpine/musl@1.2.3-r0?distro=3.16.0",
   161  										Layer: types.Layer{
   162  											DiffID: "sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3",
   163  										},
   164  									},
   165  								},
   166  							},
   167  						},
   168  						Applications: []types.Application{
   169  							{
   170  								Type:     "composer",
   171  								FilePath: "app/composer/composer.lock",
   172  								Libraries: types.Packages{
   173  									{
   174  										Name:    "pear/log",
   175  										Version: "1.13.1",
   176  										Ref:     "pkg:composer/pear/log@1.13.1",
   177  										Layer: types.Layer{
   178  											DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
   179  										},
   180  									},
   181  									{
   182  
   183  										Name:    "pear/pear_exception",
   184  										Version: "v1.0.0",
   185  										Ref:     "pkg:composer/pear/pear_exception@v1.0.0",
   186  										Layer: types.Layer{
   187  											DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
   188  										},
   189  									},
   190  								},
   191  							},
   192  							{
   193  								Type:     "gobinary",
   194  								FilePath: "app/gobinary/gobinary",
   195  								Libraries: types.Packages{
   196  									{
   197  										Name:    "github.com/package-url/packageurl-go",
   198  										Version: "v0.1.1-0.20220203205134-d70459300c8a",
   199  										Ref:     "pkg:golang/github.com/package-url/packageurl-go@v0.1.1-0.20220203205134-d70459300c8a",
   200  										Layer: types.Layer{
   201  											DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
   202  										},
   203  									},
   204  								},
   205  							},
   206  							{
   207  								Type:     "jar",
   208  								FilePath: "",
   209  								Libraries: types.Packages{
   210  									{
   211  										Name:    "org.codehaus.mojo:child-project",
   212  										Ref:     "pkg:maven/org.codehaus.mojo/child-project@1.0?file_path=app%2Fmaven%2Ftarget%2Fchild-project-1.0.jar",
   213  										Version: "1.0",
   214  										Layer: types.Layer{
   215  											DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
   216  										},
   217  										FilePath: "app/maven/target/child-project-1.0.jar",
   218  									},
   219  								},
   220  							},
   221  							{
   222  								Type:     "node-pkg",
   223  								FilePath: "",
   224  								Libraries: types.Packages{
   225  									{
   226  										Name:     "bootstrap",
   227  										Version:  "5.0.2",
   228  										Ref:      "pkg:npm/bootstrap@5.0.2?file_path=app%2Fapp%2Fpackage.json",
   229  										Licenses: []string{"MIT"},
   230  										Layer: types.Layer{
   231  											DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
   232  										},
   233  										FilePath: "app/app/package.json",
   234  									},
   235  								},
   236  							},
   237  						},
   238  					},
   239  				},
   240  				Returns: cache.ArtifactCachePutBlobReturns{},
   241  			},
   242  			want: types.ArtifactReference{
   243  				Name: filepath.Join("testdata", "sbom.cdx.intoto.jsonl"),
   244  				Type: types.ArtifactCycloneDX,
   245  				ID:   "sha256:3dca5f9082ac4e9669b5e461ae54ffe70db4ea275a09506014b17e012687e855",
   246  				BlobIDs: []string{
   247  					"sha256:3dca5f9082ac4e9669b5e461ae54ffe70db4ea275a09506014b17e012687e855",
   248  				},
   249  			},
   250  		},
   251  		{
   252  			name:     "sad path with no such directory",
   253  			filePath: filepath.Join("testdata", "unknown.json"),
   254  			wantErr: []string{
   255  				"no such file or directory",
   256  				"The system cannot find the file specified",
   257  			},
   258  		},
   259  		{
   260  			name:     "sad path PutBlob returns an error",
   261  			filePath: filepath.Join("testdata", "os-only-bom.json"),
   262  			putBlobExpectation: cache.ArtifactCachePutBlobExpectation{
   263  				Args: cache.ArtifactCachePutBlobArgs{
   264  					BlobID: "sha256:033dc76e6daf7d8ba439d678dc7e33400687098f3e9f563f6975adf4eb440eee",
   265  					BlobInfo: types.BlobInfo{
   266  						SchemaVersion: types.BlobJSONSchemaVersion,
   267  						OS: types.OS{
   268  							Family: "alpine",
   269  							Name:   "3.16.0",
   270  						},
   271  						PackageInfos: []types.PackageInfo{
   272  							{},
   273  						},
   274  					},
   275  				},
   276  				Returns: cache.ArtifactCachePutBlobReturns{
   277  					Err: errors.New("error"),
   278  				},
   279  			},
   280  			wantErr: []string{"failed to store blob"},
   281  		},
   282  	}
   283  	for _, tt := range tests {
   284  		t.Run(tt.name, func(t *testing.T) {
   285  			c := new(cache.MockArtifactCache)
   286  			c.ApplyPutBlobExpectation(tt.putBlobExpectation)
   287  
   288  			a, err := sbom.NewArtifact(tt.filePath, c, artifact.Option{})
   289  			require.NoError(t, err)
   290  
   291  			got, err := a.Inspect(context.Background())
   292  			if len(tt.wantErr) > 0 {
   293  				require.NotNil(t, err)
   294  				found := false
   295  				for _, wantErr := range tt.wantErr {
   296  					if strings.Contains(err.Error(), wantErr) {
   297  						found = true
   298  						break
   299  					}
   300  				}
   301  				assert.True(t, found)
   302  				return
   303  			}
   304  
   305  			// Not compare the original CycloneDX report
   306  			got.CycloneDX = nil
   307  
   308  			require.NoError(t, err)
   309  			assert.Equal(t, tt.want, got)
   310  		})
   311  	}
   312  }