diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 419bf65..c9cae75 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -23,14 +23,13 @@ "gamepad:default", "drpc:default", "updater:default", - { - "identifier": "opener:allow-open-path", - "allow": [ - { - "path": "**" - } - ] - }, + // security: removed opener:allow-open-path with path: "**" - that + // scoped the webview to launch ANY local path. combined with + // write_binary_file + list_directory (now disabled, see file_dialogs.rs + // and plugins.rs) a compromised webview had a full RCE primitive. + // (LCEL-01) + // TODO: scope to a specific games directory (e.g. the instance dir) + // once we know what paths the launcher actually needs to open. { "identifier": "opener:allow-reveal-item-in-dir", "allow": [ diff --git a/src-tauri/src/commands/file_dialogs.rs b/src-tauri/src/commands/file_dialogs.rs index cff7e80..16003c6 100644 --- a/src-tauri/src/commands/file_dialogs.rs +++ b/src-tauri/src/commands/file_dialogs.rs @@ -40,12 +40,20 @@ pub fn save_file_dialog(title: String, filename: String, filters: Vec) - } } +// security: write_binary_file / read_binary_file used to accept arbitrary +// paths with no validation, no dialog, no scope. combined with the +// opener:allow-open-path: ** capability, a compromised webview could +// write+launch any file anywhere. (LCEL-01) +// TODO: scope these to a launcher-specific subdirectory with canonical +// containment checks, or remove from the IPC surface entirely and route +// all binary file ops through rfd-gated dialogs. +// for now, refuse all paths. callers must use pick_file / save_file_dialog. #[tauri::command] -pub fn write_binary_file(path: String, data: Vec) -> Result<(), String> { - fs::write(path, data).map_err(|e| e.to_string()) +pub fn write_binary_file(_path: String, _data: Vec) -> Result<(), String> { + Err("write_binary_file disabled: use save_file_dialog instead".into()) } #[tauri::command] -pub fn read_binary_file(path: String) -> Result, String> { - fs::read(path).map_err(|e| e.to_string()) +pub fn read_binary_file(_path: String) -> Result, String> { + Err("read_binary_file disabled: use pick_file instead".into()) } diff --git a/src-tauri/src/commands/plugins.rs b/src-tauri/src/commands/plugins.rs index 9fdf26a..91fa33c 100644 --- a/src-tauri/src/commands/plugins.rs +++ b/src-tauri/src/commands/plugins.rs @@ -15,16 +15,13 @@ pub fn get_plugins_dir(app: tauri::AppHandle) -> Result { #[tauri::command] pub fn list_directory(path: String) -> Result, String> { - let entries = fs::read_dir(&path).map_err(|e| e.to_string())?; - let mut results = Vec::new(); - for entry in entries { - let entry = entry.map_err(|e| e.to_string())?; - results.push(DirEntry { - name: entry.file_name().to_string_lossy().to_string(), - is_dir: entry.file_type().map(|t| t.is_dir()).unwrap_or(false), - }); - } - Ok(results) + // security: list_directory used to accept arbitrary paths with no + // validation. combined with write_binary_file + opener:allow-open-path: ** + // a compromised webview could enumerate startup folders, drop a binary, + // and launch it. (LCEL-01) + // TODO: scope to the launcher's plugins dir or remove from IPC entirely. + // for now, refuse all paths. + Err("list_directory disabled: use get_plugins_dir instead".into()) } #[tauri::command]