From 5d3f8737a7e27ef4613ec7fbebe80022ff2a29fa Mon Sep 17 00:00:00 2001 From: mohsen-uipath Date: Mon, 17 Aug 2026 13:51:19 -0700 Subject: [PATCH 1/2] fix(deps): bump sqlparse 0.5.5 -> 0.6.0 to clear the pip-audit gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `pip-audit` is a Required check and currently fails on every PR against this repo, main included: sqlparse 0.5.5 accumulated four advisories after main's last green run, so the gate went red without a single line of code changing. Two of them are quadratic-blowup DoS vectors on the default parse path — CVE-2026-71491 (`group_comments` is O(n^2) on comment-only input, reached via `format(sql, strip_comments=True)`) and CVE-2026-54284 (`TokenList.__init__` materializes `str(self)` per group, making nested grouping O(n*depth); a 2 KB payload burns ~10s of CPU). Both are fixed in 0.6.0. sqlparse is transitive, not a direct dependency: it arrives via uipath-platform 0.1.8 under the optional `uipath` extra, whose constraint is `sqlparse>=0.5.5` — so 0.6.0 satisfies it and the bump is lockfile-only (`uv lock --upgrade-package sqlparse`), touching no other package. Verified with CI's exact invocation against a `--extra dev --extra uipath --extra codex` sync: "No known vulnerabilities found", exit 0. `make verify` green on main plus this change: 4,162 tests. --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index bbe0e461..211895bb 100644 --- a/uv.lock +++ b/uv.lock @@ -2253,11 +2253,11 @@ wheels = [ [[package]] name = "sqlparse" -version = "0.5.5" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/d3/3f06a1006f2261d1342aefb3c71eed02f5d4ca5bdbecd86ebc12ad38306e/sqlparse-0.6.0.tar.gz", hash = "sha256:113c35c75365ab9cc9c7231d68c6428fb11c085fc8e9eb1ad659b7ddbf6cd2b9", size = 178477, upload-time = "2026-08-13T19:16:06.396Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, + { url = "https://files.pythonhosted.org/packages/d9/50/f00935da0ec7cbf325f8dc4f772ae46fbc7b672dd62876e73f0a94adda57/sqlparse-0.6.0-py3-none-any.whl", hash = "sha256:b861c0288ce2fa56209a9a6412d2e066ac664b3873b89c26c9d8415e8e32996f", size = 50070, upload-time = "2026-08-13T19:16:04.062Z" }, ] [[package]] From dd848406bb9e9eac04c4f73249d84dd6597ec999 Mon Sep 17 00:00:00 2001 From: mohsen-uipath Date: Mon, 17 Aug 2026 14:34:42 -0700 Subject: [PATCH 2/2] fix(orchestrator): close pre/post-run subprocess transports so Windows CI stops leaking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Windows Smoke Test job has failed on every run since 2026-08-17, on every branch — including one containing nothing but a lockfile bump. It is not caused by any recent change: it is a latent resource leak that an environment change (most likely the GitHub Windows runner image) started surfacing reliably. `_run_command_list` spawns each pre/post-run command with two PIPEs and never closes the transport. asyncio offers no public way to: `Process` holds it privately and only releases it on garbage collection. On Windows' Proactor loop `_ProactorBasePipeTransport.__del__` then emits `ResourceWarning: unclosed transport` — one per pipe — which a strict test run reports as `PytestUnraisableExceptionWarning` against whichever test happened to be running when the GC fired, not against the code that leaked. That is exactly the observed shape: always two sub-exceptions per report, always attributed to test_pre_run / test_post_run / test_preservation_mode, with the victim test varying run to run while the underlying count stays constant. `_close_subprocess_transport` releases it from a `finally` covering every exit from the loop body: the normal path, the timeout branch's `continue`, and the `fail_on_error` raise. Best-effort and idempotent — a second close is a no-op and errors are swallowed, since releasing a pipe must never become a task failure. `proc` is bound before the `try` so the spawn-failure path (nothing to close) is distinguishable. Verified as far as a POSIX host allows: make verify green (4,162 tests), ruff clean, pyright 0 errors on this file, and the 63 tests across the three reporting modules pass. The leak itself cannot be reproduced here — on Unix the transport is already closing by the time `wait()` returns, so this is a no-op off Windows and CI is the real test. `isolation/docker_runner.py:572` has the same shape and is deliberately left alone: the evidence points at this loop only (two pipes per command matches the two sub-exceptions exactly, and the docker path is mocked in those tests). If Windows stays red, that site is the next candidate and reuses this helper. --- src/coder_eval/orchestrator.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/coder_eval/orchestrator.py b/src/coder_eval/orchestrator.py index 83f67da3..c087fd1c 100644 --- a/src/coder_eval/orchestrator.py +++ b/src/coder_eval/orchestrator.py @@ -76,6 +76,28 @@ _WAIT_FOR_GRACE_SECONDS = 2.0 +def _close_subprocess_transport(proc: asyncio.subprocess.Process | None) -> None: + """Release a finished subprocess's pipe transport deterministically. + + ``asyncio`` gives ``Process`` no public way to do this: the transport is held + privately and is only closed when the object is garbage-collected. On + Windows' Proactor loop ``_ProactorBasePipeTransport.__del__`` then emits + ``ResourceWarning: unclosed transport`` — one per pipe — which a strict test + run surfaces as ``PytestUnraisableExceptionWarning`` attributed to whichever + test happened to be running when the GC fired, not to the code that leaked. + That is exactly the shape of the intermittent Windows CI failures in + ``test_pre_run`` / ``test_post_run`` / ``test_preservation_mode``. + + Best-effort and idempotent: a second ``close()`` is a no-op, and any error is + swallowed because releasing a pipe must never turn into a task failure. + """ + transport = getattr(proc, "_transport", None) + if transport is None: + return + with suppress(Exception): + transport.close() + + async def _pump_stream( stream: asyncio.StreamReader | None, log_fn: Callable[..., None], @@ -2172,6 +2194,9 @@ async def _run_command_list( start = time.time() logger.info("Running %s command: %s", human.lower(), cmd.command) + # Bound outside the try so the `finally` can tell "never spawned" + # (a create_subprocess_shell failure) from "spawned, needs closing". + proc: asyncio.subprocess.Process | None = None try: proc = await asyncio.create_subprocess_shell( cmd.command, @@ -2246,6 +2271,13 @@ async def _run_command_list( if fail_on_error: raise RuntimeError(f"{human} command failed: {cmd.command!r}") from e logger.warning("%s command '%s' failed: %s", human, cmd.command, e) + finally: + # Every exit from this iteration -- normal, `continue` from the + # timeout branch, or a raised RuntimeError -- must release the + # two pipes this command opened. Without it they survive until + # GC, which on Windows reports as an unraisable ResourceWarning + # against an unrelated test. See _close_subprocess_transport. + _close_subprocess_transport(proc) async def _run_pre_run_commands(self) -> None: """Execute pre-run commands inside the sandbox before evaluation.