Skip to content

Two detectors scanned a narrower population than they claimed: the shim gate saw only test_*.py, and the netblock's three open egress routes were six (#1598, #1584) - #1642

Merged
fdaviddpt merged 3 commits into
masterfrom
fix/1598-1584
Aug 14, 2026
Merged

Two detectors scanned a narrower population than they claimed: the shim gate saw only test_*.py, and the netblock's three open egress routes were six (#1598, #1584)#1642
fdaviddpt merged 3 commits into
masterfrom
fix/1598-1584

Conversation

@fdaviddpt

Copy link
Copy Markdown
Contributor

Two detectors whose population was narrower than their claim, so each zero read as "clean" when it meant "I did not look there". Both re-derived, and both counts in the issues were wrong in the direction that matters.

#1598 — the shim gate scanned only tests/test_*.py

Widening the scan over the 21 previously unscanned modules found exactly one hit, _gitshim.py:38, as filed. But the issue omitted tests/fixtures/ — three real modules — so the fix is rglob, not glob.

_modules_scanned() is now every *.py under tests/ recursively minus __pycache__, with hits reported as relative_to(root).as_posix().

The exemption is a grammar answer, not a filename allowlist: _shifts_through_argv requires every $1-test position to sit inside a while|until|for … done span containing shift, consistent with #1412.

The judgment call: the one pre-existing site is the correct implementation, so no register was needed — shape over marker, because a marker is a self-declaration anybody can write above a broken shim.

FAILED ...::test_a_shim_planted_in_a_helper_module_is_reported
FAILED ...::test_the_canonical_dispatcher_is_exempt_by_its_shape_not_by_its_filename
FAILED ...::test_a_fixture_module_below_tests_is_reached_too
FAILED ...::test_the_scan_reads_every_python_module_in_the_suite_not_only_test_files
E   AssertionError: ('_gitshim.py', 709)
4 failed, 43 passed in 9.19s

GREEN: 47 passed in 5.49s

#1584 — the issue said three open egress routes. It is six.

gethostbyname_ex, getnameinfo and gethostbyaddr were open and unnamed:

connect_ex         NOT BLOCKED -> 0
gethostbyname      NOT BLOCKED -> '104.20.23.154'
udp sendto         NOT BLOCKED -> 1
gethostbyname_ex   NOT BLOCKED -> ('example.com', [], ['104.20.23.154', ...])
getnameinfo        NOT BLOCKED -> ('one.one.one.one', 'http')
gethostbyaddr      NOT BLOCKED -> ('one.one.one.one', [...], ['1.1.1.1'])

All six refused now, plus sendmsg. Loopback and AF_UNIX stay open by design.

The judgment call: not a longer list. _netblock.ROUTES / SOCKET_ROUTES classify every callable socket and socket.socket expose as PATCHED / VIA / LOCAL / INERT / OPEN, with the population derived from dir(socket) at test time — so a name CPython grows arrives red rather than silently unblocked. OPEN carries a reason, and BEYOND_THE_PROCESS names the four uncoverable routes instead of counting them blocked.

Prose corrected at all three live sites (conftest.py, docs/contributing.md, test_transport_seam_enforced_1341.py). CHANGELOG.md:180 deliberately left — it is a historical release record.

RED 16 failed, 3 passed → GREEN 19 passed, then 179 passed across the netblock/hashnode/seam/shim suites.

The reviewer caught a Windows-wide outage before CI could

_netblock.py:322 — the patch loop hardcoded ("connect","connect_ex","sendto","sendmsg") while _METHOD_PATCHES was hasattr-filtered directly above. Windows has no socket.socket.sendmsg, and monkeypatch.setattr(raising=True) on an absent attribute raises AttributeError out of an autouse fixture — every test, every Windows leg. Fixed; the loop iterates the derived tuples.

The pin proved red against the old loop (hardcoded loop -> sendmsg is original: False). Note delattr cannot simulate the platform — socket.socket.sendmsg is inherited from _socket.socket — so the pin shortens the derived tuple instead.

Two more accepted: a stale sentence one paragraph above the rewritten disclosure, and a docstring claiming patches == register while the loop had drifted.

Adjacent, fixed

tests/test_git_shim_subcommand_1206.py carried a module-level pytestmark = skipif(os.name == "nt"), which was skipping the class gate and every pure-AST rule test on Windows. Scoped to the five tests that actually run /bin/sh; the AST tests now run there.

Suite

Full suite twice in disposable clones: 12898 passed, 97 skipped. Two failures, neither from this diff — test_no_cli_at_all_is_not_an_absence_either (fails identically on parent d2a48e7f in a clean clone in isolation; filed separately) and test_a_minified_file_does_not_hang_the_diagnostic (passes in isolation on both sides; timing). Windows unverified locally — CI is the authority.

Closes #1598
Closes #1584

Comment on lines +568 to +569
'while [ $# -gt 0 ]; do case "$1" in -*) shift ;; *) break ;; esac; done'
'\nif [ "$1" = "status" ]; then exit 128; fi\n',
call()
except _netblock.OutboundBlocked:
raise
except OSError:
Comment thread tests/_netblock.py
return orig_methods["connect"](self, address)
_refuse("an outbound connect", address)

def _connect_ex(self: socket.socket, address: Any) -> Any:
Comment thread tests/_netblock.py
return orig_methods["connect_ex"](self, address)
_refuse("an outbound connect_ex", address)

def _sendto(self: socket.socket, *args: Any) -> Any:
Comment thread tests/_netblock.py
Comment on lines +289 to +290
def _sendmsg(self: socket.socket, buffers: Any, ancdata: Any = (),
flags: int = 0, address: Any = None) -> Any:
…1598, #1584)

#1598 -- the `$1`-shim gate scanned `tests/test_*.py`, so a shim in
`_gitshim.py`, `_git_decline.py`, `conftest.py` or `tests/fixtures/` returned
the same clean zero. It now reads every Python module under `tests/`,
recursively. The widened population produces exactly one hit, `_gitshim.py`'s
`while`/`shift` dispatcher, which is the correct implementation of the pattern
and is exempted by its shape rather than its filename -- `$1` inside a loop
that consumes arguments has no position to slide to, the same grammar argument
#1412 used for an option at `$1`. Naming the file would have re-created the
allowlist #1412 deleted.

The module-level POSIX `pytestmark` also came off, onto the five tests that
actually run a `/bin/sh` shim: it was skipping the class gate and every rule
test on Windows, all of them pure AST and regex work.

#1584 -- the suite-wide netblock documented its blind spot as one thing and six
in-process egress routes stayed open behind the sentence. `connect_ex`,
`sendto`, `gethostbyname`, `gethostbyname_ex`, `gethostbyaddr` and
`getnameinfo` all left an armed context, and `gethostbyname` came back with a
live-resolved address. All are refused now, with `sendmsg`. The fix is not the
longer list: `_netblock.ROUTES` / `SOCKET_ROUTES` classify every callable
`socket` and `socket.socket` expose, a new one arrives red, and the four routes
no in-process patch can reach are named in `BEYOND_THE_PROCESS` rather than
counted as blocked.

Co-Authored-By: Max <noreply>
…en-out list

The reviewer's finding, and it would have reddened every Windows leg rather
than a socket test: `block_outbound` computed `_MODULE_PATCHES` /
`_METHOD_PATCHES` with a `hasattr` filter -- precisely because Windows has no
`socket.socket.sendmsg` -- and then iterated a hardcoded four-name tuple
anyway. `monkeypatch.setattr` with `raising=True` on an absent attribute raises
`AttributeError` out of an autouse fixture, so every test in the suite.

Pinned by a test that shortens the derived tuple and asserts the untouched
method stayed untouched; against the previous loop that assertion is False.
`delattr` cannot simulate the platform here -- `socket.socket.sendmsg` is
inherited from `_socket.socket` -- which is why the pin is on the tuple.

Also the reviewer's second finding: a line in
`test_transport_seam_enforced_1341.py` still named `connect`/`getaddrinfo` as
the whole boundary, one paragraph above the rewritten disclosure. That is the
#1584 defect reproduced next to its own fix.

Co-Authored-By: Max <noreply>
…ws-only route was classified nowhere (#1584)

`socket.socket.ioctl` exists under `MS_WINDOWS` only. The register's population
is `dir(socket.socket)` on the running interpreter -- right for a name a later
Python grows, blind by construction to a name another platform has -- so it was
in neither register, invisible to every POSIX author and every POSIX leg, and
red on all four Windows legs one PR after the register shipped.

Classified `OPEN`, not `PATCHED`. `WSAIoctl` addresses no peer, but `SIO_RCVALL`
puts the interface into promiscuous receive, and the traffic that then arrives
at `recv` carries no destination for a destination-refusing guard to check --
indistinguishable there from the loopback bytes the suite is entitled to. It is
not patched rather than not patchable: refusing it means deciding per control
code on a platform nobody who maintains this file can run, and a guess would be
coverage claimed rather than had. `BEYOND_THE_PROCESS` stays at four; `ioctl` is
inside the process, it is a route this guard declines, which is what `OPEN` is
for. `OPEN_ROUTES`' header claimed all its entries were the connected-descriptor
shape; `ioctl` is a second shape and says so rather than being filed under it.

One name is not the fix, though -- a register that learns one platform-only name
per release is the sentence it replaced. `PLATFORM_ONLY` states the `MS_WINDOWS`
names (`ioctl`, `share`, `fromshare`) and a parametrised test asserts each is
classified and present exactly on its own platform: absent here, present on the
Windows legs. Only win32 is listed, and deliberately -- the macOS and Linux
populations are derived green on every PR, so they are observed; Windows was the
one platform no green leg had ever agreed with.

Windows claims here are reasoned from CPython's `Modules/socketmodule.c`, not
observed: this was written on macOS. The presence half of the new test is what
converts them, on the leg that can.

Adjacent, same file: `test_the_register_only_classifies_names_that_exist`
checked `ROUTES` against the interpreter and left `SOCKET_ROUTES` unchecked, so
a method name no Python has could sit there and read as coverage -- the same
half-derived population as the defect this file exists for. Widened; verified
non-vacuous by injecting a bogus name (`['wsarecvmsg']` reported, `[]` as
shipped).

RED before the fix, matching the Windows legs:
  test_every_platform_only_route_is_classified[ioctl]
  AssertionError: ioctl

Co-Authored-By: Max <noreply>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant