From fdc5e8fc9212d1cc62be4e0d64f6a6b38fae9a12 Mon Sep 17 00:00:00 2001 From: kevin9327 Date: Wed, 9 Sep 2026 13:44:06 +0900 Subject: [PATCH] test(input): inject secure-open failures on the primitive the platform uses Two cases prove the failed-secure-open contract by patching skillspector.input_handler.os.open. _HAS_SECURE_DIR_FD is False on Windows, so _open_regular_file_no_follow dispatches to _open_regular_file_from_windows_handle and os.open is never called. The failure is never injected there: the file is read normally, and both cases fail while asserting the contract they exist to protect. Deny both primitives so the failure reaches the dispatcher on either platform. The Windows stub raises _FileOpenError, which is what the real helper raises when CreateFileW fails. Signed-off-by: kevin9327 --- tests/nodes/test_build_context.py | 6 ++++++ tests/unit/test_input_handler.py | 13 ++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/nodes/test_build_context.py b/tests/nodes/test_build_context.py index a2db76561..8e1b189e8 100644 --- a/tests/nodes/test_build_context.py +++ b/tests/nodes/test_build_context.py @@ -987,7 +987,13 @@ def test_build_context_reports_read_error_without_fake_empty_content( def deny_open(*args: object, **kwargs: object) -> int: raise PermissionError("sensitive operating-system detail") + # The secure open dispatches on the platform: POSIX goes through os.open, + # Windows through the handle-based helper. Deny both so the failure is + # injected wherever the test happens to run. monkeypatch.setattr("skillspector.input_handler.os.open", deny_open) + monkeypatch.setattr( + "skillspector.input_handler._open_regular_file_from_windows_handle", deny_open + ) result = build_context({"skill_path": str(tmp_path)}) assert "broken.py" in result["components"] diff --git a/tests/unit/test_input_handler.py b/tests/unit/test_input_handler.py index ec52b678a..6620dfc5a 100644 --- a/tests/unit/test_input_handler.py +++ b/tests/unit/test_input_handler.py @@ -28,6 +28,7 @@ from skillspector.input_handler import ( ALLOWED_GIT_HOSTS, InputHandler, + _FileOpenError, _open_regular_file_from_windows_handle, _open_regular_file_no_follow, ) @@ -225,8 +226,18 @@ def test_resolve_file_open_failure_does_not_create_temp_dir(tmp_path: Path) -> N source = tmp_path / "SKILL.md" source.write_text("# Skill", encoding="utf-8") handler = InputHandler() + denied = OSError("denied") try: - with patch("skillspector.input_handler.os.open", side_effect=OSError("denied")): + # The secure open dispatches on the platform: POSIX goes through os.open, + # Windows through the handle-based helper. Deny both so the failure is + # injected wherever the test happens to run. + with ( + patch("skillspector.input_handler.os.open", side_effect=denied), + patch( + "skillspector.input_handler._open_regular_file_from_windows_handle", + side_effect=_FileOpenError(source, denied), + ), + ): with pytest.raises(ValueError, match="Could not safely open"): handler.resolve(str(source)) assert handler.temp_dir_for_cleanup() is None