Created
March 5, 2026 19:19
-
-
Save lukehinds/6471a44b521f78b04f88c6d2627366d6 to your computer and use it in GitHub Desktop.
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
| #!/bin/bash | |
| # Test: sandboxed agent routes through nono proxy -> external proxy -> internet | |
| # + localhost IPC via --allow-port | |
| # | |
| # Topology: | |
| # [sandboxed curl] -> [nono proxy] -> [external proxy :9090] -> internet | |
| # [sandboxed curl] -> localhost:8000 (local service, via --allow-port) | |
| # | |
| # The external proxy and local service run OUTSIDE the sandbox. | |
| EXTERNAL_PORT=9090 | |
| LOCAL_PORT=8000 | |
| EXT_PROXY_PID="" | |
| LOCAL_SVC_PID="" | |
| NONO="./target/debug/nono" | |
| cleanup() { | |
| kill "$EXT_PROXY_PID" 2>/dev/null || true | |
| kill "$LOCAL_SVC_PID" 2>/dev/null || true | |
| wait "$EXT_PROXY_PID" 2>/dev/null || true | |
| wait "$LOCAL_SVC_PID" 2>/dev/null || true | |
| } | |
| trap cleanup EXIT | |
| # Build first, so cargo output doesn't pollute test output | |
| cargo build --quiet 2>/dev/null | |
| echo "Starting external proxy on port $EXTERNAL_PORT..." | |
| python3 -c " | |
| import socket, threading, select | |
| def handle_connect(client, target_host, target_port): | |
| try: | |
| remote = socket.create_connection((target_host, target_port), timeout=10) | |
| client.sendall(b'HTTP/1.1 200 Connection Established\r\n\r\n') | |
| sockets = [client, remote] | |
| while True: | |
| readable, _, exceptional = select.select(sockets, [], sockets, 30) | |
| if exceptional: | |
| break | |
| for s in readable: | |
| data = s.recv(8192) | |
| if not data: | |
| raise ConnectionError | |
| if s is client: | |
| remote.sendall(data) | |
| else: | |
| client.sendall(data) | |
| except Exception: | |
| pass | |
| finally: | |
| client.close() | |
| try: remote.close() | |
| except: pass | |
| def handle_client(client, addr): | |
| try: | |
| request = client.recv(4096).decode('utf-8', errors='replace') | |
| first_line = request.split('\r\n')[0] | |
| if first_line.startswith('CONNECT'): | |
| parts = first_line.split() | |
| authority = parts[1] | |
| host, port = authority.rsplit(':', 1) | |
| handle_connect(client, host, int(port)) | |
| else: | |
| client.sendall(b'HTTP/1.1 405 Method Not Allowed\r\n\r\nOnly CONNECT supported\n') | |
| client.close() | |
| except Exception as e: | |
| try: client.close() | |
| except: pass | |
| server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
| server.bind(('127.0.0.1', $EXTERNAL_PORT)) | |
| server.listen(5) | |
| while True: | |
| client, addr = server.accept() | |
| threading.Thread(target=handle_client, args=(client, addr), daemon=True).start() | |
| " & | |
| EXT_PROXY_PID=$! | |
| echo "Starting local service on port $LOCAL_PORT..." | |
| python3 -c " | |
| from http.server import HTTPServer, BaseHTTPRequestHandler | |
| class Handler(BaseHTTPRequestHandler): | |
| def do_GET(self): | |
| self.send_response(200) | |
| self.send_header('Content-Type', 'text/plain') | |
| self.end_headers() | |
| self.wfile.write(b'Hello from local service') | |
| def log_message(self, *args): | |
| pass | |
| HTTPServer(('127.0.0.1', $LOCAL_PORT), Handler).serve_forever() | |
| " & | |
| LOCAL_SVC_PID=$! | |
| sleep 1 | |
| echo "" | |
| echo "========================================" | |
| echo " External Proxy + Allow-Port Test Suite" | |
| echo "========================================" | |
| echo "" | |
| echo "Sandbox command:" | |
| echo " nono run --network-profile minimal \\" | |
| echo " --proxy-allow '*.google.com' \\" | |
| echo " --external-proxy 127.0.0.1:$EXTERNAL_PORT \\" | |
| echo " --allow-port $LOCAL_PORT --allow-cwd" | |
| echo "" | |
| echo "--- Test 1: Proxy filtering (same sandbox session) ---" | |
| echo "" | |
| $NONO run \ | |
| --network-profile minimal \ | |
| --proxy-allow '*.google.com' \ | |
| --external-proxy "127.0.0.1:$EXTERNAL_PORT" \ | |
| --allow-port "$LOCAL_PORT" \ | |
| --allow-cwd --silent \ | |
| -- sh -c ' | |
| CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 --max-time 10 https://docs.google.com) | |
| echo " docs.google.com -> HTTP $CODE (expected: 2xx/3xx)" | |
| CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 --max-time 5 https://www.bing.com) | |
| echo " www.bing.com -> HTTP $CODE (expected: 000, blocked)" | |
| ' 2>/dev/null || true | |
| echo "" | |
| echo "--- Test 2: Direct bypass attempt (same sandbox session) ---" | |
| echo "" | |
| $NONO run \ | |
| --network-profile minimal \ | |
| --proxy-allow '*.google.com' \ | |
| --external-proxy "127.0.0.1:$EXTERNAL_PORT" \ | |
| --allow-port "$LOCAL_PORT" \ | |
| --allow-cwd --silent \ | |
| -- sh -c ' | |
| CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 --max-time 5 --noproxy "*" https://google.com) | |
| echo " google.com (no proxy) -> HTTP $CODE (expected: 000, sandbox blocked)" | |
| ' 2>/dev/null || true | |
| echo "" | |
| echo "--- Test 3: Localhost IPC (same sandbox session) ---" | |
| echo "" | |
| $NONO run \ | |
| --network-profile minimal \ | |
| --proxy-allow '*.google.com' \ | |
| --external-proxy "127.0.0.1:$EXTERNAL_PORT" \ | |
| --allow-port "$LOCAL_PORT" \ | |
| --allow-cwd --silent \ | |
| -- sh -c ' | |
| CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 --max-time 5 http://127.0.0.1:'"$LOCAL_PORT"') | |
| echo " localhost:'"$LOCAL_PORT"' -> HTTP $CODE (expected: 200)" | |
| CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 --max-time 5 http://127.0.0.1:9999) | |
| echo " localhost:9999 -> HTTP $CODE (expected: 000, wrong port)" | |
| ' 2>/dev/null || true | |
| echo "" | |
| echo "========================================" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment