* Rename addon/addons to app/apps in filenames and imports
Continues the addon→app terminology migration (#6786).
Renames all source files, test files, fixture files, and
directories that contained 'addon'/'addons' in their names,
and updates all imports accordingly.
Resolution check files in supervisor/resolution/checks/ that were
renamed override the slug property to preserve the existing API
contract (slugs are exposed via the resolution info API and used
to run checks by name).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename add-on.json fixture
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename addon→app in docstrings and comments
Updates all docstrings and inline comments across supervisor/ and
tests/ to use the new app/apps terminology. No runtime behaviour
is changed by this commit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename addon→app in code (variables, args, class names, functions)
Renames all internal Python identifiers from addon/addons to app/apps:
- Variable and argument names
- Function and method names
- Class names (Addon→App, AddonManager→AppManager, DockerAddon→DockerApp,
all exception, check, and fixup classes, etc.)
- String literals used as Python identifiers (pytest fixtures,
parametrize param names, patch.object attribute strings,
URL route match_info keys)
External API contracts are preserved: JSON keys, error codes,
discovery protocol fields, TypedDict/attr.s field names.
Import module paths (supervisor/addons/) are also unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix partial backup/restore API to remap addons key to apps
The external API accepts `addons` as the request body key (since
ATTR_APPS = "addons"), but do_backup_partial and do_restore_partial
now take an `apps` parameter after the rename. The **body expansion
in both endpoints would pass `addons=...` causing a TypeError.
Remap the key before expansion in both backup_partial and
restore_partial:
if ATTR_APPS in body:
body["apps"] = body.pop(ATTR_APPS)
Also adds test_restore_partial_with_addons_key to verify the restore
path correctly receives apps= when addons is passed in the request
body. This path had no existing test coverage.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix merge error
* Adjust AppLoggerAdapter to use app_name
Co-authored-by: Stefan Agner <stefan@agner.ch>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
* 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>
* Harden backup tar extraction with Python data filter
Replace filter="fully_trusted" with a custom backup_data_filter that
wraps tarfile.data_filter. This adds protection against symlink attacks
(absolute targets, destination escapes), device node injection, and
path traversal, while resetting uid/gid and sanitizing permissions.
Unlike using data_filter directly, the custom filter skips problematic
entries with a warning instead of aborting the entire extraction. This
ensures existing backups containing absolute symlinks (e.g. in shared
folders) still restore successfully with the dangerous entries omitted.
Also removes the now-redundant secure_path member filtering, as
data_filter is a strict superset of its protections. Fixes a standalone
bug in _folder_restore which had no member filtering at all.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Simplify security tests to test backup_data_filter directly
Test the public backup_data_filter function with plain tarfile
extraction instead of going through Backup internals. Removes
protected-access pylint warnings and unnecessary coresys setup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Switch to tar filter instead of custom data filter wrapper
Replace backup_data_filter (which wrapped data_filter and skipped
problematic entries) with the built-in tar filter. The tar filter
rejects path traversal and absolute names while preserving uid/gid
and file permissions, which is important for add-ons running as
non-root users.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Apply suggestions from code review
Co-authored-by: Erik Montnemery <erik@montnemery.com>
* Use BackupInvalidError instead of BackupError for tarfile.TarError
Make sure FilterErrors lead to BackupInvalidError instead of BackupError,
as they are not related to the backup process itself but rather to the
integrity of the backup data.
* Improve test coverage and use pytest.raises
* Only make FilterError a BackupInvalidError
* Add test case for FilterError during Home Assistant Core restore
* Add test cases for Add-ons
* Fix pylint warnings
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>