diff --git a/esphome/components/api/api_outgoing_connection.cpp b/esphome/components/api/api_outgoing_connection.cpp index 374735ad46..5f3a425c6a 100644 --- a/esphome/components/api/api_outgoing_connection.cpp +++ b/esphome/components/api/api_outgoing_connection.cpp @@ -20,6 +20,8 @@ void OutgoingConnectionManager::setup() { #ifndef API_OUTGOING_CONNECTION_HOST this->target_pref_ = global_preferences->make_preference(629847102UL, true); if (this->target_pref_.load(&this->saved_)) { + // Defend against a corrupt or truncated blob before the first read + this->saved_.host[sizeof(this->saved_.host) - 1] = '\0'; this->host_persisted_ = true; ESP_LOGD(TAG, "Loaded target %s", this->saved_.host); } else { @@ -27,8 +29,6 @@ void OutgoingConnectionManager::setup() { ESP_LOGD(TAG, "No saved target"); this->saved_ = {}; } - // Defend against a corrupt or truncated preference blob - this->saved_.host[sizeof(this->saved_.host) - 1] = '\0'; #endif } diff --git a/esphome/components/udp/udp_component.cpp b/esphome/components/udp/udp_component.cpp index 6af4c56e47..c6df8dbc3e 100644 --- a/esphome/components/udp/udp_component.cpp +++ b/esphome/components/udp/udp_component.cpp @@ -101,9 +101,15 @@ void UDPComponent::setup() { // 8266 and RP2040 `Duino for (const auto &address : this->addresses_) { auto ipaddr = IPAddress(); - ipaddr.fromString(address); + if (!ipaddr.fromString(address)) { + ESP_LOGW(TAG, "Invalid address %s", address); + continue; + } this->ipaddrs_.push_back(ipaddr); } + if (this->ipaddrs_.size() != this->addresses_.size()) { + this->status_set_warning(LOG_STR("invalid address")); + } if (this->should_listen_) this->udp_client_.begin(this->listen_port_); #endif diff --git a/tests/component_tests/api/test_outgoing_connection.py b/tests/component_tests/api/test_outgoing_connection.py index bb09910d89..48d512cf48 100644 --- a/tests/component_tests/api/test_outgoing_connection.py +++ b/tests/component_tests/api/test_outgoing_connection.py @@ -5,6 +5,7 @@ from pathlib import Path import pytest +from esphome.components import socket from esphome.components.api import ( CONFIG_SCHEMA, _validate_outgoing_host_ipv6, @@ -14,6 +15,7 @@ from esphome.components.esp32 import KEY_BOARD, KEY_VARIANT, VARIANT_ESP32 import esphome.config_validation as cv from esphome.const import PlatformFramework from esphome.core import CORE +import esphome.final_validate as fv from esphome.types import ConfigType from tests.component_tests.types import SetCoreConfigCallable @@ -81,26 +83,28 @@ def test_outgoing_connection_requires_encryption( @pytest.mark.parametrize( - ("platform_framework", "platform_data"), + ("platform_framework", "platform_data", "socket_conf"), [ - # The platform default on these two - (PlatformFramework.ESP8266_ARDUINO, None), - (PlatformFramework.RP2040_ARDUINO, None), + # The platform default on these two, resolved like AUTO_LOAD does + (PlatformFramework.ESP8266_ARDUINO, None, None), + (PlatformFramework.RP2040_ARDUINO, None, None), # An explicit selection elsewhere - (PlatformFramework.ESP32_IDF, ESP32_PLATFORM_DATA), + ( + PlatformFramework.ESP32_IDF, + ESP32_PLATFORM_DATA, + {"implementation": "lwip_tcp"}, + ), ], ) def test_outgoing_connection_rejects_lwip_tcp( set_core_config: SetCoreConfigCallable, platform_framework: PlatformFramework, platform_data: ConfigType | None, + socket_conf: ConfigType | None, ) -> None: """The resolved lwip_tcp socket is rejected at final validate.""" - set_core_config( - platform_framework, - platform_data=platform_data, - full_config={"socket": {"implementation": "lwip_tcp"}}, - ) + set_core_config(platform_framework, platform_data=platform_data) + fv.full_config.set({"socket": socket_conf or socket.CONFIG_SCHEMA({})}) config = CONFIG_SCHEMA(_api_config({"host": "192.168.1.2"})) with pytest.raises(cv.Invalid, match="lwip_tcp"): _validate_outgoing_socket_implementation(config)