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 }