[web_server] Keep allowed origins in a shared flash table (#20086)

This commit is contained in:
J. Nick Koston
2026-10-05 12:23:36 +13:00
committed by GitHub
parent acf7257293
commit ad1b6ab37e
5 changed files with 48 additions and 5 deletions
+10 -1
View File
@@ -413,7 +413,16 @@ async def to_code(config: ConfigType) -> None:
cg.add_define("USE_WEBSERVER_PRIVATE_NETWORK_ACCESS")
if (allowed_origins := config.get(CONF_ALLOWED_ORIGINS)) is not None:
cg.add_define("USE_WEBSERVER_ALLOWED_ORIGINS")
cg.add(var.set_allowed_origins(allowed_origins))
# Shared flash table ended by nullptr, so the server stores only a pointer.
cg.add(
var.set_allowed_origins(
cg.shared_progmem_array(
"web_server_allowed_origins",
cg.const_char_ptr,
[*allowed_origins, cg.nullptr],
)
)
)
if (auth := config.get(CONF_AUTH)) is not None:
cg.add_define("USE_WEBSERVER_AUTH")
# The scheme is fixed at build time so the unused Basic/Digest code path is compiled
+2 -1
View File
@@ -495,7 +495,8 @@ bool WebServer::is_request_origin_allowed_(AsyncWebServerRequest *request, const
#ifdef USE_WEBSERVER_ALLOWED_ORIGINS
// Otherwise the origin must be explicitly allowed via configuration.
for (const char *allowed_origin : this->allowed_origins_) {
for (const char *const *it = this->allowed_origins_; *it != nullptr; it++) {
const char *allowed_origin = *it;
// A single "*" entry allows any origin.
if (allowed_origin[0] == '*' && allowed_origin[1] == '\0')
return true;
+3 -3
View File
@@ -254,9 +254,9 @@ class WebServer final : public Component, public AsyncWebHandler {
*
* This list is also used to authorize Private Network Access requests when that feature is enabled.
*
* @param origins The list of allowed origins.
* @param origins nullptr-terminated table of allowed origins; must outlive the server.
*/
void set_allowed_origins(std::initializer_list<const char *> origins) { this->allowed_origins_ = origins; }
void set_allowed_origins(const char *const *origins) { this->allowed_origins_ = origins; }
#endif
// ========== INTERNAL METHODS ==========
@@ -620,7 +620,7 @@ class WebServer final : public Component, public AsyncWebHandler {
#ifdef USE_WEBSERVER_ALLOWED_ORIGINS
// Extra origins allowed to make cross-origin browser requests ("*" means any origin).
// Only compiled when allowed_origins is configured; same-origin is always allowed regardless.
FixedVector<const char *> allowed_origins_;
const char *const *allowed_origins_{nullptr};
#endif
/// Check whether the given request Origin is permitted. Same-origin (matching the Host the
@@ -0,0 +1,15 @@
---
esphome:
name: test
esp32:
board: esp32dev
wifi:
ssid: test
password: testtest
web_server:
allowed_origins:
- https://app.esphome.io
- "*"
@@ -0,0 +1,18 @@
"""Tests for the web_server allowed_origins codegen."""
from collections.abc import Callable
from pathlib import Path
def test_allowed_origins_are_a_progmem_table(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
"""Allowed origins live in a nullptr-terminated flash table instead of a heap vector."""
main_cpp = generate_main(component_config_path("allowed_origins.yaml"))
assert (
"static constexpr const char * web_server_allowed_origins[] PROGMEM = "
'{"https://app.esphome.io", "*", nullptr};' in main_cpp
)
assert "->set_allowed_origins(web_server_allowed_origins);" in main_cpp