mirror of
https://github.com/home-assistant/supervisor.git
synced 2026-04-17 23:33:35 +01:00
Bump securetar to 2026.2.0 (#6575)
* Bump securetar from 2025.12.0 to 2026.2.0 Adapt to the new securetar API: - Use SecureTarArchive for outer backup tar (replaces SecureTarFile with gzip=False for the outer container) - create_inner_tar() renamed to create_tar(), password now inherited from the archive rather than passed per inner tar - SecureTarFile no longer accepts a mode parameter (read-only by default, InnerSecureTarFile for writing) - Pass create_version=2 to keep protected backups at version 2 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Reformat imports * Rename _create_cleanup to _create_finalize and update docstring * Use constant for SecureTar create version * Add test for SecureTarReadError in validate_backup securetar >= 2026.2.0 raises SecureTarReadError instead of tarfile.ReadError for invalid passwords. Catching this exception and raising BackupInvalidError is required so Core shows the encryption key dialog to the user. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Handle InvalidPasswordError for v3 backups * Address typos * Add securetar v3 encrypted password test fixture Add a test fixture for a securetar v3 encrypted backup with password. This will be used in the test suite to verify that the backup extraction process correctly handles encrypted backups. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ import tarfile
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from securetar import AddFileError
|
||||
from securetar import AddFileError, InvalidPasswordError, SecureTarReadError
|
||||
|
||||
from supervisor.addons.addon import Addon
|
||||
from supervisor.backups.backup import Backup, BackupLocation
|
||||
@@ -234,7 +234,21 @@ async def test_consolidate_failure(coresys: CoreSys, tmp_path: Path):
|
||||
pytest.raises(
|
||||
BackupInvalidError, match="Invalid password for backup 93b462f8"
|
||||
),
|
||||
), # Invalid password
|
||||
), # Invalid password (legacy securetar exception)
|
||||
(
|
||||
None,
|
||||
SecureTarReadError,
|
||||
pytest.raises(
|
||||
BackupInvalidError, match="Invalid password for backup 93b462f8"
|
||||
),
|
||||
), # Invalid password (securetar >= 2026.2.0 raises SecureTarReadError)
|
||||
(
|
||||
None,
|
||||
InvalidPasswordError,
|
||||
pytest.raises(
|
||||
BackupInvalidError, match="Invalid password for backup 93b462f8"
|
||||
),
|
||||
), # Invalid password (securetar >= 2026.2.0 with v3 backup raises InvalidPasswordError)
|
||||
],
|
||||
)
|
||||
async def test_validate_backup(
|
||||
@@ -244,7 +258,12 @@ async def test_validate_backup(
|
||||
securetar_side_effect: type[Exception] | None,
|
||||
expected_exception: AbstractContextManager,
|
||||
):
|
||||
"""Parameterized test for validate_backup."""
|
||||
"""Parameterized test for validate_backup.
|
||||
|
||||
Note that it is paramount that BackupInvalidError is raised for invalid password
|
||||
cases, as this is used by the Core to determine if a backup password is invalid
|
||||
and offer a input field to the user to input the correct password.
|
||||
"""
|
||||
enc_tar = Path(copy(get_fixture_path("backup_example_enc.tar"), tmp_path))
|
||||
enc_backup = Backup(coresys, enc_tar, "test", None)
|
||||
await enc_backup.load()
|
||||
@@ -273,3 +292,44 @@ async def test_validate_backup(
|
||||
expected_exception,
|
||||
):
|
||||
await enc_backup.validate_backup(None)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("password", "expected_exception"),
|
||||
[
|
||||
("supervisor", does_not_raise()),
|
||||
(
|
||||
"wrong_password",
|
||||
pytest.raises(
|
||||
BackupInvalidError, match="Invalid password for backup f92f0339"
|
||||
),
|
||||
),
|
||||
(
|
||||
None,
|
||||
pytest.raises(
|
||||
BackupInvalidError, match="Invalid password for backup f92f0339"
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_validate_backup_v3(
|
||||
coresys: CoreSys,
|
||||
tmp_path: Path,
|
||||
password: str | None,
|
||||
expected_exception: AbstractContextManager,
|
||||
):
|
||||
"""Test validate_backup with a real SecureTar v3 encrypted backup.
|
||||
|
||||
SecureTar v3 uses Argon2id key derivation and raises InvalidPasswordError
|
||||
on wrong passwords. It is paramount that BackupInvalidError is raised for
|
||||
invalid password cases, as this is used by the Core to determine if a backup
|
||||
password is invalid and offer a dialog to the user to input the correct
|
||||
password.
|
||||
"""
|
||||
v3_tar = Path(copy(get_fixture_path("backup_example_sec_v3.tar"), tmp_path))
|
||||
v3_backup = Backup(coresys, v3_tar, "test", None)
|
||||
await v3_backup.load()
|
||||
v3_backup.set_password(password)
|
||||
|
||||
with expected_exception:
|
||||
await v3_backup.validate_backup(None)
|
||||
|
||||
@@ -167,7 +167,7 @@ async def test_homeassistant_restore_rejects_path_traversal(
|
||||
traversal_info.size = 9
|
||||
_create_tar_gz(tar_path, [traversal_info], {"../../etc/passwd": b"malicious"})
|
||||
|
||||
tar_file = SecureTarFile(tar_path, "r", gzip=True)
|
||||
tar_file = SecureTarFile(tar_path, gzip=True)
|
||||
with pytest.raises(BackupInvalidError):
|
||||
await coresys.homeassistant.restore(tar_file)
|
||||
|
||||
@@ -181,7 +181,7 @@ async def test_addon_restore_rejects_path_traversal(
|
||||
traversal_info.size = 9
|
||||
_create_tar_gz(tar_path, [traversal_info], {"../../etc/passwd": b"malicious"})
|
||||
|
||||
tar_file = SecureTarFile(tar_path, "r", gzip=True)
|
||||
tar_file = SecureTarFile(tar_path, gzip=True)
|
||||
with pytest.raises(BackupInvalidError):
|
||||
await install_addon_ssh.restore(tar_file)
|
||||
|
||||
@@ -203,7 +203,7 @@ async def test_addon_restore_rejects_symlink_escape(
|
||||
{"escape/evil.py": b"malicious"},
|
||||
)
|
||||
|
||||
tar_file = SecureTarFile(tar_path, "r", gzip=True)
|
||||
tar_file = SecureTarFile(tar_path, gzip=True)
|
||||
with pytest.raises(BackupInvalidError):
|
||||
await install_addon_ssh.restore(tar_file)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user