[web_server] Do not match internal entities by URL unless include_internal is set (#20883)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
sebo-b
2026-10-16 08:22:46 -10:00
committed by GitHub
co-authored by Claude Opus 5.5 J. Nick Koston J. Nick Koston
parent 5ab92ed363
commit cd6a34401b
2 changed files with 8 additions and 2 deletions
@@ -203,6 +203,10 @@ static UrlMatch match_url(const char *url_ptr, size_t url_len, bool only_domain,
EntityMatchResult UrlMatch::match_entity(EntityBase *entity) const {
EntityMatchResult result{false, this->method.empty()};
// Internal entities are unreachable by URL unless include_internal is set, as with the native API and MQTT.
if (!this->include_internal && entity->is_internal())
return result;
#ifdef USE_DEVICES
Device *entity_device = entity->get_device();
bool url_has_device = !this->device_name.empty();
@@ -2560,6 +2564,7 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
// Parse URL for component routing
// Pass HTTP method to disambiguate 3-segment URLs (GET=sub-device state, POST=main device action)
UrlMatch match = match_url(url.c_str(), url.length(), false, request->method() == HTTP_POST);
match.include_internal = this->include_internal_;
// Route to appropriate handler based on domain
// NOLINTNEXTLINE(readability-simplify-boolean-expr)
+3 -2
View File
@@ -66,7 +66,8 @@ struct UrlMatch {
#ifdef USE_DEVICES
StringRef device_name; ///< Device name within URL, empty for main device
#endif
bool valid{false}; ///< Whether this match is valid
bool valid{false}; ///< Whether this match is valid
bool include_internal{false}; ///< Whether internal entities may match, the web server's include_internal
// Helper methods for string comparisons
bool domain_equals(const char *str) const { return this->domain == str; }
@@ -78,7 +79,7 @@ struct UrlMatch {
bool method_equals(const __FlashStringHelper *str) const { return this->method == str; }
#endif
/// Match entity by name
/// Match entity by name; internal entities match only if include_internal is set
/// Returns EntityMatchResult with match status and whether action segment is empty
EntityMatchResult match_entity(EntityBase *entity) const;
};