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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedbugSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentpm:pippip / requirements.txtpriority:p1

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions