Skip to content

Commit 27ff829

Browse files
committed
fix: prevent path traversal in KMS remove_cache
1 parent cce5ff2 commit 27ff829

1 file changed

Lines changed: 48 additions & 8 deletions

File tree

kms/src/main_service.rs

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
//
33
// SPDX-License-Identifier: Apache-2.0
44

5-
use std::{path::PathBuf, sync::Arc};
5+
use std::{
6+
path::{Path, PathBuf},
7+
sync::Arc,
8+
};
69

710
use anyhow::{bail, Context, Result};
811
use dstack_kms_rpc::{
@@ -142,20 +145,57 @@ impl RpcHandler {
142145
self.state.config.image.cache_dir.join("images")
143146
}
144147

145-
fn remove_cache(&self, parent_dir: &PathBuf, sub_dir: &str) -> Result<()> {
148+
fn remove_cache(&self, parent_dir: &Path, sub_dir: &str) -> Result<()> {
146149
if sub_dir.is_empty() {
147150
return Ok(());
148151
}
149152
if sub_dir == "all" {
150153
fs::remove_dir_all(parent_dir)?;
151-
} else {
152-
let path = parent_dir.join(sub_dir);
153-
if path.is_dir() {
154-
fs::remove_dir_all(path)?;
155-
} else {
156-
fs::remove_file(path)?;
154+
155+
return Ok(());
156+
}
157+
158+
let sub_path = Path::new(sub_dir);
159+
if sub_path.is_absolute() {
160+
bail!("Invalid cache path");
161+
}
162+
163+
let mut cleaned = PathBuf::new();
164+
for component in sub_path.components() {
165+
use std::path::Component;
166+
167+
match component {
168+
Component::Normal(part) => cleaned.push(part),
169+
Component::CurDir => {}
170+
Component::ParentDir | Component::RootDir | Component::Prefix(_) => {
171+
bail!("Invalid cache path");
172+
}
157173
}
158174
}
175+
176+
if cleaned.as_os_str().is_empty() {
177+
// Only separators or current-dir components – nothing to do.
178+
return Ok(());
179+
}
180+
181+
let path = parent_dir.join(&cleaned);
182+
183+
let canonical_parent = parent_dir
184+
.canonicalize()
185+
.unwrap_or_else(|_| parent_dir.to_path_buf());
186+
let canonical = path
187+
.canonicalize()
188+
.unwrap_or_else(|_| canonical_parent.join(&cleaned));
189+
190+
if !canonical.starts_with(&canonical_parent) {
191+
bail!("Invalid cache path");
192+
}
193+
194+
if canonical.is_dir() {
195+
fs::remove_dir_all(canonical)?;
196+
} else {
197+
fs::remove_file(canonical)?;
198+
}
159199
Ok(())
160200
}
161201

0 commit comments

Comments
 (0)