Skip to content

Wire NetworkConfig through Bash + curl + interpreter (closes #5)#7

Open
ajram23 wants to merge 4 commits into
dbreunig:mainfrom
ajram23:feat/network-curl-wiring
Open

Wire NetworkConfig through Bash + curl + interpreter (closes #5)#7
ajram23 wants to merge 4 commits into
dbreunig:mainfrom
ajram23:feat/network-curl-wiring

Conversation

@ajram23
Copy link
Copy Markdown

@ajram23 ajram23 commented May 29, 2026

What this does

Upstream defines NetworkConfig and CommandContext.fetch but they never reach Bash, Interpreter, or the curl command, so passing network=NetworkConfig(...) is a no-op (see #5"command not found\n Is shown even when a network config is provided"). This PR wires the plumbing through so the documented surface actually executes network calls.

Components

Touches src/just_bash/__init__.py, bash.py, commands/curl/curl.py, interpreter/interpreter.py, types.py, and adds src/just_bash/network/__init__.py + tests/test_network.py.

  • aiohttp-backed default fetch built from NetworkConfig
  • curl command registered only when network/fetch is configured (no-op fallback otherwise — preserves existing behavior)
  • URL allow-list with origin + path-prefix matching using segment-boundary checks (not raw startsWith)
  • Allowed-methods enforcement; manual redirect handling with per-hop allow-list re-check
  • max_redirects, timeout_ms, max_response_size enforced
  • deny_private_ranges with lexical IPv4/IPv6 checks + DNS-resolution recheck, DNS-pinned aiohttp.TCPConnector to defeat rebinding between preflight and connection
  • Header transforms (RequestTransform) applied at the fetch boundary so credentials never enter the sandbox
  • Byte-preserving response body for curl -o writes

TS-parity hardening

Ported from vercel-labs/just-bash src/network/allow-list.ts:

  • Fail-fast validateAllowList from make_default_fetch unless dangerously_allow_full_internet_access=True — rejects malformed entries, missing scheme/host, non-http(s) schemes, query strings and fragments, and ambiguous path separators (\, %2f, %5c)
  • Origin matching normalized like TS URL.origin: lowercase scheme/host, strip default ports (:80, :443), exact non-default ports
  • Explicit IPv4 private-range table including CGNAT 100.64.0.0/10 (which ipaddress.IPv4Address.is_private misses in 3.11), benchmarking 198.18.0.0/15, IETF/TEST-NET, and reserved 240/4
  • Lexical IPv4 parser accepting 2130706433 and 0x7f.0.0.1 style numeric forms (socket.getaddrinfo catches them at resolve time but the lexical pass needs parity with the TS sibling)
  • Explicit IPv6 checks: ::, ::1, fe80::/10, fc00::/7, ::ffff: IPv4-mapped, 2001:db8::/32, NAT64 64:ff9b::/96, NAT64-local 64:ff9b:1::/48, 6to4 2002::/16 with embedded-v4 recheck
  • Defensive Content-Length parsing (malformed → ignored, rely on streamed body-size enforcement instead of bubbling ValueError)

Tests

tests/test_network.py covers allow-list validation + matching, allowed-methods, redirects with per-hop checks, timeout, response size, private ranges, content-length parsing, header transforms, and byte-preserving response body.

Why upstream this now

This work has been carrying as a fork (ajram23/just-bash-py tag v0.1.16.post1) since May 2026 to unblock real agent network operations (the curl command in a sandboxed agent fetch loop is the load-bearing use case). The fork's ORBIT_FORK.md explicitly tracks the retirement criterion: "Delete the fork and switch back to upstream PyPI just-bash once dbreunig/just-bash-py releases a version that wires NetworkConfig through Bash and ships the TS-parity allow-list / private-range checks." This PR is that release candidate.

Closes #5.

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.

"command not found\n" Is shown even when a network config is provided.

1 participant