fix(paths): recognize long Windows rehearsal paths inside server

This commit is contained in:
Дмитрий committed 2026-10-13 07:18:03 +05:00
1 parent be9641254a
commit 4efc02e768
2 files changed
+49

No files matched your search

@@ -0,0 +1,7 @@
---
area: manager
type: fixed
audience: admins
title: Server updates accept long Windows paths inside the private test server
---
Long SQL file paths no longer fail the folder safety check during update rehearsals. Files outside the server folder remain blocked.
+42
View File
@@ -28,6 +28,19 @@ pub fn normalize_lexical(path: &Path) -> PathBuf {
fn key(path: &Path) -> Vec<String> {
path.components()
.map(|c| {
#[cfg(windows)]
if let Component::Prefix(prefix) = c {
use std::path::Prefix;
match prefix.kind() {
Prefix::Disk(drive) | Prefix::VerbatimDisk(drive) => {
return format!("{}:", char::from(drive).to_ascii_lowercase());
}
Prefix::UNC(server, share) | Prefix::VerbatimUNC(server, share) => {
return format!("\\\\{}\\{}", server.to_string_lossy(), share.to_string_lossy()).to_lowercase();
}
_ => {}
}
}
let c = c.as_os_str().to_string_lossy();
if cfg!(windows) { c.to_lowercase() } else { c.into_owned() }
})
@@ -262,6 +275,35 @@ mod tests {
}
}
#[cfg(windows)]
#[test]
fn containment_matches_verbatim_drive_and_unc_prefixes() {
assert!(starts_with_ci(Path::new(r"\\?\C:\server\Extras\file.sql"), Path::new(r"C:\server")));
assert!(starts_with_ci(Path::new(r"C:\server\Extras\file.sql"), Path::new(r"\\?\c:\server")));
assert!(starts_with_ci(Path::new(r"\\?\UNC\host\share\server\file.sql"), Path::new(r"\\host\share\server")));
assert!(!starts_with_ci(Path::new(r"\\?\D:\server\file.sql"), Path::new(r"C:\server")));
assert!(!starts_with_ci(Path::new(r"\\?\C:\server-other\file.sql"), Path::new(r"C:\server")));
assert!(!starts_with_ci(Path::new(r"\\?\UNC\host\other\server\file.sql"), Path::new(r"\\host\share\server")));
}
#[cfg(windows)]
#[test]
fn ensure_within_accepts_long_rehearsal_sql_path() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path().join("server");
fs::create_dir_all(&root).unwrap();
let mut candidate = root.clone();
for _ in 0..5 {
candidate.push("long-private-rehearsal-directory-name-20261013");
}
fs::create_dir_all(&candidate).unwrap();
candidate.push("2026_06_13_00_ai_playerbot_missing_group_buff_reagent.sql");
fs::write(&candidate, b"SELECT 1;").unwrap();
assert!(candidate.as_os_str().len() > 260);
assert!(ensure_within(&root, &candidate).is_ok());
assert!(ensure_within(&root, &std::fs::canonicalize(candidate).unwrap()).is_ok());
}
#[test]
fn ensure_within_blocks_junction_escape() {
if !cfg!(windows) {