Repository navigation
Fix lock rewrites changing file owner (#1635) - #1636
Open
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
Open
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
Hosted and vendored rewrites (and rollback / remove / vendor --revert) replace a lockfile or manifest by staging a new file and renaming it over the old one. The stage kept the old permission bits but not the owner, so a run as root (a Docker bind mount, sudo) left poetry.lock owned by root, and the user's next `poetry add` failed with "Permission denied". A run in a shared group checkout moved the file to the runner's primary group. The shared stage-and-rename writer now gives the stage the old file's owner and group before the rename (before the chmod, so setuid bits survive), best-effort like npm's write-file-atomic: an unprivileged run keeps the group when it can and otherwise behaves as before. Fixes #1635 Assisted-by: Claude Code:claude-opus-5-5
A durable lockfile write now syncs the restored owner along with the content, so a crash right after the rename can't leave the file with the runner's owner. Refs #1635 Assisted-by: Claude Code:claude-opus-5-5
Spell out in the CLI contract that only existing project files (lockfiles, manifests, .npmrc) keep their owner, not socket-patch's own .socket/ records, and stop the root-only test from assuming a new file's group, which BSD and setgid directories set differently. Refs #1635 Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 17, 2026 21:10
Collaborator
Author
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 1e945c5. Configure here.
Collaborator
Author
|
Labeled Ready for review (burn-down agent).
Generated by Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #1635
Summary
A hosted or vendored scan run as root (a Docker bind mount,
sudo) used to leave the project'spoetry.lockowned by root, so the owner's nextpoetry add/poetry lockfailed withPermission denied. A run in a shared group checkout moved the lock to the runner's primary group. Rewritten lockfiles, manifests and.npmrcnow keep their owner and group, the same as their mode.Root cause
Every mode-preserving rewrite of a project file (
poetry.lock,package.json,.npmrc,Gemfile.lock, …, plus therollback/remove/vendor --revertrestores and the group-commit replay) runs throughstage_and_rename_blockingincrates/socket-patch-core/src/utils/fs.rs. It stages a fresh file, copies only the old file's permission bits onto it, and renames it over the old path. The fresh inode belongs to the process's euid/egid. So the defect isn't Poetry-specific: every ecosystem's lock and manifest rewrite had it.Fix
keep_ownerfchowns the stage to the old file's uid/gid before the fsync and before the chmod (chown(2)clears setuid/setgid), best-effort like npm's write-file-atomic. An unprivileged caller that can't give the file away (EPERM) still keeps the group when it belongs to it; otherwise the file keeps the runner's ownership, as before.CLI_CONTRACT.mdnow states the ownership rule.Test evidence
utils::fs::tests::preserving_writes_keep_the_destination_owner(root; every preserving writer, setuid mode kept)(0, 0, 2540)vs(1000, 1001, 2540)utils::fs::tests::preserving_write_keeps_a_supplementary_group(run as a non-root user in adevsgroup)in_process_redirect::root_hosted_scan_keeps_the_lock_owner(end-to-end hosted scan as root)(0, 0)vs(1000, 1001)Per-issue checklist:
poetry add/poetry lock#1635: root run keeps the lock's owner (preserving_writes_keep_the_destination_owner,root_hosted_scan_keeps_the_lock_owner); a group-checkout run keeps the group (preserving_write_keeps_a_supplementary_group).Commands run locally:
cargo fmt --all -- --check: clean.cargo clippy --workspace --all-features -- -D warnings: clean.cargo test -p socket-patch-core --all-features --lib: 6536 passed, 4 failed.cargo test -p socket-patch-cli --all-features --lib --test in_process_redirect --test in_process_redirect_pipenv: 3 failed inin_process_redirect. All 7 failures are read-only-directory tests that root ignores. They fail the same way onmainas root, and they pass as a non-root user against this branch. The sandbox runs as root.cargo test --workspacebuild ran out of the sandbox's disk allowance, so CI runs the rest.Review notes (
/code-review high)Fixed: the
WriteOpts::preserve_modedoc, a root-only test that assumed a new file's group, and the contract's scope wording. Not fixed, on purpose:.socket/manifest.jsonand the vendored ledgers use the plain (non-preserving) writer, and files a root run creates (a new.npmrc,.socket/vendor/dirs) are root-owned. The issue is about existing project files, and those writers don't preserve the mode either. Possible follow-up: give new files their parent directory's owner, asrestore_file_permissionsdoes for patched files.fchowncalls stay silent. Reporting them likeOWNERSHIP_NOT_RESTORED_MARKERwould need a warning channel through every writer.https://claude.ai/code/session_01XwH2o4SRLPwRpbu8QBzz2T
Generated by Claude Code