fix(sync): S-03 — symlink detection and cycle protection
This commit is contained in:
@@ -562,3 +562,84 @@ fn test_s02_platform_independent_path_construction_and_traversal_rejection() {
|
||||
// Aufräumen
|
||||
let _ = fs::remove_dir_all(&temp_root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_s03_symlink_skipping_and_cycle_protection() {
|
||||
let temp_root =
|
||||
std::env::temp_dir().join(format!("sanctum_s03_test_{}", rand::random::<u64>()));
|
||||
let container_path = temp_root.join("test.sanctum");
|
||||
let source_dir = temp_root.join("source");
|
||||
let sub_dir = source_dir.join("sub");
|
||||
|
||||
fs::create_dir_all(&sub_dir).unwrap();
|
||||
fs::write(source_dir.join("regular.txt"), b"Regular content").unwrap();
|
||||
|
||||
// Erzeuge eine rekursive Verknüpfung (Junction unter Windows / Symlink unter Unix)
|
||||
let loop_path = sub_dir.join("cycle");
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let status = std::process::Command::new("powershell")
|
||||
.args([
|
||||
"-Command",
|
||||
&format!(
|
||||
"New-Item -ItemType Junction -Path '{}' -Target '{}'",
|
||||
loop_path.display(),
|
||||
source_dir.display()
|
||||
),
|
||||
])
|
||||
.status();
|
||||
if status.is_err() || !status.unwrap().success() {
|
||||
eprintln!("Junction creation skipped (not supported in current environment)");
|
||||
let _ = fs::remove_dir_all(&temp_root);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#[cfg(unix)]
|
||||
{
|
||||
if let Err(e) = std::os::unix::fs::symlink(&source_dir, &loop_path) {
|
||||
eprintln!("Symlink creation skipped: {e}");
|
||||
let _ = fs::remove_dir_all(&temp_root);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let (db, dek) = create_test_container(&container_path);
|
||||
|
||||
let mut opts = SyncOptions::default();
|
||||
opts.direction = SyncDirection::Push;
|
||||
opts.quiet = true;
|
||||
|
||||
// Ohne S-03 Fix würde der Sync endlos in die Schleife laufen und mit Stack-Overflow abstürzen.
|
||||
// Mit Fix wird der Symlink übersprungen und gezählt.
|
||||
let stats = run_sync(
|
||||
&db,
|
||||
0,
|
||||
&dek,
|
||||
FORMAT_VERSION,
|
||||
source_dir.to_str().unwrap(),
|
||||
"/SafePush",
|
||||
&opts,
|
||||
)
|
||||
.expect("Sync push must succeed despite symlink cycle");
|
||||
|
||||
assert_eq!(
|
||||
stats.files_transferred, 1,
|
||||
"Nur reguläre Datei darf übertragen werden"
|
||||
);
|
||||
assert!(
|
||||
stats.files_skipped_symlinks >= 1,
|
||||
"Symlink/Junction muss erkannt und in files_skipped_symlinks gezählt werden"
|
||||
);
|
||||
|
||||
// Aufräumen: Unter Windows Junction vor dem remove_dir_all entfernen
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let _ = std::process::Command::new("powershell")
|
||||
.args([
|
||||
"-Command",
|
||||
&format!("[System.IO.Directory]::Delete('{}')", loop_path.display()),
|
||||
])
|
||||
.status();
|
||||
}
|
||||
let _ = fs::remove_dir_all(&temp_root);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user