github.com/anchore/syft@v1.38.2/syft/format/internal/testutil/image_input.go (about)

     1  package testutil
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/anchore/stereoscope/pkg/filetree"
    12  	"github.com/anchore/stereoscope/pkg/image"
    13  	"github.com/anchore/stereoscope/pkg/imagetest"
    14  	"github.com/anchore/syft/syft/cpe"
    15  	"github.com/anchore/syft/syft/file"
    16  	"github.com/anchore/syft/syft/linux"
    17  	"github.com/anchore/syft/syft/pkg"
    18  	"github.com/anchore/syft/syft/sbom"
    19  	"github.com/anchore/syft/syft/source/stereoscopesource"
    20  )
    21  
    22  func ImageInput(t testing.TB, testImage string, options ...ImageOption) sbom.SBOM {
    23  	t.Helper()
    24  	catalog := pkg.NewCollection()
    25  	var cfg imageCfg
    26  	var img *image.Image
    27  	for _, opt := range options {
    28  		opt(&cfg)
    29  	}
    30  
    31  	defer changeToDirectoryWithGoldenFixture(t, testImage)()
    32  
    33  	switch cfg.fromSnapshot {
    34  	case true:
    35  		img = imagetest.GetGoldenFixtureImage(t, testImage)
    36  	default:
    37  		img = imagetest.GetFixtureImage(t, "docker-archive", testImage)
    38  	}
    39  
    40  	populateImageCatalog(catalog, img)
    41  
    42  	// this is a hard coded value that is not given by the fixture helper and must be provided manually
    43  	img.Metadata.ManifestDigest = "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
    44  
    45  	src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
    46  		Reference: "user-image-input",
    47  	})
    48  
    49  	return sbom.SBOM{
    50  		Artifacts: sbom.Artifacts{
    51  			Packages: catalog,
    52  			LinuxDistribution: &linux.Release{
    53  				PrettyName: "debian",
    54  				Name:       "debian",
    55  				ID:         "debian",
    56  				IDLike:     []string{"like!"},
    57  				Version:    "1.2.3",
    58  				VersionID:  "1.2.3",
    59  			},
    60  		},
    61  		Source: src.Describe(),
    62  		Descriptor: sbom.Descriptor{
    63  			Name:    "syft",
    64  			Version: "v0.42.0-bogus",
    65  			// the application configuration should be persisted here, however, we do not want to import
    66  			// the application configuration in this package (it's reserved only for ingestion by the cmd package)
    67  			Configuration: map[string]string{
    68  				"config-key": "config-value",
    69  			},
    70  		},
    71  	}
    72  }
    73  
    74  func changeToDirectoryWithGoldenFixture(t testing.TB, testImage string) func() {
    75  	// check if test fixture exists... if not, check if there is a shared fixture relative to this dir
    76  	fn := func() {}
    77  
    78  	path := filepath.Join("test-fixtures", testImage)
    79  	if _, err := os.Stat(path); err != nil {
    80  		// change dir, restore as defer
    81  		wd, err := os.Getwd()
    82  		require.NoError(t, err)
    83  		fn = func() {
    84  			require.NoError(t, os.Chdir(wd))
    85  		}
    86  
    87  		// change dir to the testutil dir
    88  		require.NoError(t, os.Chdir(filepath.Join(wd, "..", "internal", "testutil")))
    89  		t.Cleanup(fn)
    90  
    91  		if _, err := os.Stat(path); err != nil {
    92  			t.Fatalf("unable to find test fixture: %s", path)
    93  		}
    94  	}
    95  	return fn
    96  }
    97  
    98  func populateImageCatalog(catalog *pkg.Collection, img *image.Image) {
    99  	// TODO: this helper function is coupled to the image-simple fixture, which seems like a bad idea
   100  	_, ref1, _ := img.SquashedTree().File("/somefile-1.txt", filetree.FollowBasenameLinks)
   101  	_, ref2, _ := img.SquashedTree().File("/somefile-2.txt", filetree.FollowBasenameLinks)
   102  	ctx := context.TODO()
   103  	// populate catalog with test data
   104  	if ref1 != nil {
   105  		catalog.Add(pkg.Package{
   106  			Name:    "package-1",
   107  			Version: "1.0.1",
   108  			Locations: file.NewLocationSet(
   109  				file.NewLocationFromImage(string(ref1.RealPath), *ref1.Reference, img),
   110  			),
   111  			Type:     pkg.PythonPkg,
   112  			FoundBy:  "the-cataloger-1",
   113  			Language: pkg.Python,
   114  			Licenses: pkg.NewLicenseSet(
   115  				pkg.NewLicenseWithContext(ctx, "MIT"),
   116  			),
   117  			Metadata: pkg.PythonPackage{
   118  				Name:    "package-1",
   119  				Version: "1.0.1",
   120  			},
   121  			PURL: "a-purl-1", // intentionally a bad pURL for test fixtures
   122  			CPEs: []cpe.CPE{
   123  				cpe.Must("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*", cpe.GeneratedSource),
   124  			},
   125  		})
   126  	}
   127  
   128  	if ref2 != nil {
   129  		catalog.Add(pkg.Package{
   130  			Name:    "package-2",
   131  			Version: "2.0.1",
   132  			Locations: file.NewLocationSet(
   133  				file.NewLocationFromImage(string(ref2.RealPath), *ref2.Reference, img),
   134  			),
   135  			Type:    pkg.DebPkg,
   136  			FoundBy: "the-cataloger-2",
   137  			Metadata: pkg.DpkgDBEntry{
   138  				Package: "package-2",
   139  				Version: "2.0.1",
   140  			},
   141  			PURL: "pkg:deb/debian/package-2@2.0.1",
   142  			CPEs: []cpe.CPE{
   143  				cpe.Must("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
   144  			},
   145  		})
   146  	}
   147  }