diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index b1e4768a0a7..c09586848df 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -56,6 +56,7 @@ from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.network import get_url +from homeassistant.helpers.template import Template from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass @@ -912,15 +913,16 @@ async def async_handle_snapshot_service( ) -> None: """Handle snapshot services calls.""" hass = camera.hass - filename = service_call.data[ATTR_FILENAME] + filename: Template = service_call.data[ATTR_FILENAME] filename.hass = hass snapshot_file = filename.async_render(variables={ATTR_ENTITY_ID: camera}) # check if we allow to access to that file if not hass.config.is_allowed_path(snapshot_file): - _LOGGER.error("Can't write %s, no access to path!", snapshot_file) - return + raise HomeAssistantError( + f"Cannot write `{snapshot_file}`, no access to path; `allowlist_external_dirs` may need to be adjusted in `configuration.yaml`" + ) image = await camera.async_camera_image() diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index 79d86e1ef33..f0202e3d958 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -245,6 +245,26 @@ async def test_snapshot_service(hass: HomeAssistant, mock_camera) -> None: assert mock_write.mock_calls[0][1][0] == b"Test" +async def test_snapshot_service_not_allowed_path( + hass: HomeAssistant, mock_camera +) -> None: + """Test snapshot service with a not allowed path.""" + mopen = mock_open() + + with patch("homeassistant.components.camera.open", mopen, create=True), patch( + "homeassistant.components.camera.os.makedirs", + ), pytest.raises(HomeAssistantError, match="/test/snapshot.jpg"): + await hass.services.async_call( + camera.DOMAIN, + camera.SERVICE_SNAPSHOT, + { + ATTR_ENTITY_ID: "camera.demo_camera", + camera.ATTR_FILENAME: "/test/snapshot.jpg", + }, + blocking=True, + ) + + async def test_websocket_stream_no_source( hass: HomeAssistant, hass_ws_client: WebSocketGenerator, mock_camera, mock_stream ) -> None: