diff --git a/crates/engine/src/clip_group_file.rs b/crates/engine/src/clip_group_file.rs
--- a/crates/engine/src/clip_group_file.rs
+++ b/crates/engine/src/clip_group_file.rs
@@ -89,7 +89,7 @@ fn decode_group_audio(
     let (mut decoded, mut index, mut file_of) = (Vec::new(), BTreeMap::new(), BTreeMap::new());
     for (old, rel) in audio_entries {
         let full = dir.join(rel);
-        if !full.starts_with(dir) || rel.contains("..") {
+        if !full.starts_with(dir) || Path::new(rel).components().any(|c| matches!(c, std::path::Component::ParentDir)) {
             return Err(EngineError::BadParams(cmd_id.into(), format!("audio path escapes the group folder: {rel}")));
         }
         let key = crate::io::file_key(&full);
@@ -231,6 +231,34 @@ mod tests {
         let _ = std::fs::remove_dir_all(&dir);
     }
 
+    #[test]
+    fn import_accepts_audio_names_with_consecutive_dots_but_not_parent_dirs() {
+        let dir = std::env::temp_dir().join(format!("sc-grp-dots-{}", std::process::id()));
+        let _ = std::fs::create_dir_all(&dir);
+        let path = dir.join("drums.scgrp").to_string_lossy().to_string();
+        let mut e = crate::demo::demo_engine();
+        let kick = e.session().tracks.iter().find(|t| t.name == "Kick").map(|t| t.id).unwrap();
+        let ids: Vec<_> = e.session().track(kick).unwrap().clips().iter().take(1).map(|c| c.id.0).collect();
+        e.execute("file.export_clip_groups", &json!({"path": path, "clips": ids})).unwrap();
+        let mut file: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(&path).unwrap()).unwrap();
+        for (i, rel) in file["audio"].as_object_mut().unwrap().values_mut().enumerate() {
+            let old = dir.join(rel.as_str().unwrap());
+            let new = format!("Wait... what_{i}.wav");
+            std::fs::rename(&old, dir.join(&new)).unwrap();
+            *rel = json!(new);
+        }
+        std::fs::write(&path, file.to_string()).unwrap();
+        let r = e.execute("file.import_clip_groups", &json!({"path": path, "at": 1000, "track": "Bass"}));
+        assert!(r.is_ok(), "{r:?}");
+        for rel in file["audio"].as_object_mut().unwrap().values_mut() {
+            *rel = json!("../escape.wav");
+        }
+        std::fs::write(&path, file.to_string()).unwrap();
+        let r = e.execute("file.import_clip_groups", &json!({"path": path, "at": 1000, "track": "Bass"}));
+        assert!(matches!(r, Err(crate::EngineError::BadParams(..))), "{r:?}");
+        let _ = std::fs::remove_dir_all(&dir);
+    }
+
     #[test]
     fn positions_in_a_group_file_are_kept_within_reach() {
         let dir = std::env::temp_dir().join(format!("sc-grp-far-{}", std::process::id()));
