# SPDX-License-Identifier: GPL-3.0-or-later """input_snooper — processes reading keyboard/HID event devices. Keyloggers commonly open ``/dev/input/event*`` or HID raw devices directly. Normal display servers, compositors, and input remappers can do this too, so known local input stack processes are configurable allow-list entries. """ from __future__ import annotations from collections.abc import Iterator from ..alert import Alert, Severity from ..config import Config from ..system import SystemState SID_INPUT_SNOOPER = 100032 _INPUT_PREFIXES = ( "/dev/input/event", "/dev/uinput", "/dev/hidraw", ) def _fd_targets(proc) -> dict[str, str]: targets = getattr(proc, "fd_targets", {}) return targets() if callable(targets) else targets def _is_input_device(target: str) -> bool: return target.startswith(_INPUT_PREFIXES) def detect(state: SystemState, cfg: Config) -> Iterator[Alert]: for proc in state.processes: comm = proc.comm or "?" if comm in cfg.input_snooper_allow_comms: continue for fd, target in _fd_targets(proc).items(): if not _is_input_device(target): continue yield Alert( severity=Severity.HIGH, signature="input_snooper", key=f"input:{proc.pid}:{target}", detail=( f"pid={proc.pid} comm={comm} fd={fd} has input device " f"open: {target}" ), pids=(proc.pid,), sid=SID_INPUT_SNOOPER, classtype="credential-keylogging", ) break