Skip to content

Fix VEX pyproject product purl name (#1547) - #1577

Merged
Mikola Lysenko (mikolalysenko) merged 3 commits into
mainfrom
agent/fix-vex-pyproject-purl-name
Oct 15, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 3 commits into
mainfrom
agent/fix-vex-pyproject-purl-name

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #1547

Summary

When vex, scan --vex or apply --vex detects the product from
pyproject.toml, the product purl now uses the canonical PEP 503 name.
My_Service → pkg:pypi/my-service@1.2.0, and Demo_Pkg.Core →
pkg:pypi/demo-pkg-core@0.1.0. These now match the SBOM and the pypi
component purls in the same document. Before this change, OpenVEX
consumers, which match products by exact purl, silently ignored every
statement in the document for any Python project whose name had
uppercase letters, _ or ..

Root cause

parse_pyproject (crates/socket-patch-core/src/vex/product.rs) built
format!("pkg:pypi/{name}@{version}") from [project].name,
[tool.poetry].name or [tool.pdm].name exactly as written. It now goes
through utils::purl::pypi_purl, the builder the pypi component purls
already use, which canonicalizes the name.

Two related corrections:

  • Poetry's [project].name vs [tool.poetry].name conflict check now
    compares canonical names. My_Service and my-service build the same
    distribution, so they no longer count as a conflict (before, None).
  • The version is trimmed, as packaging does. A " 1.2.0 " version no
    longer puts spaces inside the purl.

The npm, PyPI and gem wrappers only dispatch to the binary, so they need
no change.

Tests (red → green)

Issue variant Test Without fix With fix
#1547 PEP 621 My_Service (binary, vex --no-verify) e2e_vex::auto_detect_pyproject_uses_canonical_pypi_name pkg:pypi/My_Service@1.2.0 ❌ ✅
#1547 PEP 621 My_Service (detector) vex::product::tests::detect_pyproject_canonicalizes_pep621_name ❌ ✅
#1547 legacy [tool.poetry] My_Service and Demo_Pkg.Core pyproject_canonicalizes_tool_poetry_name pkg:pypi/My_Service@0.1.0 ❌ ✅
[tool.pdm] legacy table pyproject_canonicalizes_tool_pdm_name ❌ ✅
Poetry names equal after canonicalization pyproject_poetry_names_equal_after_canonicalization ❌ ✅
Whitespace around name and version pyproject_trims_name_and_version n/a (added after review) ✅

Run locally:

  • cargo fmt --all -- --check: clean.
  • cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test -p socket-patch-core --all-features: 6519 passed, 4
    failed. The 4 failures are chmod-denial tests (copy_tree,
    vlt_heal, pypi_poetry, pypi_requirements) that fail only because
    the sandbox runs as root, which ignores permission bits. None of them
    touch files in this diff.
  • cargo test -p socket-patch-cli --all-features --lib --test e2e_vex --test vex_terminal_output --test cli_parse_vex --test covgap_commands_vex --test e2e_embedded_vex: all pass (921 + 132).
  • I could not run the full cargo test --workspace locally: its ~30 GB
    of test binaries fill the sandbox's disk allowance. CI covers the rest.

Review notes (/code-review high)

  • Fixed: the untrimmed version (see above).
  • Not fixed, follow-up: the version is not converted to PEP 440 normal
    form (1.0.0-rc1 stays as written, while a built wheel records
    1.0.0rc1). The purl spec doesn't require version normalization, and
    full PEP 440 normalization is a separate, larger change than this
    name fix. It is a candidate for utils::purl::pypi_purl itself.
  • Not fixed, by design: pypi_purl rejects names or versions containing
    /, \\, : or NUL. None of these can appear in a valid PEP 503 name
    or PEP 440 version, and a purl built from them was malformed anyway.

Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
A Python project named `My_Service` got the VEX product
`pkg:pypi/My_Service@1.2.0`. SBOMs, and the pypi components in the same
document, use the PEP 503 name (`my-service`). OpenVEX consumers match
products by exact purl, so they silently ignored every statement in
the document.

The pyproject.toml product (PEP 621 `[project]`, `[tool.poetry]` and
legacy `[tool.pdm]`) is now built through `utils::purl::pypi_purl`,
which normalizes the name. Poetry's two name fields are now compared
after normalization: `My_Service` and `my-service` name the same
distribution, so they no longer count as a conflict.

Fixes #1547

Assisted-by: Claude Code:claude-opus-5-5
A pyproject.toml version written with surrounding spaces ended up
inside the product purl, so the purl was invalid and matched nothing.
The name was already trimmed when it was canonicalized.

Refs #1547

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 15, 2026 15:01
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 4a442ec. Configure here.

Merged via the queue into main with commit c8e7c32 Oct 15, 2026
86 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-vex-pyproject-purl-name branch October 15, 2026 16:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VEX product purl detected from pyproject.toml keeps the raw project name (pkg:pypi/My_Service) instead of the normalized pypi name

3 participants