@@ -339,9 +339,40 @@ mod tests {
339339 let err = result. unwrap_err ( ) ;
340340 assert ! ( matches!( err, SofosError :: InvalidPath ( _) ) ) ;
341341
342+ // Verify error message mentions file size
342343 if let SofosError :: InvalidPath ( msg) = err {
343344 assert ! ( msg. contains( "too large" ) ) ;
344345 assert ! ( msg. contains( "50 MB" ) ) ;
345346 }
346347 }
348+
349+ #[ test]
350+ #[ cfg( unix) ] // Symlinks work differently on Windows
351+ fn test_symlink_escape_blocked ( ) {
352+ use std:: os:: unix:: fs:: symlink;
353+
354+ let temp_workspace = tempfile:: tempdir ( ) . unwrap ( ) ;
355+ let temp_outside = tempfile:: tempdir ( ) . unwrap ( ) ;
356+
357+ let fs_tool = FileSystemTool :: new ( temp_workspace. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
358+
359+ // Create a file outside the workspace
360+ let outside_file = temp_outside. path ( ) . join ( "secret.txt" ) ;
361+ fs:: write ( & outside_file, "secret data" ) . unwrap ( ) ;
362+
363+ // Try to create a symlink inside workspace pointing outside
364+ let symlink_path = temp_workspace. path ( ) . join ( "escape_link" ) ;
365+ symlink ( & outside_file, & symlink_path) . unwrap ( ) ;
366+
367+ // Attempt to read via symlink should fail with path violation
368+ let result = fs_tool. read_file ( "escape_link" ) ;
369+ assert ! ( result. is_err( ) ) ;
370+
371+ let err = result. unwrap_err ( ) ;
372+ assert ! ( matches!( err, SofosError :: PathViolation ( _) ) ) ;
373+
374+ if let SofosError :: PathViolation ( msg) = err {
375+ assert ! ( msg. contains( "outside the workspace" ) ) ;
376+ }
377+ }
347378}
0 commit comments