Files
ltk/SECURITY.md

128 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Security policy
`ltk` is a UI toolkit for Wayland clients. It runs inside the trust boundary
of the application that links it: the toolkit decodes images, parses theme
JSON, lays out widget trees and drives a GLES / software renderer. A bug in
any of those paths can crash the host application or, in worst-case, expose
information that a co-tenant Wayland client should not have.
This document describes how to report a vulnerability and what we consider in
or out of scope.
## Supported versions
`ltk` is in active development. Security fixes land on the latest released
minor version on crates.io and on the `master` branch in this repository.
Older minor versions are not patched.
## Reporting a vulnerability
Email **info@liberux.net** with:
- a description of the issue,
- the smallest reproducer you can share (Rust source preferred),
- the `ltk` version and Rust toolchain you used,
- whether the issue is exploitable today or requires a follow-up condition.
Please do **not** open a public GitHub issue, pull request, or discussion for
unpatched vulnerabilities. We aim to acknowledge reports within 5 working
days and ship a fix within 30 days for high-severity issues. If you do not
hear back, escalate by sending a follow-up email.
You may use PGP if you prefer — request the current public key in your first
message and we will reply with it before sharing any sensitive details.
## In scope
- memory-safety bugs reachable from any public API on the `ltk` crate root
or its `core::UiSurface` runtime-free surface,
- panics or undefined behaviour triggered by a theme JSON document, a font
file, or an image buffer that an application would reasonably accept from
user content (e.g. a wallpaper picker, a notification icon),
- denial-of-service through unbounded memory growth in caches or layout
recursion that the toolkit performs on behalf of the application,
- credential leaks in widgets that expose a `secure` mode (currently
`TextEdit`) — for example, plaintext password residue in heap dumps after
the widget is dropped.
## Out of scope
- bugs that require a malicious Wayland compositor: ltk treats the
compositor as part of the trusted computing base and does not defend
against compositor-level spoofing, layer-shell privilege misuse, or
protocol-level attacks,
- bugs that require an attacker to set environment variables on the user's
process (`LTK_THEMES_DIR`, `LTK_FORCE_SOFTWARE`, `XDG_DATA_HOME`):
ltk is not designed to run inside setuid binaries or anywhere the env is
not under the user's control,
- bugs in third-party dependencies (`tiny-skia`, `fontdue`, `image`,
`smithay-client-toolkit`, …): report those upstream. We will track the
fix and bump the dependency in a release note.
## Hardening features
- `TextEdit::secure( true )` zeroizes the underlying string buffer on drop
via `secure_mem::secure_zero` (volatile writes + a `compiler_fence` so
the optimiser cannot elide the wipe). The guarantee covers two copies
the runtime owns: the `TextEdit` itself and the per-frame
`WidgetHandlers::TextEdit` snapshot the layout pass produces for input
dispatch — both implement `Drop` and run the wipe before the
allocation returns to the allocator. The next `view()` rebuild
replaces the previous frame's snapshot, so the typical lifecycle is
"one frame on the heap, then overwritten on drop". Single-line and
multiline are mutually exclusive with secure (passwords have no line
breaks); the text-input-v3 IME path is also skipped in secure mode so
preedit / commit strings never reach the compositor's IME stack.
### What `TextEdit::secure` does **not** cover
The wipe only reaches buffers ltk allocated. Callers retain
responsibility for everything outside the widget tree:
- **Application-owned `String`s.** `text_edit( "Pwd", &self.password )`
only borrows `self.password`; the credential lives on the consumer's
struct. Wipe it explicitly (via `secure_mem::secure_zero` on
`password.as_bytes_mut()`) once the auth handshake completes, or put
the credential in a wrapper type with a `Drop` impl that does the
wipe.
- **`on_change` callback copies.** Each keystroke fires a closure with
a fresh `String` clone of the current value. If the closure stores
or forwards that value (e.g. to a worker thread for PAM), each
stored copy is the consumer's responsibility — ltk no longer owns
it past the closure call.
- **OS-level disclosure surfaces.** Swap, hibernation images, and
core dumps are outside any user-space wipe's reach. For threat
models that require resistance to these, link an `mlock`-aware
allocator, restrict the process's `PR_SET_DUMPABLE` to `0`, and
consider running on a no-swap mount. ltk itself does not call
`mlock` because the buffers it owns are short-lived (single frame)
and `mlock`-ing them per-frame would dominate the cost without
materially improving the threat model.
- **Compositor-side state.** The compositor sees pointer / keyboard
events for the surface but never the rendered glyphs (since
`secure` paints bullets). Custom IMEs that the user has installed
can still observe keystrokes — that is an OS-level concern outside
ltk's trust boundary.
Login / lock-screen consumers should pair `TextEdit::secure` with an
explicit wipe of their own state on the success path. The
[`docs/cookbook.md`](./docs/cookbook.md) "Password field with PAM
submit" recipe shows the canonical shape: clear `self.password` after
spawning the worker thread so the in-flight clone in the worker is
the only remaining copy, and let the worker's drop-on-completion run
its own wipe.
- `draw_image_data` (both backends) refuses to upload a buffer whose length
does not match `width × height × 4`. The mismatch path logs once and
draws nothing.
- Glyph caches and gradient stops have soft caps to bound memory growth
under unusual content. Exceeding the cap drains older entries (glyphs)
or truncates the input (gradient stops) with a stderr warning.
## Supply chain
ltk targets the Rust toolchain shipped with Debian stable (currently 1.85).
Direct dependencies are pinned at the minor-version level except `image`,
which is pinned to an exact version because the crate's MSRV and feature
surface change between patch releases. We monitor RustSec advisories
manually; running `cargo audit` in your own CI is recommended.