|
2 | 2 | // |
3 | 3 | // SPDX-License-Identifier: Apache-2.0 |
4 | 4 |
|
5 | | -use std::{path::PathBuf, sync::Arc}; |
| 5 | +use std::{ |
| 6 | + path::{Path, PathBuf}, |
| 7 | + sync::Arc, |
| 8 | +}; |
6 | 9 |
|
7 | 10 | use anyhow::{bail, Context, Result}; |
8 | 11 | use dstack_kms_rpc::{ |
@@ -142,20 +145,57 @@ impl RpcHandler { |
142 | 145 | self.state.config.image.cache_dir.join("images") |
143 | 146 | } |
144 | 147 |
|
145 | | - fn remove_cache(&self, parent_dir: &PathBuf, sub_dir: &str) -> Result<()> { |
| 148 | + fn remove_cache(&self, parent_dir: &Path, sub_dir: &str) -> Result<()> { |
146 | 149 | if sub_dir.is_empty() { |
147 | 150 | return Ok(()); |
148 | 151 | } |
149 | 152 | if sub_dir == "all" { |
150 | 153 | 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 | + } |
157 | 173 | } |
158 | 174 | } |
| 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 | + } |
159 | 199 | Ok(()) |
160 | 200 | } |
161 | 201 |
|
|
0 commit comments