fix(pickled-core): close inet_aton short-form bypass of MCP --allow-public guard - #29
Merged
Merged
Conversation
…c guard POSIX socket.bind accepts legacy inet_aton-style IPv4 forms (e.g. '0', '0.0', '0.0.0', '0x0', '00000000') and canonicalises them to 0.0.0.0. asyncio/uvicorn forward the host string unchanged to socket.bind, so 'pickled-* mcp serve --transport http --host 0' silently bound to every public interface — the same accidental-exposure footgun PR #27 closed for the IPv6 wildcard, just via a different vector. ipaddress.ip_address rejects these short forms, so the previous guard returned False and let them through. The fix falls back to socket.inet_aton when ipaddress parsing fails and treats any host whose inet_aton-canonical packed form equals 0.0.0.0 as unspecified. Adds 12 new pytest-parametrised cases covering the bypass forms, inet_aton-style specific addresses (still allowed) and hostnames that incidentally contain 'zero' (still allowed). Co-authored-by: Bartłomiej Rosa <bartrosa@users.noreply.github.com>
bartrosa
marked this pull request as ready for review
June 1, 2026 11:36
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug and impact
pickled-* mcp serve --transport http --host 0(and several otherinet_aton-compatible aliases of0.0.0.0) silently binds to everypublic interface on POSIX hosts, bypassing the
--allow-publicguard.This is the same accidental-exposure footgun PR #27 closed for the IPv6
wildcard (
::,[::],::ffff:0.0.0.0), exploited via a differentvector. On a cloud VM with a routable IPv4 address, any developer who
copy-pastes
--host 0(a common shorthand) immediately makes the MCPserver reachable from the public internet without the explicit opt-in.
Root cause
packages/pickled-core/src/pickled_core/mcp/transport.py::_is_unspecified_addressrelies on
ipaddress.ip_addressto detect wildcard hosts. That parserintentionally rejects legacy
inet_atonforms:…but POSIX
socket.bind(("0", port)),("0.0", port),("0.0.0", port),("0x0", port),("00000000", port)and("0.00.0.0", port)all succeed and canonicalise to0.0.0.0(verified locally). uvicorn / asyncio forward the host string verbatim
to
socket.bind, so the guard never fires:Fix and validation
When
ipaddress.ip_addressrejects the host, fall back tosocket.inet_aton. If the packed result equalsb"\x00\x00\x00\x00",treat the host as the unspecified address and refuse the bind unless
--allow-publicwas passed. Hostnames that incidentally contain digits(
localhost,zero.example.com) andinet_atonspecific addresses(
127.0.0.1,0.0.0.1,10.0.0.1) are still allowed — we only blockstrings that canonicalise to
0.0.0.0.Validation:
test_mcp_transport.py:test_refuse_inet_aton_short_forms_without_flag— every bypassform raises
RuntimeError("wildcard").test_allow_inet_aton_specific_addresses_without_flag— shortforms that resolve to specific addresses pass through.
test_hostnames_with_zero_letters_are_not_wildcards— DNS-stylehostnames are still allowed and never resolved.
forms, loopback whitelist) still pass — total 34 transport tests.
uv run pytest -q→ 453 passed, 4 skipped.Companion to PR #27 — same guard, complementary vector.