~/f4n6 $ grep -r "mise permits untrusted local configuration to override default shell arguments" ./investigations/ --include="*.md"

mise permits untrusted local configuration to override default shell arguments

Jeff Davies 16 Oct 2026 8 min read

1. Executive summary

An incomplete fix in the mise developer toolchain leaves four shell-interpreter settings out of the global_only denylist that the CVE-2026-55448 remediation relies on, so an untrusted .mise.toml in a cloned repository can set the interpreter used for every command mise spawns and achieve arbitrary command execution without any trust prompt. The flaw is source-verified at HEAD 7160170206782fdd3ef287c8302369e32152fb71 but has not been run end to end, and no CVE identifier has been assigned to it in the material reviewed. The three related CVEs carry only moderate-to-high scores and none is in CISA KEV: CVE-2026-55448 is CVSS 6.3 MEDIUM (CWE-78), CVE-2026-35533 is CVSS 7.7 HIGH (CWE-284), and CVE-2026-54557 is CVSS 5.5 MEDIUM (CWE-22), all at EPSS 0%. The exposure for EMEA financial services is not the CVSS number but the delivery path: a developer cloning an untrusted repository and running a task, install, hook or template render executes attacker-chosen code on a workstation that typically holds source, CI credentials and production access.

2. Regulatory framing

Article Trigger (the fact in this item) Practical impact
DORA Art. 28: ICT third-party risk — general principles mise is a third-party development toolchain component whose incomplete fix leaves a code-execution path open in the version clients run Clients must treat the toolchain as an in-scope ICT third-party dependency and confirm the vendor fix state before relying on it
NIS2 Art. 21(2)(d): supply chain security measures The attack vector is a malicious configuration file delivered inside a cloned third-party repository Clients need a control that governs what untrusted repository content can execute on developer endpoints

No other article in the regulatory reference is engaged by the facts here. The item is a vulnerability disclosure with no confirmed incident, so the DORA incident-management and reporting articles (17, 18, 19) and the NIS2 reporting article (23) are not triggered unless a client confirms exploitation.

3. Technical analysis & attack chain

The defect is a gap in a denylist, not a new class of flaw. CVE-2026-55448 and CVE-2026-35533 fixed the fact that a local project [settings] table is harvested by the settings preloader independent of the trust store, meaning an untrusted .mise.toml sets settings without the user ever trusting the file. The remediation was a per-field global_only denylist: settings marked global_only are stripped from non-global config. The maintainers marked the three forge credential_command sinks (forgejo.credential_command around line 775 of settings.toml, github.credential_command around 828, gitlab.credential_command around 990) but did not mark the four shell-interpreter-args settings, which are also command-execution settings because they choose the interpreter for every command mise spawns:

  • unix_default_file_shell_args (settings.toml around 2920)
  • unix_default_inline_shell_args (around 2926)
  • windows_default_file_shell_args (around 3004)
  • windows_default_inline_shell_args (around 3010)

Confirmed attack chain:

  1. An attacker places a .mise.toml containing a [settings] table in a repository, or in any ancestor directory of the working directory. The file is never trusted by the victim.
  2. The settings preloader loads every config path including the local project file (all_settings_files, src/config/settings.rs line 778). In default (non-safe) mode there is no trust check; safe_mode defaults false (src/config/settings.rs line 1141).
  3. strip_local_only_settings (src/config/settings.rs line 409) filters by meta.global_only (line 416: filter_map(|(key, meta)| meta.global_only.then_some(*key))), so any field not marked global_only survives from the untrusted local config. The four shell-args settings are not marked, so they survive.
  4. default_inline_shell (src/config/settings.rs line 1032) reads self.unix_default_inline_shell_args (line 1040), and default_file_shell reads unix_default_file_shell_args. Settings::get returns the merged global-plus-local value minus global_only, so the untrusted project value overrides the built-in default of sh -c -o errexit.
  5. The real command is appended after the attacker's arguments, so the payload runs and the real command is demoted to $0. An attacker setting unix_default_inline_shell_args = "sh -c 'PAYLOAD'" produces sh -c 'PAYLOAD' <command>.

The injection semantics are consistent across four invocation sites:

  • tera inline exec builtin: src/tera.rs (tera1_exec around 1358 and around 1470) builds shell[1..] + [command] with program = shell[0].
  • postinstall runner: src/backend/mod.rs around 2405 calls cmd_body_args(shell_args, body); src/cmd.rs cmd_body_args (non-Windows, around 520) returns program flags... body, the same demotion.
  • task execution: src/task/task_executor.rs (around 916, 939, 993) uses task.shell()?.unwrap_or(default_inline_shell()/default_file_shell()), so a trusted task with no explicit shell (the common case) runs through the attacker's interpreter.
  • hooks: src/hooks.rs (around 540, 603) also fall back to default_inline_shell().

Reachability. The executed command can come from a trusted or global source; only the interpreter is attacker-controlled. Any of the following, run while the untrusted .mise.toml sits in an ancestor directory, fires the payload: mise run <task> for a task defined in the global config or a trusted parent repo (inline run, no explicit shell); mise install of any tool with a postinstall directive; any tera template that calls exec(...) in the victim's own trusted or global config; a directory-enter hook from any trusted config in scope.

Proof of concept. In an untrusted repository, .mise.toml:

[settings]
unix_default_inline_shell_args = "sh -c 'id > /tmp/mise_pwned'"

The victim cds in without trusting the file and later runs any global or trusted-parent task with no explicit shell, or mise install of a tool with a postinstall, or a config that renders {{ exec(...) }}. mise builds sh -c 'id > /tmp/mise_pwned' <realcommand>, so the payload runs.

Confidence caveats. The researcher states the flaw is source-verified at HEAD but that mise was not run end to end, so the end-to-end execution is inferred from the code paths rather than observed. The advisory is single-sourced to the GitHub advisory GHSA-g74g-rg72-j2p3; verify before enforcement. The load path itself is the same one two shipped CVEs (CVE-2026-55448, CVE-2026-35533) already proved reachable, which raises confidence in the mechanism but does not substitute for a live test.

Secondary hardening note (weaker, not the primary finding). The CVE-2026-54557 path-sanitization fix added sanitize_install_version_name only inside the HTTP backend (src/backend/http.rs around 628), while the general path builder ToolVersion::tv_pathname (src/toolset/tool_version.rs around 292) still only replaces : and / and does not neutralize .. or backslash. Impact is narrow (one level up, stays in the mise data dir on Unix), so this is a hardening note rather than a primary finding.

Discrepancy note. The verified reference data records CVE-2026-55448 as CWE-78 (OS Command Injection) and CVE-2026-35533 as CWE-284 (Improper Access Control). The source describes the same two CVEs as trust-store bypasses; both classifications describe the same flaw, and the verified values are used here.

4. Mitigation & containment

P1 (within 24h)

  • Identify every developer endpoint and CI runner with mise installed. Run mise --version and record the version against the vendor fix state. No patched version is named in the source material, so treat the fix as unconfirmed until the maintainers publish one; do not assume a version is safe because it postdates the CVE-2026-55448 fix.
  • Where mise cannot be immediately updated or the fix state is unknown, block execution of untrusted project config by running mise in safe mode (safe_mode = true) so the preloader applies a trust check. This is the control the source identifies as absent in default mode.
  • Search developer workstations and repositories for .mise.toml files containing unix_default_inline_shell_args, unix_default_file_shell_args, windows_default_inline_shell_args or windows_default_file_shell_args in a [settings] table. Any such file in a repository the organisation did not author is a suspected payload.

P2 (within 72h)

  • Add a pre-clone or post-clone scan that flags .mise.toml [settings] keys outside an approved allowlist, and fail the checkout or the CI job on a hit.
  • Restrict mise execution on developer endpoints to repositories on an internal allowlist until a vendor fix is confirmed. The attack requires the victim to cd into an untrusted repository and then run a task, install, hook or template render, so removing untrusted repositories from the working set removes the trigger.
  • Review CI pipelines that run mise install or mise run against third-party repositories. A postinstall directive in a tool definition is a direct execution path.

P3 (within 7 days)

  • Apply the vendor fix when published. The source's suggested fix is to mark all four *_default_*_shell_args settings global_only = true, matching the treatment of credential_command, or to flip default-mode policy to an allowlist so any setting not explicitly known-safe is stripped from non-global config. Until the maintainers ship one of these, the denylist gap remains.
  • For the secondary path-traversal hardening note, track the ToolVersion::tv_pathname change separately; it is not the primary finding and does not change the P1/P2 actions.

5. Indicators of compromise

No atomic indicators (hashes, domains, IPs, file paths of dropped payloads) are present in the source material. The observable artefacts are configuration strings and file locations, which are behavioural.

type value confidence source
config key unix_default_inline_shell_args high GHSA-g74g-rg72-j2p3
config key unix_default_file_shell_args high GHSA-g74g-rg72-j2p3
config key windows_default_inline_shell_args high GHSA-g74g-rg72-j2p3
config key windows_default_file_shell_args high GHSA-g74g-rg72-j2p3
file .mise.toml high GHSA-g74g-rg72-j2p3
file path /tmp/mise_pwned (PoC output path) medium GHSA-g74g-rg72-j2p3

Behavioural indicators

behaviour where to observe confidence
mise spawns a shell whose arguments include an attacker-supplied sh -c '...' prefix before the real command process creation telemetry on developer endpoints and CI runners (parent mise, child sh) medium
.mise.toml in a repository or ancestor directory contains a [settings] table with any of the four shell-args keys file integrity monitoring, repository scanning, pre-commit hooks high
mise install of a tool with a postinstall directive executes a command through a non-default interpreter CI job logs, EDR process tree medium
config_key  unix_default_inline_shell_args
config_key  unix_default_file_shell_args
config_key  windows_default_inline_shell_args
config_key  windows_default_file_shell_args
file  .mise.toml
file_path  /tmp/mise_pwned

6. Detection

The source provides distinctive configuration strings and a PoC output path. The YARA rule targets .mise.toml files that set any of the four shell-args keys, which is the artefact of the attack itself. The Sigma rule targets the process behaviour: a mise process spawning a shell with an inline -c argument that precedes the real command.

rule MISE_Untrusted_Shell_Args_Override
{
    meta:
        author = "Adverse Trace"
        date = "2026-10-16"
        reference = "https://github.com/advisories/GHSA-g74g-rg72-j2p3"
        description = "Detects .mise.toml files that set shell interpreter args in a [settings] table, the configuration override used to achieve command execution from an untrusted repository"
    strings:
        $s1 = "unix_default_inline_shell_args"
        $s2 = "unix_default_file_shell_args"
        $s3 = "windows_default_inline_shell_args"
        $s4 = "windows_default_file_shell_args"
        $settings = "[settings]"
    condition:
        $settings and any of ($s*)
}
title: mise spawns shell with attacker-controlled inline arguments
id: 8f3c1a2e-6b4d-4e9a-9c1f-2d7e5a0b3c44
status: experimental
description: Detects a mise process spawning a shell with an inline -c argument, consistent with the untrusted .mise.toml shell-args override
author: Adverse Trace
date: 2026-10-16
references:

    - https://github.com/advisories/GHSA-g74g-rg72-j2p3
logsource:
    category: process_creation
    product: linux
detection:
    selection_parent:
        ParentImage|endswith: '/mise'
    selection_child:
        Image|endswith:

            - '/sh'
            - '/bash'
        CommandLine|contains: ' -c '
    condition: selection_parent and selection_child
falsepositives:

    - Legitimate mise tasks that explicitly set a shell with -c
level: medium

CVE assessment

3 referenced CVEs

CVE CVSS Exploited EPSS Summary
CVE-2026-35533 7.7 High — 0% mise manages dev tools like node, python, cmake, and terraform. From 2026.2.18 through 2026.4.5, mise loads trust-control setti…
CVE-2026-55448 6.3 Medium — 0% mise manages dev tools like node, python, cmake, and terraform. From 2026.3.15 until 2026.6.4, mise loads github.credential_com…
CVE-2026-54557 5.5 Medium — 0% mise manages dev tools like node, python, cmake, and terraform. Prior to 2026.6.1, the mise HTTP backend builds its install sym…

7. Sources

  • GitHub Security Advisories, "mise permits untrusted local configuration to override default shell arguments", https://github.com/advisories/GHSA-g74g-rg72-j2p3, published 2026-10-15
  • GitHub Security Advisories, "Local settings bypass config trust checks", https://github.com/advisories/GHSA-436v-8fw5-4mj8, no date given in the fetched content
  • GitHub Advisory Database, "CVE-2026-103929", https://github.com/advisories/GHSA-g74g-rg72-j2p3, no date given in the fetched content

8. Adverse Trace position

Adverse Trace assesses this as a medium-severity code-execution flaw in a developer toolchain, not a critical one: the verified CVSS values for the related CVEs are 6.3, 7.7 and 5.5, none is in CISA KEV, and all sit at EPSS 0%, so there is no evidence of exploitation in the wild. The risk to EMEA financial services clients is concentrated in developer endpoints and CI runners that clone third-party repositories, where the payload executes with the developer's own privileges and typically reaches source, CI credentials and production access. The finding is single-sourced to GHSA-g74g-rg72-j2p3 and source-verified but not run end to end, so we treat the end-to-end execution as inferred rather than observed; verify before enforcing blocks. We will track the vendor fix for the four *_default_*_shell_args settings and re-issue this advisory when a patched version is named. Clients should complete the P1 identification and safe-mode step within 24 hours and report back the mise version inventory so we can correlate against the fix when it lands.


Read the original source →

Published via PulseTrace — Adverse Trace threat intelligence.

Post this to LinkedIn
Formatting is converted automatically — headings, bullets, a link back & hashtags. Paste straight in.
J
Jeff Davies