From 27385cee325a9e758c9b8eed22ab3cf70a8b4d64 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 26 May 2026 22:06:25 -0500 Subject: [PATCH] [core] Tighten legacy redaction regex to avoid mid-word matches The trailing \w* after the fragment over-matched fields like key_value_pair: which the prior regex did not catch. Drop it so the fragment must end the captured field name, preserving the previous matching scope while still capturing the full field name (via the leading \w*) for the warning message. --- esphome/__main__.py | 7 +++++-- tests/unit_tests/test_main.py | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index d62fa1bafd..7049f8025e 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -1427,9 +1427,12 @@ def command_config(args: ArgsProtocol, config: ConfigType) -> int | None: # in 2026.12.0 once canonical sensitive fields are migrated. # # The negative lookahead ``(?!\\033\[8m)`` skips values already wrapped by the -# SensitiveStr representer, so explicit tagging silences the warning. +# SensitiveStr representer, so explicit tagging silences the warning. No +# trailing ``\w*`` after the fragment so the fragment must end the field +# name (preserves the prior regex's matching scope while letting the +# leading ``\w*`` capture the full field name for the warning). _LEGACY_REDACTION_RE = re.compile( - r"(?P\w*(?:password|key|psk|ssid)\w*)\: (?!\\033\[8m)(?P.+)" + r"(?P\w*(?:password|key|psk|ssid))\: (?!\\033\[8m)(?P.+)" ) _LEGACY_REDACTION_REMOVAL = "2026.12.0" diff --git a/tests/unit_tests/test_main.py b/tests/unit_tests/test_main.py index a103f14da5..ef8d922a8e 100644 --- a/tests/unit_tests/test_main.py +++ b/tests/unit_tests/test_main.py @@ -387,6 +387,18 @@ def test_redact_with_legacy_fallback__deduplicates_warnings( assert len(password_warnings) == 1 +def test_redact_with_legacy_fallback__does_not_match_fragment_in_middle( + caplog: pytest.LogCaptureFixture, +) -> None: + """Fragment must end the field name; embedded matches like + ``key_value_pair`` are unrelated to a sensitive key and must not be + redacted (matching the prior regex's scope).""" + with caplog.at_level(logging.WARNING, logger="esphome.__main__"): + out = _redact_with_legacy_fallback("key_value_pair: abc\n") + assert "\\033[8m" not in out + assert not any("legacy substring" in rec.message for rec in caplog.records) + + def test_choose_upload_log_host_with_string_default() -> None: """Test with a single string default device.""" setup_core()