Repository navigation
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
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
- Expected: the same as Vendored PyPI wiring isn't treated as contested when Pipfile.lock (or requirements.txt) points the same release at a non-Socket file/URL/path, so vendor --check stays green and vex attests not_affected while pipenv sync installs upstream #1549 / Fix PyPI direct refs not contesting wiring (#1549) #1562: "a requirements line like
six @ https://...whlinstalls the upstream release … These references now count as resolving the package elsewhere". pip treats a requirement as a local path when it contains a path separator or starts with., or when it names an archive file (.whl,.tar.gz,.zip) that exists (pip._internal.req.constructors._looks_like_path/_get_url_from_path). Every such line should contest the same release, as./wheels/…already does. - Actual: discovery treats any line whose first character is alphanumeric as a name and skips it, so
vexattests a release thatpip install -rinstalls unpatched.
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_pinreturningNonefor such a line gives no signal at all.
Activity
pip bughunt: 2026-10-15 evening run (#1587)