diff --git a/esphome/build_gen/arduino8266.py b/esphome/build_gen/arduino8266.py index 86e4f70ad5..0dabab57c2 100644 --- a/esphome/build_gen/arduino8266.py +++ b/esphome/build_gen/arduino8266.py @@ -36,16 +36,17 @@ if TYPE_CHECKING: _LOGGER = logging.getLogger(__name__) # Values that land unquoted on generated command lines are shape-checked -# against these before use -_MMU_VALUE_RE = re.compile(r"(?:0[xX][0-9a-fA-F]+|\d+)[uUlL]*") -_MMU_HEX_VALUE_RE = re.compile(r"0[xX][0-9a-fA-F]+[uUlL]*") +# against these before use. re.ASCII: a Unicode digit or word character +# (Arabic-Indic numerals) would pass \d/\w and defeat the named error +_MMU_VALUE_RE = re.compile(r"(?:0[xX][0-9a-fA-F]+|\d+)[uUlL]*", re.ASCII) +_MMU_HEX_VALUE_RE = re.compile(r"0[xX][0-9a-fA-F]+[uUlL]*", re.ASCII) # Only these land in the preprocessed script's ``len =`` fields, which # build_surgery's segment parser reads back as hex; the other MMU_* macros # (MMU_EXTERNAL_HEAP=128) are consumed by mmu_iram.h and may be decimal _MMU_SEGMENT_SIZE_NAMES = ("MMU_IRAM_SIZE", "MMU_ICACHE_SIZE") -_BOARD_NAME_RE = re.compile(r"[\w.-]+") -_F_CPU_RE = re.compile(r"\d+L?") -_FLASH_LD_NAME_RE = re.compile(r"[\w.-]+\.ld") +_BOARD_NAME_RE = re.compile(r"[\w.-]+", re.ASCII) +_F_CPU_RE = re.compile(r"\d+L?", re.ASCII) +_FLASH_LD_NAME_RE = re.compile(r"[\w.-]+\.ld", re.ASCII) # Every supported board ships this clock; board_build.f_cpu overrides _DEFAULT_F_CPU = "80000000L" @@ -430,6 +431,9 @@ def _defines_flags( The returned tokens already carry shell-level escaping (the board defines embed ``\"``), so they must be emitted unquoted; wrapping them in ``_shell_token`` would deliver literal backslashes to gcc. + ``flash_mode`` also lands unquoted: callers pass it pre-validated + against ``BUILD_FLASH_MODES`` (cv.one_of at config time, the + ``_FLASH_MODES`` check at the emission half's read site). """ if not _BOARD_NAME_RE.fullmatch(board): # The name lands unquoted in two -D bodies; reject it by name diff --git a/tests/unit_tests/build_gen/test_arduino8266.py b/tests/unit_tests/build_gen/test_arduino8266.py index 361032dc4c..a394924892 100644 --- a/tests/unit_tests/build_gen/test_arduino8266.py +++ b/tests/unit_tests/build_gen/test_arduino8266.py @@ -998,10 +998,12 @@ def test_pio_option_blank_value_raises() -> None: arduino8266._pio_option("board_build.f_cpu", "80000000L") -def test_defines_flags_invalid_f_cpu_raises() -> None: +@pytest.mark.parametrize("value", ["160 MHz", "\u0661\u0666\u0660"]) +def test_defines_flags_invalid_f_cpu_raises(value: str) -> None: """A non-numeric board_build.f_cpu is rejected by name; it would land - unquoted on the compile line.""" - CORE.platformio_options = {"board_build.f_cpu": "160 MHz"} + unquoted on the compile line. Unicode digits count as non-numeric: the + shape checks are ASCII-only.""" + CORE.platformio_options = {"board_build.f_cpu": value} with pytest.raises(EsphomeError, match="Invalid board_build.f_cpu"): _defines_flags( _resolve(),