Files
backup-tool/tests/security/test_restore_paths.py
T

75 lines
2.3 KiB
Python

from __future__ import annotations
import importlib
import stat
from pathlib import Path
import pytest
from backup_tool.config import Settings
snapshot = importlib.import_module("backup_tool.snapshot")
def settings_for(tmp_path: Path) -> Settings:
key = tmp_path / "master.key"
key.write_bytes(b"m6-restore-path-test-master-key-material")
key.chmod(0o600)
repositories = tmp_path / "repositories"
sources = tmp_path / "sources"
restores = tmp_path / "restores"
for directory in (repositories, sources, restores):
directory.mkdir()
return Settings(
data_dir=tmp_path,
database_url=f"sqlite+aiosqlite:///{tmp_path / 'metadata.db'}",
repository_roots=(repositories,),
local_source_roots=(sources,),
restore_roots=(restores,),
master_key_file=key,
min_free_bytes=1,
)
@pytest.mark.parametrize("name", ["existing", "outside", "root"])
def test_restore_destination_rejects_existing_or_outside_paths(tmp_path: Path, name: str) -> None:
settings = settings_for(tmp_path)
root = settings.restore_roots[0]
outside = tmp_path / "outside"
outside.mkdir()
existing = root / "existing"
existing.mkdir()
destinations = {
"existing": existing,
"outside": outside / "restore",
"root": root,
}
with pytest.raises(snapshot.SnapshotError):
snapshot.validate_restore_destination(settings, str(destinations[name]))
def test_restore_strips_special_permission_bits(tmp_path: Path) -> None:
target = tmp_path / "restored"
target.write_bytes(b"content")
snapshot._apply_metadata(
target,
{"metadata_support": ["mode"], "mode": 0o7777},
)
mode = stat.S_IMODE(target.stat().st_mode)
assert mode == 0o777
assert mode & (stat.S_ISUID | stat.S_ISGID | stat.S_ISVTX) == 0
def test_restore_destination_rejects_a_symlinked_parent(tmp_path: Path) -> None:
settings = settings_for(tmp_path)
outside = tmp_path / "outside"
outside.mkdir()
(settings.restore_roots[0] / "linked").symlink_to(outside, target_is_directory=True)
with pytest.raises(snapshot.SnapshotError):
snapshot.validate_restore_destination(
settings, str(settings.restore_roots[0] / "linked" / "restore")
)