Skip to content

A requirements.txt wheel path without a leading ./ (wheels/six-1.16.0-…whl) doesn't contest another lock's Socket wiring, so vex still attests not_affected while pip install -r installs the upstream wheel (#1549 residual) #1587

Open

Description

[agent] Found by the scheduled pip / requirements.txt bug-hunt routine (ledger #309).

Summary

#1562 (fix for #1549) made non-Socket direct references in requirements.txt contest a Socket wiring of the same release in another lock (Pipfile.lock etc.), so vex withholds the statement with patched_ref_unattributable. The fix only recognizes a bare local path when its first character is not alphanumeric (./wheels/…, ../…, /abs/…). pip also installs bare relative paths that start with a letter or digit, which is the usual way to list a wheels directory:

wheels/six-1.16.0-py2.py3-none-any.whl
six-1.16.0-py2.py3-none-any.whl          # file in the project root
dist/six-1.16.0.tar.gz

requirement_location (crates/socket-patch-core/src/vex/discover/pypi_other.rs:359-362) sends such a line to pep508_direct_reference, which finds no @ and returns None. registry_pin then finds no ==, so the line counts as no evidence at all, the same behaviour #1549 had for every direct reference.

Impact

Take a project whose Pipfile.lock is Socket-wired (hosted or vendored), with a requirements.txt beside it for plain-pip users (Heroku, Docker, CI) that lists wheels/six-1.16.0-py2.py3-none-any.whl. vex exits 0 and attests not_affected ("Patched via Socket patch …"), but pip install -r requirements.txt installs the upstream, unpatched wheel. The only difference from the case #1562 fixed is the spelling: ./wheels/… is withheld (exit 2), while wheels/… is attested.

Repro

This uses the e2e harness added in #1562 (crates/socket-patch-cli/tests/vex_pipenv_pip_common/mod.rs, contested_by_a_non_socket_direct_reference). Add this throwaway test to crates/socket-patch-cli/tests/e2e_vex_lockfile/pipenv.rs:

#[test]
fn bare_wheel_path_without_dot_contests() {
    contested_by_a_non_socket_direct_reference(
        &flavors()[..1],
        "requirements.txt",
        &[
            ("wheel path, no ./", format!("wheels/{NAME}-{VERSION}-py3-none-any.whl\n")),
            ("wheel filename only", format!("{NAME}-{VERSION}-py3-none-any.whl\n")),
            ("sdist path, no ./", format!("dist/{NAME}-{VERSION}.tar.gz\n")),
        ],
    );
}
cargo test --release -p socket-patch-cli --test e2e_vex_lockfile bare_wheel_path_without_dot_contests

All three cases fail assert_ne!(out.code, Some(0)): vex exits 0 with a statement. Results on main 4cc50f4, reproduced twice:

requirements.txt line beside the wired Pipfile.lock hosted wiring vendored wiring
./wheels/vexdemo-1.2.3-py3-none-any.whl (control) exit 2, patched_ref_unattributable ✅ exit 2, patched_ref_unattributable ✅
wheels/vexdemo-1.2.3-py3-none-any.whl exit 0, not_affected statement ❌ exit 0, not_affected statement ❌
vexdemo-1.2.3-py3-none-any.whl exit 0 ❌ not run
dist/vexdemo-1.2.3.tar.gz exit 0 ❌ not run
vexdemo @ https://…whl ; python_version >= "3", name @ url with a --hash continuation, file:///…whl, VexDemo @ url contested ✅ not run

Real pip installs the bare relative path from requirements.txt. This was checked with the real six 1.16.0 wheel:

mkdir -p p/wheels && cd p
pip download --no-deps six==1.16.0 -d wheels
echo 'wheels/six-1.16.0-py2.py3-none-any.whl' > requirements.txt
python -m venv .venv && .venv/bin/python -m pip install --no-index -r requirements.txt
# Successfully installed six-1.16.0  (upstream bytes)
pip CPython wheels/six-…whl line installs
20.3.4 3.9 yes
24.0 3.11 yes
26.2.1 3.13 yes

A bare six-1.16.0-py2.py3-none-any.whl (the file in the CWD) also installs on pip 24.0.

Expected vs actual

OS × version

Linux only (main 4cc50f4). This is a pure parsing gap with no OS-specific code path, so macOS is expected to match. On Windows a wheels\six-…whl line takes the same branch. No probe branch was run, because probe branch cleanup is blocked for this routine (see ledger #309).

First bad

None. Before #1562, no direct reference contested a wiring (#1549). #1562 covers only the ./, / and ../ spellings.

Suspect code

  • crates/socket-patch-core/src/vex/discover/pypi_other.rs:356-375 (requirement_location): the !first.starts_with(alphanumeric) heuristic. A line whose first token contains / (or \), or ends in a wheel or sdist suffix, is a path to pip.
  • In crates/socket-patch-core/src/vex/discover/pypi_other.rs:290-293, registry_pin returning None for such a line gives no signal at all.

Activity

  1. added
    bugSomething isn't working
    bughuntFound by a scheduled package-manager bug-hunt agent
    pm:pippip / requirements.txt
    on Oct 15, 2026
  2. mikolalysenko commented on Oct 16, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: requirement_location in vex/discover/pypi_other.rs only reads a requirements line as a local path when its first character isn't alphanumeric, unlike pip's _looks_like_path / is_archive_file). Branch: agent/fix-requirements-bare-path-contest. Claim-ID: 2026-10-16T04:21:02Z-03a1c9


    Generated by Claude Code

  3. mikolalysenko commented on Oct 16, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1599


    Generated by Claude Code

  4. added 2 commits that reference this issue on Oct 16, 2026
    fcf64b0
    3a4de37
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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions