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 }