Reject backup output inside data dir (#1587)

This commit is contained in:
red person
2026-06-03 02:38:27 +03:00
committed by GitHub
parent f39c87561b
commit db3a5c17b0
2 changed files with 22 additions and 0 deletions

View File

@@ -56,6 +56,16 @@ def _sqlite_safe_copy(src: Path, dst: Path) -> None:
dst.write_bytes(src.read_bytes()) dst.write_bytes(src.read_bytes())
def _reject_output_inside_data(out_path: Path) -> None:
try:
resolved = out_path.resolve()
data_root = _DATA_DIR.resolve()
resolved.relative_to(data_root)
except ValueError:
return
fail("backup output path must be outside data/")
def cmd_snapshot(args): def cmd_snapshot(args):
"""Write a tar.gz of the entire data/ directory. """Write a tar.gz of the entire data/ directory.
@@ -68,6 +78,7 @@ def cmd_snapshot(args):
out_path = Path(args.out) if args.out else ( out_path = Path(args.out) if args.out else (
_BACKUP_DIR / f"odysseus-backup-{datetime.now().strftime('%Y%m%d-%H%M%S')}.tar.gz" _BACKUP_DIR / f"odysseus-backup-{datetime.now().strftime('%Y%m%d-%H%M%S')}.tar.gz"
) )
_reject_output_inside_data(out_path)
out_path.parent.mkdir(parents=True, exist_ok=True) out_path.parent.mkdir(parents=True, exist_ok=True)
sqlite_dbs = [p for p in _DATA_DIR.rglob("*.db") if p.is_file() and not p.is_symlink()] sqlite_dbs = [p for p in _DATA_DIR.rglob("*.db") if p.is_file() and not p.is_symlink()]

View File

@@ -30,6 +30,17 @@ def _verify_args(path: Path):
return SimpleNamespace(path=str(path), pretty=False) return SimpleNamespace(path=str(path), pretty=False)
def test_snapshot_rejects_output_inside_data_dir(tmp_path, monkeypatch):
backup = _load_backup_cli()
repo = tmp_path / "repo"
data = repo / "data"
data.mkdir(parents=True)
_patch_repo(backup, monkeypatch, repo)
with pytest.raises(SystemExit):
backup._reject_output_inside_data(data / "self.tar.gz")
def test_restore_rejects_symlink_escape(tmp_path, monkeypatch): def test_restore_rejects_symlink_escape(tmp_path, monkeypatch):
backup = _load_backup_cli() backup = _load_backup_cli()
repo = tmp_path / "repo" repo = tmp_path / "repo"