security: update callers for hardened credential API

- Handle Result from WebDavClient::new in CLI sync, core sync, and Tauri
- Unwrap Zeroizing<String> at Tauri serialization boundary
- Use .as_str() for basic_auth calls with Zeroizing<String> fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tristan Michael 2026-04-03 08:56:32 -07:00
parent 0c4073c998
commit be6b8d0d90
3 changed files with 25 additions and 5 deletions

View file

@ -99,6 +99,13 @@ fn repo_mut(state: &mut AppState) -> Result<&mut TaskRepository, String> {
state.repo.as_mut().ok_or_else(|| "Repository not initialized".to_string()) state.repo.as_mut().ok_or_else(|| "Repository not initialized".to_string())
} }
// ── Debug ───────────────────────────────────────────────────────────
#[tauri::command]
fn log_debug(msg: String) {
eprintln!("[frontend] {msg}");
}
// ── Config commands ────────────────────────────────────────────────── // ── Config commands ──────────────────────────────────────────────────
#[tauri::command] #[tauri::command]
@ -476,7 +483,9 @@ fn store_credentials(
#[tauri::command] #[tauri::command]
fn load_credentials(domain: String) -> Result<(String, String), String> { fn load_credentials(domain: String) -> Result<(String, String), String> {
webdav::load_credentials(&domain).map_err(|e| e.to_string()) webdav::load_credentials(&domain)
.map(|(u, p)| ((*u).clone(), (*p).clone()))
.map_err(|e| e.to_string())
} }
#[tauri::command] #[tauri::command]
@ -485,7 +494,8 @@ async fn test_webdav_connection(
username: String, username: String,
password: String, password: String,
) -> Result<(), String> { ) -> Result<(), String> {
let client = onyx_core::webdav::WebDavClient::new(&url, &username, &password); let client = onyx_core::webdav::WebDavClient::new(&url, &username, &password)
.map_err(|e| e.to_string())?;
client client
.test_connection() .test_connection()
.await .await
@ -507,6 +517,7 @@ async fn sync_workspace(
"pull" => SyncMode::Pull, "pull" => SyncMode::Pull,
_ => SyncMode::Full, _ => SyncMode::Full,
}; };
eprintln!("[sync] starting sync: workspace={workspace_name} path={workspace_path} url={webdav_url} mode={mode}");
let result = sync::sync_workspace( let result = sync::sync_workspace(
&PathBuf::from(&workspace_path), &PathBuf::from(&workspace_path),
&webdav_url, &webdav_url,
@ -516,15 +527,22 @@ async fn sync_workspace(
None, None,
) )
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| {
eprintln!("[sync] sync_workspace error: {e}");
e.to_string()
})?;
eprintln!("[sync] sync complete: uploaded={} downloaded={} errors={}", result.uploaded, result.downloaded, result.errors.len());
// Persist last_sync timestamp to config // Persist last_sync timestamp to config
{ {
eprintln!("[sync] acquiring state lock...");
let mut s = lock_state(&state)?; let mut s = lock_state(&state)?;
eprintln!("[sync] lock acquired, saving config...");
if let Some(ws) = s.config.workspaces.get_mut(&workspace_name) { if let Some(ws) = s.config.workspaces.get_mut(&workspace_name) {
ws.last_sync = Some(Utc::now()); ws.last_sync = Some(Utc::now());
} }
s.config.save_to_file(&s.config_path.clone()).map_err(|e| e.to_string())?; s.config.save_to_file(&s.config_path.clone()).map_err(|e| e.to_string())?;
eprintln!("[sync] config saved");
} }
Ok(result.into()) Ok(result.into())
@ -614,6 +632,7 @@ pub fn run() {
Ok(()) Ok(())
}) })
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
log_debug,
get_config, get_config,
save_config, save_config,
add_workspace, add_workspace,

View file

@ -39,7 +39,8 @@ pub fn setup(workspace_name: Option<String>) -> Result<()> {
output::info("Testing connection..."); output::info("Testing connection...");
let rt = tokio::runtime::Runtime::new().context("Failed to create async runtime")?; let rt = tokio::runtime::Runtime::new().context("Failed to create async runtime")?;
let client = WebDavClient::new(&url, &username, &password); let client = WebDavClient::new(&url, &username, &password)
.context("Invalid WebDAV URL")?;
match rt.block_on(client.test_connection()) { match rt.block_on(client.test_connection()) {
Ok(()) => { Ok(()) => {

View file

@ -510,7 +510,7 @@ pub async fn sync_workspace(
mode: SyncMode, mode: SyncMode,
on_progress: Option<ProgressCallback>, on_progress: Option<ProgressCallback>,
) -> Result<SyncResult> { ) -> Result<SyncResult> {
let client = WebDavClient::new(webdav_url, username, password); let client = WebDavClient::new(webdav_url, username, password)?;
let mut sync_state = SyncState::load(workspace_path); let mut sync_state = SyncState::load(workspace_path);
let queue = OfflineQueue::load(workspace_path); let queue = OfflineQueue::load(workspace_path);
let mut result = SyncResult::default(); let mut result = SyncResult::default();