Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Hosted and vendored rewrites replace poetry.lock with a file owned by the user running socket-patch, so after a root scan (e.g. a Docker bind mount) the lock's owner gets "Permission denied" from `poetry add` / `poetry lock`

Open
#1,635 3 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@mikolalysenko is already working on this.

Since Oct 17, 2026.

  • #1636 by @mikolalysenko — open

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
32/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Stale
Tech stack
docker, rust

Research direction

Start with crates/{{USERNAME_vczr4cqb31}}-core/src/utils/fs.rs, especially stage_and_rename_blocking, and compare it with restore_file_permissions. Trace the Poetry callers in vendor/pypi_poetry.rs and patch/redirect/staged.rs; add or identify tests that verify uid/gid preservation for host-owned files after staged rewrites. Done means hosted, vendored, and rollback lockfile rewrites preserve ownership where permitted, with relevant platform-specific tests passing.

Written by the indexing model from the issue text.

Description

agent:claimed agent:triaged bug bughunt pm:poetry priority:p1

[agent] Found by the scheduled Poetry bug-hunt routine (ledger #311).

Summary

Hosted and vendored lock rewrites (and rollback / vendor --revert) write poetry.lock by staging a new file and renaming it over the old one (atomic_write_bytes_preserving_mode). The stage keeps the old file's permission bits but not its owner or group, so the rewritten poetry.lock belongs to whoever ran socket-patch.

  • Run as root on a project owned by a normal user (the usual docker run -v "$PWD:/app" … socket-patch scan setup, or sudo): poetry.lock turns into root:root 0644. Its real owner can still read it, but every Poetry command that writes the lock (poetry add, poetry lock, poetry remove, poetry update) then fails with [Errno 13] Permission denied: '…/poetry.lock'. Poetry writes the lock in place, so it can't replace a root-owned file. (Running git checkout poetry.lock or another socket-patch run as the user does work, because those replace the directory entry.)
  • Run as a normal user in a shared checkout whose files belong to a team group (ubuntu:devs 0664, directory not setgid): the lock is reassigned to the user's primary group (ubuntu:ubuntu 0664), so the other devs members lose write access to it.

Control: Poetry itself running as root (poetry add --lock idna==3.7) rewrites the same lock and keeps it owned by 1000:1000. Afterwards the owner's poetry add works.

The patch engine already restores uid/gid for files it patches (restore_file_permissions, mentioned in the doc comment at crates/socket-patch-core/src/utils/fs.rs:440). The lockfile writer doesn't.

Impact

After a containerised or sudo run, the next dependency change by the project's owner fails until they chown the lock back. Nothing tells them socket-patch changed the owner. pyproject.toml (which hosted and vendored don't write) keeps its owner, so the failure looks like a broken lock rather than a permissions change.

Repro (Linux, real Poetry 2.5.1; same with 1.8.5)

# as root; uid 1000 = ubuntu
mkdir /srv/own && cd /srv/own
cat > pyproject.toml <<'EOF'
[tool.poetry]
name = "demo"
version = "0.1.0"
description = ""
authors = ["x <[email protected]>"]
[tool.poetry.dependencies]
python = "^3.8"
six = "1.16.0"
EOF
poetry lock && chown -R 1000:1000 /srv/own
stat -c '%u:%g %a' poetry.lock          # 1000:1000 644
socket-patch scan --mode hosted --yes   # or --mode vendored; a patch for [email protected]
stat -c '%u:%g %a' poetry.lock          # 0:0 644
su ubuntu -c 'cd /srv/own && poetry add --lock attrs==23.2.0'
# [Errno 13] Permission denied: '/srv/own/poetry.lock'   (exit 1)
socket-patch rollback --yes; stat -c '%u:%g' poetry.lock   # still 0:0

The patch data came from a local mock of the patch API serving a patched copy of the real six-1.16.0 wheel (patches-api.socket.dev is unreachable from the sandbox). The repo's vex_pypi_real_common mock has the same shape. Ownership doesn't depend on the patch content.

Expected vs actual

  • Expected: an edit to a file the user owns keeps its owner and group, the same as its mode. The doc comment on atomic_write_bytes_preserving_mode says it's for "files the user owns and we merely edit … matching npm's write-file-atomic", and write-file-atomic also chowns the new file to the old file's uid/gid when it can. crates/socket-patch-cli/CLI_CONTRACT.md says nothing about ownership; it only promises that a rewrite's stage keeps the old permission bits.
  • Actual: the replacement file is owned by the euid/egid of the socket-patch process.

Matrix

OS Poetry Mode Run as Result
Linux 2.5.1 hosted scan root fail (0:0, owner's poetry add --lock exit 1), 2/2
Linux 2.5.1 vendored scan root fail (same)
Linux 1.8.5 vendored scan, then root rollback root fail (0:0 after both, owner's poetry add --lock exit 1)
Linux 2.5.1 hosted scan non-root member of the file's group fail (ubuntu:devs → ubuntu:ubuntu)
Linux 2.5.1 control: poetry add --lock root pass (stays 1000:1000)
macOS / Windows – – – untested (no probe branch this run; Windows has no POSIX owner, and macOS behaves like Linux for rename)

Release v5.0.1 (npm @socketsecurity/socket-patch) behaves the same as main 7d3086f, so this isn't a recent regression.

Suspect code

  • crates/socket-patch-core/src/utils/fs.rs:735 (stage_and_rename_blocking): only metadata(path).permissions() is carried over to the stage (set_permissions at ~:752). There's no fchown to the old uid/gid before the rename. A best-effort std::os::unix::fs::fchown(&file, Some(uid), Some(gid)) (ignoring EPERM, as write-file-atomic does) would fix it.
  • Callers that write poetry.lock: crates/socket-patch-core/src/vendor/pypi_poetry.rs:399, crates/socket-patch-core/src/patch/redirect/staged.rs:60 / :88, crates/socket-patch-cli/src/commands/scan/hosted.rs:2079 / :2133, crates/socket-patch-cli/src/commands/vendor.rs:270 / :1676.

This writer is shared, so every ecosystem's lockfile and manifest rewrite (package.json, .npmrc, Gemfile.lock, …) probably behaves the same way. I've only verified the Poetry path.

Dominant language
Rust
Stars
8
Forks
1
Avg merge
16h 39m
Merged PRs (30d)
584

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from SocketDev/socket-patch

All issues in SocketDev/socket-patch

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.