Skip to content

Tracking: build and classify purls through one validated utils::purl API #748

Open
0 of 1 issue completed

Description

[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: discussion #560 register.

Kind: tracking. Source: review 6.4, 7.3; register C20.

Problem (verified on 045d7ec)

utils/purl.rs has two builder families:

On top of these, 42 production format!("pkg:…") sites outside utils/purl.rs build purls by hand. The review counted 48; corrected here. The largest groups:

  • vex/product.rs: 13 sites, including versionless purls
  • vendor/path.rs::leaf_to_purl: 10
  • vendor/lock_inventory/recover.rs: 6
  • vex/discover/mod.rs: 5, with their own PyPI/composer/nuget canonicalization in discover/mod.rs L1455-L1468
  • hosted/engine.rs:622

There are also 24 inline starts_with("pkg:<type>/") checks beside Ecosystem::from_purl.

Drift already present. The families disagree on canonicalization:

  • composer_purl lowercases, while build_composer_purl, leaf_to_purl and recover.rs don't.
  • pypi_purl canonicalizes the name, while leaf_to_purl, recover.rs and the hosted skip purl (hosted/engine.rs:622) don't. vex::discover re-canonicalizes afterwards.
  • vlt.rs:290 alone percent-encodes the npm scope @.

Every consumer that compares purls therefore needs purl_eq/normalize_purl to paper over the differences.

Target design

  • utils::purl exposes one validated constructor per ecosystem (or Purl::new(Ecosystem, ns, name, version) -> Option<String>) that owns name canonicalization (PyPI PEP 503, composer and nuget lowercase), plus one versionless base_purl. build_* becomes private or is deleted.
  • Type checks go through Ecosystem::from_purl.

Checklist (one PR each, in order)

  • Route purl ecosystem checks through Ecosystem::from_purl instead of 24 inline starts_with("pkg:<type>/") tests #747: purl type checks through Ecosystem::from_purl (mechanical; can start now).
  • Vendored ledger keys (vendor/{gem,maven_repo,nuget_feed,composer_lock}.rs) and redirect/golang_local.rs move to the validated builders. An unsafe coordinate becomes a refusal instead of a ledger key. Owner: ecosystems area.
  • vendor/path.rs::leaf_to_purl and lock_inventory/recover.rs build through the validated builders, canonicalizing PyPI and composer once. Delete the re-canonicalization in vex::discover.
  • vex/product.rs versionless/product purls through one base_purl builder.
  • Delete the build_* family and purl_name_version, which only has a test caller.

Dependencies


Consolidated work — backlog review, 2026-10-08

The following standalone issues are now tracked here. Their closure consolidates scheduling; it does not mean their implementation is complete. Original reports and discussion remain linked below.

#630: Move the crawler coordinate guards and composer's leading-v rule into utils and delete the copies

Preserved scope and acceptance criteria from #630

Proposed change

  • Add path_safety::is_safe_name_version(name, version) and move is_safe_maven_coordinate into utils::path_safety.
  • Call them from the three crawlers, simple_purl, maven_purl and vendor/maven_repo.rs.
  • Make utils::composer_version::strip_leading_v pub(crate) and point every normalize_version caller at it: the composer crawler and its oracle, formats::composer, lock_inventory::composer and upstream::composer.

Delete: is_safe_{cargo,gem,nuget}_coordinate and their three test copies (keep one table test on the shared function), plus composer_crawler::normalize_version.

Size and scope

About 40 production lines deleted and about 10 added, in crawlers/{cargo,ruby,nuget,maven,composer}_crawler.rs, utils/{path_safety,purl,composer_version}.rs, formats/composer/mod.rs, patch/redirect/upstream/composer.rs and vendor/maven_repo.rs. There is no behavior change. Out of scope: the go and deno guards, which use multi-segment and JSR-component rules, and the purl builder families (C20).

Acceptance criteria

  • One single-segment name/version guard and one Maven guard, both in utils::path_safety.
  • formats/, utils/ and patch/redirect/ no longer import crawlers::composer_crawler::normalize_version or crawlers::maven_crawler::is_safe_maven_coordinate.
  • One table test covers the traversal, colon, NUL, backslash and empty cases now spread over the three crawler tests. purl_builders_validate_coordinates and the composer test_normalize_version cases, moved to strip_leading_v, stay green.
  • cargo test -p socket-patch-core --lib crawlers and cargo clippy --workspace --all-features -- -D warnings stay green.

Activity

  1. added
    arch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)
    refactorStructural change: duplicated code or logic, missing abstraction, layering, dead code
    on Oct 4, 2026
  2. mikolalysenko commented on Oct 4, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged: priority:p3 (tracking/refactor). Child work starts with #747. Related: #630.


    Generated by Claude Code

  3. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue for the architecture refactor routine (highest leverage: the #630 item's three byte-identical crawler coordinate guards and simple_purl's inline copy are the best item in files no open PR touches). Branch: arch-refactor/748-name-version-guard. Claim-ID: 2026-10-08T17:56:10Z-d68635

    Slice: one path_safety::is_safe_name_version for the cargo, gem and NuGet crawlers and purl::simple_purl, deleting is_safe_{cargo,gem,nuget}_coordinate and their three test copies. The composer leading-v half waits for #1108 (it touches upstream/composer.rs); the Maven guard already lives in formats::maven.


    Generated by Claude Code

  4. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR for the #630 item (coordinate guards slice): #1153.


    Generated by Claude Code

  5. mikolalysenko commented on Oct 13, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Janitor: released the stale claim Claim-ID: 2026-10-08T17. 56:10Z-d68635:#1153 (merged 2026-10-08) and #1045 slices merged; no open PR for the rest, and the claimer has posted nothing here in more than 48h. The issue stays open and can be claimed again.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)priority:p3refactorStructural change: duplicated code or logic, missing abstraction, layering, dead code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions