@@ -191,6 +191,54 @@ def test_absolute_paths(self, tmp_path):
191191 assert not result , f"Absolute path '{ path } ' should be blocked"
192192
193193
194+ class TestValidateProjectPathDoubleDotInFilename :
195+ """Test that filenames containing '..' as part of the name are allowed."""
196+
197+ def test_double_dot_in_filename_allowed (self , tmp_path ):
198+ """Filenames like 'hi-everyone..md' should NOT be blocked.
199+
200+ This was a production bug: a title ending with a period (e.g. "Hi everyone.")
201+ produced a file path like "hi-everyone..md" which the old substring check
202+ ('..' in path) incorrectly flagged as path traversal.
203+ """
204+ project_path = tmp_path / "project"
205+ project_path .mkdir ()
206+
207+ safe_paths_with_dots = [
208+ "hi-everyone..md" ,
209+ "notes/hi-everyone..md" ,
210+ "version-2..0.md" ,
211+ "file...name.md" ,
212+ "docs/report..final.txt" ,
213+ ]
214+
215+ for path in safe_paths_with_dots :
216+ assert validate_project_path (path , project_path ), (
217+ f"Path '{ path } ' with '..' in filename should be allowed"
218+ )
219+
220+ def test_actual_traversal_still_blocked (self , tmp_path ):
221+ """Ensure '..' as a path segment is still blocked."""
222+ project_path = tmp_path / "project"
223+ project_path .mkdir ()
224+
225+ attack_paths = [
226+ "../file.md" ,
227+ "notes/../../etc/passwd" ,
228+ "foo/../../../bar" ,
229+ "..\\ Windows\\ System32" ,
230+ # Windows normalizes trailing dots/spaces to ".."
231+ ".. /file.md" ,
232+ ".. ./file.md" ,
233+ "notes/.. /etc/passwd" ,
234+ ]
235+
236+ for path in attack_paths :
237+ assert not validate_project_path (path , project_path ), (
238+ f"Traversal path '{ path } ' should still be blocked"
239+ )
240+
241+
194242class TestValidateProjectPathEdgeCases :
195243 """Test edge cases and error conditions."""
196244
0 commit comments