rustsec-2026-0430
CVSS 4.7 osv_rustsecThe 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.
- Published
- unknown
- Last Modified
- unknown
CVSS details not available.
No product information available.
No linked vulnerabilities found.
{
"affected": [
{
"database_specific": {
"categories": [
"file-disclosure"
],
"cvss": "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N",
"informational": null,
"source": "https://github.com/rustsec/advisory-db/blob/osv/crates/RUSTSEC-2026-0430.json"
},
"ecosystem_specific": {
"affected_functions": null,
"affects": {
"arch": [],
"functions": [
"rio_vt::ansi::kitty_graphics_protocol::parse"
],
"os": []
}
},
"package": {
"ecosystem": "crates.io",
"name": "rio-vt",
"purl": "pkg:cargo/rio-vt"
},
"ranges": [
{
"events": [
{
"introduced": "0.0.0-0"
},
{
"fixed": "0.5.29"
}
],
"type": "SEMVER"
}
]
}
],
"database_specific": {
"license": "CC0-1.0"
},
"details": "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).\n\nBecause 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.\n\n## Details\n\nThe 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:\n\n```rust\nlet path = Path::new(path_str);\nif !path.is_file() { return Err(GraphicError::FileNotFound); } // literal path, resolves `..`\n// reject /proc/ /sys/ /dev/ (lowercased substring)\nif cmd.medium == TransmissionMedium::TempFile\n && !path_str.contains(\"tty-graphics-protocol\") { // SUBSTRING, not a component\n return Err(GraphicError::InvalidData);\n}\n// ...\nlet mut file = File::open(path)...; // reads file bytes\nif cmd.medium == TransmissionMedium::TempFile {\n let _ = std::fs::remove_file(path); // deletes the file\n}\n```\n\nThis 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.\n\n## The fix\n\nFixed 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.",
"id": "RUSTSEC-2026-0430",
"modified": "2026-11-02T11:00:02.247101984Z",
"published": "2026-11-01T12:00:00Z",
"references": [
{
"type": "PACKAGE",
"url": "https://crates.io/crates/rio-vt"
},
{
"type": "ADVISORY",
"url": "https://rustsec.org/advisories/RUSTSEC-2026-0430.html"
},
{
"type": "ADVISORY",
"url": "https://github.com/raphamorim/rio/security/advisories/GHSA-m926-9wrf-jr8w"
},
{
"type": "WEB",
"url": "https://github.com/raphamorim/rio/pull/1998"
}
],
"schema_version": "1.9.2",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Path traversal in `rio-vt` Kitty graphics protocol handler allows out-of-bounds file delete (and read)"
}