# Threat Model

This document defines what Foundry Sandbox protects against, why these protections exist, and how they're implemented.

## Documentation Map

| If you want to... | See... |
|-------------------|--------|
| Understand what threats exist and how they're addressed | This document |
| Learn how each security pillar works | [Security Architecture](security-architecture.md) |
| Understand credential isolation in depth | [Credential Isolation](credential-isolation.md) |
| Configure network modes and domains | [Network Isolation](network-isolation.md) |
| Get a quick security overview | [Security Overview](index.md) |

---

## What We're Protecting

### Assets at Risk

| Asset | Risk Level | Protection |
|-------|------------|------------|
| Host filesystem | Critical | Read-only root, container isolation |
| Git history | High | Read-only filesystem, unified-proxy force-push blocking |
| Production credentials | High | Sandboxes don't have access by default |
| Other projects | Medium | Branch isolation (deny-by-default ref validation, output filtering), separate Docker networks |
| System stability | Medium | Resource limits, no root access |

### Threat Actors

The primary "threat actor" is not malicious - it's **AI coding assistants** that may:

1. **Hallucinate dangerous commands** - An AI might suggest `rm -rf /` or `git reset --hard` without understanding the consequences
2. **Act without full context** - The AI may not know it's in a sandbox vs. production
3. **Make honest mistakes** - Just like humans, but potentially faster and at scale
4. **Follow user instructions too literally** - "Clean up the repo" could be interpreted destructively

This is not about defending against intentional attacks. It's about providing guardrails for well-intentioned but error-prone automation.

---

## Security Pillar Quick Reference

Each pillar blocks specific threat categories. For implementation details, see [Security Architecture](security-architecture.md).

| Pillar | What It Blocks | What It Doesn't Block |
|--------|----------------|----------------------|
| **Read-only Filesystem** | Filesystem writes, system modification, persistent malware | Writes to tmpfs mounts (/tmp, /home/ubuntu) |
| **Network Isolation** | Unauthorized egress, direct external access, DNS exfiltration, IP spoofing/ARP poisoning (CAP_NET_RAW dropped), direct-IP requests (all encodings blocked) | Traffic to allowed domains (GitHub, AI APIs); normal TCP/UDP networking |
| **Sudoers Allowlist** | Arbitrary sudo commands, privilege escalation | Allowed commands (apt-get install, service management) |
| **Credential Isolation** | Credential theft, API key exfiltration, env var scraping | Authorized API calls via gateway/proxy |
| **Branch Isolation** | Cross-sandbox branch access, unauthorized ref checkout, branch listing leaks | Access to well-known branches (main, master, develop, etc.) and tags |
| **Git Safety** | Force pushes to protected branches, branch/tag deletion, dangerous GitHub API operations, PR merges (REST and GraphQL), CI/CD pipeline modifications on push | Git operations on sandbox's own branch; commits/pushes to non-restricted files |

---

## Threat Landscape

### Threat-to-Defense Matrix

| Threat | Primary Defense | Secondary Defense | Details |
|--------|-----------------|-------------------|---------|
| Filesystem destruction | Read-only Filesystem | Sudoers Allowlist | [Filesystem Threats](#1-filesystem-destruction) |
| Local git destruction | Read-only Worktree | — | [Git Threats](#2-git-operations) |
| Remote git destruction | Unified proxy (force-push blocking) | — | [Git Threats](#2-git-operations) |
| Credential theft | Credential Isolation | Network Isolation | [Credential Threats](#3-credential-theft) |
| Supply chain attacks | Credential Isolation | Network + CAP_NET_RAW | [Supply Chain](#4-supply-chain-attacks) |
| Lateral movement | Network (ICC=false) | CAP_NET_RAW dropped | [Lateral Movement](#5-lateral-movement) |
| Cross-sandbox branch access | Branch Isolation | Output filtering | [Branch Isolation](#branch-isolation) |
| Session hijacking | IP binding | CAP_NET_RAW dropped | [Session Attacks](#6-session-token-attacks) |
| DNS exfiltration | Network (DNS filter) | Domain allowlist | [DNS Exfiltration](#7-dns-exfiltration) |
| Sudo escalation | Sudoers Allowlist | Read-only Filesystem | [Sudo Escalation](#8-sudo-escalation) |
| IP encoding bypass (SSRF) | IP literal detection (regex + inet_aton) | Domain allowlist | [IP Encoding Bypass](#9-ip-encoding-bypass) |
| PR merge (REST + GraphQL) | Early-exit merge blocking | GitHub API blocklist (defense-in-depth) | [PR Merge Prevention](#10-pr-merge-prevention) |
| CI/CD pipeline modification | Push-time file validation | Commit-time file validation | [CI/CD Pipeline Protection](#11-cicd-pipeline-protection) |
| Proxy pack parsing exploit | Pack size limit + timeout | Fail-closed, isolated tempdir | [Proxy-Side Attack Surface](#proxy-side-attack-surface) |

---

### 1. Filesystem Destruction

AI assistants execute bash commands. Any command the AI runs could potentially delete files, overwrite history, or modify system state.

**Attack Vectors:**
- `rm -rf /` or `rm -rf .`
- `git clean -f` - Deletes untracked files
- `sudo rm` - Bypasses user permissions

**Defense Layers:**

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | Read-only Filesystem | All writes fail with "Read-only file system" error |
| Secondary | Sudoers Allowlist | `sudo rm` is not permitted |

**Why This Works:** The read-only filesystem stops all writes regardless of how the command is invoked. See [Read-only Filesystem](security-architecture.md#read-only-filesystem) for implementation.

---

### 2. Git Operations

Git commands can destroy work that may be unrecoverable. We distinguish between **local** destruction (affects your worktree) and **remote** destruction (affects collaborators).

**Attack Vectors:**

| Attack | Scope | Impact |
|--------|-------|--------|
| `git reset --hard` | Local | Discards uncommitted changes |
| `git clean -f` | Local | Deletes untracked files |
| `git checkout -- <file>` | Local | Discards file changes |
| `git push --force` | Remote | Overwrites remote history (affects collaborators) |
| `git push :refs/heads/main` | Remote | Deletes remote branch |

**Defense Layers:**

*Local git destruction:*

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | Read-only Worktree | Worktree files cannot be modified—`git reset --hard` silently fails |

*Remote git destruction:*

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | Git proxy (unified-proxy) | Blocks force pushes to protected branches, blocks all branch/tag deletions |
| Secondary | Protected branch enforcement | Blocks ALL direct pushes to main/master/release/production (not just force pushes) |
| Tertiary | Bot mode restrictions | In bot mode, pushes restricted to `sandbox/*` branches only |

**Why This Works:** For local destruction, the read-only filesystem is the actual security control—writes fail regardless of how commands are invoked. For remote destruction, the unified proxy's `git_proxy` addon parses git pkt-line protocol data to detect and block force pushes, branch deletions, and unauthorized repository access. See [Read-only Filesystem](security-architecture.md#read-only-filesystem) for local protection details.

#### Git Shadow Mode

In credential isolation mode, sandboxes cannot directly access the `.git` directory or run git commands against the local repository. Instead, all git operations are proxied through the unified-proxy's git API server.

**How it works:**

1. **`.git` is hidden** — The worktree's `.git` file (a gitdir pointer) is bind-mounted to `/dev/null` in docker-compose, then overlaid with a 1KB tmpfs in `entrypoint-root.sh`. The sandbox sees an empty directory via both `ls` and `stat`, with no `/dev/null` fingerprint. If the tmpfs overlay fails, the `/dev/null` bind mount remains as a fallback
2. **Git wrapper intercepts commands** — `stubs/git-wrapper.sh` is mounted at `/usr/local/bin/git`, taking precedence over `/usr/bin/git`. For any git command under `/workspace`, the wrapper proxies the request to the git API server
3. **Git API server** — The unified-proxy runs a git API endpoint on port 8083 that receives JSON-encoded git commands, validates them, and executes them against the bare repository
4. **HMAC-SHA256 authentication** — Each request is signed with a per-sandbox HMAC secret. The signature covers the HTTP method, path, request body hash, timestamp, and a random nonce to prevent replay attacks
5. **Policy enforcement** — Before executing any command, the git API applies the same policy checks as the HTTPS proxy: repo authorization, force-push blocking, branch deletion blocking, and bot mode branch restrictions

**What the proxy enforces:**

| Policy | Effect |
|--------|--------|
| Repo authorization | Container can only access repos listed in its registration metadata |
| Branch deletion blocking | `git push` that would delete any branch or tag is rejected |
| Force-push blocking | Non-fast-forward pushes to protected branches are rejected |
| Bot mode | When `auth_mode=bot`, pushes restricted to `sandbox/*` branches |
| Push size limits | Pushes exceeding 100MB are rejected (413 response) |
| Malformed payload rejection | Pushes with unparseable pkt-line headers are rejected (fail closed) |

#### Git HTTPS Credential Injection

For remote git operations (`git push`, `git pull`), the unified-proxy injects `FOUNDRY_PROXY_GIT_TOKEN` into HTTPS requests to GitHub. The sandbox never receives the real GitHub token — it is held exclusively by the proxy container and injected at the network level during credential interception.

#### Branch Isolation

In credential isolation mode, each sandbox is restricted to its own branch via the `branch_isolation.py` module. This prevents one sandbox from accessing, modifying, or even seeing another sandbox's branches.

**How it works:**

1. **Deny-by-default ref validation** — For commands that reference branches (checkout, switch, fetch, pull, merge, rebase, cherry-pick), the validator checks that all ref arguments are allowed: the sandbox's own branch, well-known branches (main, master, develop, production, release/*, hotfix/*), and tags
2. **SHA reachability enforcement** — SHA arguments are validated to ensure they are ancestors of allowed branches, preventing access to commits on other sandboxes' branches
3. **Output filtering** — Commands that list refs (`git branch`, `for-each-ref`, `ls-remote`, `show-ref`, `log --decorate`) have their output filtered to hide disallowed branches
4. **Fail-closed** — Sandboxes without branch identity metadata in their container registration cannot execute git operations

**Defense layers:**

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | Deny-by-default ref validation | Git commands can only reference allowed branches |
| Secondary | SHA reachability | SHA args verified as ancestors of allowed branches |
| Tertiary | Output filtering | Branch listings hide other sandboxes' branches |
| Fail-safe | Missing metadata check | No branch identity = no git operations |

---

### 3. Credential Theft

A compromised sandbox could attempt to steal credentials and exfiltrate them.

**Attack Vectors:**
- Reading environment variables (`$GITHUB_TOKEN`, `$ANTHROPIC_API_KEY`)
- Searching filesystem for `.env` files or config with secrets
- Memory scraping of processes
- Intercepting network traffic to capture credentials in transit

**Defense Layers:**

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | Credential Isolation | Real credentials never enter sandbox |
| Secondary | Network Isolation | Cannot exfiltrate to unauthorized destinations |
| UX | Credential Redaction | Masks secrets in command output |

**Why This Works:** Sandboxes receive placeholder values (`CREDENTIAL_PROXY_PLACEHOLDER`) instead of real API keys. The unified-proxy container holds real credentials and injects them into outbound requests via two mechanisms: API gateways (plaintext HTTP endpoints on the internal Docker network for Anthropic on `:9848`, OpenAI on `:9849`, and GitHub on `:9850`) inject credentials without HTTPS interception, while MITM-required providers (Gemini, Tavily, Semantic Scholar, Perplexity, Zhipu) use a dedicated mitmproxy instance. Even if code reads the environment, it gets nothing useful. See [Credential Isolation](credential-isolation.md) for the complete architecture.

---

### 4. Supply Chain Attacks

Malicious npm packages, Python libraries, or other dependencies may contain code that attempts credential theft or data exfiltration.

**Attack Vectors:**
- Package postinstall scripts that read environment variables
- Dependencies that phone home with system information
- Typosquatting packages with malicious payloads
- Compromised maintainer accounts pushing malicious updates

**Defense Layers:**

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | Credential Isolation | Package reads placeholder values, not real credentials |
| Secondary | Network Isolation | Cannot exfiltrate to unauthorized domains |
| Tertiary | CAP_NET_RAW dropped | Cannot use raw sockets to bypass network controls |

**Why This Works:** When a malicious package runs `process.env.ANTHROPIC_API_KEY`, it gets `CREDENTIAL_PROXY_PLACEHOLDER`. When it tries to send this to `evil.com`, network isolation blocks the request. Even if it attempts raw packet crafting to bypass network rules, CAP_NET_RAW is dropped. See [Supply Chain Attack Scenario](credential-isolation.md#scenario-1-malicious-npm-package-in-sandbox-supply-chain-attack) in the credential isolation threat model for a detailed walkthrough.

---

### 5. Lateral Movement

One compromised sandbox could attempt to attack or access another sandbox running on the same host.

**Attack Vectors:**
- Direct container-to-container network connections
- ARP spoofing to intercept traffic meant for other containers
- IP spoofing to impersonate another container
- DNS rebinding to redirect traffic

**Defense Layers:**

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | ICC=false | Docker blocks L3/L4 traffic between containers |
| Secondary | CAP_NET_RAW dropped | Cannot create raw sockets for L2 attacks |
| Tertiary | IP binding | Sessions bound to originating container IP |

**Why This Works:** Docker's ICC (Inter-Container Communication) setting blocks TCP/UDP between containers at the network layer. However, ICC doesn't block Layer 2 (Ethernet) traffic. Dropping CAP_NET_RAW prevents creation of raw sockets needed for ARP spoofing or packet crafting. See [Lateral Movement Scenario](credential-isolation.md#scenario-3-compromised-container-attempts-lateral-movement) in the credential isolation threat model for details.

---

### 6. Session Token Attacks

Attackers could attempt to steal session tokens and use them from other locations.

**Attack Vectors:**
- Reading session token from proxy internal socket
- Network sniffing to capture tokens in transit
- IP spoofing to bypass session IP binding
- Waiting for session reuse from different context

**Defense Layers:**

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | IP binding | Sessions only valid from originating container IP |
| Secondary | CAP_NET_RAW dropped | Cannot spoof source IP addresses |
| Tertiary | Explicit unregistration | Sessions removed on sandbox destroy; optional TTL if configured |
| Network | ICC=false | Cannot access other containers to reuse their sessions |

**Why This Works:** Even if an attacker obtains a session token, it's bound to the container's IP address. Using it from another location fails validation. IP spoofing would require CAP_NET_RAW, which is dropped. See [Session Token Scenario](credential-isolation.md#scenario-4-session-token-theft-and-reuse) in the credential isolation threat model for the complete analysis.

---

### 7. DNS Exfiltration

Data can be encoded in DNS queries and sent to attacker-controlled nameservers.

**Attack Vectors:**
- Encoding data in DNS subdomains: `secret-data.evil.com`
- Using DNS TXT records for bidirectional communication
- Leveraging DNS-over-HTTPS to bypass monitoring

**Defense Layers:**

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | DNS filter | All DNS routed through unified-proxy |
| Secondary | Domain allowlist | Only allowed domains resolve |
| Tertiary | Internal network | No direct access to external DNS servers |

**Why This Works:** The unified-proxy runs a DNS filter addon that intercepts all DNS queries. Only domains on the allowlist resolve. Attempts to query `evil.com` return NXDOMAIN. Direct access to external DNS (8.8.8.8) is blocked by network isolation. See [Network Isolation](network-isolation.md) for configuration.

---

### 8. Sudo Escalation

If the AI gains unrestricted sudo access, it could bypass container restrictions.

**Attack Vectors:**
- `sudo rm -rf /` - Delete everything writable
- `sudo chmod` - Modify file permissions
- `sudo apt-get remove` - Remove security tooling
- Installing malicious packages with elevated privileges

**Defense Layers:**

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | Sudoers Allowlist | Only whitelisted commands permitted |
| Secondary | Read-only Filesystem | Even with sudo, can't write to read-only mounts |

**Why This Works:** The sudoers allowlist permits only safe commands (`sudo apt-get install *`, `sudo service * start/stop/restart/status`). There's no fallback `PASSWD:ALL` line. Even if a command were somehow permitted, the read-only filesystem would block destructive writes. See [Sudoers Allowlist](security-architecture.md#sudoers-allowlist) for the implementation.

---

### 9. IP Encoding Bypass

Agents could bypass domain-based network filtering by constructing URLs with IP addresses encoded in non-standard formats that resolve to internal or unauthorized hosts.

**Attack Vectors:**
- Dotted decimal: `http://1.2.3.4/`
- Octal encoding: `http://0177.0.0.1/` (resolves to 127.0.0.1)
- Hex encoding: `http://0x7f000001/`
- Integer encoding: `http://2130706433/` (resolves to 127.0.0.1)
- Mixed encoding: `http://0x7f.0.0.01/`
- IPv6 brackets: `http://[::1]/`

**Defense Layers:**

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | `is_ip_literal()` in policy engine | Rejects all IP-encoded hostnames before domain allowlist lookup |
| Secondary | `socket.inet_aton()` fallback | Catches mixed encodings that regex patterns miss |
| Tertiary | Squid IP literal ACLs | Duplicates IP blocking at the forward proxy layer |

**Why This Works:** The `is_ip_literal()` check runs at the top of the request handler, before `normalize_host()` or any allowlist lookup. It combines regex patterns for common encodings with a `socket.inet_aton()` fallback that handles mixed forms (e.g., `0x7f.0.0.01`). Any all-numeric hostname is either an integer-encoded IP or invalid, and is blocked either way. The check is duplicated in Squid ACLs for defense-in-depth.

---

### 10. PR Merge Prevention

An AI agent could merge a pull request — either through the REST API or a GraphQL mutation — causing irreversible changes to the target branch without human review.

**Attack Vectors:**
- REST: `PUT /repos/owner/repo/pulls/1/merge`
- REST: `PUT /repos/owner/repo/pulls/1/auto-merge`
- GraphQL: `mergePullRequest` mutation
- GraphQL: `enablePullRequestAutoMerge` mutation

**Defense Layers:**

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | Early-exit `is_merge_request()` | Unconditional check before any domain matching, credential injection, or policy evaluation |
| Secondary | GitHub API blocklist (Step 3) | Redundant fallback — same patterns checked again in `_check_github_blocklist()` |
| Tertiary | GitHub API gateway policy | Merge blocking duplicated at the gateway layer for defense-in-depth |

**Why This Works:** `is_merge_request()` is the first check in the request handler. It scans both the request path (using `search()` for substring matching) and the request body (for GraphQL mutation keywords). This runs before domain matching or credential injection, so a bug in earlier processing steps cannot bypass merge blocking. The body keyword scan is intentionally broad — false positives block legitimate requests, which is safer than false negatives that allow merges.

---

### 11. CI/CD Pipeline Protection

An agent could modify CI/CD configuration files (GitHub Actions workflows, Makefiles, pre-commit hooks) and push them, enabling persistent compromise outside the sandbox.

**Attack Vectors:**
- Modifying `.github/workflows/ci.yml` to inject malicious steps
- Adding a new workflow file to `.github/actions/`
- Modifying `Makefile`, `Justfile`, or `Taskfile.yml` to run arbitrary commands
- Altering `.pre-commit-config.yaml` to execute hooks on other developers' machines
- Modifying `CODEOWNERS` to change approval requirements

**Defense Layers:**

| Layer | Control | Effect |
|-------|---------|--------|
| Primary | Push-time file validation | `check_push_files()` enumerates changed files and rejects pushes modifying blocked patterns |
| Secondary | Commit-time file validation | `check_file_restrictions()` on staged files gives early feedback before push |
| Tertiary | Path traversal prevention | Paths containing `..` after normalization are rejected |
| Fail-safe | Fail-closed on diff failure | If `git diff` fails, the push is rejected |

**Configuration:** Blocked and warned patterns are defined in `config/push-file-restrictions.yaml`. Blocked patterns (e.g., `.github/workflows/`, `Makefile`) always reject. Warned patterns (e.g., `package.json`, `Dockerfile`) are logged or rejected depending on `warn_action` setting.

**Why This Works:** The validation runs in the `/git/exec` handler before the push command executes. It enumerates files changed between the remote tracking branch (or default branch for first push) and HEAD, then matches each against the pattern lists. The git API server is single-threaded per sandbox, eliminating TOCTOU races between the check and the push.

---

## Why These Protections?

### Mistake Recovery Should Be Easy

When an AI makes a mistake (it will), recovery should be:
- **Obvious** - Clear error messages explain what happened
- **Reversible** - Worktrees can be destroyed and recreated
- **Contained** - Damage limited to the sandbox

### Defense in Depth

No single control is sufficient. Security comes from multiple overlapping layers:

1. **Credential Isolation** ensures secrets never enter the sandbox
2. **Network Isolation** blocks unauthorized egress even if code tries to exfiltrate
3. **Read-only Filesystem** prevents persistent damage even if commands execute
4. **Sudoers Allowlist** restricts privilege escalation even for allowed sudo use
5. **CAP_NET_RAW dropped** closes Layer 2 attack vectors that bypass higher-level controls
6. **IP literal blocking** prevents DNS filter bypass via encoded IP addresses
7. **Early-exit merge blocking** prevents PR merges before any other request processing
8. **Push-time file validation** prevents CI/CD pipeline modifications from leaving the sandbox
9. **Gateway credential injection** eliminates MITM for major providers, reducing CA trust surface

Each layer catches what others might miss. See [Security Architecture](security-architecture.md) for the complete model.

### Security Over Theater

The real security comes from the pillars (read-only filesystem, network isolation, credential isolation, sudoers allowlist).

---

## Explicit Non-Goals and Accepted Risks

The following are explicitly outside our threat model.

### Intentional Human Actions

If a human intentionally wants to destroy data, they can:
- `docker exec` into the container with root
- Remove the sandbox directory from the host
- Use operator approval to allow dangerous commands
- Modify `docker-compose.yml` to disable protections

**Rationale:** This isn't a security product protecting against malicious users. It's a safety net for AI-assisted development. The human operator is trusted.

### Container Escape Vulnerabilities

If an attacker exploits a Docker or kernel vulnerability to escape the container, sandbox protections don't help.

**Rationale:** We rely on Docker's security model. Container escape is an infrastructure-level concern outside this implementation's scope. Mitigation is through infrastructure patching and host hardening.

### Authorized Network Traffic

Sandboxes can send data to allowed services (GitHub, AI APIs). A compromised sandbox could:
- Push malicious code to an authorized repository
- Send data to AI APIs (within rate limits)
- Download packages that may be malicious

**Rationale:** Network isolation blocks unauthorized destinations. For authorized services, the trust model assumes the sandbox is doing legitimate development work. Repository-level protections (branch protection, code review) provide the next layer of defense.

### Disabled Credential Isolation

If credential isolation is disabled (`--no-isolate-credentials`), the AI can read real environment variables containing credentials.

**Rationale:** Credential isolation is opt-out for compatibility. When disabled, the user accepts that credentials may be exposed. See [Credential Isolation](credential-isolation.md) for why you should keep it enabled.

### Container Filesystem Write Capability

The sandbox container runs with `read_only: false` (required for DNS configuration and tmpfs setup).

**Mitigations:**
- Non-root user (uid 1000 via gosu) limits write scope
- Network isolation (`internal: true`) prevents data exfiltration
- Tmpfs `/home` means writes don't persist across restarts
- The worktree is mounted read-only; `/workspace/.git` is a tmpfs overlay

**Rationale:** Full read-only mode prevents DNS firewall setup via iptables. Shell override protections (e.g., custom shells) were removed as they were bypassable via `/bin/rm` or direct binary execution, making them security theater rather than a real control.

### NET_ADMIN Capability

The sandbox container is granted `CAP_NET_ADMIN` for iptables DNS firewall rules.

**Mitigations:**
- Privilege drop to uid 1000 via gosu occurs after iptables rules are set in `entrypoint-root.sh`
- After privilege drop, the unprivileged user cannot modify iptables rules (requires effective uid 0)
- `CAP_NET_RAW` is separately dropped to prevent raw socket abuse

**Rationale:** DNS firewall requires iptables. The capability is only useful during root-phase initialization.

### Mitmproxy CA Trust

The sandbox trusts the unified-proxy's mitmproxy CA certificate for HTTPS interception of providers that lack `*_BASE_URL` env var support (Gemini, Tavily, Semantic Scholar, Perplexity, Zhipu). Major providers (Anthropic, OpenAI, GitHub) now route through plaintext HTTP gateways and do not require MITM.

**Mitigations:**
- The CA private key never enters the sandbox (stored only on the proxy container)
- The sandbox only receives the public CA certificate (`mitmproxy-ca.pem`)
- Network isolation ensures only the unified-proxy can use this CA for interception
- CA generation is gated behind `ENABLE_MITM_FALLBACK` and MITM provider credential detection — when no MITM providers are configured, no CA is generated and mitmproxy runs only for DNS filtering

**Rationale:** MITM is only needed for providers that do not support custom base URL configuration. The gateway architecture eliminates MITM for the highest-traffic providers (Anthropic, OpenAI, GitHub), reducing the CA trust surface. See [Operations](../operations.md#gateway-rollback-procedures) for rollback procedures.

### Proxy-Side Attack Surface

The unified-proxy runs `git unpack-objects` on untrusted pack data received from the sandbox during push operations. The proxy is the most privileged component in the architecture — it holds real credentials and has network access to GitHub.

**Risk:** A memory-safety bug in git's pack parser could allow code execution on the proxy host, potentially compromising all held credentials and bypassing network controls.

**Mitigations:**

| # | Control | Effect |
|---|---------|--------|
| 1 | Pack size bounded by `DEFAULT_MAX_PUSH_SIZE` (100MB) | Limits the attack surface to bounded input; rejects oversized payloads before parsing (`git_proxy.py:52`) |
| 2 | 10-second subprocess timeout | Kills hung or exploited git processes before they can establish persistence |
| 3 | Isolated temp directory | `unpack-objects` runs in a dedicated temporary object store, not the main bare repo |
| 4 | Minimal subprocess environment | Git subprocesses run with a stripped environment, reducing available attack primitives |
| 5 | Fail-closed on any error | Parse failures, timeouts, or unexpected exit codes all result in push rejection |

**Accepted risk rationale:** A pure-Python pack parser would be equally complex and less battle-tested against adversarial input than git's C implementation. Git's pack format handling has extensive fuzzing coverage upstream, and the mitigations above bound the blast radius of any exploit. The alternative — not inspecting pack data — would leave the restricted-paths check (`DEFAULT_RESTRICTED_PUSH_PATHS` in `git_policies.py:21`) unenforceable, which is a worse security outcome.

---

### In-Memory State Reset

Rate limiters, circuit breakers, and nonce replay stores reset on proxy restart.

**Mitigations:**
- Rate limiter reset only allows a brief burst before new limits take effect
- Nonce replay protection is bounded by the HMAC timestamp window (clock skew tolerance), so expired nonces are rejected regardless of store state
- Circuit breakers reset to closed (healthy) state, which is the safe default

**Rationale:** Persistence adds complexity without meaningful security gain given the bounded time windows.

---

## Design Principles

1. **Fail safe** - When in doubt, block the operation
2. **Prefer UX** - Friendly messages over cryptic errors
3. **Assume good intent** - Protect against accidents, not attacks
4. **Minimize friction** - Safe operations should "just work"
5. **Recoverability** - Destroying and recreating sandboxes is cheap

---

## Next Steps

- [Security Architecture](security-architecture.md) - Security pillars and defense layers
- [Credential Isolation](credential-isolation.md) - Credential isolation architecture and threat model
- [Network Isolation](network-isolation.md) - Network modes and configuration
- [Security Overview](index.md) - Security architecture quick reference
