diff --git a/src/seedpass/api.py b/src/seedpass/api.py index 31de348a..0ba14c91 100644 --- a/src/seedpass/api.py +++ b/src/seedpass/api.py @@ -567,6 +567,9 @@ def backup_parent_seed( if data is not None: p = data.get("path") if p: + # Prevent directory traversal + if "/" in p or "\\" in p or ".." in p: + raise HTTPException(status_code=400, detail="Invalid filename") path = Path(p) _pm.handle_backup_reveal_parent_seed(path) return {"status": "ok"} diff --git a/verify_fix.py b/verify_fix.py new file mode 100644 index 00000000..ef562cf8 --- /dev/null +++ b/verify_fix.py @@ -0,0 +1,47 @@ +import sys +import os +from unittest.mock import MagicMock +from fastapi.testclient import TestClient +from pathlib import Path + +# Add src to sys.path +sys.path.insert(0, os.path.abspath("src")) + +# Mock seedpass.api._pm before importing api +import seedpass.api +seedpass.api._pm = MagicMock() +# No need to mock is_valid_filename now as I check inline +seedpass.api._token = "testtoken" # Set a dummy token + +from seedpass.api import app + +client = TestClient(app) + +def test_fix(): + evil_path = "/tmp/evil_file" + response = client.post( + "/api/v1/vault/backup-parent-seed", + json={"path": evil_path}, + headers={"Authorization": "Bearer testtoken"} + ) + + assert response.status_code == 400, f"Expected 400, got {response.status_code}" + assert response.json() == {"detail": "Invalid filename"} + + # Check that handle_backup_reveal_parent_seed was NOT called + seedpass.api._pm.handle_backup_reveal_parent_seed.assert_not_called() + print("Fix verified: Rejected arbitrary path.") + + # Test valid filename + valid_file = "backup.enc" + response = client.post( + "/api/v1/vault/backup-parent-seed", + json={"path": valid_file}, + headers={"Authorization": "Bearer testtoken"} + ) + assert response.status_code == 200, f"Expected 200, got {response.status_code}" + seedpass.api._pm.handle_backup_reveal_parent_seed.assert_called_with(Path(valid_file)) + print("Fix verified: Accepted valid filename.") + +if __name__ == "__main__": + test_fix()