From 5f9717e655ea2aa7c39d111a1adead7b57d083c6 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 21 Apr 2026 14:30:18 -0700 Subject: [PATCH 1/2] Clean `./` noise from upload_dir paths in logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `config_dir.join(&wavedash_config.upload_dir)` preserves both segments literally, so when the user's wavedash.toml has `upload_dir = "./..."` the resolved path rendered as `././...` in every error and log message. Add a small `clean_path` helper that collapses `CurDir` (`.`) and resolves `ParentDir` (`..`) where safe — no filesystem hit, no new dep. Applied at the single site where the upload_dir path is constructed, so everything downstream (error messages, `ServeDir` logging) benefits automatically. Verified: `upload_dir = "./nonexistent"` now prints `Error: Upload directory does not exist or is not a directory: nonexistent` instead of `././nonexistent`. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/dev/mod.rs | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/dev/mod.rs b/src/dev/mod.rs index 47448d8..c27d0c8 100644 --- a/src/dev/mod.rs +++ b/src/dev/mod.rs @@ -1,6 +1,6 @@ use std::env; use std::net::{SocketAddr, TcpListener as StdTcpListener}; -use std::path::PathBuf; +use std::path::{Component, Path, PathBuf}; use anyhow::{Context, Result}; use axum::{ @@ -106,7 +106,7 @@ pub async fn handle_dev(config_path: Option, verbose: bool, no_open: bo let wavedash_config = WavedashConfig::load(&config_path)?; let config_dir = config_parent_dir(&config_path)?; - let upload_dir = config_dir.join(&wavedash_config.upload_dir); + let upload_dir = clean_path(&config_dir.join(&wavedash_config.upload_dir)); if !upload_dir.exists() || !upload_dir.is_dir() { anyhow::bail!( @@ -331,3 +331,28 @@ async fn shutdown_signal() { println!("\nReceived Ctrl+C, shutting down dev server..."); } } + +/// Collapse `.` segments and resolve `..` without touching the filesystem, so +/// paths shown in logs and errors don't end up with leading `./` noise when +/// `wavedash.toml`'s upload_dir already starts with `./`. +fn clean_path(p: &Path) -> PathBuf { + let mut out = PathBuf::new(); + for comp in p.components() { + match comp { + Component::CurDir => {} + Component::ParentDir => { + // Only pop a regular segment; otherwise preserve the `..`. + if matches!(out.components().next_back(), Some(Component::Normal(_))) { + out.pop(); + } else { + out.push(".."); + } + } + other => out.push(other), + } + } + if out.as_os_str().is_empty() { + out.push("."); + } + out +} From b7a0e7269b55fa9750cfb5a91ec44f80a3d519bb Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 21 Apr 2026 14:33:56 -0700 Subject: [PATCH 2/2] Simplify clean_path and move to shared util module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on review of the original commit: - Simplify `clean_path` — drop the speculative `..`-resolution branch and narration comment. The helper is display-only; `..` handling was dead code for the paths we actually feed it (config_dir + wavedash.toml upload_dir). The remaining implementation is a one-line components filter. - Move to `src/util.rs` so `builds.rs` can reuse it — it had the exact same `config_dir.join(upload_dir)` pattern without any cleanup. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/builds.rs | 2 +- src/dev/mod.rs | 28 ++-------------------------- src/main.rs | 1 + src/util.rs | 19 +++++++++++++++++++ 4 files changed, 23 insertions(+), 27 deletions(-) create mode 100644 src/util.rs diff --git a/src/builds.rs b/src/builds.rs index 9addf1c..479e2a8 100644 --- a/src/builds.rs +++ b/src/builds.rs @@ -138,7 +138,7 @@ pub async fn handle_build_push(config_path: PathBuf, verbose: bool, message: Opt let config_dir = config_path .parent() .ok_or_else(|| anyhow::anyhow!("Config file has no parent directory"))?; - let upload_dir = config_dir.join(&wavedash_config.upload_dir); + let upload_dir = crate::util::clean_path(&config_dir.join(&wavedash_config.upload_dir)); // Verify source directory exists if !upload_dir.exists() { diff --git a/src/dev/mod.rs b/src/dev/mod.rs index c27d0c8..f68139a 100644 --- a/src/dev/mod.rs +++ b/src/dev/mod.rs @@ -1,6 +1,6 @@ use std::env; use std::net::{SocketAddr, TcpListener as StdTcpListener}; -use std::path::{Component, Path, PathBuf}; +use std::path::PathBuf; use anyhow::{Context, Result}; use axum::{ @@ -23,6 +23,7 @@ use url::Url; use crate::auth::AuthManager; use crate::config::{self, EngineKind, WavedashConfig}; use crate::file_staging::FileStaging; +use crate::util::clean_path; mod cert; mod entrypoint; @@ -331,28 +332,3 @@ async fn shutdown_signal() { println!("\nReceived Ctrl+C, shutting down dev server..."); } } - -/// Collapse `.` segments and resolve `..` without touching the filesystem, so -/// paths shown in logs and errors don't end up with leading `./` noise when -/// `wavedash.toml`'s upload_dir already starts with `./`. -fn clean_path(p: &Path) -> PathBuf { - let mut out = PathBuf::new(); - for comp in p.components() { - match comp { - Component::CurDir => {} - Component::ParentDir => { - // Only pop a regular segment; otherwise preserve the `..`. - if matches!(out.components().next_back(), Some(Component::Normal(_))) { - out.pop(); - } else { - out.push(".."); - } - } - other => out.push(other), - } - } - if out.as_os_str().is_empty() { - out.push("."); - } - out -} diff --git a/src/main.rs b/src/main.rs index 7de2303..39d1082 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod dev; mod file_staging; mod init; mod updater; +mod util; use anyhow::Result; use auth::{login_with_browser, AuthManager, AuthSource}; diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..264f1ee --- /dev/null +++ b/src/util.rs @@ -0,0 +1,19 @@ +use std::path::{Component, Path, PathBuf}; + +/// Collapse `.` segments so paths rendered in logs and errors don't end up +/// with leading `./` noise when `wavedash.toml`'s `upload_dir` already starts +/// with `./`. Display-only — does not touch the filesystem and does not +/// resolve `..` (paths we consume come from `config_dir.join(upload_dir)`, so +/// the `..` case is vanishingly rare and correctly passed through to the +/// filesystem calls that follow). +pub fn clean_path(p: &Path) -> PathBuf { + let cleaned: PathBuf = p + .components() + .filter(|c| !matches!(c, Component::CurDir)) + .collect(); + if cleaned.as_os_str().is_empty() { + PathBuf::from(".") + } else { + cleaned + } +}