From f0e9dccb5816438377282c30facdc12ed5009d18 Mon Sep 17 00:00:00 2001
From: RoboMagus <68224306+RoboMagus@users.noreply.github.com>
Date: Thu, 10 Aug 2023 18:11:15 +0200
Subject: [PATCH] Only handle shell commands output when return_response
 requested (#97777)

---
 .../components/shell_command/__init__.py      | 27 ++++++++++-----
 tests/components/shell_command/test_init.py   | 34 +++++++++++++++++++
 2 files changed, 52 insertions(+), 9 deletions(-)

diff --git a/homeassistant/components/shell_command/__init__.py b/homeassistant/components/shell_command/__init__.py
index 8430d7284ee..b2f38f54b20 100644
--- a/homeassistant/components/shell_command/__init__.py
+++ b/homeassistant/components/shell_command/__init__.py
@@ -105,14 +105,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
 
             raise
 
-        service_response: JsonObjectType = {
-            "stdout": "",
-            "stderr": "",
-            "returncode": process.returncode,
-        }
-
         if stdout_data:
-            service_response["stdout"] = stdout_data.decode("utf-8").strip()
             _LOGGER.debug(
                 "Stdout of command: `%s`, return code: %s:\n%s",
                 cmd,
@@ -120,7 +113,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
                 stdout_data,
             )
         if stderr_data:
-            service_response["stderr"] = stderr_data.decode("utf-8").strip()
             _LOGGER.debug(
                 "Stderr of command: `%s`, return code: %s:\n%s",
                 cmd,
@@ -132,7 +124,24 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
                 "Error running command: `%s`, return code: %s", cmd, process.returncode
             )
 
-        return service_response
+        if service.return_response:
+            service_response: JsonObjectType = {
+                "stdout": "",
+                "stderr": "",
+                "returncode": process.returncode,
+            }
+            try:
+                if stdout_data:
+                    service_response["stdout"] = stdout_data.decode("utf-8").strip()
+                if stderr_data:
+                    service_response["stderr"] = stderr_data.decode("utf-8").strip()
+                return service_response
+            except UnicodeDecodeError:
+                _LOGGER.exception(
+                    "Unable to handle non-utf8 output of command: `%s`", cmd
+                )
+                raise
+        return None
 
     for name in conf:
         hass.services.async_register(
diff --git a/tests/components/shell_command/test_init.py b/tests/components/shell_command/test_init.py
index ac594c811ed..1efcc9dc919 100644
--- a/tests/components/shell_command/test_init.py
+++ b/tests/components/shell_command/test_init.py
@@ -174,6 +174,40 @@ async def test_stdout_captured(mock_output, hass: HomeAssistant) -> None:
     assert response["returncode"] == 0
 
 
+@patch("homeassistant.components.shell_command._LOGGER.debug")
+async def test_non_text_stdout_capture(
+    mock_output, hass: HomeAssistant, caplog: pytest.LogCaptureFixture
+) -> None:
+    """Test handling of non-text output."""
+    assert await async_setup_component(
+        hass,
+        shell_command.DOMAIN,
+        {
+            shell_command.DOMAIN: {
+                "output_image": "curl -o - https://raw.githubusercontent.com/home-assistant/assets/master/misc/loading-screen.gif"
+            }
+        },
+    )
+
+    # No problem without 'return_response'
+    response = await hass.services.async_call(
+        "shell_command", "output_image", blocking=True
+    )
+
+    await hass.async_block_till_done()
+    assert not response
+
+    # Non-text output throws with 'return_response'
+    with pytest.raises(UnicodeDecodeError):
+        response = await hass.services.async_call(
+            "shell_command", "output_image", blocking=True, return_response=True
+        )
+
+    await hass.async_block_till_done()
+    assert not response
+    assert "Unable to handle non-utf8 output of command" in caplog.text
+
+
 @patch("homeassistant.components.shell_command._LOGGER.debug")
 async def test_stderr_captured(mock_output, hass: HomeAssistant) -> None:
     """Test subprocess that has stderr."""
-- 
GitLab