RUSTSEC-2026-0430 json
Path traversal in `rio-vt` Kitty graphics protocol handler allows out-of-bounds file delete (and read)
published 2026-11-01 7 d ago modified 2026-11-02 6 d ago
- cvss
- 4.7
- version 3.1, OSV
- cisa kev
- Not listed
- not in CISA's catalog
- epss
- —
- no score, EPSS covers CVEs only
- public exploits
- 0
- none known
Summary
The Kitty graphics protocol handler in `rio-vt` builds a filesystem path from an attacker-controlled APC escape sequence and, for the temp-file medium, only checks that the path *contains the substring* `"tty-graphics-protocol"` before it opens, reads, and `remove_file`s it. There is no `..` rejection and no canonicalization, so a path containing the marker substring plus `..` escapes the terminal's temp directory (path traversal, CWE-22/CWE-23).
Because a terminal renders whatever arrives on the PTY (ssh output, a `cat`'d file, CI logs), untrusted output can delete a file outside the temp area and cause its bytes to be read back in.
## Details
The sink is `create_graphic_data()` in `src/ansi/kitty_graphics_protocol.rs`, in the `TransmissionMedium::TempFile` branch, reachable through the public API `rio_vt::ansi::kitty_graphics_protocol::parse`. The path is the base64 APC payload of an `a=t,t=t` command, and the guard is:
```rust let path = Path::new(path_str); if !path.is_file() { return Err(GraphicError::FileNotFound); } // literal path, resolves `..` // reject /proc/ /sys/ /dev/ (lowercased substring) if cmd.medium == TransmissionMedium::TempFile && !path_str.contains("tty-graphics-protocol") { // SUBSTRING, not a component return Err(GraphicError::InvalidData); } // ... let mut file = File::open(path)...; // reads file bytes if cmd.medium == TransmissionMedium::TempFile { let _ = std::fs::remove_file(path); // deletes the file } ```
This affects any terminal emulator or application built on `rio-vt` (e.g. the Rio terminal) that renders attacker-influenced output: an out-of-bounds file is deleted, and its contents are read into the process.
## The fix
Fixed in `rio-vt` 0.5.29 (Rio 0.5.29) by [PR #1998](https://github.com/raphamorim/rio/pull/1998). For the temp-file medium the parent directory is resolved with `canonicalize` before any check, the resolved entry must lie inside a temp directory (`temp_dir()`, `/tmp` or `/dev/shm`) and contain the marker, and the delete goes through `unlinkat` on a handle to the validated directory, so neither `..` nor a swapped symlink can redirect it. Path checks for the file medium also run on the file actually opened.
Patch / fix
CVSS scores
| assigned by | version | score | vector |
|---|---|---|---|
| OSV | 3.1 | 4.7 | CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N |
Affected packages
| ecosystem | package | affected | fixed in |
|---|---|---|---|
| crates.io | rio-vt | >=0.0.0-0, <0.5.29 |
0.5.29 |
References
Sources
Fencer Vulns keeps one entry per vulnerability: the CVE id wins, then GHSA, and records that alias each other are merged, except that two CVEs always keep separate entries. The same data is available as JSON.