@@ -223,6 +223,25 @@ async def test_local_file_rejects_symlinked_source_leaf(tmp_path: Path) -> None:
223223 assert session .writes == {}
224224
225225
226+ @pytest .mark .asyncio
227+ async def test_local_file_rejects_symlinked_source_before_checksum (tmp_path : Path ) -> None :
228+ target_dir = tmp_path / "secret-dir"
229+ target_dir .mkdir ()
230+ _symlink_or_skip (tmp_path / "link.txt" , target_dir , target_is_directory = True )
231+ session = _RecordingSession ()
232+
233+ with pytest .raises (LocalFileReadError ) as excinfo :
234+ await LocalFile (src = Path ("link.txt" )).apply (
235+ session ,
236+ Path ("/workspace/copied.txt" ),
237+ tmp_path ,
238+ )
239+
240+ assert excinfo .value .context ["reason" ] == "symlink_not_supported"
241+ assert excinfo .value .context ["child" ] == "link.txt"
242+ assert session .writes == {}
243+
244+
226245@pytest .mark .asyncio
227246async def test_local_dir_copy_falls_back_when_safe_dir_fd_open_unavailable (
228247 monkeypatch : pytest .MonkeyPatch ,
@@ -255,6 +274,9 @@ async def test_local_dir_copy_revalidates_swapped_paths_during_open(
255274 monkeypatch : pytest .MonkeyPatch ,
256275 tmp_path : Path ,
257276) -> None :
277+ if not artifacts_module ._OPEN_SUPPORTS_DIR_FD or not artifacts_module ._HAS_O_DIRECTORY :
278+ pytest .skip ("safe dir_fd open pinning is unavailable on this platform" )
279+
258280 src_root = tmp_path / "src"
259281 src_root .mkdir ()
260282 src_file = src_root / "safe.txt"
@@ -276,7 +298,7 @@ def swap_then_open(
276298 nonlocal swapped
277299 if (path == "safe.txt" or Path (path ) == src_file ) and not swapped :
278300 src_file .unlink ()
279- src_file . symlink_to ( secret )
301+ _symlink_or_skip ( src_file , secret )
280302 swapped = True
281303 if dir_fd is None :
282304 return original_open (path , flags , mode )
@@ -306,6 +328,9 @@ async def test_local_dir_copy_pins_parent_directories_during_open(
306328 monkeypatch : pytest .MonkeyPatch ,
307329 tmp_path : Path ,
308330) -> None :
331+ if not artifacts_module ._OPEN_SUPPORTS_DIR_FD or not artifacts_module ._HAS_O_DIRECTORY :
332+ pytest .skip ("safe dir_fd open pinning is unavailable on this platform" )
333+
309334 src_root = tmp_path / "src"
310335 src_root .mkdir ()
311336 nested_dir = src_root / "nested"
@@ -330,7 +355,7 @@ def swap_parent_then_open(
330355 nonlocal swapped
331356 if path == "safe.txt" and not swapped :
332357 (src_root / "nested" ).rename (src_root / "nested-original" )
333- (src_root / "nested" ). symlink_to ( secret_dir , target_is_directory = True )
358+ _symlink_or_skip (src_root / "nested" , secret_dir , target_is_directory = True )
334359 swapped = True
335360 if dir_fd is None :
336361 return original_open (path , flags , mode )
@@ -355,6 +380,9 @@ async def test_local_dir_apply_rejects_source_root_swapped_to_symlink_after_vali
355380 monkeypatch : pytest .MonkeyPatch ,
356381 tmp_path : Path ,
357382) -> None :
383+ if not artifacts_module ._OPEN_SUPPORTS_DIR_FD or not artifacts_module ._HAS_O_DIRECTORY :
384+ pytest .skip ("safe dir_fd open pinning is unavailable on this platform" )
385+
358386 src_root = tmp_path / "src"
359387 src_root .mkdir ()
360388 (src_root / "safe.txt" ).write_text ("safe" , encoding = "utf-8" )
@@ -420,7 +448,7 @@ def swap_root_then_open(
420448 nonlocal swapped
421449 if Path (path ) == src_root / "safe.txt" and not swapped :
422450 src_root .rename (tmp_path / "src-original" )
423- (tmp_path / "src" ). symlink_to ( secret_dir , target_is_directory = True )
451+ _symlink_or_skip (tmp_path / "src" , secret_dir , target_is_directory = True )
424452 swapped = True
425453 if dir_fd is None :
426454 return original_open (path , flags , mode )
@@ -492,7 +520,7 @@ async def test_local_dir_rejects_symlinked_source_ancestors(tmp_path: Path) -> N
492520 nested_dir = target_dir / "sub"
493521 nested_dir .mkdir ()
494522 (nested_dir / "secret.txt" ).write_text ("secret" , encoding = "utf-8" )
495- (tmp_path / "link" ). symlink_to ( target_dir , target_is_directory = True )
523+ _symlink_or_skip (tmp_path / "link" , target_dir , target_is_directory = True )
496524 session = _RecordingSession ()
497525
498526 with pytest .raises (LocalDirReadError ) as excinfo :
@@ -508,7 +536,7 @@ async def test_local_dir_rejects_symlinked_source_root(tmp_path: Path) -> None:
508536 target_dir = tmp_path / "secret-dir"
509537 target_dir .mkdir ()
510538 (target_dir / "secret.txt" ).write_text ("secret" , encoding = "utf-8" )
511- (tmp_path / "src" ). symlink_to ( target_dir , target_is_directory = True )
539+ _symlink_or_skip (tmp_path / "src" , target_dir , target_is_directory = True )
512540 session = _RecordingSession ()
513541
514542 with pytest .raises (LocalDirReadError ) as excinfo :
@@ -526,7 +554,7 @@ async def test_local_dir_rejects_symlinked_files(tmp_path: Path) -> None:
526554 (src_root / "safe.txt" ).write_text ("safe" , encoding = "utf-8" )
527555 secret = tmp_path / "secret.txt"
528556 secret .write_text ("secret" , encoding = "utf-8" )
529- (src_root / "link.txt" ). symlink_to ( secret )
557+ _symlink_or_skip (src_root / "link.txt" , secret )
530558 session = _RecordingSession ()
531559
532560 with pytest .raises (LocalDirReadError ) as excinfo :
@@ -545,7 +573,7 @@ async def test_local_dir_rejects_symlinked_directories(tmp_path: Path) -> None:
545573 target_dir = tmp_path / "secret-dir"
546574 target_dir .mkdir ()
547575 (target_dir / "secret.txt" ).write_text ("secret" , encoding = "utf-8" )
548- (src_root / "linked-dir" ). symlink_to ( target_dir , target_is_directory = True )
576+ _symlink_or_skip (src_root / "linked-dir" , target_dir , target_is_directory = True )
549577 session = _RecordingSession ()
550578
551579 with pytest .raises (LocalDirReadError ) as excinfo :
@@ -591,10 +619,11 @@ async def test_git_repo_uses_fetch_checkout_path_for_commit_refs() -> None:
591619@pytest .mark .asyncio
592620async def test_dir_metadata_strips_file_type_bits_before_chmod () -> None :
593621 session = _RecordingSession ()
622+ dest = Path ("/workspace/dir" )
594623
595- await Dir ()._apply_metadata (session , Path ( "/workspace/dir" ) )
624+ await Dir ()._apply_metadata (session , dest )
596625
597- assert ("chmod" , "0755" , "/workspace/dir" ) in session .exec_calls
626+ assert ("chmod" , "0755" , str ( dest ) ) in session .exec_calls
598627
599628
600629@pytest .mark .asyncio
@@ -625,5 +654,5 @@ async def test_apply_manifest_raises_on_chgrp_failure() -> None:
625654 with pytest .raises (ExecNonZeroError ):
626655 await session .apply_manifest ()
627656
628- assert ("chgrp" , "sandbox-user" , "/workspace/copied.txt" ) in session .exec_calls
657+ assert ("chgrp" , "sandbox-user" , str ( Path ( "/workspace/copied.txt" )) ) in session .exec_calls
629658 assert not any (call [0 ] == "chmod" for call in session .exec_calls )
0 commit comments