[core] Suppress unactionable legacy-redaction warning for substitutions (#17242)

This commit is contained in:
Jonathan Swoboda
2026-06-27 18:05:00 -04:00
committed by GitHub
parent fd7fc6b8e8
commit 0fb100f2d1
2 changed files with 52 additions and 5 deletions
+22 -5
View File
@@ -1488,12 +1488,29 @@ _LEGACY_REDACTION_REMOVAL = "2026.12.0"
def _redact_with_legacy_fallback(output: str) -> str:
unmarked: set[str] = set()
# Track the top-level ``substitutions:`` block. Its keys are arbitrary
# user-chosen names with no schema validator, so the ``cv.sensitive(...)``
# migration named in the warning can't be applied to them. Their values are
# still redacted, but emitting the (unactionable) deprecation warning would
# only confuse users.
in_substitutions = False
def _replace(m: re.Match[str]) -> str:
unmarked.add(m.group("key"))
return f"{m.group('key')}: \\033[8m{m.group('val')}\\033[28m"
output = _LEGACY_REDACTION_RE.sub(_replace, output)
lines = output.split("\n")
for i, line in enumerate(lines):
# A non-indented, non-blank line is a top-level key that opens or
# closes the substitutions block.
if line and not line[0].isspace():
in_substitutions = line.startswith(f"{CONF_SUBSTITUTIONS}:")
m = _LEGACY_REDACTION_RE.search(line)
if m is None:
continue
if not in_substitutions:
unmarked.add(m.group("key"))
lines[i] = (
f"{line[: m.start()]}{m.group('key')}: "
f"\\033[8m{m.group('val')}\\033[28m{line[m.end() :]}"
)
output = "\n".join(lines)
for key in sorted(unmarked):
_LOGGER.warning(
"Field '%s' is being redacted by a legacy substring heuristic. "
+30
View File
@@ -442,6 +442,36 @@ def test_redact_with_legacy_fallback__does_not_match_fragment_as_suffix(
assert not any("legacy substring" in rec.message for rec in caplog.records)
def test_redact_with_legacy_fallback__substitutions_redacted_without_warning(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Substitution keys have no schema validator, so their values are still
redacted but the unactionable cv.sensitive migration warning is suppressed
(see issue #17225)."""
text = "substitutions:\n ota_password: apolloautomation\nesphome:\n name: x\n"
with caplog.at_level(logging.WARNING, logger="esphome.__main__"):
out = _redact_with_legacy_fallback(text)
assert "ota_password: \\033[8mapolloautomation\\033[28m" in out
assert not any("legacy substring" in rec.message for rec in caplog.records)
def test_redact_with_legacy_fallback__warns_after_substitutions_block(
caplog: pytest.LogCaptureFixture,
) -> None:
"""The suppression ends at the next top-level key; a sensitive-shaped field
in a later block (a real schema field) still warns, while the substitution
above it does not."""
text = (
"substitutions:\n ota_password: apolloautomation\nwifi:\n password: hunter2\n"
)
with caplog.at_level(logging.WARNING, logger="esphome.__main__"):
out = _redact_with_legacy_fallback(text)
assert "ota_password: \\033[8mapolloautomation\\033[28m" in out
assert "password: \\033[8mhunter2\\033[28m" in out
assert any("'password'" in rec.message for rec in caplog.records)
assert not any("ota_password" in rec.message for rec in caplog.records)
def test_command_config__invokes_legacy_fallback_when_redacting(
tmp_path: Path, capfd: CaptureFixture[str]
) -> None: