diff --git a/Cargo.lock b/Cargo.lock index 47957aa..3ee4cdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -634,7 +634,7 @@ dependencies = [ [[package]] name = "codesearch" -version = "1.0.71" +version = "1.0.72" dependencies = [ "anyhow", "arroy", diff --git a/Cargo.toml b/Cargo.toml index b2d2043..8867500 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "codesearch" -version = "1.0.71" +version = "1.0.72" edition = "2021" authors = ["codesearch contributors"] license = "Apache-2.0" diff --git a/src/db_discovery/repos.rs b/src/db_discovery/repos.rs index 522edc9..0a1ef69 100644 --- a/src/db_discovery/repos.rs +++ b/src/db_discovery/repos.rs @@ -235,7 +235,18 @@ pub fn config_dir() -> Result { pub fn config_path() -> Result { if let Ok(override_path) = std::env::var(crate::constants::REPOS_CONFIG_ENV) { - return Ok(PathBuf::from(override_path)); + let path = PathBuf::from(&override_path); + // Validate the env-var override points to a .json file to prevent + // path traversal / arbitrary file read (CodeQL: uncontrolled data in path). + let ext = path.extension().and_then(|e| e.to_str()).unwrap_or(""); + if ext.eq_ignore_ascii_case("json") { + return Ok(path); + } + anyhow::bail!( + "{} must point to a .json file, got: {}", + crate::constants::REPOS_CONFIG_ENV, + override_path + ); } Ok(config_dir()?.join(REPOS_CONFIG_FILE)) } diff --git a/src/serve/mod.rs b/src/serve/mod.rs index 8590a4a..d3dd6c4 100644 --- a/src/serve/mod.rs +++ b/src/serve/mod.rs @@ -195,6 +195,13 @@ impl ServeState { }, }; + // Canonicalize to resolve symlinks and prevent path traversal. + // CodeQL: path derives from env var (CODESEARCH_REPOS_CONFIG) — validate before use. + let config_path = match std::fs::canonicalize(&config_path) { + Ok(p) => p, + Err(_) => return Ok(()), // file doesn't exist yet — nothing to reload + }; + let mtime = std::fs::metadata(&config_path) .and_then(|m| m.modified()) .ok();