T09 · Insecure Skill Coding Practices
Warning
- Location
- references/templates/storage-mod.md:26
- Finding
- Unvalidated Storage Filename Allows Path Traversal Outside the Application Data Directory<![CDATA[ ## Vulnerability Details **File Location**: `references/templates/storage-mod.md:26-27` **Vulnerability Type**: Path traversal and unrestricted filesystem access **Risk Level**: Medium ### Vulnerable Code ```rust pub fn get_storage_path(app: &AppHandle, filename: &str) -> Result<PathBuf, String> { Ok(get_app_data_dir(app)?.join(filename)) } ``` ### Technical Analysis The storage helper joins an unrestricted `filename` value directly onto the application data directory. It does not reject: - Absolute paths, which may replace the base path when passed to `PathBuf::join`. - Parent-directory components such as `../`. - Root or platform-specific path prefixes. - Paths that resolve through symbolic links outside the intended directory. The generated `load_json` and `save_json` functions use this helper for filesystem reads and writes. Although the current template primarily expects fixed internal filenames, the helper is public and is explicitly prescribed as the shared storage primitive for generated modules. If a generated command passes user- or WebView-controlled input into it, the intended app-data-directory boundary can be bypassed. This conflicts with the Skill's stated requirement that raw storage operations remain restricted to paths derived safely from `app_data_dir`. ### Attack Path 1. A generated Tauri command or module accepts a filename from the frontend. 2. A malicious or compromised WebView supplies a value such as `../../target-file` or an absolute path. 3. The command passes the value to `load_json`, `save_json`, or `get_storage_path`. 4. `get_storage_path` joins the unvalidated value with the application data directory. 5. The resulting filesystem operation reads or overwrites a file outside the intended storage directory, subject to the desktop application's operating-system permissions. No current command accepting such a filename was identified in the audited package, so exploitation depends on generated or subsequently ...[truncated 661 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Restrict storage names to a single normal path component: ```rust use std::path::{Component, Path, PathBuf}; pub fn get_storage_path(app: &AppHandle, filename: &str) -> Result<PathBuf, String> { let name = Path::new(filename); let mut components = name.components(); match (components.next(), components.next()) { (Some(Component::Normal(_)), None) => {} _ => return Err("Invalid storage filename".into()), } Ok(get_app_data_dir(app)?.join(name)) } ``` 2. If nested paths are required, explicitly reject `ParentDir`, `RootDir`, and platform prefix components. 3. Canonicalize the base directory and the destination or its existing parent, then verify that the destination remains beneath the canonical base. 4. Account for symbolic-link traversal before performing writes. 5. Prefer fixed internal filenames or a closed enum rather than accepting arbitrary strings. 6. Add tests covering `../file`, absolute paths, nested traversal, Windows path prefixes, and symbolic links. 7. Do not expose this helper directly to frontend-controlled command arguments. ]]>
