[CLI] allow_local_binding does not cover IPv4-mapped IPv6 loopback connections
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
What version of Codex CLI is running?
codex-cli 0.149.0 (bundled with the Codex Desktop app)
What subscription do you have?
Plus
Which model were you using?
Not relevant to this networking-only reproducer.
What platform is your computer?
macOS 26.6.2 (build 25G83), arm64; Codex Desktop 26.825.32147 (build 7303). uname -mprs: Darwin 25.6.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
No response
Codex doctor report
What issue are you seeing?
With allow_local_binding=true, a local loopback connection from the Codex sandbox succeeds for 127.0.0.1 and ::1, but fails when the same IPv4 listener is reached through the IPv4-mapped IPv6 address ::ffff:127.0.0.1.
This is direct loopback traffic and does not pass through the network proxy. The issue is that the Codex sandbox's local-binding permission does not appear to cover this address representation.
What steps can reproduce the bug?
-
Enable the Codex managed network proxy and allow local binding.
-
Save this minimal source as
/tmp/codex_loopback.py:import socket import threading def listener(family, host): server = socket.socket(family, socket.SOCK_STREAM) if family == socket.AF_INET6: server.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) server.bind((host, 0)) server.listen(1) port = server.getsockname()[1] def accept_one(): connection, _ = server.accept() connection.close() server.close() thread = threading.Thread(target=accept_one, daemon=True) thread.start() return port, thread def connect(label, family, host, port, dual_stack=False): client = socket.socket(family, socket.SOCK_STREAM) if dual_stack: client.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) try: client.connect((host, port)) print(f"{label}: CONNECTED") except OSError as error: print(f"{label}: {type(error).__name__}: {error}") finally: client.close() ipv4_port, ipv4_thread = listener(socket.AF_INET, "127.0.0.1") connect("AF_INET 127.0.0.1", socket.AF_INET, "127.0.0.1", ipv4_port) ipv4_thread.join(timeout=1) ipv6_port, ipv6_thread = listener(socket.AF_INET6, "::1") connect("AF_INET6 ::1", socket.AF_INET6, "::1", ipv6_port) ipv6_thread.join(timeout=1) mapped_port, mapped_thread = listener(socket.AF_INET, "127.0.0.1") connect( "AF_INET6 dual-stack ::ffff:127.0.0.1", socket.AF_INET6, "::ffff:127.0.0.1", mapped_port, dual_stack=True, ) mapped_thread.join(timeout=1) -
Run it in a Codex sandbox:
codex sandbox --log-denials /bin/sh -c 'python3 /tmp/codex_loopback.py'The first two connections succeed. The mapped connection fails with
PermissionError: [Errno 1] Operation not permitted, and the sandbox log records a network denial.
What is the expected behavior?
When allow_local_binding=true, ::ffff:127.0.0.1 should have the same local loopback permission as 127.0.0.1. A dual-stack client should be able to connect to a local IPv4 listener.
Additional information
The following are separate follow-up diagnostic experiments, not steps in the
Codex reproducer above.
Codex 0.149.0 was observed to generate this Seatbelt exception for local
binding:
(allow network-outbound (remote ip "localhost:*"))
Running an independent sandbox-exec probe with that rule reproduced the same result: 127.0.0.1 and ::1 were allowed, while ::ffff:127.0.0.1 failed with EPERM. Attempts to express a narrower rule with an IP literal were rejected by the parser (host must be * or localhost in network address). Wildcard rules (*:* and *:port) allowed the mapped connection, but broaden egress and are not suitable fixes.
Apple does not document SBPL for third-party use, and sandbox-exec(1) does not define these address-matching semantics. No safe, narrow rule for the mapped loopback address was identified. Please confirm whether this is a limitation of macOS Seatbelt’s localhost matching. If Codex can safely support IPv4-mapped loopback connections without allowing non-loopback egress, please consider doing so. Otherwise, please document the limitation and provide a supported workaround.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the local-binding sandbox policy and the /tmp/codex_loopback.py reproducer. Run codex sandbox --log-denials /bin/sh -c 'python3 /tmp/codex_loopback.py' and inspect how the Seatbelt localhost:* rule handles IPv4-mapped IPv6 addresses. Done means the mapped loopback connection is safely permitted without broadening egress, or the limitation and a supported workaround are documented.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, rust
- Domain
- cli, operating-systems, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100