Skip to content

fix(purl): encode gitmodules PURLs and drop pnpm's unreachable fallback - #1353

Merged
mstykow merged 2 commits into
mainfrom
fix/encode-remaining-purls
Aug 11, 2026
Merged

fix(purl): encode gitmodules PURLs and drop pnpm's unreachable fallback#1353
mstykow merged 2 commits into
mainfrom
fix/encode-remaining-purls

Conversation

@mstykow

@mstykow mstykow commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • .gitmodules built pkg:github/{ns}/{name} and the gitlab equivalent with format!, so a repository path needing percent-encoding produced a PURL that did not survive a round trip.
  • pnpm's create_purl fell back to a hand-formatted string when npm_purl returned None. That branch cannot be reached — the type is a literal, with_namespace rejects only namespace-prohibited types, and with_version has no error path at all — and it would have emitted an unencoded PURL if it somehow were. It now returns the Option rather than inventing a value.

This completes the sweep: no production site builds a PURL with format!("pkg:…") any more.

Scope and exclusions

Case is unaffected for gitmodules, which is what made this safe to do mechanically: normalize_purl already lowercases namespace and name for github, gitlab and bitbucket at the output boundary, so only the encoding half was missing.

Two sites are deliberately left as they are, because both are documented decisions rather than hand-rolled strings — each already builds through the crate and then does one targeted, explained edit:

  • CocoaPods appends the subspec literally so a conventional + in a subspec name (NSData+zlib) is not escaped to %2B.
  • Go splices its namespace in to preserve path-part case, which normalize_purl then lowercases host-only, per the acknowledged spec direction (purl-spec#308).

How to verify

printf '[submodule "x"]\n\tpath = x\n\turl = https://github.com/My Org/repo.git\n' > /tmp/g/.gitmodules
provenant --package --json-pp - /tmp/g | jq '[.files[].package_data[].dependencies[].purl]'

The whole-sweep check is the useful one: rg 'format!\("pkg:' src/ --glob '!*test*' should return nothing.

Intentional differences from Python

  • None.

Expected-output fixture changes

  • None; all 324 parser goldens, 44 assembly goldens and the full 5,671-test lib suite pass unchanged. Two pnpm unit tests now unwrap the Option that create_purl always returned in practice.

`.gitmodules` built `pkg:github/{ns}/{name}` and the gitlab equivalent with
`format!`, so a repository path needing percent-encoding produced a PURL that did
not survive a round trip. Case is unaffected: the central normalizer already
lowercases namespace and name for github, gitlab and bitbucket at the output
boundary, so this is purely the encoding half.

pnpm's `create_purl` fell back to a hand-formatted string when `npm_purl`
returned `None`. That branch cannot be reached — the type is a literal, and the
crate's `with_namespace` rejects only namespace-prohibited types while
`with_version` has no error path at all — and it would have emitted an unencoded
PURL if it somehow were. Return the `Option` instead of inventing a value.

Leaves two sites that are deliberate and documented rather than hand-rolled:
CocoaPods appends a subspec literally so a conventional `+` in a subspec name is
not escaped, and Go splices its namespace to preserve path-part case, which
`normalize_purl` then lowercases host-only per the spec direction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Maxim Stykow <maxim.stykow@gmail.com>
@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown

Greptile Summary

The PR replaces hand-formatted PURLs with shared encoded constructors and removes pnpm’s unreachable unencoded fallback.

  • GitHub and GitLab submodule repository paths now use namespaced_purl.
  • pnpm dependency construction now preserves the optional result from npm_purl.
  • pnpm unit tests explicitly require valid scoped and unscoped package inputs to produce PURLs.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/parsers/gitmodules.rs Replaces direct GitHub and GitLab PURL formatting with the shared namespaced constructor so components are encoded consistently.
src/parsers/pnpm_lock.rs Propagates optional npm PURLs through dependency records and removes the hand-formatted fallback; the revised contract documentation resolves the prior review concern.
src/parsers/pnpm_lock_test.rs Updates scoped and unscoped PURL tests to unwrap the now-explicit optional return value.

Reviews (2): Last reviewed commit: "docs(pnpm): state create_purl's contract..." | Re-trigger Greptile

Comment thread src/parsers/pnpm_lock.rs Outdated
Greptile review: the doc comment narrated what the function used to do and why
the old branch was unreachable, which is commit-message material and goes stale
as soon as the helper changes. State the return contract only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Maxim Stykow <maxim.stykow@gmail.com>
@mstykow
mstykow merged commit d1aa5de into main Aug 11, 2026
13 checks passed
@mstykow
mstykow deleted the fix/encode-remaining-purls branch August 11, 2026 23:24
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.

1 participant