Repository navigation
feat!: auto-discovered devices start disabled (opt-in) - #25
Merged
Merged
Conversation
Since 2.1.0 an empty enabled_devices meant "enable all", so every auto-discovered keyboard-like device was enabled and publishing keys by default. On a host with a local keyboard that is keylogging to the broker out of the box. Found by an adversarial security review. Only devices listed in devices or enabled_devices now start enabled; everything else, including every auto-discovered device, starts disabled and must be opted in via the HA switch or the config. State file gets a schema version (2). A 2.0/2.1 file was seeded "on" for all devices under the old default, so "on" can't be told apart from a real choice; on load, such a file is migrated: "on" is kept only for devices matching devices/enabled_devices, everything else is disabled, one WARNING names what was disabled and how to re-enable it, then the file is rewritten at the new schema. A version-2+ file is trusted as-is. Reader behaviour already gated publish/grab on the enabled flag and kept draining the fd while disabled, so a disabled device was already safe against a stale-buffer replay on enable; added scenario coverage for it at the gateway and real-kernel-device (uinput) tiers. Docs: README (config table, device selection, enable/grab/persistence, new "Upgrading to 3.0" section) and translations/en.yaml updated.
# Conflicts: # README.md
This was referenced Sep 28, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Security fix (adversarial review finding, opt-in chosen by the owner): since 2.1.0, an empty
enabled_devicesmeant "enable all", so every auto-discovered keyboard-like device was enabled and publishing its keys to MQTT by default. On any host with a local keyboard attached, that's keylogging to the broker out of the box, since auto-discovered devices are read but not grabbed.What changes for users
devicesorenabled_devices(by id, path, symlink or name) starts enabled, as before.State file migration
The state file gets a schema version (2). 2.0/2.1 state files were seeded
"enabled": truefor every device because of the old all-enabled default, so an old file can't tell a real user choice apart from that default. On loading an old or unversioned file:"enabled": trueis kept only for devices that still matchdevices/enabled_devices"enabled": falseWARNINGnames every device that was disabled and how to re-enable itA schema-2+ file is trusted as-is (no re-migration). Migration is unit-tested in
tests/test_state.py(unversioned file, explicitversion: 1, id/path/name matching, already-off devices, no-op when nothing needs disabling).Reader / grab behaviour
DeviceReaderalready gates both grab and event emission on theenabledflag, and keeps draining the device fd while disabled (only skips constructing/publishing the event) rather than pausing reads — so there's no stale-kernel-buffer replay when a device is enabled later. Added scenario coverage for this at the gateway level (tests/test_gateway.py) and against a real kernel device via uinput (tests/integration/test_uinput.py, CI-only tier).Docs
README: config table, "Device selection", "Enable, grab and persistence" (states the privacy rationale), and a new "Upgrading to 3.0" section.
translations/en.yamloption descriptions updated. NoDOCS.mdexists on master.Test plan
pytest -m "not broker and not uinput"— 212 passedruff check/ruff format --check/mypy src— cleantest_disabled_at_start_is_not_grabbednow seeds a pre-2 state file) — not run locally per instructions (keystrokes leak to the desktop); CI runs ittest_switch_roundtrip_and_events,test_persistence_roundtrip) already use a listed device so are unaffected by the default change; CI runs themhttps://claude.ai/code/session_01TJskTZQqRHiHQBc8kE5Lr2