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