fix: safe symlink handling in uninstall

- Broken symlinks now removable (lexists check via is_symlink fallback)
- Symlinks never hashed (avoids following to external targets)
- Symlinks only removed with force=True, otherwise skipped
This commit is contained in:
Manfred Riem
2026-03-31 09:57:47 -05:00
parent a84598617f
commit dcd93e699a

View File

@@ -159,15 +159,22 @@ class IntegrationManifest:
normed.relative_to(root) normed.relative_to(root)
except (ValueError, OSError): except (ValueError, OSError):
continue continue
if not path.exists(): if not path.exists() and not path.is_symlink():
continue continue
# Skip directories — manifest only tracks files # Skip directories — manifest only tracks files
if not path.is_file() and not path.is_symlink(): if not path.is_file() and not path.is_symlink():
skipped.append(path) skipped.append(path)
continue continue
if not force and _sha256(path) != expected_hash: # Never follow symlinks when comparing hashes. Only remove
skipped.append(path) # symlinks when forced, to avoid acting on tampered entries.
continue if path.is_symlink():
if not force:
skipped.append(path)
continue
else:
if not force and _sha256(path) != expected_hash:
skipped.append(path)
continue
path.unlink() path.unlink()
removed.append(path) removed.append(path)
# Clean up empty parent directories up to project root # Clean up empty parent directories up to project root