# Defense-in-depth score: mini-swe-agent

**Repo:** https://github.com/SWE-agent/mini-swe-agent · **Commit:** `04d809ceab9df28f9adaed044884180159172930` · **Reviewed:** 2026-10-05
**What it is:** Minimal AI software engineering agent from the SWE-bench team: a ~100-line agent loop that solves coding tasks using only bash, run interactively via the `mini` CLI or in batch on benchmarks.
**Category:** Coding
**Scored configuration:** The `mini` interactive CLI with no flags after first-run setup: built-in mini.yaml config, InteractiveAgent in confirm mode, LocalEnvironment running commands on the host, LiteLLM model.
**Agent surface (default):** code execution yes · filesystem write yes · network egress yes · external credentials yes · persistent memory no · untrusted input yes · third party extensions no · sub agents no · external communication yes

## Score: 4.0 / 10.0 (Minimal)

| # | Criterion | S | C | D | B | Raw | Cap | Score | Confidence |
|---|---|---|---|---|---|---|---|---|---|
| C1 | Identity & least privilege | L0 | L0 | L0 | L1 | 0.05 | none | **0.05** | High |
| C2 | Approval gates | L2 | L3 | L2 | L0 | 0.47 | none | **0.47** | High |
| C3 | Tool & action scoping | L0 | L0 | L0 | L0 | 0.00 | none | **0.00** | High |
| C4 | Code-execution isolation | L2 | L3 | L0 | L2 | 0.47 | G1 | **0.47** (alt) | High |
| C5 | Untrusted input blast radius | L2 | L3 | L2 | L2 | 0.57 | none | **0.57** | High |
| C6 | Memory, context & configuration integrity | L2 | L2 | L2 | L1 | 0.45 | none | **0.45** | High |
| C7 | Third-party extensions | SA | SA | SA | SA | 1.00 | none | **1.00** (SA) | High |
| C8 | Secrets & sensitive-data protection | L0 | L0 | L1 | L0 | 0.05 | none | **0.05** | High |
| C9 | Audit & traceability | L2 | L2 | L2 | L2 | 0.50 | none | **0.50** | High |
| C10 | Limits & kill switch | L2 | L2 | L2 | L1 | 0.45 | none | **0.45** | High |

Controls where a risk surface exists: 3.02 / 9.0 (34%); 1 criterion scored SA (surface absent).

mini runs every command the model writes directly on your machine, as you, with your full environment (including its own API keys) and open network access. The main safeguard is confirm mode, on by default: you see the proposed commands and must press Enter before anything runs, but there are no risk tiers, nothing can be undone, and a flag or a config setting switches it to fully automatic. Use one of the shipped container backends and keep confirm mode on when working on untrusted code.

## Critical gaps
- Model commands run on the host as the user with the full process environment, including API keys, and open network; isolation backends exist but are off by default. (ASI05, T11, LLM05; C4). Evidence: [src/minisweagent/environments/local.py:29](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L29); [src/minisweagent/environments/local.py:74-80](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L74-L80); [src/minisweagent/run/mini.py:100](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L100)

## Criterion details

### C1 Identity & least privilege: 0.05 (high confidence)

mini runs as the user who launched it and does nothing to narrow that authority. API keys from the global config file are loaded into the agent's own process environment, and every bash command the model runs inherits that whole environment, along with any cloud, Git or SSH credentials the user already has. There is no authorization layer in code; the only thing between the model and the user's account is the per-command confirmation prompt, which is scored under approval gates. The credentials mini itself holds are LLM provider keys, so its own exposure is mostly spend, but a hijacked session reaches whatever the user's shell can.

- **S L0:** Ambient OS-user authority; the global .env is loaded into os.environ and commands run as the user with no narrowing. Evidence: [src/minisweagent/__init__.py:36](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/__init__.py#L36); [src/minisweagent/environments/local.py:29](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L29) (verified)
  - *To reach the next level:* No dedicated or scoped identity; L1 needs at least a separate identity for the agent's actions.
- **C L0:** No authorization check exists on the single bash action path; each command receives the full process environment. Evidence: [src/minisweagent/environments/local.py:29](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L29); [src/minisweagent/environments/local.py:74-80](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L74-L80) (verified)
  - *To reach the next level:* No authorization layer on any path and no environment scrubbing for subprocesses.
- **D L0:** The default (and only) local mode runs with the user's full privileges and environment. Evidence: [src/minisweagent/run/mini.py:100](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L100); [src/minisweagent/environments/local.py:29](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L29) (verified)
  - *To reach the next level:* No narrower default identity or environment; least privilege requires the user to run mini in a separate account or container.
- **B L1:** mini's own credentials are long-lived LLM provider keys; approved commands additionally reach everything the user's account can, with the per-command confirmation as the surviving layer. Evidence: [src/minisweagent/environments/local.py:29](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L29); [src/minisweagent/run/utilities/config.py:82-87](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/utilities/config.py#L82-L87) (verified)
  - *To reach the next level:* Commands can write across every system the user's credentials reach; L2 needs reach limited to one system.
- **Cap:** none

### C2 Approval gates: 0.47 (high confidence)

mini starts in confirm mode: before any batch of model commands runs, it prints the commands and waits for the user to press Enter or type a rejection. The gate sits on the only action path, there is one tool (bash), unknown tool names are refused, and only the user's own typed input can switch to yolo mode. But there are no risk tiers or argument rules: every command gets the same yes/no prompt, and the optional allow-list matches regular expressions against the raw command string. Confirm mode can be turned off by a command-line flag or silently by a config file or the environment variable that selects it. Nothing can be undone: approved commands run directly on the host with no checkpoint.

- **S L2:** Per-batch human approval of the model's proposed bash commands, with reject (with a message) as a first-class outcome; no risk tiers or argument-level policy. Evidence: [src/minisweagent/agents/interactive.py:162-163](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L162-L163); [src/minisweagent/agents/interactive.py:165-182](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L165-L182); [src/minisweagent/agents/interactive.py:124-132](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L124-L132) (verified)
  - *To reach the next level:* No risk tiers deciding what needs a human and no argument-level allow, deny or escalate policy.
- **C L3:** Every model action reaches execution only through execute_actions and the confirmation check; the single registered tool is bash and unknown tools are rejected; the allow-list is empty by default. Evidence: [src/minisweagent/agents/interactive.py:124-132](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L124-L132); [src/minisweagent/models/utils/actions_toolcall.py:61-62](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/models/utils/actions_toolcall.py#L61-L62); [src/minisweagent/agents/interactive.py:27-28](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L27-L28) (verified)
  - *To reach the next level:* Allow-list entries are regex matches on the raw command string; L4 needs parsed compound commands checked part by part.
- **D L2:** Confirm is the default mode and only the user's typed /y switches it at runtime, but the -y flag, an agent.mode=yolo config value, or MSWEA_MINI_CONFIG_PATH pointing at another config disables it with no warning. Evidence: [src/minisweagent/config/mini.yaml:103](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/config/mini.yaml#L103); [src/minisweagent/run/mini.py:22](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L22); [src/minisweagent/run/mini.py:61](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L61); [src/minisweagent/agents/interactive.py:201-208](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L201-L208) (verified)
  - *To reach the next level:* Disabling via config file or environment variable is silent and the yolo mode is not time- or session-bounded beyond the run.
- **B L0:** An approved or bypassed command runs as the user on the host and can delete data, push code or send data anywhere, with no checkpoint or undo. Evidence: [src/minisweagent/environments/local.py:74-80](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L74-L80); searched `rg -n -i 'checkpoint|undo|rollback|snapshot|dry.run'` in `src/` → 0 hits (no checkpoint, undo or dry-run feature exists) (verified)
  - *To reach the next level:* No checkpoints or rollback for filesystem state and no preview for external actions.
- **Cap:** none

### C3 Tool & action scoping: 0.00 (high confidence)

mini has exactly one tool, bash, and passes the model's command string to the shell unchanged. There is no path containment, URL or host allow-list, or bound on what a command may touch; the optional allow-list only decides which commands skip the confirmation prompt. This is the project's stated design ("no tools other than bash"), and it means a hijacked or mistaken model has the full reach of a shell on the user's machine.

- **S L0:** Raw passthrough: the only tool takes an arbitrary shell string. Evidence: [src/minisweagent/models/utils/actions_toolcall.py:11-27](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/models/utils/actions_toolcall.py#L11-L27); [src/minisweagent/environments/local.py:74-80](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L74-L80) (verified)
  - *To reach the next level:* No validation at all; L1 needs at least a denylist filter.
- **C L0:** No tool validates its input; the single tool is the unvalidated shell. Evidence: searched `rg -n -i 'realpath|resolve\(\)|allowed_hosts|is_relative_to|allowlist'` in `src/` → 1 hits (single hit is package_dir resolution in __init__.py; no path, host or argument validation exists) (verified)
  - *To reach the next level:* No input validation on any tool.
- **D L0:** The default and only tool set is a shell with write, exec and network reach. Evidence: [src/minisweagent/models/litellm_model.py:66-70](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/models/litellm_model.py#L66-L70) (verified)
  - *To reach the next level:* No read-only or narrower default tool set.
- **B L0:** The tool can run any command against the whole machine and any host as the user. Evidence: [src/minisweagent/environments/local.py:74-80](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L74-L80); [src/minisweagent/environments/local.py:27](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L27) (verified)
  - *To reach the next level:* No workspace scoping; L1 needs at least some limit on reach.
- **Cap:** none

### C4 Code-execution isolation: 0.47 (high confidence)

By default every command the model writes runs directly on the user's machine through a shell, as the user, with the full environment (including API keys) and unrestricted network. Commands get a 30-second timeout and the whole process group is killed when it expires, but there is no isolation. The project ships several execution backends that do isolate (Docker or Podman, Singularity, an experimental bubblewrap sandbox, and remote services), and every command goes through the chosen backend with no fallback to the host, but `mini` uses the local backend unless you pick another. The Docker backend uses a stock container (root inside, default capabilities, open network), does not mount the host or pass secrets by default, and is removed after the run.

- **default configuration** (default; raw 0.00 → 0.00)
  - **S L0:** Same-user host subprocess via shell=True; no isolation primitive in the default backend. Evidence: [src/minisweagent/environments/local.py:74-80](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L74-L80); [src/minisweagent/run/mini.py:100](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L100) (verified)
    - *To reach the next level:* No isolation; L1 needs at least a filter or separate working area, L2 OS-level separation.
  - **C L0:** The main and only exec path (bash) runs on the host. Evidence: [src/minisweagent/agents/interactive.py:131-132](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L131-L132); [src/minisweagent/environments/local.py:74-80](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L74-L80) (verified)
    - *To reach the next level:* The main exec tool is not sandboxed by default.
  - **D L0:** The local host backend is the default for the mini CLI. Evidence: [src/minisweagent/run/mini.py:100](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L100) (verified)
    - *To reach the next level:* Isolation is off by default.
  - **B L0:** Host-equivalent: commands run as the user with the full environment, API keys included, the home directory and unrestricted network. Evidence: [src/minisweagent/environments/local.py:29](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L29); [src/minisweagent/environments/local.py:74-80](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L74-L80) (verified)
    - *To reach the next level:* Credentials and the whole host filesystem are in reach; L1 needs at least credentials kept out of the execution environment.
- **opt-in Docker/Podman backend (environment_class=docker)** (alt; raw 0.47, cap G1 → 0.47) ← counted
  - **S L2:** Stock container started with docker run and only --rm by default: root inside, default capabilities, no seccomp or read-only root configured. Evidence: [src/minisweagent/environments/docker.py:77-89](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/docker.py#L77-L89); [src/minisweagent/environments/docker.py:30](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/docker.py#L30) (verified)
    - *To reach the next level:* No hardening (non-root, dropped capabilities, no-new-privileges, read-only root).
  - **C L3:** Every model command is executed via docker exec inside the container; there is no host fallback if the container fails to start. Evidence: [src/minisweagent/environments/docker.py:107-113](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/docker.py#L107-L113); [src/minisweagent/environments/docker.py:91-97](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/docker.py#L91-L97) (verified)
    - *To reach the next level:* Capped one level above Strength; the backend would otherwise meet full coverage.
  - **D L0:** Opt-in: mini uses the local backend unless the user selects another environment class. Evidence: [src/minisweagent/run/mini.py:100](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L100) (verified)
    - *To reach the next level:* Not on by default.
  - **B L2:** No host mounts and no forwarded environment by default, container removed after the run, but unrestricted network and no CPU, memory or PID limits. Evidence: [src/minisweagent/environments/docker.py:21](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/docker.py#L21); [src/minisweagent/environments/docker.py:30](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/docker.py#L30) (verified)
    - *To reach the next level:* Network egress is open and there are no resource limits.
- **Cap:** G1: Opt-in mechanism: off in the scored default configuration.

### C5 Untrusted input blast radius: 0.57 (high confidence)

mini reads untrusted text all the time: repository files, command output and anything fetched from the web come back as tool results with nothing marking them as untrusted. What limits a hijacked model is the confirm-mode prompt: in the default mode no command, including network access, runs without the user pressing Enter. That gate is not tied to what was read, and it is the only layer: once a user approves a command, or runs in yolo mode, it can read the user's credentials and send them anywhere or make irreversible changes. Yolo mode can be selected by a flag or silently by configuration.

- **S L2:** Every model command, including egress, needs human approval in the default confirm mode, but nothing tracks untrusted content and the approval prompt is the sole barrier. Evidence: [src/minisweagent/agents/interactive.py:162-163](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L162-L163); searched `rg -n -i 'untrusted|taint|provenance|prompt.injection|quarantin'` in `src/` → 0 hits (nothing marks or tracks untrusted content) (verified)
  - *To reach the next level:* No provenance-aware restriction; L3 needs Rule of Two enforcement that holds independently of a single confirmation prompt.
- **C L3:** The gate applies to every action whatever its source; the only untrusted inputs are tool results (command output), and there are no extensions or sub-agents. Evidence: [src/minisweagent/models/utils/actions_toolcall.py:105-109](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/models/utils/actions_toolcall.py#L105-L109); [src/minisweagent/agents/interactive.py:124-132](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L124-L132) (verified)
  - *To reach the next level:* Tool output still enters as ordinary tool messages that can instruct the model; L4 needs third-party content treated strictly as data.
- **D L2:** On by default; nothing the agent reads can switch modes at runtime, but the operator can disable it silently through config or the config-path environment variable. Evidence: [src/minisweagent/config/mini.yaml:103](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/config/mini.yaml#L103); [src/minisweagent/run/mini.py:22](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L22) (verified)
  - *To reach the next level:* Disabling via config or environment variable is silent; L3 needs an explicit, warned opt-out.
- **B L2:** Assuming a hijack, exfiltration and irreversible actions both require the user to approve the command in the default mode; nothing happens unattended. Evidence: [src/minisweagent/agents/interactive.py:162-163](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L162-L163); [src/minisweagent/environments/local.py:29](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L29) (verified)
  - *To reach the next level:* Once approved, commands reach secrets and can act irreversibly; L3 needs only low-sensitivity data or reversible actions under approval.
- **Cap:** none

### C6 Memory, context & configuration integrity: 0.45 (high confidence)

mini has no long-term memory and does not load instruction files such as AGENTS.md; each run starts from the built-in config and the task, and the saved trajectory is never read back into a later session. Its settings come from user scope: a .env file in the user's config directory, loaded at startup, plus environment variables that can choose the config file, the model and the limits. Those settings can switch off confirm mode or change the model endpoint, and nothing protects them from the agent itself: because commands run unsandboxed as the user, an approved command can rewrite that file and change every later session.

- **S L2:** No memory and no auto-loaded workspace or instruction files by default; security-relevant settings live in a user-scope .env that the agent's own shell can write. Evidence: [src/minisweagent/__init__.py:26-36](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/__init__.py#L26-L36); searched `rg -n -i 'AGENTS\.md|CLAUDE\.md|cursorrules|remember|vector|long_term'` in `src/` → 0 hits (no memory store and no instruction-file loader) (verified)
  - *To reach the next level:* Security settings are not protected from the agent's own shell; L3 needs writes to persistent config gated or validated by a dedicated control.
- **C L2:** There is no memory store or instruction-file loader to control; the one persistent influence, the global .env and the config it selects, is not controlled beyond the general command prompt. Evidence: [src/minisweagent/__init__.py:26-36](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/__init__.py#L26-L36); [src/minisweagent/run/mini.py:22](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L22) (verified)
  - *To reach the next level:* The user-scope config that sets modes and endpoints has no integrity check or change review.
- **D L2:** Single-user local tool with per-user config; the trajectory is written to the user config dir and never re-injected. Evidence: [src/minisweagent/run/mini.py:23](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L23); [src/minisweagent/__init__.py:26-36](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/__init__.py#L26-L36) (verified)
  - *To reach the next level:* The model's commands can change the user-scope settings; L3 needs the model unable to alter them.
- **B L1:** A poisoned global .env persists across all of the user's sessions and can switch off confirmation or redirect the model, enabling unattended tool use. Evidence: [src/minisweagent/__init__.py:26-36](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/__init__.py#L26-L36); [src/minisweagent/agents/interactive.py:25](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L25) (verified)
  - *To reach the next level:* Poisoned config is not limited to text output or gated actions; L2 needs persistence that can't lift the gate.
- **Cap:** none

### C7 Third-party extensions: 1.00 (high confidence)

mini has no plugin, skill or MCP system and does not download tools, agents or model files. Agent, model and environment classes are chosen by name in the user's own config and imported from installed Python packages, which is operator configuration rather than third-party code loaded at runtime. The model can still ask to install packages with ordinary bash commands; those go through the same per-command confirmation and host execution scored under approval gates and code-execution isolation, so this criterion's surface is treated as absent.

- **Structural absence:** searched `rg -n -i 'mcp|plugin|entry_points|trust_remote_code|pickle|torch\.load'` in `src/` → 0 hits (no extension loading, remote-code model loading or unsafe deserialization); searched `rg -n 'importlib.import_module'` in `src/` → 3 hits (model, agent and environment class resolution from operator-chosen config names (models/__init__.py:104, agents/__init__.py:18, environments/__init__.py:23); not runtime third-party code)

### C8 Secrets & sensitive-data protection: 0.05 (high confidence)

API keys are typed in during first-run setup and stored in plain text in a .env file in the user's config directory, then loaded into the process environment, where every command the model runs can read them (and print them back into the conversation). mini has no masking or redaction anywhere. It sends no telemetry of its own and keeps debug logging off by default, but the full trajectory of each run, including all command output, is written unredacted to the user's config directory.

- **S L0:** Keys are stored in a plaintext .env and exported to the process environment; there is no masking or redaction on any path. Evidence: [src/minisweagent/run/utilities/config.py:82-87](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/utilities/config.py#L82-L87); [src/minisweagent/__init__.py:36](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/__init__.py#L36); searched `rg -n -i 'redact|mask|scrub|SecretStr|sanitiz'` in `src/` → 1 hits (single hit is Anthropic 'redacted_thinking' block handling, not secret redaction) (verified)
  - *To reach the next level:* No masking at all; L1 needs at least masking on one path.
- **C L0:** No path is protected: subprocess environments, model-bound tool output and the saved trajectory all carry whatever secrets appear. Evidence: [src/minisweagent/environments/local.py:29](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L29); searched `rg -n -i 'redact|mask|scrub|SecretStr|sanitiz'` in `src/` → 1 hits (single hit is Anthropic 'redacted_thinking' block handling, not secret redaction) (verified)
  - *To reach the next level:* No protected path; L1 needs at least one (for example, a scrubbed subprocess environment).
- **D L1:** No telemetry and debug logging is off, but the full unredacted trajectory is saved after every step by default. Evidence: [src/minisweagent/run/mini.py:23](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L23); [src/minisweagent/agents/default.py:182-190](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/default.py#L182-L190); searched `rg -n -i 'sentry|posthog|telemetry|analytics|opentelemetry'` in `src/` → 0 hits (no telemetry SDK in the project) (verified)
  - *To reach the next level:* Capped one level above Strength; a default unredacted transcript keeps it from L2 regardless.
- **B L0:** Long-lived provider keys and every credential in the user's environment are reachable by every model command. Evidence: [src/minisweagent/environments/local.py:29](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L29) (verified)
  - *To reach the next level:* Long-lived, broadly scoped keys are in reach of every subprocess.
- **Cap:** none

### C9 Audit & traceability: 0.50 (high confidence)

After every step mini rewrites a JSON trajectory with the full message history: each model reply with its parsed commands, each command's output and return code, timestamps, cost and the exit status. User rejections appear in it because they are added as messages, but approvals are not recorded, and there is no notion of who acted. The default file sits in the user's config directory rather than the workspace, but it has a fixed name, so each run overwrites the previous one, and the agent's own shell can edit it.

- **S L2:** Structured JSON trajectory of every model message, command, output, return code and timestamp. Evidence: [src/minisweagent/agents/default.py:159-180](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/default.py#L159-L180); [src/minisweagent/models/utils/actions_toolcall.py:95-104](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/models/utils/actions_toolcall.py#L95-L104) (verified)
  - *To reach the next level:* No actor attribution separating the user's commands, approvals and the agent's actions.
- **C L2:** All tool calls (one bash path) and user rejections are recorded; approvals (Enter) are not. Evidence: [src/minisweagent/agents/interactive.py:173-182](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L173-L182); [src/minisweagent/agents/default.py:115-116](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/default.py#L115-L116) (verified)
  - *To reach the next level:* Approval decisions are not recorded.
- **D L2:** On by default, written to the user's config directory outside the workspace, but by the same user the model's commands run as, and overwritten by the next run. Evidence: [src/minisweagent/run/mini.py:23](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L23); [src/minisweagent/run/mini.py:64](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/run/mini.py#L64) (verified)
  - *To reach the next level:* The record is writable by the model's shell and not kept across runs; L3 needs a writer the model can't control.
- **B L2:** The trajectory is rewritten after every step in a finally block and write errors propagate, but the whole file is rewritten in place each time. Evidence: [src/minisweagent/agents/default.py:120-121](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/default.py#L120-L121); [src/minisweagent/agents/default.py:187-189](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/default.py#L187-L189) (verified)
  - *To reach the next level:* Non-atomic full rewrites and per-run overwrite mean the record isn't durable or replayable across runs.
- **Cap:** none

### C10 Limits & kill switch: 0.45 (high confidence)

mini stops a run once its estimated model spend passes $3 by default, and stops with an error if it can't price a model call unless told to ignore cost errors. Each command has a 30-second timeout that kills the whole process group. Step and wall-clock limits exist but are off by default, and when the cost limit trips in an interactive terminal the user is simply asked for new limits. There are no sub-agents. Pressing Ctrl-C stops the loop, but a command that is still running is not killed, and anything a command starts in the background keeps running.

- **S L2:** Cost cap, step cap and wall-clock cap checked before each model call, plus a per-command timeout that kills the process group; halt is cooperative. Evidence: [src/minisweagent/agents/default.py:132-147](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/default.py#L132-L147); [src/minisweagent/environments/local.py:86-91](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L86-L91); [src/minisweagent/models/litellm_model.py:113-125](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/models/litellm_model.py#L113-L125) (verified)
  - *To reach the next level:* No rate limit on side-effecting commands or repeated-action breaker; the iteration and wall-clock caps are off by default.
- **C L2:** Limits cover the top-level loop and each command; there is no delegation. Evidence: [src/minisweagent/agents/default.py:132-147](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/default.py#L132-L147); [src/minisweagent/environments/local.py:84](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L84) (verified)
  - *To reach the next level:* Background processes started by commands escape every limit.
- **D L2:** Sensible $3 cost default the model cannot change at runtime; step and time limits default to 0 (unlimited); raising limits needs the user at the prompt. Evidence: [src/minisweagent/config/mini.yaml:101-102](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/config/mini.yaml#L101-L102); [src/minisweagent/agents/default.py:26-31](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/default.py#L26-L31); [src/minisweagent/agents/interactive.py:88-94](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L88-L94) (verified)
  - *To reach the next level:* No hard ceilings; cost tracking can be set to ignore errors from the user config, and the model's shell can write that config.
- **B L1:** Ctrl-C ends the loop, but the running command was started in its own session so it does not receive the interrupt and is not killed; background work outlives the run. Evidence: [src/minisweagent/environments/local.py:84-91](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/environments/local.py#L84-L91); [src/minisweagent/agents/interactive.py:109-122](https://github.com/SWE-agent/mini-swe-agent/blob/04d809ceab9df28f9adaed044884180159172930/src/minisweagent/agents/interactive.py#L109-L122) (verified)
  - *To reach the next level:* Stopping leaves in-flight and background processes running.
- **Cap:** none

## Rule-of-Two check
[A] untrusted input: Repository files, command output and web content returned as tool results (src/minisweagent/models/utils/actions_toolcall.py:105-109) · [B] sensitive data/systems: User's full environment including LLM API keys and any ambient cloud/Git credentials passed to every command (src/minisweagent/environments/local.py:29) · [C] state change / egress: Arbitrary host shell with network access (src/minisweagent/environments/local.py:74-80), gated per batch in confirm mode (src/minisweagent/agents/interactive.py:162-163) · Same default session? Yes

## Highest-impact improvements
1. Run commands with a scrubbed environment that omits the LLM API keys and other credentials unless the user forwards them explicitly. (C1 C L0→L1, +0.075 before caps; Playbook 4)
2. Kill the running command's process group when the user presses Ctrl-C, as already done on timeout. (C10 B L1→L2, +0.050 before caps; Playbook 3 step 3)
3. Print a prominent warning whenever confirm mode is disabled by a config file or environment variable, not only by the -y flag. (C2 D L2→L3, +0.050 before caps; Playbook 5)
4. Record approval decisions in the trajectory and keep one trajectory per run instead of overwriting a single file. (C9 C L2→L3, +0.075 before caps; Playbook 1 step 3)
5. Make a hardened container backend (non-root, dropped capabilities, no network by default) the default for mini. (C4 S L0→L3, +0.225 before caps; Playbook 3)

## Re-audit log
- C5 S: L3 → L2. Confirm mode gates every action, but it is the single layer, is not tied to provenance, and can be turned off silently by configuration; burden of proof on the control.
- C8 S: L1 → L0. L1 requires masking on at least one path; a search found no masking or redaction anywhere in src/.

## Limitations
- Static source review of the pinned commit only; nothing was executed, installed, or probed.
- The pinned commit is on the main branch after the v2.4.6 tag; the package reports version 2.4.6, so version is left null.
- Scored the `mini` interactive CLI as the README leads with it. Batch benchmark runners (swebench, programbench) use container backends and their own configs and were not scored; the Python API (DefaultAgent) has no approval step at all.
- Behaviour of third-party libraries (LiteLLM, Rich, prompt_toolkit, python-dotenv) was not audited beyond how mini calls them, including any telemetry or logging LiteLLM may perform.
- Opt-in backends other than Docker (Singularity, bubblewrap, SWE-ReX, ConTree) were read only briefly; the C4 alternative is scored on the Docker backend.
- No reviewer-steering text was found in AGENTS.md, CLAUDE.md or other instruction files in the repository.
