mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 11:12:35 +02:00
The delete_archive() function in backend/app/services/archive.py now has these safety checks: 1. Empty path check: Refuses to delete files if file_path is empty/whitespace 2. Path containment check: Verifies archive_dir is inside settings.archive_dir 3. Depth check: Ensures we're at least 1 level deep inside archive directory 4. Logging: All security refusals are logged with SECURITY prefix 5. Database record still deleted: Even if file deletion is refused, the orphan DB record is cleaned up Before the fix: file_path = settings.base_dir / archive.file_path # If empty: /opt/bambuddy archive_dir = file_path.parent # = /opt shutil.rmtree(archive_dir) # DELETES /opt!
84 lines
3.0 KiB
Plaintext
84 lines
3.0 KiB
Plaintext
=== BAMBUDDY FILE DELETION ISSUE - Jan 8, 2026 ===
|
|
=== ROOT CAUSE IDENTIFIED ===
|
|
|
|
WHAT HAPPENED:
|
|
- /opt was COMPLETELY DELETED on TWO containers:
|
|
- Container 109 (claude): ~11:22 and ~12:22
|
|
- Container 107 (3dp): ~13:28
|
|
- Container 107 was "untouched" (no SSH, no Claude Code) - just running BamBuddy
|
|
|
|
ROOT CAUSE FOUND:
|
|
Bug in backend/app/services/archive.py delete_archive() function (lines 914-929):
|
|
|
|
file_path = settings.base_dir / archive.file_path
|
|
if file_path.exists():
|
|
archive_dir = file_path.parent
|
|
shutil.rmtree(archive_dir, ignore_errors=True) # <-- THE BUG
|
|
|
|
If archive.file_path is EMPTY or MALFORMED:
|
|
- file_path = /opt/bambuddy / "" = /opt/bambuddy
|
|
- archive_dir = file_path.parent = /opt
|
|
- shutil.rmtree("/opt") --> DELETES ENTIRE /opt DIRECTORY!
|
|
|
|
TRIGGER:
|
|
- User was deleting archives via BamBuddy web UI on container 107 (3dp)
|
|
- One archive had corrupted/empty file_path in database
|
|
- Deleting that archive triggered shutil.rmtree("/opt")
|
|
- This deleted the entire /opt directory including BamBuddy itself
|
|
|
|
TIMELINE FOR CONTAINER 107 (3dp):
|
|
- 13:28:19 - Normal operation (WebSocket disconnect)
|
|
- 13:28:44 - DELETE /api/v1/archives/* requests failing with 500
|
|
(database already gone because /opt was deleted)
|
|
- ls -la / shows root directory modified at 13:28
|
|
|
|
FIX APPLIED (on container 109):
|
|
Safety checks added to delete_archive() in archive.py:
|
|
1. Check if file_path is not empty
|
|
2. Verify archive_dir is inside settings.archive_dir
|
|
3. Ensure archive_dir is at least 2 levels deep
|
|
4. Log error and refuse to delete if checks fail
|
|
|
|
TO INVESTIGATE AFTER ROLLBACK:
|
|
On container 107, after rolling back to autodaily260108003006:
|
|
|
|
# Find corrupted archive records
|
|
sqlite3 /opt/bambuddy/data/bambuddy.db \
|
|
"SELECT id, filename, file_path FROM print_archives
|
|
WHERE file_path = '' OR file_path IS NULL
|
|
OR file_path NOT LIKE 'archive/%';"
|
|
|
|
# Check all file_path values
|
|
sqlite3 /opt/bambuddy/data/bambuddy.db \
|
|
"SELECT id, file_path FROM print_archives ORDER BY id;"
|
|
|
|
CONTAINER 109 (this host):
|
|
- Were you also deleting archives around 11:22 and 12:22?
|
|
- Same bug could have been triggered here too
|
|
|
|
PROXMOX COMMANDS FOR ROLLBACK:
|
|
# Container 107 (3dp)
|
|
pct rollback 107 autodaily260108003006
|
|
pct start 107
|
|
|
|
# Container 109 (claude) - already done via UI
|
|
# Current snapshot: autodaily260108003004
|
|
|
|
WHAT TO DO NEXT:
|
|
1. Rollback container 107 to morning snapshot
|
|
2. Run the SQL query above to find corrupted archive
|
|
3. Apply the fix from container 109 to container 107
|
|
4. Understand how the file_path got corrupted in the first place
|
|
|
|
THE FIX (apply to both containers):
|
|
In backend/app/services/archive.py, the delete_archive function now has:
|
|
- Empty file_path check
|
|
- Path traversal protection (relative_to check)
|
|
- Minimum depth check (must be 2+ levels inside archive dir)
|
|
- Error logging for refused deletions
|
|
|
|
NOT CLAUDE CODE'S FAULT:
|
|
This was a bug in BamBuddy's own code that was triggered by:
|
|
1. Corrupted database record (unknown how it got corrupted)
|
|
2. User action (deleting archives via web UI)
|