From 3788a950e62fe3284ecf74d12bd61dcac933bce1 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 7 Nov 2022 11:14:57 -0500 Subject: [PATCH] Validate matcher field case in `usb.async_is_plugged_in` (#81514) * Support case-insensitive matching * Revert "Support case-insensitive matching" This reverts commit 0fdb2aa6bc6165d9adae39ecbe7f6698e7b94715. * Explicitly check the case of matcher fields in `async_is_plugged_in` --- homeassistant/components/usb/__init__.py | 18 +++++++++++++++ tests/components/usb/test_init.py | 29 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/homeassistant/components/usb/__init__.py b/homeassistant/components/usb/__init__.py index 7c0355fa24c..0f81d2e42d6 100644 --- a/homeassistant/components/usb/__init__.py +++ b/homeassistant/components/usb/__init__.py @@ -64,6 +64,24 @@ def async_register_scan_request_callback( @hass_callback def async_is_plugged_in(hass: HomeAssistant, matcher: USBCallbackMatcher) -> bool: """Return True is a USB device is present.""" + + vid = matcher.get("vid", "") + pid = matcher.get("pid", "") + serial_number = matcher.get("serial_number", "") + manufacturer = matcher.get("manufacturer", "") + description = matcher.get("description", "") + + if ( + vid != vid.upper() + or pid != pid.upper() + or serial_number != serial_number.lower() + or manufacturer != manufacturer.lower() + or description != description.lower() + ): + raise ValueError( + f"vid and pid must be uppercase, the rest lowercase in matcher {matcher!r}" + ) + usb_discovery: USBDiscovery = hass.data[DOMAIN] return any( _is_matching(USBDevice(*device_tuple), matcher) diff --git a/tests/components/usb/test_init.py b/tests/components/usb/test_init.py index ca978af75f2..c7196fed0c5 100644 --- a/tests/components/usb/test_init.py +++ b/tests/components/usb/test_init.py @@ -877,6 +877,35 @@ async def test_async_is_plugged_in(hass, hass_ws_client): assert usb.async_is_plugged_in(hass, matcher) +@pytest.mark.parametrize( + "matcher", + [ + {"vid": "abcd"}, + {"pid": "123a"}, + {"serial_number": "1234ABCD"}, + {"manufacturer": "Some Manufacturer"}, + {"description": "A description"}, + ], +) +async def test_async_is_plugged_in_case_enforcement(hass, matcher): + """Test `async_is_plugged_in` throws an error when incorrect cases are used.""" + + new_usb = [{"domain": "test1", "vid": "ABCD"}] + + with patch("pyudev.Context", side_effect=ImportError), patch( + "homeassistant.components.usb.async_get_usb", return_value=new_usb + ), patch("homeassistant.components.usb.comports", return_value=[]), patch.object( + hass.config_entries.flow, "async_init" + ): + assert await async_setup_component(hass, "usb", {"usb": {}}) + await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) + await hass.async_block_till_done() + + with pytest.raises(ValueError): + usb.async_is_plugged_in(hass, matcher) + + async def test_web_socket_triggers_discovery_request_callbacks(hass, hass_ws_client): """Test the websocket call triggers a discovery request callback.""" mock_callback = Mock()