Write the backup store with private=True so the backup password is not world-readable (#182259)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
dbl-0
2026-09-16 07:18:56 +02:00
committed by GitHub
co-authored by Claude Opus 5
parent 5dae5ff1f3
commit 365fef6659
4 changed files with 211 additions and 31 deletions
+6 -1
View File
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
STORE_DELAY_SAVE = 30
STORAGE_KEY = DOMAIN
STORAGE_VERSION = 1
STORAGE_VERSION_MINOR = 7
STORAGE_VERSION_MINOR = 8
class StoredBackupData(TypedDict):
@@ -39,6 +39,8 @@ class _BackupStore(Store[StoredBackupData]):
STORAGE_KEY,
max_readable_version=self._MAX_READABLE_VERSION,
minor_version=STORAGE_VERSION_MINOR,
# The store persists the automatic backup password in plaintext.
private=True,
)
@override
@@ -85,6 +87,9 @@ class _BackupStore(Store[StoredBackupData]):
for backup in data["backups"]:
backup["failed_addons"] = []
backup["failed_folders"] = []
# Version 1.8 does not change the data. The minor version was bumped
# so that stores written before the store was made private are
# rewritten once, with mode 0600, on the first load.
# Note: We allow reading data with major version 2 in which the unused key
# data["config"]["schedule"]["state"] will be removed. The bump to 2 is
+138 -14
View File
@@ -44,7 +44,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -93,7 +93,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -142,7 +142,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -191,7 +191,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -250,7 +250,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -309,7 +309,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -362,7 +362,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -415,7 +415,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -468,7 +468,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -521,7 +521,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -577,7 +577,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -633,7 +633,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -695,7 +695,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -757,7 +757,131 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
# name: test_store_migration[store_data7]
dict({
'data': dict({
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
dict({
'name': 'Test add-on',
'slug': 'test_addon',
'version': '1.0.0',
}),
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
'ssl',
]),
}),
]),
'config': dict({
'agents': dict({
'test.remote': dict({
'protected': True,
'retention': dict({
'copies': None,
'days': None,
}),
}),
}),
'automatic_backups_configured': True,
'create_backup': dict({
'agent_ids': list([
]),
'include_addons': None,
'include_all_addons': False,
'include_database': True,
'include_folders': None,
'name': None,
'password': 'hunter2',
}),
'last_attempted_automatic_backup': None,
'last_completed_automatic_backup': None,
'retention': dict({
'copies': None,
'days': None,
}),
'schedule': dict({
'days': list([
]),
'recurrence': 'never',
'state': 'never',
'time': None,
}),
}),
}),
'key': 'backup',
'minor_version': 8,
'version': 1,
})
# ---
# name: test_store_migration[store_data7].1
dict({
'data': dict({
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
dict({
'name': 'Test add-on',
'slug': 'test_addon',
'version': '1.0.0',
}),
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
'ssl',
]),
}),
]),
'config': dict({
'agents': dict({
'test.remote': dict({
'protected': True,
'retention': dict({
'copies': None,
'days': None,
}),
}),
}),
'automatic_backups_configured': True,
'create_backup': dict({
'agent_ids': list([
'test-agent',
]),
'include_addons': None,
'include_all_addons': False,
'include_database': True,
'include_folders': None,
'name': None,
'password': 'hunter2',
}),
'last_attempted_automatic_backup': None,
'last_completed_automatic_backup': None,
'retention': dict({
'copies': None,
'days': None,
}),
'schedule': dict({
'days': list([
]),
'recurrence': 'never',
'time': None,
}),
}),
}),
'key': 'backup',
'minor_version': 8,
'version': 1,
})
# ---
@@ -1330,7 +1330,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -1446,7 +1446,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -1562,7 +1562,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -1692,7 +1692,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -1969,7 +1969,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -2083,7 +2083,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -2197,7 +2197,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -2313,7 +2313,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -2431,7 +2431,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -2547,7 +2547,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -2667,7 +2667,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -2791,7 +2791,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -2907,7 +2907,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -3023,7 +3023,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -3139,7 +3139,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
@@ -3255,7 +3255,7 @@
}),
}),
'key': 'backup',
'minor_version': 7,
'minor_version': 8,
'version': 1,
})
# ---
+51
View File
@@ -302,6 +302,57 @@ def mock_delay_save() -> Generator[None]:
"minor_version": 7,
"version": 1,
},
{
"data": {
"backups": [
{
"backup_id": "abc123",
"failed_addons": [
{
"name": "Test add-on",
"slug": "test_addon",
"version": "1.0.0",
}
],
"failed_agent_ids": ["test.remote"],
"failed_folders": ["ssl"],
}
],
"config": {
"agents": {
"test.remote": {
"protected": True,
"retention": {"copies": None, "days": None},
}
},
"automatic_backups_configured": True,
"create_backup": {
"agent_ids": [],
"include_addons": None,
"include_all_addons": False,
"include_database": True,
"include_folders": None,
"name": None,
"password": "hunter2",
},
"last_attempted_automatic_backup": None,
"last_completed_automatic_backup": None,
"retention": {
"copies": None,
"days": None,
},
"schedule": {
"days": [],
"recurrence": "never",
"state": "never",
"time": None,
},
},
},
"key": DOMAIN,
"minor_version": 8,
"version": 1,
},
],
)
async def test_store_migration(