diff --git a/homeassistant/components/smtp/notify.py b/homeassistant/components/smtp/notify.py index 02a5a6408b6..dcc2f49db0f 100644 --- a/homeassistant/components/smtp/notify.py +++ b/homeassistant/components/smtp/notify.py @@ -32,6 +32,7 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ServiceValidationError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.reload import setup_reload_service from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -255,13 +256,26 @@ def _attach_file(hass, atch_name, content_id=""): """ try: file_path = Path(atch_name).parent - if not hass.config.is_allowed_path(str(file_path)): - _LOGGER.warning( - "'%s' is not secure to load data from, ignoring attachment '%s'!", - file_path, - atch_name, + if os.path.exists(file_path) and not hass.config.is_allowed_path( + str(file_path) + ): + allow_list = "allowlist_external_dirs" + file_name = os.path.basename(atch_name) + url = "https://www.home-assistant.io/docs/configuration/basic/" + raise ServiceValidationError( + f"Cannot send email with attachment '{file_name} " + f"from directory '{file_path} which is not secure to load data from. " + f"Only folders added to `{allow_list}` are accessible. " + f"See {url} for more information.", + translation_domain=DOMAIN, + translation_key="remote_path_not_allowed", + translation_placeholders={ + "allow_list": allow_list, + "file_path": file_path, + "file_name": file_name, + "url": url, + }, ) - return with open(atch_name, "rb") as attachment_file: file_bytes = attachment_file.read() except FileNotFoundError: diff --git a/homeassistant/components/smtp/strings.json b/homeassistant/components/smtp/strings.json index b711c2f2009..38dd81ac196 100644 --- a/homeassistant/components/smtp/strings.json +++ b/homeassistant/components/smtp/strings.json @@ -4,5 +4,10 @@ "name": "[%key:common::action::reload%]", "description": "Reloads smtp notify services." } + }, + "exceptions": { + "remote_path_not_allowed": { + "message": "Cannot send email with attachment '{file_name} form directory '{file_path} which is not secure to load data from. Only folders added to `{allow_list}` are accessible. See {url} for more information." + } } } diff --git a/tests/components/smtp/test_notify.py b/tests/components/smtp/test_notify.py index 06110a3e5dc..182b45d9c1b 100644 --- a/tests/components/smtp/test_notify.py +++ b/tests/components/smtp/test_notify.py @@ -11,6 +11,7 @@ from homeassistant.components.smtp.const import DOMAIN from homeassistant.components.smtp.notify import MailNotificationService from homeassistant.const import SERVICE_RELOAD from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ServiceValidationError from homeassistant.setup import async_setup_component from tests.common import get_fixture_path @@ -111,7 +112,7 @@ EMAIL_DATA = [ ), ( "Test msg", - {"html": HTML, "images": ["test.jpg"]}, + {"html": HTML, "images": ["tests/testing_config/notify/test_not_exists.jpg"]}, "Content-Type: multipart/related", ), ( @@ -156,7 +157,6 @@ def test_send_message( ) def test_sending_insecure_files_fails( hass: HomeAssistant, - caplog: pytest.LogCaptureFixture, message_data, data, content_type, @@ -165,10 +165,19 @@ def test_sending_insecure_files_fails( """Verify if we cannot send messages with insecure attachments.""" sample_email = "" message.hass = hass - with patch("email.utils.make_msgid", return_value=sample_email): + with patch("email.utils.make_msgid", return_value=sample_email), pytest.raises( + ServiceValidationError + ) as exc: result, _ = message.send_message(message_data, data=data) assert content_type in result - assert "test.jpg' is not secure to load data from, ignoring attachment" + assert exc.value.translation_key == "remote_path_not_allowed" + assert exc.value.translation_domain == DOMAIN + assert ( + str(exc.value.translation_placeholders["file_path"]) + == "tests/testing_config/notify" + ) + assert exc.value.translation_placeholders["url"] + assert exc.value.translation_placeholders["file_name"] == "test.jpg" def test_send_text_message(hass: HomeAssistant, message) -> None: