github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/pkg/cataloger/python/cataloger_test.go (about)

     1  package python
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/require"
     7  
     8  	"github.com/anchore/syft/syft/file"
     9  	"github.com/anchore/syft/syft/pkg"
    10  	"github.com/lineaje-labs/syft/syft/pkg/cataloger/internal/pkgtest"
    11  )
    12  
    13  func Test_PackageCataloger(t *testing.T) {
    14  	tests := []struct {
    15  		name            string
    16  		fixtures        []string
    17  		expectedPackage pkg.Package
    18  	}{
    19  		{
    20  			name:     "egg-file-no-version",
    21  			fixtures: []string{"test-fixtures/no-version-py3.8.egg-info"},
    22  			expectedPackage: pkg.Package{
    23  				Name:     "no-version",
    24  				PURL:     "pkg:pypi/no-version",
    25  				Type:     pkg.PythonPkg,
    26  				Language: pkg.Python,
    27  				FoundBy:  "python-installed-package-cataloger",
    28  				Metadata: pkg.PythonPackage{
    29  					Name:                 "no-version",
    30  					SitePackagesRootPath: "test-fixtures",
    31  				},
    32  			},
    33  		},
    34  		{
    35  			name: "egg-info directory",
    36  			fixtures: []string{
    37  				"test-fixtures/egg-info/PKG-INFO",
    38  				"test-fixtures/egg-info/RECORD",
    39  				"test-fixtures/egg-info/top_level.txt",
    40  			},
    41  			expectedPackage: pkg.Package{
    42  				Name:     "requests",
    43  				Version:  "2.22.0",
    44  				PURL:     "pkg:pypi/requests@2.22.0",
    45  				Type:     pkg.PythonPkg,
    46  				Language: pkg.Python,
    47  				Licenses: pkg.NewLicenseSet(
    48  					pkg.NewLicenseFromLocations("Apache 2.0", file.NewLocation("test-fixtures/egg-info/PKG-INFO")),
    49  				),
    50  				FoundBy: "python-installed-package-cataloger",
    51  				Metadata: pkg.PythonPackage{
    52  					Name:                 "requests",
    53  					Version:              "2.22.0",
    54  					Platform:             "UNKNOWN",
    55  					Author:               "Kenneth Reitz",
    56  					AuthorEmail:          "me@kennethreitz.org",
    57  					SitePackagesRootPath: "test-fixtures",
    58  					Files: []pkg.PythonFileRecord{
    59  						{Path: "requests-2.22.0.dist-info/INSTALLER", Digest: &pkg.PythonFileDigest{"sha256", "zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg"}, Size: "4"},
    60  						{Path: "requests/__init__.py", Digest: &pkg.PythonFileDigest{"sha256", "PnKCgjcTq44LaAMzB-7--B2FdewRrE8F_vjZeaG9NhA"}, Size: "3921"},
    61  						{Path: "requests/__pycache__/__version__.cpython-38.pyc"},
    62  						{Path: "requests/__pycache__/utils.cpython-38.pyc"},
    63  						{Path: "requests/__version__.py", Digest: &pkg.PythonFileDigest{"sha256", "Bm-GFstQaFezsFlnmEMrJDe8JNROz9n2XXYtODdvjjc"}, Size: "436"},
    64  						{Path: "requests/utils.py", Digest: &pkg.PythonFileDigest{"sha256", "LtPJ1db6mJff2TJSJWKi7rBpzjPS3mSOrjC9zRhoD3A"}, Size: "30049"},
    65  					},
    66  					TopLevelPackages: []string{"requests"},
    67  				},
    68  			},
    69  		},
    70  		{
    71  			name: "egg-info directory case sensitive",
    72  			fixtures: []string{
    73  				"test-fixtures/casesensitive/EGG-INFO/PKG-INFO",
    74  				"test-fixtures/casesensitive/EGG-INFO/RECORD",
    75  				"test-fixtures/casesensitive/EGG-INFO/top_level.txt",
    76  			},
    77  			expectedPackage: pkg.Package{
    78  				Name:     "requests",
    79  				Version:  "2.22.0",
    80  				PURL:     "pkg:pypi/requests@2.22.0",
    81  				Type:     pkg.PythonPkg,
    82  				Language: pkg.Python,
    83  				Licenses: pkg.NewLicenseSet(
    84  					pkg.NewLicenseFromLocations("Apache 2.0", file.NewLocation("test-fixtures/casesensitive/EGG-INFO/PKG-INFO")),
    85  				),
    86  				FoundBy: "python-installed-package-cataloger",
    87  				Metadata: pkg.PythonPackage{
    88  					Name:                 "requests",
    89  					Version:              "2.22.0",
    90  					Platform:             "UNKNOWN",
    91  					Author:               "Kenneth Reitz",
    92  					AuthorEmail:          "me@kennethreitz.org",
    93  					SitePackagesRootPath: "test-fixtures/casesensitive",
    94  					Files: []pkg.PythonFileRecord{
    95  						{Path: "requests-2.22.0.dist-info/INSTALLER", Digest: &pkg.PythonFileDigest{"sha256", "zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg"}, Size: "4"},
    96  						{Path: "requests/__init__.py", Digest: &pkg.PythonFileDigest{"sha256", "PnKCgjcTq44LaAMzB-7--B2FdewRrE8F_vjZeaG9NhA"}, Size: "3921"},
    97  						{Path: "requests/__pycache__/__version__.cpython-38.pyc"},
    98  						{Path: "requests/__pycache__/utils.cpython-38.pyc"},
    99  						{Path: "requests/__version__.py", Digest: &pkg.PythonFileDigest{"sha256", "Bm-GFstQaFezsFlnmEMrJDe8JNROz9n2XXYtODdvjjc"}, Size: "436"},
   100  						{Path: "requests/utils.py", Digest: &pkg.PythonFileDigest{"sha256", "LtPJ1db6mJff2TJSJWKi7rBpzjPS3mSOrjC9zRhoD3A"}, Size: "30049"},
   101  					},
   102  					TopLevelPackages: []string{"requests"},
   103  				},
   104  			},
   105  		},
   106  		{
   107  			name: "dist-info directory",
   108  			fixtures: []string{
   109  				"test-fixtures/dist-info/METADATA",
   110  				"test-fixtures/dist-info/RECORD",
   111  				"test-fixtures/dist-info/top_level.txt",
   112  				"test-fixtures/dist-info/direct_url.json",
   113  			},
   114  			expectedPackage: pkg.Package{
   115  				Name:     "Pygments",
   116  				Version:  "2.6.1",
   117  				PURL:     "pkg:pypi/Pygments@2.6.1?vcs_url=git+https://github.com/python-test/test.git%40aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
   118  				Type:     pkg.PythonPkg,
   119  				Language: pkg.Python,
   120  				Licenses: pkg.NewLicenseSet(
   121  					pkg.NewLicenseFromLocations("BSD License", file.NewLocation("test-fixtures/dist-info/METADATA")),
   122  				),
   123  				FoundBy: "python-installed-package-cataloger",
   124  				Metadata: pkg.PythonPackage{
   125  					Name:                 "Pygments",
   126  					Version:              "2.6.1",
   127  					Platform:             "any",
   128  					Author:               "Georg Brandl",
   129  					AuthorEmail:          "georg@python.org",
   130  					SitePackagesRootPath: "test-fixtures",
   131  					Files: []pkg.PythonFileRecord{
   132  						{Path: "../../../bin/pygmentize", Digest: &pkg.PythonFileDigest{"sha256", "dDhv_U2jiCpmFQwIRHpFRLAHUO4R1jIJPEvT_QYTFp8"}, Size: "220"},
   133  						{Path: "Pygments-2.6.1.dist-info/AUTHORS", Digest: &pkg.PythonFileDigest{"sha256", "PVpa2_Oku6BGuiUvutvuPnWGpzxqFy2I8-NIrqCvqUY"}, Size: "8449"},
   134  						{Path: "Pygments-2.6.1.dist-info/RECORD"},
   135  						{Path: "pygments/__pycache__/__init__.cpython-38.pyc"},
   136  						{Path: "pygments/util.py", Digest: &pkg.PythonFileDigest{"sha256", "586xXHiJGGZxqk5PMBu3vBhE68DLuAe5MBARWrSPGxA"}, Size: "10778"},
   137  
   138  						{Path: "pygments/x_util.py", Digest: &pkg.PythonFileDigest{"sha256", "qpzzsOW31KT955agi-7NS--90I0iNiJCyLJQnRCHgKI="}, Size: "10778"},
   139  					},
   140  					TopLevelPackages: []string{"pygments", "something_else"},
   141  					DirectURLOrigin:  &pkg.PythonDirectURLOriginInfo{URL: "https://github.com/python-test/test.git", VCS: "git", CommitID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
   142  				},
   143  			},
   144  		},
   145  		{
   146  			name: "dist-info directory case sensitive",
   147  			fixtures: []string{
   148  				"test-fixtures/casesensitive/DIST-INFO/METADATA",
   149  				"test-fixtures/casesensitive/DIST-INFO/RECORD",
   150  				"test-fixtures/casesensitive/DIST-INFO/top_level.txt",
   151  				"test-fixtures/casesensitive/DIST-INFO/direct_url.json",
   152  			},
   153  			expectedPackage: pkg.Package{
   154  				Name:     "Pygments",
   155  				Version:  "2.6.1",
   156  				PURL:     "pkg:pypi/Pygments@2.6.1?vcs_url=git+https://github.com/python-test/test.git%40aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
   157  				Type:     pkg.PythonPkg,
   158  				Language: pkg.Python,
   159  				Licenses: pkg.NewLicenseSet(
   160  					pkg.NewLicenseFromLocations("BSD License", file.NewLocation("test-fixtures/casesensitive/DIST-INFO/METADATA")),
   161  				),
   162  				FoundBy: "python-installed-package-cataloger",
   163  				Metadata: pkg.PythonPackage{
   164  					Name:                 "Pygments",
   165  					Version:              "2.6.1",
   166  					Platform:             "any",
   167  					Author:               "Georg Brandl",
   168  					AuthorEmail:          "georg@python.org",
   169  					SitePackagesRootPath: "test-fixtures/casesensitive",
   170  					Files: []pkg.PythonFileRecord{
   171  						{Path: "../../../bin/pygmentize", Digest: &pkg.PythonFileDigest{"sha256", "dDhv_U2jiCpmFQwIRHpFRLAHUO4R1jIJPEvT_QYTFp8"}, Size: "220"},
   172  						{Path: "Pygments-2.6.1.dist-info/AUTHORS", Digest: &pkg.PythonFileDigest{"sha256", "PVpa2_Oku6BGuiUvutvuPnWGpzxqFy2I8-NIrqCvqUY"}, Size: "8449"},
   173  						{Path: "Pygments-2.6.1.dist-info/RECORD"},
   174  						{Path: "pygments/__pycache__/__init__.cpython-38.pyc"},
   175  						{Path: "pygments/util.py", Digest: &pkg.PythonFileDigest{"sha256", "586xXHiJGGZxqk5PMBu3vBhE68DLuAe5MBARWrSPGxA"}, Size: "10778"},
   176  
   177  						{Path: "pygments/x_util.py", Digest: &pkg.PythonFileDigest{"sha256", "qpzzsOW31KT955agi-7NS--90I0iNiJCyLJQnRCHgKI="}, Size: "10778"},
   178  					},
   179  					TopLevelPackages: []string{"pygments", "something_else"},
   180  					DirectURLOrigin:  &pkg.PythonDirectURLOriginInfo{URL: "https://github.com/python-test/test.git", VCS: "git", CommitID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
   181  				},
   182  			},
   183  		},
   184  		{
   185  			name: "malformed-record",
   186  			fixtures: []string{
   187  				"test-fixtures/malformed-record/dist-info/METADATA",
   188  				"test-fixtures/malformed-record/dist-info/RECORD",
   189  			},
   190  			expectedPackage: pkg.Package{
   191  				Name:     "Pygments",
   192  				Version:  "2.6.1",
   193  				PURL:     "pkg:pypi/Pygments@2.6.1",
   194  				Type:     pkg.PythonPkg,
   195  				Language: pkg.Python,
   196  				Licenses: pkg.NewLicenseSet(
   197  					pkg.NewLicenseFromLocations("BSD License", file.NewLocation("test-fixtures/malformed-record/dist-info/METADATA")),
   198  				),
   199  				FoundBy: "python-installed-package-cataloger",
   200  				Metadata: pkg.PythonPackage{
   201  					Name:                 "Pygments",
   202  					Version:              "2.6.1",
   203  					Platform:             "any",
   204  					Author:               "Georg Brandl",
   205  					AuthorEmail:          "georg@python.org",
   206  					SitePackagesRootPath: "test-fixtures/malformed-record",
   207  					Files: []pkg.PythonFileRecord{
   208  						{Path: "flask/json/tag.py", Digest: &pkg.PythonFileDigest{"sha256", "9ehzrmt5k7hxf7ZEK0NOs3swvQyU9fWNe-pnYe69N60"}, Size: "8223"},
   209  						{Path: "../../Scripts/flask.exe", Digest: &pkg.PythonFileDigest{"sha256", "mPrbVeZCDX20himZ_bRai1nCs_tgr7jHIOGZlcgn-T4"}, Size: "93063"},
   210  						{Path: "../../Scripts/flask.exe", Size: "89470", Digest: &pkg.PythonFileDigest{"sha256", "jvqh4N3qOqXLlq40i6ZOLCY9tAOwfwdzIpLDYhRjoqQ"}},
   211  						{Path: "Flask-1.0.2.dist-info/INSTALLER", Size: "4", Digest: &pkg.PythonFileDigest{"sha256", "zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg"}},
   212  					},
   213  				},
   214  			},
   215  		},
   216  		{
   217  			// in cases where the metadata file is available and the record is not we should still record there is a package
   218  			// additionally empty top_level.txt files should not result in an error
   219  			name:     "partial dist-info directory",
   220  			fixtures: []string{"test-fixtures/partial.dist-info/METADATA"},
   221  			expectedPackage: pkg.Package{
   222  				Name:     "Pygments",
   223  				Version:  "2.6.1",
   224  				PURL:     "pkg:pypi/Pygments@2.6.1",
   225  				Type:     pkg.PythonPkg,
   226  				Language: pkg.Python,
   227  				Licenses: pkg.NewLicenseSet(
   228  					pkg.NewLicenseFromLocations("BSD License", file.NewLocation("test-fixtures/partial.dist-info/METADATA")),
   229  				),
   230  				FoundBy: "python-installed-package-cataloger",
   231  				Metadata: pkg.PythonPackage{
   232  					Name:                 "Pygments",
   233  					Version:              "2.6.1",
   234  					Platform:             "any",
   235  					Author:               "Georg Brandl",
   236  					AuthorEmail:          "georg@python.org",
   237  					SitePackagesRootPath: "test-fixtures",
   238  				},
   239  			},
   240  		},
   241  		{
   242  			name:     "egg-info regular file",
   243  			fixtures: []string{"test-fixtures/test.egg-info"},
   244  			expectedPackage: pkg.Package{
   245  				Name:     "requests",
   246  				Version:  "2.22.0",
   247  				PURL:     "pkg:pypi/requests@2.22.0",
   248  				Type:     pkg.PythonPkg,
   249  				Language: pkg.Python,
   250  				Licenses: pkg.NewLicenseSet(
   251  					pkg.NewLicenseFromLocations("Apache 2.0", file.NewLocation("test-fixtures/test.egg-info")),
   252  				),
   253  				FoundBy: "python-installed-package-cataloger",
   254  				Metadata: pkg.PythonPackage{
   255  					Name:                 "requests",
   256  					Version:              "2.22.0",
   257  					Platform:             "UNKNOWN",
   258  					Author:               "Kenneth Reitz",
   259  					AuthorEmail:          "me@kennethreitz.org",
   260  					SitePackagesRootPath: "test-fixtures",
   261  				},
   262  			},
   263  		},
   264  	}
   265  
   266  	for _, test := range tests {
   267  		t.Run(test.name, func(t *testing.T) {
   268  			resolver := file.NewMockResolverForPaths(test.fixtures...)
   269  
   270  			locations, err := resolver.FilesByPath(test.fixtures...)
   271  			require.NoError(t, err)
   272  
   273  			test.expectedPackage.Locations = file.NewLocationSet(locations...)
   274  
   275  			pkgtest.NewCatalogTester().
   276  				WithResolver(resolver).
   277  				Expects([]pkg.Package{test.expectedPackage}, nil).
   278  				TestCataloger(t, NewInstalledPackageCataloger())
   279  		})
   280  	}
   281  }
   282  
   283  func Test_PackageCataloger_IgnorePackage(t *testing.T) {
   284  	tests := []struct {
   285  		MetadataFixture string
   286  	}{
   287  		{
   288  			MetadataFixture: "test-fixtures/Python-2.7.egg-info",
   289  		},
   290  		{
   291  			MetadataFixture: "test-fixtures/empty-1.0.0-py3.8.egg-info",
   292  		},
   293  	}
   294  
   295  	for _, test := range tests {
   296  		t.Run(test.MetadataFixture, func(t *testing.T) {
   297  			resolver := file.NewMockResolverForPaths(test.MetadataFixture)
   298  
   299  			actual, _, err := NewInstalledPackageCataloger().Catalog(resolver)
   300  			require.NoError(t, err)
   301  
   302  			if len(actual) != 0 {
   303  				t.Fatalf("Expected 0 packages but found: %d", len(actual))
   304  			}
   305  		})
   306  	}
   307  }
   308  
   309  func Test_IndexCataloger_Globs(t *testing.T) {
   310  	tests := []struct {
   311  		name     string
   312  		fixture  string
   313  		expected []string
   314  	}{
   315  		{
   316  			name:    "obtain index files",
   317  			fixture: "test-fixtures/glob-paths",
   318  			expected: []string{
   319  				"src/requirements.txt",
   320  				"src/extra-requirements.txt",
   321  				"src/requirements-dev.txt",
   322  				"src/1-requirements-dev.txt",
   323  				"src/setup.py",
   324  				"src/poetry.lock",
   325  				"src/Pipfile.lock",
   326  			},
   327  		},
   328  	}
   329  
   330  	for _, test := range tests {
   331  		t.Run(test.name, func(t *testing.T) {
   332  			pkgtest.NewCatalogTester().
   333  				FromDirectory(t, test.fixture).
   334  				ExpectsResolverContentQueries(test.expected).
   335  				TestCataloger(t, NewPackageCataloger(DefaultCatalogerConfig()))
   336  		})
   337  	}
   338  }
   339  
   340  func Test_PackageCataloger_Globs(t *testing.T) {
   341  	tests := []struct {
   342  		name     string
   343  		fixture  string
   344  		expected []string
   345  	}{
   346  		{
   347  			name:    "obtain index files",
   348  			fixture: "test-fixtures/glob-paths",
   349  			expected: []string{
   350  				"site-packages/v.DIST-INFO/METADATA",
   351  				"site-packages/w.EGG-INFO/PKG-INFO",
   352  				"site-packages/x.dist-info/METADATA",
   353  				"site-packages/y.egg-info/PKG-INFO",
   354  				"site-packages/z.egg-info",
   355  			},
   356  		},
   357  	}
   358  
   359  	for _, test := range tests {
   360  		t.Run(test.name, func(t *testing.T) {
   361  			pkgtest.NewCatalogTester().
   362  				FromDirectory(t, test.fixture).
   363  				ExpectsResolverContentQueries(test.expected).
   364  				TestCataloger(t, NewInstalledPackageCataloger())
   365  		})
   366  	}
   367  }