delete_extras() in src/shared/utils.c:123-125 walks the destination tree and removes files/directories not present in the sender manifest. The helper delete_extras_walk() uses stat() (which follows symlinks) and unlink()/rmdir() on paths constructed by concatenating the root with manifest-relative entries. If the destination tree contains a symlink, the walker may follow it and delete files outside the intended destination directory.
Location
src/shared/utils.c:84-120 — delete_extras_walk() follows symlinks via stat() and deletes the target.
src/shared/file.c:521-539 — receive_manifest() feeds the manifest directly into delete_extras().
Suggested fix
Use lstat() instead of stat() and never follow symlinks during cleanup.
Open the destination root as a dirfd and use unlinkat(dirfd, ...) relative to it.
Optionally require --force before deleting directories, and count deletions for a --max-delete safety limit.
Severity
Medium
Category
security
This issue was automatically generated by the issue-creator agent.
## Description
`delete_extras()` in `src/shared/utils.c:123-125` walks the destination tree and removes files/directories not present in the sender manifest. The helper `delete_extras_walk()` uses `stat()` (which follows symlinks) and `unlink()`/`rmdir()` on paths constructed by concatenating the root with manifest-relative entries. If the destination tree contains a symlink, the walker may follow it and delete files outside the intended destination directory.
## Location
- `src/shared/utils.c:84-120` — `delete_extras_walk()` follows symlinks via `stat()` and deletes the target.
- `src/shared/file.c:521-539` — `receive_manifest()` feeds the manifest directly into `delete_extras()`.
## Suggested fix
1. Use `lstat()` instead of `stat()` and never follow symlinks during cleanup.
2. Open the destination root as a dirfd and use `unlinkat(dirfd, ...)` relative to it.
3. Optionally require `--force` before deleting directories, and count deletions for a `--max-delete` safety limit.
## Severity
Medium
## Category
security
---
_This issue was automatically generated by the issue-creator agent._
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Description
delete_extras()insrc/shared/utils.c:123-125walks the destination tree and removes files/directories not present in the sender manifest. The helperdelete_extras_walk()usesstat()(which follows symlinks) andunlink()/rmdir()on paths constructed by concatenating the root with manifest-relative entries. If the destination tree contains a symlink, the walker may follow it and delete files outside the intended destination directory.Location
src/shared/utils.c:84-120—delete_extras_walk()follows symlinks viastat()and deletes the target.src/shared/file.c:521-539—receive_manifest()feeds the manifest directly intodelete_extras().Suggested fix
lstat()instead ofstat()and never follow symlinks during cleanup.unlinkat(dirfd, ...)relative to it.--forcebefore deleting directories, and count deletions for a--max-deletesafety limit.Severity
Medium
Category
security
This issue was automatically generated by the issue-creator agent.