Thanks to visit codestin.com
Credit goes to github.com

Skip to content

test(input): inject secure-open failures on the primitive the platform uses - #503

Open
kevin9327 wants to merge 1 commit into
NVIDIA:mainfrom
kevin9327:fix/secure-open-failure-injection
Open

test(input): inject secure-open failures on the primitive the platform uses#503
kevin9327 wants to merge 1 commit into
NVIDIA:mainfrom
kevin9327:fix/secure-open-failure-injection

Conversation

@kevin9327

Copy link
Copy Markdown
Contributor

Problem

Two cases assert what happens when the secure, no-follow open fails. Both inject that
failure by patching os.open:

  • tests/unit/test_input_handler.py::test_resolve_file_open_failure_does_not_create_temp_dir
    — "Failed secure opens leave no handler-owned temporary directory behind."
  • tests/nodes/test_build_context.py::test_build_context_reports_read_error_without_fake_empty_content
    — "Unreadable files remain inventoried but are absent from the content cache."
with patch("skillspector.input_handler.os.open", side_effect=OSError("denied")):

os.open is only reached on the POSIX branch:

_HAS_SECURE_DIR_FD = os.open in os.supports_dir_fd and hasattr(os, "O_NOFOLLOW")
...
    if _HAS_SECURE_DIR_FD:
        return _open_regular_file_from_trusted_directory(absolute_path)
    if _IS_WINDOWS:
        return _open_regular_file_from_windows_handle(absolute_path)

os.supports_dir_fd is empty on Windows, so _HAS_SECURE_DIR_FD is False and the dispatcher
goes to _open_regular_file_from_windows_handle, which opens through CreateFileW and never
calls os.open. The patch therefore injects nothing: the open succeeds, the file is read, and
the assertions about the failure path never get a failure to observe.

The visible effect is that both cases fail. The substantive effect is that the failed-open
contract — no temporary directory left behind, no fabricated empty content in the cache — has
no coverage at all on the platform that uses the handle-based implementation.

Fix

Deny both primitives, so the failure reaches the dispatcher whichever branch it takes. The
Windows stub raises _FileOpenError, which is exactly what _open_regular_file_from_windows_handle
raises when CreateFileW returns INVALID_HANDLE_VALUE:

if handle == invalid_handle_value:
    error = _windows_last_error()
    if error.errno == ENOENT:
        raise FileNotFoundError(f"File not found: {file_path}") from None
    raise _FileOpenError(file_path, error)

On POSIX the added patch targets a helper that branch never calls, so behaviour there is
unchanged and the cases keep exercising the real os.open path.

Reproduction

Windows 11, Python 3.12.10, against unmodified main (704bc95):

$ python -m pytest -p no:randomly \
    "tests/unit/test_input_handler.py::test_resolve_file_open_failure_does_not_create_temp_dir" \
    "tests/nodes/test_build_context.py::test_build_context_reports_read_error_without_fake_empty_content" \
    -q --no-header

        with patch("skillspector.input_handler.os.open", side_effect=OSError("denied")):
>           with pytest.raises(ValueError, match="Could not safely open"):
E           Failed: DID NOT RAISE ValueError
tests\unit\test_input_handler.py:230: Failed

        monkeypatch.setattr("skillspector.input_handler.os.open", deny_open)
        result = build_context({"skill_path": str(tmp_path)})
        assert "broken.py" in result["components"]
>       assert "broken.py" not in result["file_cache"]
E       AssertionError: assert 'broken.py' not in {'broken.py': 'print(1)\r\n'}
tests\nodes\test_build_context.py:994: AssertionError
=========================== short test summary info ===========================
FAILED tests/unit/test_input_handler.py::test_resolve_file_open_failure_does_not_create_temp_dir
FAILED tests/nodes/test_build_context.py::test_build_context_reports_read_error_without_fake_empty_content
2 failed in 5.88s

DID NOT RAISE and the populated file_cache are the same symptom: the open was never denied.

With this change, same command:

..                                                                       [100%]
2 passed in 4.88s

They pass rather than skip, so the contract is now actually asserted on this platform.

What must still hold

Both files in full, before and after:

file before after
tests/unit/test_input_handler.py 1 failed, 22 passed, 6 skipped 23 passed, 6 skipped
tests/nodes/test_build_context.py 12 failed, 66 passed, 2 skipped 11 failed, 67 passed, 2 skipped

In both files the change converts exactly one case from failing to passing and moves nothing
else. The eleven remaining test_build_context.py failures are unrelated to secure-open
dispatch and are not touched here.

ruff check src/ tests/All checks passed!
ruff format --check src/ tests/223 files already formatted

…m 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 <[email protected]>

@rng1995 rng1995 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SkillSpector Review]

Reviewed head fdc5e8fc9212d1cc62be4e0d64f6a6b38fae9a12 — APPROVE.

The tests now inject failure into both secure-open implementations, with the unit test preserving the Windows helper's _FileOpenError contract. This makes the same cleanup and failed-cache behavior observable on POSIX and Windows without changing production code. I found no required changes.

Required checks pass, but GitHub currently reports mergeStateStatus=BEHIND; update against current main and re-run required checks before merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants