mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Bump securetar to 2025.1.3 (#135762)
* Bump securetar to 2025.1.3 * Remove outdated fixture
This commit is contained in:
parent
1cff45b8b7
commit
6cbe18ebbd
@ -8,5 +8,5 @@
|
|||||||
"integration_type": "system",
|
"integration_type": "system",
|
||||||
"iot_class": "calculated",
|
"iot_class": "calculated",
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": ["cronsim==2.6", "securetar==2025.1.2"]
|
"requirements": ["cronsim==2.6", "securetar==2025.1.3"]
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,7 @@ import tarfile
|
|||||||
from typing import IO, Self, cast
|
from typing import IO, Self, cast
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from securetar import (
|
from securetar import SecureTarError, SecureTarFile, SecureTarReadError
|
||||||
PLAINTEXT_SIZE_HEADER,
|
|
||||||
VERSION_HEADER,
|
|
||||||
SecureTarError,
|
|
||||||
SecureTarFile,
|
|
||||||
SecureTarReadError,
|
|
||||||
)
|
|
||||||
|
|
||||||
from homeassistant.backup_restore import password_to_key
|
from homeassistant.backup_restore import password_to_key
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -205,8 +199,6 @@ def validate_password_stream(
|
|||||||
for obj in input_tar:
|
for obj in input_tar:
|
||||||
if not obj.name.endswith((".tar", ".tgz", ".tar.gz")):
|
if not obj.name.endswith((".tar", ".tgz", ".tar.gz")):
|
||||||
continue
|
continue
|
||||||
if obj.pax_headers.get(VERSION_HEADER) != "2.0":
|
|
||||||
raise UnsupportedSecureTarVersion
|
|
||||||
istf = SecureTarFile(
|
istf = SecureTarFile(
|
||||||
None, # Not used
|
None, # Not used
|
||||||
gzip=False,
|
gzip=False,
|
||||||
@ -215,6 +207,8 @@ def validate_password_stream(
|
|||||||
fileobj=input_tar.extractfile(obj),
|
fileobj=input_tar.extractfile(obj),
|
||||||
)
|
)
|
||||||
with istf.decrypt(obj) as decrypted:
|
with istf.decrypt(obj) as decrypted:
|
||||||
|
if istf.securetar_header.plaintext_size is None:
|
||||||
|
raise UnsupportedSecureTarVersion
|
||||||
try:
|
try:
|
||||||
decrypted.read(1) # Read a single byte to trigger the decryption
|
decrypted.read(1) # Read a single byte to trigger the decryption
|
||||||
except SecureTarReadError as err:
|
except SecureTarReadError as err:
|
||||||
@ -270,10 +264,6 @@ def _decrypt_backup(
|
|||||||
if not obj.name.endswith((".tar", ".tgz", ".tar.gz")):
|
if not obj.name.endswith((".tar", ".tgz", ".tar.gz")):
|
||||||
output_tar.addfile(obj, input_tar.extractfile(obj))
|
output_tar.addfile(obj, input_tar.extractfile(obj))
|
||||||
continue
|
continue
|
||||||
if obj.pax_headers.get(VERSION_HEADER) != "2.0":
|
|
||||||
raise UnsupportedSecureTarVersion
|
|
||||||
decrypted_obj = copy.deepcopy(obj)
|
|
||||||
decrypted_obj.size = int(obj.pax_headers[PLAINTEXT_SIZE_HEADER])
|
|
||||||
istf = SecureTarFile(
|
istf = SecureTarFile(
|
||||||
None, # Not used
|
None, # Not used
|
||||||
gzip=False,
|
gzip=False,
|
||||||
@ -282,6 +272,10 @@ def _decrypt_backup(
|
|||||||
fileobj=input_tar.extractfile(obj),
|
fileobj=input_tar.extractfile(obj),
|
||||||
)
|
)
|
||||||
with istf.decrypt(obj) as decrypted:
|
with istf.decrypt(obj) as decrypted:
|
||||||
|
if (plaintext_size := istf.securetar_header.plaintext_size) is None:
|
||||||
|
raise UnsupportedSecureTarVersion
|
||||||
|
decrypted_obj = copy.deepcopy(obj)
|
||||||
|
decrypted_obj.size = plaintext_size
|
||||||
output_tar.addfile(decrypted_obj, decrypted)
|
output_tar.addfile(decrypted_obj, decrypted)
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ PyTurboJPEG==1.7.5
|
|||||||
pyudev==0.24.1
|
pyudev==0.24.1
|
||||||
PyYAML==6.0.2
|
PyYAML==6.0.2
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
securetar==2025.1.2
|
securetar==2025.1.3
|
||||||
SQLAlchemy==2.0.36
|
SQLAlchemy==2.0.36
|
||||||
standard-aifc==3.13.0;python_version>='3.13'
|
standard-aifc==3.13.0;python_version>='3.13'
|
||||||
standard-telnetlib==3.13.0;python_version>='3.13'
|
standard-telnetlib==3.13.0;python_version>='3.13'
|
||||||
|
@ -66,7 +66,7 @@ dependencies = [
|
|||||||
"python-slugify==8.0.4",
|
"python-slugify==8.0.4",
|
||||||
"PyYAML==6.0.2",
|
"PyYAML==6.0.2",
|
||||||
"requests==2.32.3",
|
"requests==2.32.3",
|
||||||
"securetar==2025.1.2",
|
"securetar==2025.1.3",
|
||||||
"SQLAlchemy==2.0.36",
|
"SQLAlchemy==2.0.36",
|
||||||
"standard-aifc==3.13.0;python_version>='3.13'",
|
"standard-aifc==3.13.0;python_version>='3.13'",
|
||||||
"standard-telnetlib==3.13.0;python_version>='3.13'",
|
"standard-telnetlib==3.13.0;python_version>='3.13'",
|
||||||
|
2
requirements.txt
generated
2
requirements.txt
generated
@ -38,7 +38,7 @@ psutil-home-assistant==0.0.1
|
|||||||
python-slugify==8.0.4
|
python-slugify==8.0.4
|
||||||
PyYAML==6.0.2
|
PyYAML==6.0.2
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
securetar==2025.1.2
|
securetar==2025.1.3
|
||||||
SQLAlchemy==2.0.36
|
SQLAlchemy==2.0.36
|
||||||
standard-aifc==3.13.0;python_version>='3.13'
|
standard-aifc==3.13.0;python_version>='3.13'
|
||||||
standard-telnetlib==3.13.0;python_version>='3.13'
|
standard-telnetlib==3.13.0;python_version>='3.13'
|
||||||
|
2
requirements_all.txt
generated
2
requirements_all.txt
generated
@ -2665,7 +2665,7 @@ screenlogicpy==0.10.0
|
|||||||
scsgate==0.1.0
|
scsgate==0.1.0
|
||||||
|
|
||||||
# homeassistant.components.backup
|
# homeassistant.components.backup
|
||||||
securetar==2025.1.2
|
securetar==2025.1.3
|
||||||
|
|
||||||
# homeassistant.components.sendgrid
|
# homeassistant.components.sendgrid
|
||||||
sendgrid==6.8.2
|
sendgrid==6.8.2
|
||||||
|
2
requirements_test_all.txt
generated
2
requirements_test_all.txt
generated
@ -2147,7 +2147,7 @@ sanix==1.0.6
|
|||||||
screenlogicpy==0.10.0
|
screenlogicpy==0.10.0
|
||||||
|
|
||||||
# homeassistant.components.backup
|
# homeassistant.components.backup
|
||||||
securetar==2025.1.2
|
securetar==2025.1.3
|
||||||
|
|
||||||
# homeassistant.components.emulated_kasa
|
# homeassistant.components.emulated_kasa
|
||||||
# homeassistant.components.sense
|
# homeassistant.components.sense
|
||||||
|
Binary file not shown.
@ -186,7 +186,7 @@
|
|||||||
'type': 'result',
|
'type': 'result',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_can_decrypt_on_download[backup.local-ed1608a9-hunter2]
|
# name: test_can_decrypt_on_download[backup.local-c0cb53bd-hunter2]
|
||||||
dict({
|
dict({
|
||||||
'id': 1,
|
'id': 1,
|
||||||
'result': None,
|
'result': None,
|
||||||
@ -194,7 +194,7 @@
|
|||||||
'type': 'result',
|
'type': 'result',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_can_decrypt_on_download[backup.local-ed1608a9-wrong_password]
|
# name: test_can_decrypt_on_download[backup.local-c0cb53bd-wrong_password]
|
||||||
dict({
|
dict({
|
||||||
'error': dict({
|
'error': dict({
|
||||||
'code': 'password_incorrect',
|
'code': 'password_incorrect',
|
||||||
@ -216,7 +216,7 @@
|
|||||||
'type': 'result',
|
'type': 'result',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_can_decrypt_on_download[no_such_agent-ed1608a9-hunter2]
|
# name: test_can_decrypt_on_download[no_such_agent-c0cb53bd-hunter2]
|
||||||
dict({
|
dict({
|
||||||
'error': dict({
|
'error': dict({
|
||||||
'code': 'home_assistant_error',
|
'code': 'home_assistant_error',
|
||||||
|
@ -106,14 +106,14 @@ async def test_downloading_remote_encrypted_backup(
|
|||||||
hass_client: ClientSessionGenerator,
|
hass_client: ClientSessionGenerator,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test downloading a local backup file."""
|
"""Test downloading a local backup file."""
|
||||||
backup_path = get_fixture_path("test_backups/ed1608a9.tar", DOMAIN)
|
backup_path = get_fixture_path("test_backups/c0cb53bd.tar", DOMAIN)
|
||||||
await setup_backup_integration(hass)
|
await setup_backup_integration(hass)
|
||||||
hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest(
|
hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest(
|
||||||
"test",
|
"test",
|
||||||
[
|
[
|
||||||
AgentBackup(
|
AgentBackup(
|
||||||
addons=[AddonInfo(name="Test", slug="test", version="1.0.0")],
|
addons=[AddonInfo(name="Test", slug="test", version="1.0.0")],
|
||||||
backup_id="ed1608a9",
|
backup_id="c0cb53bd",
|
||||||
database_included=True,
|
database_included=True,
|
||||||
date="1970-01-01T00:00:00Z",
|
date="1970-01-01T00:00:00Z",
|
||||||
extra_metadata={},
|
extra_metadata={},
|
||||||
@ -141,7 +141,7 @@ async def _test_downloading_encrypted_backup(
|
|||||||
"""Test downloading an encrypted backup file."""
|
"""Test downloading an encrypted backup file."""
|
||||||
# Try downloading without supplying a password
|
# Try downloading without supplying a password
|
||||||
client = await hass_client()
|
client = await hass_client()
|
||||||
resp = await client.get(f"/api/backup/download/ed1608a9?agent_id={agent_id}")
|
resp = await client.get(f"/api/backup/download/c0cb53bd?agent_id={agent_id}")
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
backup = await resp.read()
|
backup = await resp.read()
|
||||||
# We expect a valid outer tar file, but the inner tar file is encrypted and
|
# We expect a valid outer tar file, but the inner tar file is encrypted and
|
||||||
@ -158,7 +158,7 @@ async def _test_downloading_encrypted_backup(
|
|||||||
|
|
||||||
# Download with the wrong password
|
# Download with the wrong password
|
||||||
resp = await client.get(
|
resp = await client.get(
|
||||||
f"/api/backup/download/ed1608a9?agent_id={agent_id}&password=wrong"
|
f"/api/backup/download/c0cb53bd?agent_id={agent_id}&password=wrong"
|
||||||
)
|
)
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
backup = await resp.read()
|
backup = await resp.read()
|
||||||
@ -171,7 +171,7 @@ async def _test_downloading_encrypted_backup(
|
|||||||
|
|
||||||
# Finally download with the correct password
|
# Finally download with the correct password
|
||||||
resp = await client.get(
|
resp = await client.get(
|
||||||
f"/api/backup/download/ed1608a9?agent_id={agent_id}&password=hunter2"
|
f"/api/backup/download/c0cb53bd?agent_id={agent_id}&password=hunter2"
|
||||||
)
|
)
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
backup = await resp.read()
|
backup = await resp.read()
|
||||||
|
@ -2560,13 +2560,13 @@ async def test_subscribe_event(
|
|||||||
("agent_id", "backup_id", "password"),
|
("agent_id", "backup_id", "password"),
|
||||||
[
|
[
|
||||||
# Invalid agent or backup
|
# Invalid agent or backup
|
||||||
("no_such_agent", "ed1608a9", "hunter2"),
|
("no_such_agent", "c0cb53bd", "hunter2"),
|
||||||
("backup.local", "no_such_backup", "hunter2"),
|
("backup.local", "no_such_backup", "hunter2"),
|
||||||
# Legacy backup, which can't be streamed
|
# Legacy backup, which can't be streamed
|
||||||
("backup.local", "2bcb3113", "hunter2"),
|
("backup.local", "2bcb3113", "hunter2"),
|
||||||
# New backup, which can be streamed, try with correct and wrong password
|
# New backup, which can be streamed, try with correct and wrong password
|
||||||
("backup.local", "ed1608a9", "hunter2"),
|
("backup.local", "c0cb53bd", "hunter2"),
|
||||||
("backup.local", "ed1608a9", "wrong_password"),
|
("backup.local", "c0cb53bd", "wrong_password"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("mock_backups")
|
@pytest.mark.usefixtures("mock_backups")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user