mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 11:07:04 +00:00
fix(security): validate book id paths
load/save/delete_book built paths from the raw id, and ids round-trip from file JSON, so a crafted id could escape the library directory. Route all three through a book_path helper that rejects anything but [A-Za-z0-9_-]. Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
This commit is contained in:
1 parent
2f98f5ec1c
commit
3572c7a6b9
1 file changed
+10
-3
@@ -29,6 +29,13 @@ fn library_dir(app: &tauri::AppHandle) -> Result<PathBuf, String> {
|
|||||||
Ok(dir)
|
Ok(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn book_path(app: &tauri::AppHandle, id: &str) -> Result<PathBuf, String> {
|
||||||
|
if id.is_empty() || !id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') {
|
||||||
|
return Err("invalid book id".to_string());
|
||||||
|
}
|
||||||
|
Ok(library_dir(app)?.join(format!("{id}.margin")))
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn list_books(app: tauri::AppHandle) -> Result<Vec<BookSummary>, String> {
|
pub fn list_books(app: tauri::AppHandle) -> Result<Vec<BookSummary>, String> {
|
||||||
let dir = library_dir(&app)?;
|
let dir = library_dir(&app)?;
|
||||||
@@ -84,19 +91,19 @@ pub fn list_books(app: tauri::AppHandle) -> Result<Vec<BookSummary>, String> {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn load_book(app: tauri::AppHandle, id: String) -> Result<String, String> {
|
pub fn load_book(app: tauri::AppHandle, id: String) -> Result<String, String> {
|
||||||
let path = library_dir(&app)?.join(format!("{id}.margin"));
|
let path = book_path(&app, &id)?;
|
||||||
fs::read_to_string(&path).map_err(|e| e.to_string())
|
fs::read_to_string(&path).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn save_book(app: tauri::AppHandle, id: String, contents: String) -> Result<(), String> {
|
pub fn save_book(app: tauri::AppHandle, id: String, contents: String) -> Result<(), String> {
|
||||||
let path = library_dir(&app)?.join(format!("{id}.margin"));
|
let path = book_path(&app, &id)?;
|
||||||
crate::project::atomic_write(&path, contents.as_bytes(), true)
|
crate::project::atomic_write(&path, contents.as_bytes(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn delete_book(app: tauri::AppHandle, id: String) -> Result<(), String> {
|
pub fn delete_book(app: tauri::AppHandle, id: String) -> Result<(), String> {
|
||||||
let path = library_dir(&app)?.join(format!("{id}.margin"));
|
let path = book_path(&app, &id)?;
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
fs::remove_file(&path).map_err(|e| e.to_string())?;
|
fs::remove_file(&path).map_err(|e| e.to_string())?;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user