#19388·esphome

cv.sensitive cannot mask int-valued secrets; openthread network_key goes plaintext when the legacy heuristic is removed in 2026.12.0

Author: pleasantoneCreated Sep 17, 2026Updated Sep 17, 2026

The problem

SensitiveValidator.__call__ only tags string results, so cv.sensitive(...) is a silent no-op on any field whose validator returns an int — it validates fine and masks nothing:

python
# esphome/config_validation.py:550-556
validated = self.inner(value)
# Tag string results so yaml_util.dump can mask them. Non-string
# results pass through unchanged; ...
if isinstance(validated, str) and not isinstance(validated, SensitiveStr):
    return SensitiveStr(validated)
return validated

Today that's masked by _redact_with_legacy_fallback in __main__.py, which works on the dumped text and so catches ints too. But that fallback is removed in 2026.12.0 (_LEGACY_REDACTION_REMOVAL), and the migration it advertises — "Mark this field's schema validator with cv.sensitive(...)" — cannot help these fields. They lose masking with no available replacement:

Field Validator
openthreadnetwork_key cv.hex_int (components/openthread/__init__.py:166) the Thread network key — joins the mesh
fingerprint_growpassword cv.uint32_t (components/fingerprint_grow/__init__.py:93) sensor access password
fingerprint_grownew_password cv.uint32_t (components/fingerprint_grow/__init__.py:94)

network_key is the one that worries me: it's the credential for joining the Thread network, and after December esphome config prints it in full — including in logs and pastes in this tracker.

Reproduce

Any openthread: config with a network_key: set, on 2026.10.0-dev:

esphome config thread.yaml | grep network_key
  network_key: \033[8m...\033[28m      # today: masked by the legacy fallback

Wrapping the schema in cv.sensitive(cv.hex_int) changes nothing about that output — the value is an int by the time SensitiveValidator sees it, so it is returned untagged and dump() has nothing to conceal. After 2026.12.0 removes the fallback, the value prints in the clear.

Possible directions

I don't have a view on which is right for the codebase:

  1. A SensitiveInt-style marker alongside SensitiveStr (int subclass, same representer hook).
  2. Tag at dump time from the schema rather than from the value, so masking doesn't depend on the validated type at all.
  3. Have these specific validators return strings, which seems worse — it changes codegen for network_key and the fingerprint passwords.

Which version of ESPHome has the issue?

2026.10.0-dev (also 2026.9.0)

What type of installation are you using?

pip

Component causing the issue

config_validation / openthread, fingerprint_grow

Additional information

Found while auditing every field the legacy heuristic matches for esphome/esphome#19387, which marks the three remaining string-valued secrets. These int-valued ones are the leftovers that annotation can't reach, so I split them out rather than shipping a no-op wrapper that would look like coverage.

Happy to implement whichever direction you prefer.