CVE-2026-55441
mise: Arbitrary command execution via task-include files in an untrusted, config-less repository
mise manages dev tools like node, python, cmake, and terraform. Prior to 2026.6.4, 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. This vulnerability is fixed in 2026.6.4.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.6
mise: Arbitrary command execution via task-include files in an untrusted, config-less repository
mise manages dev tools like node, python, cmake, and terraform. Prior to 2026.6.4, 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. This vulnerability is fixed in 2026.6.4.
- Record
CVE-2026-55441- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H - Weakness
- CWE-78, CWE-94, CWE-732
- Updated
- 2026-11-02
EUVDEUVD-2026-39815
8.6
mise manages dev tools like node, python, cmake, and terraform. Prior to 2026.6.4, 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. This vulnerability is fixed in 2026.6.4.
- Record
EUVD-2026-39815- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-77g9-363w-rccq
8.6
Mise vulnerable to arbitrary command execution via task-include files in an untrusted, config-less repository
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/
…
- Record
GHSA-77g9-363w-rccq- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H - Weakness
- CWE-78, CWE-94, CWE-732
- Updated
- 2026-11-02
NVD
8.6
mise manages dev tools like node, python, cmake, and terraform. Prior to 2026.6.4, 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. This vulnerability is fixed in 2026.6.4.
- Record
CVE-2026-55441- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H - Weakness
- CWE-78, CWE-94, CWE-732
- Updated
- 2026-11-02
OSV
8.6
mise: Arbitrary command execution via task-include files in an untrusted, config-less repository
mise manages dev tools like node, python, cmake, and terraform. Prior to 2026.6.4, 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. This vulnerability is fixed in 2026.6.4.
- Record
CVE-2026-55441- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H - Weakness
- CWE-732, CWE-78, CWE-94
- Updated
- 2026-11-02
OSVGHSA-77g9-363w-rccq
8.6
Mise vulnerable to arbitrary command execution via task-include files in an untrusted, config-less repository
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/
…
- Record
GHSA-77g9-363w-rccq- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H - Weakness
- CWE-732, CWE-78, CWE-94
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
misecrates.io |
before 2026.6.4 | 2026.6.4 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-55441 is a high vulnerability (CVSS 8.6) in Jdx Mise, published June 23, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.18%, the 8th percentile. Fixed in mise 2026.6.4.
References
Left out here: the rest of 2 source descriptions. The JSON record has them.