Skip to content

Fix PyPI direct refs not contesting wiring (#1549) - #1562

Merged
Mikola Lysenko (mikolalysenko) merged 7 commits into
mainfrom
agent/fix-pypi-direct-ref-contest
Oct 15, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 7 commits into
mainfrom
agent/fix-pypi-direct-ref-contest

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 #1549

Summary

A vendored or hosted PyPI wiring in one install source (requirements.txt
or Pipfile.lock) is now contested when the other source installs the
same release from a non-Socket artifact: a Pipfile.lock file url or
path wheel, a requirements name @ <url> line, a bare url / wheel path
line, or an editable / VCS checkout. Before this, vendor --check stayed
green and vex attested not_affected while pipenv sync /
pip install -r installed the upstream, unpatched release. A registry
== pin in the same place was already contested; now these references
behave the same way (patched_ref_unattributable, naming both files, and
no statement).

Root cause

vex/discover/pypi_other.rs recorded contesting evidence
(resolved_elsewhere) only for registry pins. A non-Socket direct
reference reached emit, which returned on Ok(None) without recording
anything. The poetry.lock extractor already counts such references.

Fix

  • Only a live Pipfile.lock (a Pipfile beside it) contributes the new evidence; an abandoned lock installs nothing (Hosted scan never sees the Pipfile, so a live Pipfile.lock conflict is treated as abandoned and a sibling requirements.txt is redirected anyway #333).
  • emit now hands a non-Socket reference from Pipfile.lock or a
    requirements file to foreign_reference, which records it as evidence.
    The release comes from the artifact's wheel / sdist filename (when it
    names the declared package), else an exact version pin (never a
    wildcard like ==1.16.*). The package name comes from the entry, the
    filename, or an #egg= fragment.
  • A reference with no readable version (a source directory, a VCS
    checkout, an editable -e …#egg=six) may install any version, so it is
    recorded with the new Discovery::resolved_elsewhere_any_version, a
    versionless purl that contests every version of that package.
    contest_across_locks matches it through ResolvedElsewhere::covers,
    and the diagnostic says the other file resolves the package "at a
    version it doesn't name".
  • Hatch's pyproject.toml / hatch.toml dependency tables are
    unchanged: they are declarations a resolver reads, not an install source
    of their own.

Tests (red → green)

Issue case Test Without fix With fix
Pipfile.lock file url / path wheel / file:// url / exact version beside a path / wildcard pin beside a wheel, contesting a vendored requirements.txt vex::discover::pypi_other::tests::issue_1549_non_socket_direct_references_contest_wiring FAIL (ref attested) pass
Mirror: requirements six @ url, bare url, bare wheel path, contesting a vendored Pipfile.lock same test FAIL pass
Unversioned: path dir, versionless url, VCS entry, six @ git+…, six @ file:///dir, bare git+…#egg=six, -e / --editable lines; another package or another release contests nothing issue_1549_unversioned_direct_references_contest_every_version FAIL pass
CLI, real scan wiring then vex (hosted + vendored, --no-verify too): requirements direct ref beside a wired Pipfile.lock e2e_vex_lockfile pipenv::requirements_direct_reference_contests_the_lock_wiring FAIL pass
CLI: Pipfile.lock file url / path wheel beside wired requirements.txt e2e_vex_lockfile pip::pipfile_lock_direct_reference_contests_the_requirements_wiring FAIL pass

Commands run locally:

  • cargo fmt --all -- --check: clean
  • cargo clippy --workspace --all-features -- -D warnings: clean
  • cargo test -p socket-patch-core --lib: all pass except 4 tests that
    also fail on main in this sandbox because it runs as root (chmod-based
    unwritable-dir tests: relax_loop_must_not_traverse_symlinked_root,
    an_unremovable_hidden_lock_keeps_every_store_entry,
    wire_write_failure_maps_error_and_leaves_lock_untouched,
    wire_failure_rolls_back_already_written_files)
  • cargo test -p socket-patch-cli --test e2e_vex_lockfile -- direct_reference_contests: 2 pass (2 fail with the core fix reverted)
  • SOCKET_PATCH_PIPENV_E2E_VERSIONS=2026.8.0 cargo test -p socket-patch-cli --test e2e_vex_build -- pipenv:: --ignored (real Pipenv): 2 pass
  • cargo test --workspace --all-features --no-fail-fast: everything passes except tests that fail identically on main in this sandbox (checked by running them against main's sources): the 4 root-permission core tests above, in_process_redirect write-failure tests (3), repair unremovable-lock tests (2), and e2e_bun_lockb::binary_shared_bundled_record_hosted_pin_is_managed (needs patches-api.socket.dev, unreachable here)
  • cargo test -p socket-patch-cli --test in_process_redirect_pipenv: 11 pass. The full run caught abandoned_pipfile_lock_conflict_does_not_veto_the_requirements_redirect (Hosted scan never sees the Pipfile, so a live Pipfile.lock conflict is treated as abandoned and a sibling requirements.txt is redirected anyway #333): a user file entry in a leftover Pipfile.lock with no Pipfile started vetoing the hosted requirements redirect. Pipenv installs from the lock only with its Pipfile, so the new evidence is now recorded only when a Pipfile is present (e97bceb, with a unit case for it).

Code review (/code-review high)

Fixed: editable lines and #egg=-named VCS lines now count; the
artifact filename wins over a beside-pin and wildcard pins are ignored;
resolves_package keeps its old meaning; pypi_name_purl validates
directly; the CLI cell asserts the contest phrase, not just the file name.

Not fixed here, deliberately:

  • A ref contested by a user's own reference is filed as rewirable
    (withhold_rewirable), like every cross-lock contest today, including
    the poetry.lock precedent. A re-run can't rewrite a user's reference, so
    "shadowed" would fit better for rollback/remove. That changes the
    management commands for all cross-lock contests, so it belongs in its
    own PR.
  • A foreign entry in another category of the same Pipfile.lock as the
    wiring doesn't contest it. That's pre-existing for registry pins too
    (cross-lock evidence never contests its own file).

🤖 Generated with Claude Code

https://claude.ai/code/session_01R2uDxa7vv5ajQTMGpCK2ug


Note

Medium Risk
Changes VEX discovery and attestation for contested PyPI wirings; incorrect matching could wrongly attest or block patches, but behavior is heavily regression-tested.

Overview
Fixes #1549: when one Python install file wires a package to a Socket patch but another (requirements.txt vs Pipfile.lock) installs the same release from a non-Socket URL, wheel path, or direct reference, discovery now treats that like an existing registry == pin—withholds attestation and emits patched_ref_unattributable naming both files.

Discovery changes: pypi_other records user direct refs via foreign_reference (version from wheel filename or exact pin; versionless/VCS/editables via new resolved_elsewhere_any_version). Pipfile.lock evidence only applies when a live Pipfile is present (#333). Cross-lock matching uses ResolvedElsewhere::covers for versionless contests.

Tests: unit regressions plus shared E2E helper contested_by_a_non_socket_direct_reference for pip/pipenv flows.

Reviewed by Cursor Bugbot for commit 07503e7. Configure here.


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
A Pipfile.lock `file` url or `path` wheel, or a requirements line like
`six @ https://...whl`, installs the upstream release. Discovery took
such a reference for no evidence at all, so when another lock wired the
same release to a Socket patch, `vendor --check` stayed green and `vex`
attested not_affected while `pipenv sync` / `pip install -r` shipped
the unpatched package.

These references now count as resolving the package elsewhere, exactly
like a registry `==` pin: the other lock's wiring is withheld with
patched_ref_unattributable naming both files. The release comes from
an exact `version` pin or the wheel / sdist filename. A reference with
no readable version (a source directory, a VCS checkout) may install
any version, so it contests every version of that package.

Fixes #1549

Assisted-by: Claude Code:claude-opus-5-5
End-to-end cells for #1549: the real CLI wires a Pipfile.lock or a
requirements.txt, then a second install source pins the same release
to a non-Socket url or wheel path. `vex` must withhold the statement
and explain the contest, as it already does for a registry pin.

Assisted-by: Claude Code:claude-opus-5-5
Review follow-ups for #1549. pip installs `-e git+...#egg=six` and a
bare `git+...#egg=six` line from source, so both now count as resolving
six elsewhere at any version. A Pipfile.lock wheel's own filename now
decides its release ahead of a `version` pin beside it, and a wildcard
pin like `==1.16.*` is never taken for a release. The "which packages
does any lock resolve" query keeps its old meaning.

Assisted-by: Claude Code:claude-opus-5-5
Pipenv installs from Pipfile.lock only when its Pipfile sits beside it.
A user's own file/path/VCS entry in a leftover lock with no Pipfile
installs nothing, so it no longer contests a requirements.txt wiring,
and a hosted scan still redirects requirements.txt there (#333).

Refs #1549

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 15, 2026 05:41
@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.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/vex/discover/pypi_other.rs
`#egg=six-1.16.0` names the package six, not "six-1.16.0". The egg name
now stops at the first `-<digit>`, so a legacy editable or VCS line
still contests a Socket wiring of the real package (#1549, Bugbot).

Assisted-by: Claude Code:claude-opus-5-5
@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.

Stale Bugbot comment from a previous run.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 15, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Ready for review at 543e124.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final review brief: #1562 at 543e124

What it does: One Python install source (requirements.txt or Pipfile.lock) may wire a release to a Socket patch while the other installs the same package from a non-Socket artifact: a file/path wheel, a name @ url line, a bare url, a VCS checkout or an editable. That other reference now counts as contesting evidence, so vex withholds the ref (patched_ref_unattributable) instead of attesting not_affected. A reference with no readable version contests every version of that package.

Risk: medium-low. This is security-relevant attestation logic, but almost every way it can fail withholds an attestation rather than making a false one. Matching for versioned entries is unchanged, and Hatch tables are excluded.

Look here:

  • pypi_other.rs:569:`` foreign_reference picks the version (wheel/sdist filename first, then an exact pin; wildcards are ignored) and falls back to "any version".
  • pypi_other.rs:177:``` the live Pipfile gate (an abandoned lock installs nothing) and the VCS branch.
  • pypi_other.rs:313:```` editable_location and `egg_name`, including the legacy `name-version` egg fragments.
  • mod.rs:468: ResolvedElsewhere::covers and purl_without_version.
  • mod.rs:1096: the contest match and its new "at a version it doesn't name" wording.

Verified:

Changes I made: none.

Open questions (non-blocking, possible follow-ups):

  • editable_location does not handle a value attached to -e with no space (-e./src/six#egg=six), which pip accepts. That line records no evidence, so the ref is still attested, the same as on main today.
  • Environment markers (; python_version<"3") are ignored, so such a line contests unconditionally. That fails safe.
  • Registry == pins in an abandoned Pipfile.lock still count as evidence (unchanged from main). Only the new direct-ref and VCS evidence is gated on live.

Auto-merge (squash) is armed, so approving sends this straight to the merge queue.


Generated by Claude Code

pip accepts `-e./src/six#egg=six`, the value attached to the short
option. Discovery skipped that spelling, so such a line didn't contest
a Socket wiring of six (#1549, final-review note).

Assisted-by: Claude Code:claude-opus-5-5
@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 07503e7. Configure here.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Auto-merge disarmed. 07503e7 ("Read -e editable values with no space") landed after my final-review brief for 543e124, and it is a code change, not a merge from main. The brief no longer covers the head. The next final-review pass will re-check the new commit once CI and Bugbot finish, then re-arm auto-merge.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final review brief: #1562 at 07503e7

What it does: When one Python install source (requirements.txt or Pipfile.lock) wires a release to a Socket patch and another installs the same package from a non-Socket artifact (a file/path wheel, name @ url, a bare url, a VCS checkout or an editable), that reference now counts as contesting evidence. vex then withholds the ref (patched_ref_unattributable) instead of attesting not_affected. Since the last brief (543e124), 07503e7 also reads -e<loc> with no space, as pip does. That was the first open question in the last brief.

Risk: medium-low. This is attestation logic, but nearly every way it can fail withholds an attestation rather than making a false one. The new commit is a 13-line parser change in one function.

Look here:

  • pypi_other.rs:313:`` editable_location, the only code changed since the last brief. The long flag still needs `=` or a space; the short flag now takes its value attached.
  • pypi_other.rs:572:``` foreign_reference picks the version (wheel/sdist filename first, then an exact pin) and otherwise falls back to "any version".
  • mod.rs:468: ResolvedElsewhere::covers and purl_without_version.
  • mod.rs:1117: the contest match and its new "at a version it doesn't name" wording.

Verified:

  • Re-read the delta 543e124..07503e7. It is one file, with a new -e./src/six#egg=six test case. A --extra-index-url line can't match the short-flag branch.
  • Ran cargo test -p socket-patch-core --lib vex::discover locally: 410 passed. cargo fmt --check is clean.
  • The rest of the PR was reviewed at 543e124, with no blockers.
  • CI: 35/35 run checks green and 19 skipped by path filters, including ci-ok and clippy. Cursor Bugbot: no new issues on 07503e7. There are no unresolved threads, and CHANGELOG.md is untouched.

Changes I made: none.

Open questions (non-blocking, carried over): Environment markers (; python_version<"3") are ignored, so such a line contests unconditionally. That fails safe. Registry == pins in an abandoned Pipfile.lock still count as evidence, as on main.

Auto-merge (squash) is armed, so approving sends this straight to the merge queue.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Still ready for review at the new head 07503e7 (label was applied at 543e124).

  • CI: 35/35 run checks green, 19 skipped by path filters (54 total); no main-wide failures.
  • Bugbot: reviewed 07503e7, no findings; no unresolved review threads.
  • Mergeable against main. No push from this run, so auto-merge was left as the final reviewer set it.

Slack announcement not sent this run (Slack send unavailable); the next run retries.


Generated by Claude Code

Merged via the queue into main with commit 0d741d8 Oct 15, 2026
54 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

3 participants