Split a multi-line AI_SBX_NETWORK correctly

A long host list is naturally written as a multi-line TOML string, but read
stops at the first newline, so only the first host was ever allowed. The rest
failed later as connection errors with nothing pointing back at the list.

Newlines and carriage returns are now flattened alongside commas before
splitting, and the test covers a multi-line value; it fails without the fix.

Also records the measured host requirements for a full Neovim configuration.
The Balanced policy already permits github, npm, pypi, crates, go, ubuntu,
nodejs, hashicorp releases, Copilot and the LLM APIs, which covers 93 lazy.nvim
plugins, both mason registries and all 55 mason packages. Only the .NET and
Terraform registries need declaring.
This commit is contained in:
2026-08-03 12:37:53 -05:00
parent 2890b1dd98
commit ad2b33f984
3 changed files with 51 additions and 15 deletions
+36 -14
View File
@@ -164,9 +164,9 @@ expect ≥ 0.10. Prefer the upstream tarball or keep it on mise rather than `apt
.config/nvim/ the smallest config that is pleasant on a fresh machine .config/nvim/ the smallest config that is pleasant on a fresh machine
``` ```
Keep the nvim config deliberately minimal. A plugin-manager bootstrap that fetches Ship the **full** config. The network objection that would have argued for trimming it
from the network on first launch will hit the default-deny policy, and debugging that does not survive measurement — see below — so the only real cost is a slower first
inside a microVM is a bad first experience. launch while plugins and LSP servers download.
### The launcher ### The launcher
@@ -293,22 +293,44 @@ Setting a distinct prefix in the sandbox `.tmux.conf` avoids a confusing fight o
**Terminal type.** `TERM` must survive into the sandbox or nvim renders badly. **Terminal type.** `TERM` must survive into the sandbox or nvim renders badly.
`sbx exec -t` should handle it; confirm rather than assume. `sbx exec -t` should handle it; confirm rather than assume.
**LazyVim bootstrap — measured, and not a problem.** Every host the config needs is **Plugin bootstrap — measured host by host with `sbx policy check network`.**
already permitted by the `Balanced` policy, checked individually with
`sbx policy check network`: Already permitted by `Balanced`, so nothing to declare:
```text ```text
github.com codeload.github.com raw/objects.githubusercontent.com Allowed github.com codeload.github.com raw/objects.githubusercontent.com
registry.npmjs.org pypi.org files.pythonhosted.org Allowed registry.npmjs.org pypi.org files.pythonhosted.org
crates.io static.crates.io proxy.golang.org sum.golang.org Allowed crates.io static.crates.io proxy.golang.org sum.golang.org
nodejs.org deb.debian.org archive/security.ubuntu.com
releases.hashicorp.com checkpoint-api.hashicorp.com
api.githubcopilot.com copilot-proxy.githubusercontent.com
api.anthropic.com api.openai.com
``` ```
That covers all 93 pinned lazy.nvim plugins and all 55 mason packages (22 npm, 4 pypi, That covers all 93 pinned lazy.nvim plugins, both mason registries (`mason-org` and
29 GitHub releases). First launch will be slow, not blocked. `crashdummyy`, both `github:`), all 55 mason packages, Copilot, and codecompanion.
The hosts that *were* denied are the work ones — `npm.fontawesome.com`, Denied, and therefore declared in `AI_SBX_NETWORK`:
`proget.careevolution.com`, `localstack.cloud` — now handled by `AI_SBX_NETWORK` and
by automatic allowance of any host backing a provisioned secret. | Host | Needed by |
| --- | --- |
| `*.nuget.org` | roslyn, easy-dotnet, NuGet restore |
| `pkgs.dev.azure.com` | Azure-hosted NuGet feeds |
| `builds.dotnet.microsoft.com`, `dotnetcli.azureedge.net`, `dotnetbuilds.azureedge.net`, `dotnetcli.blob.core.windows.net`, `ci.dot.net` | .NET SDK downloads |
| `registry.terraform.io` | provider downloads for terragrunt |
| `mise.jdx.dev` | mise self-resolution |
| `default.exp-tas.com` | Copilot feature flags |
Confirmed reachable from inside the sandbox afterwards: `api.nuget.org` and
`registry.terraform.io` both return HTTP 200.
Wildcards match a single label: `*.nuget.org` covers `api.`, `www.`, `globalcdn.` and
the bare domain, but `*.azureedge.net` does **not** reach
`dotnetcli.blob.core.windows.net`. Prefer explicit hosts over a broad CDN wildcard —
`*.azureedge.net` would admit every Azure CDN customer, not just Microsoft's.
The work registries — `npm.fontawesome.com`, `proget.careevolution.com`,
`localstack.cloud` — are allowed automatically, since they back provisioned secrets.
## Acceptance ## Acceptance
+7 -1
View File
@@ -806,8 +806,14 @@ allow_sandbox_host() {
install_sandbox_network() { install_sandbox_network() {
[[ -n "$DEFAULT_NETWORK" ]] || return 0 [[ -n "$DEFAULT_NETWORK" ]] || return 0
# A multi-line TOML string is a natural way to write a long list, and read
# stops at the first newline, so separators are flattened before splitting.
local normalized="${DEFAULT_NETWORK//,/ }"
normalized="${normalized//$'\n'/ }"
normalized="${normalized//$'\r'/ }"
local -a allow_hosts local -a allow_hosts
read -r -a allow_hosts <<<"${DEFAULT_NETWORK//,/ }" read -r -a allow_hosts <<<"$normalized"
((${#allow_hosts[@]})) || return 0 ((${#allow_hosts[@]})) || return 0
+8
View File
@@ -30,6 +30,14 @@ for host in a.example.com b.example.com c.example.com; do
fail "install_sandbox_network skipped $host (comma and space must both split)" fail "install_sandbox_network skipped $host (comma and space must both split)"
done done
allowed=""
DEFAULT_NETWORK="$(printf '*.nuget.org,\nregistry.terraform.io,\nmise.jdx.dev')" \
install_sandbox_network >/dev/null
for host in '*.nuget.org' registry.terraform.io mise.jdx.dev; do
[[ "$allowed" == *"$host"* ]] ||
fail "multi-line AI_SBX_NETWORK dropped $host"
done
allowed="" allowed=""
DEFAULT_NETWORK="" install_sandbox_network >/dev/null DEFAULT_NETWORK="" install_sandbox_network >/dev/null
[[ -z "${allowed// /}" ]] || [[ -z "${allowed// /}" ]] ||