Repository navigation
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
Description
[agent] Found by the scheduled Pipenv bug-hunt routine (ledger #313).
Summary
patch/redirect/pipenv.rs handles a conflicting Pipfile.lock entry (another version pinned, a foreign source, a VCS/path/file dependency) in one of two ways. It decides which by checking files.contains_key("Pipfile"):
- Live lock (a Pipfile beside it): refuse the patch and veto the sibling pypi rewriters, "so nothing is half-redirected".
- Abandoned lock (no Pipfile): refuse only this file, and still let
requirements.txt/uv.lock/ … be redirected.
But the disk hosted flow builds files from REDIRECT_CANDIDATE_FILES (crates/socket-patch-cli/src/commands/scan/hosted.rs:40), and that list contains Pipfile.lock but not Pipfile. So files.contains_key("Pipfile") is always false. Every live Pipenv project takes the "abandoned lock" branch, and:
- the conflict veto never fires, so a sibling
requirements.txt(for example one exported withpipenv requirements > requirements.txtfor Docker or Heroku) is redirected; - the scan reports
redirected: 1,status: success, exit 0; - the warning says
(no Pipfile beside the lock: the sibling Python files are still redirected), which is false.
The unit tests in pipenv.rs pass a Pipfile key directly (pipenv.rs:558, :866), so they don't exercise the real file set.
Impact
Pipenv installs from Pipfile.lock, which still points at the user's own (unpatched) wheel. The scan still reports the patch as redirected. pipenv sync into a clean venv installs unpatched six (verified). vex does correctly refuse (no_applicable_patches), so there's no false attestation. But the scan's success and redirected count are wrong. The half-redirect is exactly the state that the comment at pipenv.rs:280-293 says the veto exists to prevent.
The same missing key also produces the misleading message in the vendored → hosted case reported on #328.
Repro (Linux; mock patch API as in the Poetry/uv bughunt probes, serving six 1.16.0)
mkdir c1 && cd c1 && mkdir wheels
cp six-1.16.0-py2.py3-none-any.whl wheels/ # the pristine PyPI wheel
cat > Pipfile <<'EOF'
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
six = {file = "wheels/six-1.16.0-py2.py3-none-any.whl"}
EOF
PIPENV_VENV_IN_PROJECT=1 pipenv install
echo 'six==1.16.0' > requirements.txt
git init -q
export SOCKET_API_URL=http://127.0.0.1:18080 SOCKET_API_TOKEN=fake SOCKET_ORG_SLUG=test-org
socket-patch scan --mode hosted --json --yes; echo "exit=$?"
# status: success, redirected: 1, rewrittenFiles: ["requirements.txt"]
# warning redirect_pipenv_refused: "Pipenv source for six already exists
# (no Pipfile beside the lock: the sibling Python files are still redirected)"
cat requirements.txt
# six @ http://127.0.0.1:18080/patch/pypi/six/1.16.0/.../six-1.16.0-py2.py3-none-any.whl --hash=sha256:096e50…
rm -rf .venv && pipenv sync
pipenv run python -c "import six; print(hasattr(six, 'SOCKET_PATCHED'))" # FalseReproduced twice on the current main (scan → rollback → scan).
Expected vs actual
- Expected: per the rewriter's own contract (
pipenv.rs:280-293), a conflict in a livePipfile.lockmeans Pipenv won't pick the patch up, so the patch is refused project-wide. That meansredirected: 0,requirements.txtuntouched, and aredirect_pipenv_refusedwarning without the "no Pipfile" clause. docs/testing/pipenv-compatibility.md also describes the CLI as reading the project's Pipfile. - Actual: the patch is half-redirected into
requirements.txt, the scan reports success withredirected: 1, and the warning claims there is no Pipfile.
OS × version
| Pipenv (Linux) | half-redirect of requirements.txt |
|---|---|
| 2018.11.26 (py3.8) | ❌ reproduces |
| 2022.12.19 (py3.8) | ❌ reproduces |
| 2026.8.0 (py3.12) | ❌ reproduces (twice) |
The macOS and Windows cells are running in the probe below. The code path is pure file-set logic with no OS-specific branches.
First bad: 07a6b88 (#242, "Support hosted Pipenv patches and safe vendoring"), which introduced the guard. It first shipped in 4.0.0. Release 3.3.0 had no hosted mode.
Suspect code
crates/socket-patch-cli/src/commands/scan/hosted.rs:40:REDIRECT_CANDIDATE_FILEShas no"Pipfile".crates/socket-patch-core/src/patch/redirect/pipenv.rs:295:Err(PlanError::Conflict(detail)) if files.contains_key("Pipfile")is unreachable in the disk flow.- It may also be worth checking whether the in-memory engine's file set (
crates/socket-patch-cli/src/hosted_memory/) carriesPipfile.
Probe run (macOS/Windows): https://github.com/SocketDev/socket-patch/actions/runs/36739663342
Activity
mikolalysenko commentedon Sep 30, 2026
[agent] Probe results (https://github.com/SocketDev/socket-patch/actions/runs/36739663342): the half-redirect reproduces in every cell, with
status: success,redirected: 1,rewrittenFiles: ["requirements.txt"]and the "no Pipfile beside the lock" warning each time.Generated by Claude Code
mikolalysenko commentedon Sep 30, 2026
[agent] Triaged:
priority:p1(Pipenv). Not a duplicate, and no existing fix PR. The cause is that the hosted disk flow's file set (REDIRECT_CANDIDATE_FILESinscan/hosted.rs) has noPipfile, so the live-lock guard inredirect/pipenv.rscan't fire. That also explains the misleading "no Pipfile beside the lock" text in the vendored → hosted row reported on #328. #328 has a different main cause, though: PyPI mode takeover isn't supported. So this is cross-linked there, not clustered.Generated by Claude Code
mikolalysenko commentedon Oct 1, 2026
[agent] Re-triaged on main
2463257(#277, v5): still reproduces on Pipenv 2026.8.0 (Linux), with the issue's repro run twice. Each run givesstatus: success,redirected: 1,rewrittenFiles: ["requirements.txt"], and the same "no Pipfile beside the lock" warning.REDIRECT_CANDIDATE_FILESstill has noPipfile, andpipenv.rs:219is stillif files.contains_key("Pipfile").New in v5: the half-redirect is now harder to undo.
socket-patch rollbackrefuses to restore the redirectedrequirements.txt(partial_failure, exit 1: "every requirement in requirements.txt is a hosted pin, so whether the original used pip's hash-checking mode (--hash) is not derivable; restore it from version control instead"). A user who didn't commit first is left with arequirements.txtthat Pipenv never asked for, and the only remedy is git.Generated by Claude Code
mikolalysenko commentedon Oct 1, 2026
[agent] Claiming this issue (shared root cause: the hosted disk flow's file set never includes
Pipfile, so the live-lock guard inredirect/pipenv.rscan't fire). Branch: agent/fix-pipenv-hosted-pipfile-candidate. Claim-ID: 2026-10-01T05:20:53Z-553ad3Generated by Claude Code
mikolalysenko commentedon Oct 1, 2026
[agent] Draft fix PR: #425
Generated by Claude Code
Hosted scan: read the Pipfile beside Pipfile.lock
Ignore user refs in an abandoned Pipfile.lock