b8029973aa
## Summary Follow-up to #1688. Three fixes on the `network.egressProxy` / `network["egress_proxy"]` paths a caller reaches by bypassing the types, plus the one changelog snippet that does not run. **The guards disagreed.** Python raised `InvalidArgumentException` for a proxy without a string `address`; JS had none, and since `buildEgressProxyBody` rebuilds the body from the known fields, an address that isn't there simply vanished and the caller got an API error about a `{}` they never wrote. Nothing leaked — `address` is `required` in the spec, so all of these already failed closed at the API — but the error named the wrong thing. ```ts function buildEgressProxyBody(egressProxy: SandboxEgressProxyOpts) { + if (!isPlainObject(egressProxy) || typeof egressProxy.address !== 'string') { + throw new InvalidArgumentError( + `network egressProxy must be an object with a string 'address' (e.g. 'proxy.example.com:1080').` + ) + } ``` | caller passes | JS before | JS now | Python | |---|---|---|---| | `{}` | `{"egressProxy":{}}` | `InvalidArgumentError` | `InvalidArgumentException` | | `{address: 1080}` | `{"egressProxy":{"address":1080}}` | `InvalidArgumentError` | `InvalidArgumentException` | | `'proxy.example.com:1080'` | `{"egressProxy":{}}` — address dropped | `InvalidArgumentError` | `InvalidArgumentException` | **`null` credentials reached the wire.** Both SDKs checked `!== undefined` / `in`, so a `null` / `None` username or password serialized as a JSON null the API rejects (`type: string`). Reading a credential out of an unset environment variable is the way to land there, and it is the one case where the caller means "this proxy takes no credentials" — both now skip a nullish credential (`!= null` in JS, `.get(...) is not None` in Python). ```ts await Sandbox.create({ network: { egressProxy: { address: 'proxy.example.com:1080', // Unset in the environment; the proxy takes no credentials. username: process.env.PROXY_USER, }, }, }) ``` ```python Sandbox.create( network={ "egress_proxy": { "address": "proxy.example.com:1080", "username": os.environ.get("PROXY_USER"), }, }, ) ``` **The published `get_info` snippet.** The 2.41.0 entry in both changelogs printed `info.network["egress_proxy"]`, but `SandboxInfo.network` is `Optional` and `egress_proxy` is `NotRequired` — pyright rejects it (`reportOptionalSubscript`, `reportTypedDictNotRequiredAccess`) and it raises at runtime for a sandbox without a proxy, unlike the TS line right above it (`info.network?.egressProxy`). It now reads `print((info.network or {}).get("egress_proxy"))`. Tests cover the three rejected JS shapes and the null credentials in both SDKs. Link to Devin session: https://app.devin.ai/sessions/216bb02ad7924e4ba5f2968aaadc7938 Requested by: @mishushakov --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: mish@e2b.dev <mish@e2b.dev>