# Security ## Supported version ClickAble does not yet have a production-approved release. This branch is a release candidate; it requires real-desktop acceptance on exact recorded Omarchy and Hyprland versions before production use or marketplace submission. Once released, security fixes will target the latest supported ClickAble version on the current supported Omarchy Quattro release. ## Report privately Please use [GitHub's private vulnerability reporting](https://github.com/josephbriones/omarchy-clickable/security/advisories/new). Do not open a public issue for a vulnerability that can cause unintended input, bypass a guard, expose another user's runtime socket, or survive plugin shutdown. ## Security model ClickAble runs as the signed-in user inside Omarchy's shell. It does not request administrator privileges, open a network listener, install a package, create a virtual input device, or rewrite Hyprland configuration. The QML service controls when clicking is allowed. A foreground Python helper owns the safety-critical countdown and compositor requests. The shell owns a launcher process; the launcher owns the worker; Linux parent-death signals and bounded shutdown keep that process tree tied to the shell even when Quickshell forcefully destroys its direct child. ## Guard and failure boundaries - ClickAble loads paused and does not persist an armed state. - Every command and event carries the current positive session epoch. Stale output cannot commit a click in a new session. - Commands and JSON events use exact schemas, unique keys, bounded lines, bounded rates, finite coordinate ranges, and legal state transitions. - Hyprland request strings are fixed constants. User-controlled text is never interpolated into a compositor request or shell command. - Persistent settings cross a separate, argument-vector-only helper. It walks real directories through `O_NOFOLLOW` directory descriptors, requires the final directory to be user-owned mode `0700`, and accepts only a user-owned regular file. File type, owner, and the 1 KiB limit are checked before a nonblocking bounded read; writes use a private same-directory temporary file, `fsync`, and descriptor-relative atomic replacement. - A symlink, FIFO, device, oversized file, ownership mismatch, non-private directory, or persistence-helper failure disables persistence for that shell load. ClickAble continues with safe in-memory defaults and never arms as a side effect of loading settings. - The runtime path and Hyprland instance signature are validated before opening the local Unix socket. - A click is requested only after a fresh pointer sample, a movement-tolerance check, an immediate best-effort lock-state preflight, and a final check for pending QML guard input. - Non-positional activity detected with a stationary pointer cancels a dwell. Pointer movement beyond the configured tolerance resets it; smaller involuntary motion may preserve progress, but commit remains blocked until the idle signal reports a quiet input interval. - Arming, unblocking, and every successful or uncertain click require the pointer to move before another dwell can begin. - If a dispatch times out, ClickAble treats its outcome as unknown and pauses. A later explicit Arm starts from a fresh baseline and requires movement. A partial double-click is a fatal session error. - Observed lock and relevant desktop-scene changes suspend the countdown and require fresh movement. Stale-epoch output is ignored; helper exit, malformed current-session protocol data, and output loss pause or fault the session rather than clicking. ## Compositor dependency ClickAble uses Hyprland's targetless `send_shortcut` mouse dispatcher so the existing pointer-focus surface receives the button action. This is version-sensitive compositor behavior. CI pins the shell contract; release acceptance must re-prove exactly-once delivery on the target Omarchy/Hyprland release across native Wayland, XWayland, and layer-shell surfaces. The lock query and click dispatch are separate AF_UNIX requests. If the session begins locking after `j/locked` reports `false` but before Hyprland processes the dispatch, ClickAble cannot atomically reject or withdraw that click. A double click uses one preflight for the dwell action, so the same transition can begin between its two complete click requests. The shell lock guard and final pending-input check reduce the exposure window; they do not remove this residual transition race. Lock handling is therefore a best-effort immediate preflight, not a hard security boundary. ClickAble does not silently fall back to kernel input injection or a native compositor extension if that contract changes. ## Known limits ClickAble cannot determine what an application will do with a valid click. It does not inspect target semantics, undo application actions, or claim suitability as the only control for a safety-critical operation. Omarchy's idle signal also does not identify the active input device; non-positional input coinciding with in-tolerance pointer jitter can be interpreted as tolerated motion, although commit still waits for a quiet interval. Drag and scrolling are excluded from the 0.1 candidate because they introduce held-button and cancellation states that require separate proof.