diff --git a/audit_test.go b/audit_test.go index d335f6041..58e57acdf 100644 --- a/audit_test.go +++ b/audit_test.go @@ -670,15 +670,21 @@ func TestXrayAuditPoetry(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - validations.ValidateCommandOutput(t, testXrayAuditPoetry(t, tc.format), tc.format, validations.ValidationParams{ + validations.ValidateCommandOutput(t, testXrayAuditPoetry(t, "poetry-project", tc.format), tc.format, validations.ValidationParams{ + Total: &validations.TotalCount{Licenses: 1, Vulnerabilities: 3}, + }) + }) + // Same dependencies as "poetry-project", declared in the PEP 621 [project] table (Poetry 2.0+ default layout). + t.Run(tc.name+" PEP 621", func(t *testing.T) { + validations.ValidateCommandOutput(t, testXrayAuditPoetry(t, "poetry-pep621-project", tc.format), tc.format, validations.ValidationParams{ Total: &validations.TotalCount{Licenses: 1, Vulnerabilities: 3}, }) }) } } -func testXrayAuditPoetry(t *testing.T, format format.OutputFormat) string { - _, cleanUp := securityTestUtils.CreateTestProjectEnvAndChdir(t, filepath.Join(filepath.FromSlash(securityTests.GetTestResourcesPath()), "projects", "package-managers", "python", "poetry", "poetry-project")) +func testXrayAuditPoetry(t *testing.T, projectName string, format format.OutputFormat) string { + _, cleanUp := securityTestUtils.CreateTestProjectEnvAndChdir(t, filepath.Join(filepath.FromSlash(securityTests.GetTestResourcesPath()), "projects", "package-managers", "python", "poetry", projectName)) defer cleanUp() // Add dummy descriptor file to check that we run only specific audit addDummyPackageDescriptor(t, false) diff --git a/go.mod b/go.mod index 12328af71..8a779b376 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-plugin v1.6.3 github.com/jedib0t/go-pretty/v6 v6.8.3 - github.com/jfrog/build-info-go v1.13.1-0.20261028123549-f0cd26bfc2de + github.com/jfrog/build-info-go v1.13.1-0.20261105093150-46c74789c5ff github.com/jfrog/froggit-go v1.24.0 github.com/jfrog/gofrog v1.7.7 github.com/jfrog/jfrog-apps-config v1.0.1 diff --git a/go.sum b/go.sum index 787773260..5c568c65b 100644 --- a/go.sum +++ b/go.sum @@ -161,8 +161,8 @@ github.com/jedib0t/go-pretty/v6 v6.8.3 h1:yVSk5aemoYHCvcrtqyXklwqcgHQIQzmy/oUzFl github.com/jedib0t/go-pretty/v6 v6.8.3/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= github.com/jfrog/archiver/v3 v3.6.5 h1:AiNXJoe8jYDOtyykfVuwh26aM4rk/ei+YzBpfBukdzU= github.com/jfrog/archiver/v3 v3.6.5/go.mod h1:5V9l+Fte30Y4qe9dUOAd3yNTf8lmtVNuhKNrvI8PMhg= -github.com/jfrog/build-info-go v1.13.1-0.20261028123549-f0cd26bfc2de h1:ycdUCYyOZ5TCcRIVT0EfjG/oRMk+QtY3u2XOUcjgt0s= -github.com/jfrog/build-info-go v1.13.1-0.20261028123549-f0cd26bfc2de/go.mod h1:3sv87ey6WTxUckYkj0Iqu9aUaiygQMZKoXNmvT9lK5g= +github.com/jfrog/build-info-go v1.13.1-0.20261105093150-46c74789c5ff h1:mvjYNq/bHbfsgtGpbBOPf6skAfsNyjnHjjWazl8Yq3M= +github.com/jfrog/build-info-go v1.13.1-0.20261105093150-46c74789c5ff/go.mod h1:3sv87ey6WTxUckYkj0Iqu9aUaiygQMZKoXNmvT9lK5g= github.com/jfrog/froggit-go v1.24.0 h1:qSAywpxlPdQfIFIJPprFzfAkbNfKb/OXMo7YD3dVKrE= github.com/jfrog/froggit-go v1.24.0/go.mod h1:wRDryqyp3oe+eHgME2mpnEQmO8XBECIPagFwj0nHmdI= github.com/jfrog/gofrog v1.7.7 h1:I2MSi+ytPqfprhOC+MLDk5fVvuRzVjTqNkzrp9RtEvY= diff --git a/sca/bom/buildinfo/technologies/python/python_test.go b/sca/bom/buildinfo/technologies/python/python_test.go index 506ef461d..d8e2dfdc3 100644 --- a/sca/bom/buildinfo/technologies/python/python_test.go +++ b/sca/bom/buildinfo/technologies/python/python_test.go @@ -211,6 +211,28 @@ func TestBuildPoetryDependencyList(t *testing.T) { } } +// Poetry 2.0+ declares dependencies in the PEP 621 [project] table by default, instead of [tool.poetry.dependencies]. +func TestBuildPoetryDependencyListPep621(t *testing.T) { + _, cleanUp := technologies.CreateTestWorkspace(t, filepath.Join("projects", "package-managers", "python", "poetry", "poetry-pep621-project")) + defer cleanUp() + expectedPoetryUniqueDeps := []string{ + PythonPackageTypeIdentifier + "django:1.11.15", + PythonPackageTypeIdentifier + "pytz:2026.6", + PythonPackageTypeIdentifier + "urllib3:1.22", + PythonPackageTypeIdentifier + "werkzeug:0.9.6", + } + // SkipAutoInstall relies on build-info-go's direct pyproject.toml and poetry.lock parsing, without running `poetry install`. + rootNode, uniqueDeps, _, err := BuildDependencyTree(technologies.BuildInfoBomGeneratorParams{SkipAutoInstall: true}, techutils.Poetry) + require.NoError(t, err) + assert.ElementsMatch(t, uniqueDeps, expectedPoetryUniqueDeps, "First is actual, Second is Expected") + require.Len(t, rootNode, 1) + assert.Len(t, rootNode[0].Nodes, 3) + djangoNode := tests.GetAndAssertNode(t, rootNode[0].Nodes, "django:1.11.15") + if assert.NotNil(t, djangoNode) { + tests.GetAndAssertNode(t, djangoNode.Nodes, "pytz:2026.6") + } +} + func TestBuildDependencyTreeWhenInstallForbidden(t *testing.T) { // This feature is currently supported and tested for Pip and Poetry only testcases := []struct { diff --git a/tests/testdata/projects/package-managers/python/poetry/poetry-pep621-project/poetry.lock b/tests/testdata/projects/package-managers/python/poetry/poetry-pep621-project/poetry.lock new file mode 100644 index 000000000..cc92a818b --- /dev/null +++ b/tests/testdata/projects/package-managers/python/poetry/poetry-pep621-project/poetry.lock @@ -0,0 +1,64 @@ +# This file is automatically @generated by Poetry 2.5.1 and should not be changed by hand. + +[[package]] +name = "django" +version = "1.11.15" +description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "Django-1.11.15-py2.py3-none-any.whl", hash = "sha256:8176ac7985fe6737ce3d6b2531b4a2453cb7c3377c9db00bacb2b3320f4a1311"}, + {file = "Django-1.11.15.tar.gz", hash = "sha256:b18235d82426f09733d2de9910cee975cf52ff05e5f836681eb957d105a05a40"}, +] + +[package.dependencies] +pytz = "*" + +[package.extras] +argon2 = ["argon2-cffi (>=16.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "pytz" +version = "2026.6" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "pytz-2026.6-py2.py3-none-any.whl", hash = "sha256:7fed3fee8fd6bd4a2b7b75a8d56021d0624127bf69bd5691f115db929ac570b3"}, + {file = "pytz-2026.6.tar.gz", hash = "sha256:3b0fc2727707663d6e517936f444fd8a8b240f1fc32f4bf7ee03274245945284"}, +] + +[[package]] +name = "urllib3" +version = "1.22" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "urllib3-1.22-py2.py3-none-any.whl", hash = "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b"}, + {file = "urllib3-1.22.tar.gz", hash = "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"}, +] + +[package.extras] +secure = ["certifi", "cryptography (>=1.3.4) ; python_version <= \"2.7\"", "idna (>=2.0.0) ; python_version <= \"2.7\"", "ipaddress ; python_version <= \"2.7\"", "pyOpenSSL (>=0.14) ; python_version <= \"2.7\""] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "werkzeug" +version = "0.9.6" +description = "The Swiss Army knife of Python web development" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "Werkzeug-0.9.6.tar.gz", hash = "sha256:7f11e7e2e73eb22677cac1b11113eb6106f66cedef13d140e83cf6563c90b79c"}, +] + +[metadata] +lock-version = "2.1" +python-versions = "*" +content-hash = "ddd4ede7ff0d948947c392b6119877a71ba94707e71f7d6288f1bd173ae1dd23" diff --git a/tests/testdata/projects/package-managers/python/poetry/poetry-pep621-project/pyproject.toml b/tests/testdata/projects/package-managers/python/poetry/poetry-pep621-project/pyproject.toml new file mode 100644 index 000000000..3119e3c3d --- /dev/null +++ b/tests/testdata/projects/package-managers/python/poetry/poetry-pep621-project/pyproject.toml @@ -0,0 +1,19 @@ +[project] +name = "poetry-pep621-project" +version = "0.1.0" +description = "" +authors = [ + {name = "Your Name", email = "you@example.com"} +] +dependencies = [ + "urllib3 (<1.24)", + "django (<1.11.16)", + "Werkzeug (<0.10)" +] + +[tool.poetry] +package-mode = false + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api"