Skip to content

feat(mitmproxy): predefined network allowlist presets - #105

Merged
adamw merged 1 commit into
masterfrom
feat/2-network-presets
Sep 4, 2026
Merged

adamw merged 1 commit into
masterfrom
feat/2-network-presets

Conversation

@shejnowicz

Copy link
Copy Markdown
Collaborator

Closes #2.

Summary

Adds network presets — {"preset": "<name>"} entries in the network policy that expand in place to predefined allow-rule groups, as requested in #2 (modeled on agent-sandbox's enforcer lists and tsk's squid.conf).

  • Expansion preserves rule order, so first-match-wins works across presets (a deny before a preset shadows its hosts).
  • Fail-loud: unknown preset name or preset combined with other keys raises at load() — the proxy refuses to start rather than run a policy different from the one asked for.
  • Presets are host-only (no method restriction) — matching the domain-level lists they're modeled on; method-tightening registries breaks legitimate flows (npm audit POSTs) for marginal gain.
  • One preset per init stack (python/node/java/scala/go/rust/ruby/dotnet/zig — a test keeps this in sync with STACK_NAMES), plus nix, vscode, jetbrains, github, anthropic, openai.
  • Ecosystem presets are self-contained (scala repeats Maven hosts); duplicates across combined presets are harmless under first-match-wins.

This PR does not change the default policy — the project template keeps allow * GET. A follow-up PR will add an opt-in strict mode that replaces the wildcard with stack presets.

Test plan

  • pytest: 337 passed (14 new preset tests × both addon variants), incl. in-place ordering, fail-loud on unknown/mixed keys, definitions-vs-STACK_NAMES sync guard
  • Full bats: 16/16 suites
  • Hands-on integration in a real container: project policy set to {"preset":"python"}, {"preset":"github"} (no wildcard) → pip download from pypi succeeds through the proxy, curl https://example.com → 403, DNS for non-preset hosts REFUSED; unknown preset → mitmproxy fails to become healthy (fail-loud verified live)

🤖 Generated with Claude Code

Closes #2.

A `network` entry of `{"preset": "<name>"}` now expands, in place, to a
predefined group of allow rules — so tightening a policy no longer means
hand-maintaining every registry host per ecosystem. Expansion preserves
rule order (first-match-wins works across presets: a deny placed before a
preset shadows its hosts), a preset entry must carry the `preset` key
alone, and an unknown name raises at load() so the proxy fails to start
instead of silently running a different policy than asked for.

Presets (host-only, modeled on the domain-level allowlists from the issue —
agent-sandbox's enforcer and tsk's squid.conf): one per `sandcat init`
stack (python, node, java, scala, go, rust, ruby, dotnet, zig — kept in
sync with STACK_NAMES by a test), plus nix (runtime devbox installs),
vscode, jetbrains, github, anthropic, openai. Ecosystem presets are
self-contained (scala repeats the Maven hosts) so a single preset never
has a hidden dependency; duplicates across presets are harmless under
first-match-wins.

Docs: new "Network presets" section; the hand-maintained per-stack domain
table is replaced by a pointer to it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@adamw
adamw merged commit d8ed249 into master Sep 4, 2026
6 checks passed
@adamw
adamw deleted the feat/2-network-presets branch September 4, 2026 09:10
shejnowicz added a commit that referenced this pull request Sep 7, 2026
…ildcard

Builds on the network presets (#2/#105). `sandcat init --features
strict-network` (or SANDCAT_STRICT_NETWORK=true) generates project settings
whose `network` list holds one `{"preset": "<stack>"}` entry per resolved
stack and no allow-all-GET wildcard, so anything beyond the stack
registries and the user-settings layer (the agent's own API hosts) is
denied by default — including DNS resolution.

Kept opt-in on purpose: flipping the default would surprise every new
project with "sandcat blocks the internet"; the summary line now states
which policy the project got and how to switch.

`init settings` grows --strict-network/--stacks flags; with no stacks the
strict list is empty (user-settings layer only). Preset names are expanded
by the addon at proxy start, so domain lists update with the sandcat
version instead of freezing in the project file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
adamw pushed a commit that referenced this pull request Sep 7, 2026
…ildcard (#106)

Stacked on #105 (network presets) — the base branch is
`feat/2-network-presets`; retarget to master after #105 merges.
Follow-up to #2; no separate issue, so no close keyword.

## Summary

`sandcat init --features strict-network` (or
`SANDCAT_STRICT_NETWORK=true`) generates project settings whose
`network` list holds one `{"preset": "<stack>"}` entry per resolved
stack and **no allow-all-GET wildcard**. Everything beyond the stack
registries and the user-settings layer (the agent's own API hosts) is
then denied by default — including DNS resolution, so blocked hosts
never even resolve.

- Opt-in by design: flipping the default would surprise every new
project with "sandcat blocks the internet". The init summary states
which policy the project got (`Network: strict — stack presets: python,
java` vs `default (allow all GET; tighten with --features
strict-network)`).
- `init settings` grows `--strict-network` / `--stacks` flags; with no
stacks the strict list is empty (user-settings layer only).
- Only preset *names* land in the project file — the addon expands them
at proxy start, so domain lists update with the sandcat version instead
of freezing per project.
- `scala` resolves to `java scala` via the existing stack-deps
mechanism, so both presets are seeded.

## Test plan

- [x] bats: init 167/167 (new: feature parsing incl. error-message
listing, strict/default summary lines, settings --strict-network
seeding, empty-stacks case, unknown-option rejection; interactive stubs
updated for the new feature label)
- [x] Full bats: 16/16 suites
- [x] Hands-on integration in a real container: `init --stacks python
--features strict-network` generates
`{\"network\":[{\"preset\":\"python\"}]}` (no wildcard); stack healthy;
pypi.org **200** (stack preset), www.anthropic.com + github.com **200**
(presets expanded from the *user-settings layer* — expansion works
across layers), example.com **DNS REFUSED** (rc=6 before HTTP); real
PyPI package fetch end-to-end through the proxy (JSON API → wheel from
files.pythonhosted.org, valid zip). Note: `pip download` itself is
unavailable in the devbox/nix python (no ensurepip) — toolchain quirk,
not policy; the fetch above covers the network path.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

improvement: Predefined allowlist definitions

2 participants