Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/cli/trust.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ parsing `mise.toml`. Without trust, mise may prompt, skip the config in some
discovery paths, fail with an untrusted-config error when it cannot prompt,
or assume trust in detected CI unless paranoid mode is enabled.

Safe config files do not require trust: files that only contain
`min_version`, `[tools]` entries with plain version strings (or arrays
of them), and `[tasks]` (no templates and no tool options) are loaded
without prompting, since nothing in them executes code at load time —
tools install and tasks run only on explicit commands like `mise install`
or `mise run`.

## Arguments

### `[CONFIG_FILE]`
Expand Down
7 changes: 6 additions & 1 deletion docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,12 @@ mise upgrade --bump node

## My config file is being ignored / `mise trust` issues

mise requires you to trust config files that were not created by you. Common issues:
mise requires you to trust config files that were not created by you. Safe config files —
those that only contain `min_version`, `[tools]` entries with plain version strings (or
arrays of them), and `[tasks]` (no templates and no tool options) — are loaded without trust, since nothing in
them executes code at load time: tools install and tasks run only on explicit commands like
`mise install` or `mise run`. Everything else (env vars, hooks, settings, aliases, templates,
tool options) requires trust. Common issues:

- **Accidentally denied trust**: If mise prompted you to trust a file and you said no, it gets
added to the ignore list. Check the `ignored-configs` directory in your
Expand Down
112 changes: 112 additions & 0 deletions e2e/config/test_trust_safe_config
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env bash

# Safe mise.toml files (min_version, [tools] with plain version strings, and
# [tasks]) can be loaded without trusting them first. Anything that can execute
# code at load time or change mise's behavior still requires trust.

export MISE_TRUSTED_CONFIG_PATHS=""
unset CI GITHUB_ACTIONS GITHUB_ACTION 2>/dev/null || true

# Fail if any trust marker file/symlink exists under trusted-configs. This is
# precise (a stale empty dir won't mask a wrongly-written marker) unlike a bare
# directory-existence check.
assert_no_trust_marker() {
local found
found=$(find "$MISE_STATE_DIR/trusted-configs" -mindepth 1 \( -type f -o -type l \) 2>/dev/null || true)
[[ -z $found ]] || fail "$1 (found trust markers: $found)"
}

mkdir -p project
cd project || exit 1

cat <<'EOF' >mise.toml
min_version = "2024.1.1"

[tools]
tiny = "3.1.0"

[tasks.hi]
run = "echo task-ran-without-trust"
EOF

# loads without prompting and without creating a trust marker
MISE_YES=0 mise install tiny
assert_contains "MISE_YES=0 mise ls tiny" "3.1.0"
Comment thread
greptile-apps[bot] marked this conversation as resolved.
assert_no_trust_marker "safe config should not be silently trusted"

# config tasks only execute on explicit `mise run`, so they don't need trust
assert_contains "MISE_YES=0 mise run hi" "task-ran-without-trust"

# template-free file tasks are inert at load time and runnable without trust
mkdir -p mise-tasks
cat <<'EOF' >mise-tasks/filetask
#!/usr/bin/env bash
#MISE description="a plain file task"
echo file-task-ran-without-trust
EOF
chmod +x mise-tasks/filetask
assert_contains "MISE_YES=0 mise run filetask" "file-task-ran-without-trust"
assert_no_trust_marker "file task should not require or create trust"

# a template in a file task header renders at load time, so it requires trust
cat <<'EOF' >mise-tasks/evil
#!/usr/bin/env bash
#MISE description="{{ exec(command='echo PWNED > marker') }}"
echo evil
EOF
chmod +x mise-tasks/evil
output=$(MISE_YES=0 mise tasks 2>&1 || true)
[[ ! -f marker ]] || fail "file task header template executed without trust"
echo "$output" | grep -qi "trust" || fail "expected trust error for templated file task, got: $output"
rm mise-tasks/evil

# a template in a version string requires trust and must not execute
cat <<'EOF' >mise.toml
[tools]
tiny = "{{ exec(command='echo PWNED > marker') }}"
EOF
output=$(MISE_YES=0 mise ls 2>&1 || true)
[[ ! -f marker ]] || fail "template executed in untrusted config"
echo "$output" | grep -qi "trust" || fail "expected trust error for template, got: $output"

# a template in a task renders at load time, so it requires trust
cat <<'EOF' >mise.toml
[tasks.hi]
run = "echo hi"
description = "{{ exec(command='echo PWNED > marker') }}"
EOF
output=$(MISE_YES=0 mise tasks 2>&1 || true)
[[ ! -f marker ]] || fail "task template executed in untrusted config"
echo "$output" | grep -qi "trust" || fail "expected trust error for task template, got: $output"

# escaped Tera delimiters ({ == '{', } == '}') decode to a template
# after TOML parsing; this must not bypass the safety check and exec
printf '[tools]\ntiny = "\\u007b\\u007b exec(command=%stouch marker%s) \\u007d\\u007d"\n' "'" "'" >mise.toml
output=$(MISE_YES=0 mise ls 2>&1 || true)
[[ ! -f marker ]] || fail "escaped template executed in untrusted config (tools)"
echo "$output" | grep -qi "trust" || fail "expected trust error for escaped tools template, got: $output"

printf '[tasks.hi]\nrun = "echo hi"\ndescription = "\\u007b\\u007b exec(command=%stouch marker%s) \\u007d\\u007d"\n' "'" "'" >mise.toml
output=$(MISE_YES=0 mise tasks 2>&1 || true)
[[ ! -f marker ]] || fail "escaped template executed in untrusted config (tasks)"
echo "$output" | grep -qi "trust" || fail "expected trust error for escaped task template, got: $output"

# tool options can run code (postinstall) or alter installs (install_env)
cat <<'EOF' >mise.toml
[tools]
tiny = { version = "3.1.0", postinstall = "touch marker" }
EOF
output=$(MISE_YES=0 mise ls 2>&1 || true)
echo "$output" | grep -qi "trust" || fail "expected trust error for tool options, got: $output"

# env vars require trust
cat <<'EOF' >mise.toml
[env]
FOO = "bar"
EOF
output=$(MISE_YES=0 mise env 2>&1 || true)
echo "$output" | grep -qi "trust" || fail "expected trust error for env, got: $output"

# unsafe configs still load normally once trusted
mise trust
assert_contains "mise env" "FOO=bar"
23 changes: 23 additions & 0 deletions e2e/config/test_trust_safe_config_tracked
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash

# A safe (untrusted) mise.toml is tracked and its tool pins remain visible
# through tracked-config reload paths like `mise ls --all-sources`, even when
# invoked from a different directory. Regression: tracked-config loading used
# to skip any config without a trust marker, which safe configs don't create.

export MISE_TRUSTED_CONFIG_PATHS=""
unset CI GITHUB_ACTIONS GITHUB_ACTION 2>/dev/null || true

mkdir -p proj other
cat <<'EOF' >proj/mise.toml
[tools]
tiny = "3.1.0"
EOF

# install from the safe config (loads + tracks it, no trust prompt/marker)
(cd proj && MISE_YES=0 mise install tiny)

# from an unrelated directory, the tracked safe config's pin is still listed
cd other || exit 1
assert_contains "MISE_YES=0 mise ls --all-sources 2>&1" "proj/mise.toml"
assert_contains "MISE_YES=0 mise ls --all-sources 2>&1" "tiny"
8 changes: 6 additions & 2 deletions e2e/tasks/test_task_monorepo_trust
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ assert_not_contains "mise tasks ls --all 2>&1" "Trust them"
assert_contains "mise run '//projects/frontend:build'" "frontend build"
assert_contains "mise run '//projects/frontend/components:test'" "component test"

# Test that trust warnings DO appear for configs outside monorepo
# Create a parent directory with its own config (not part of monorepo)
# Test that trust warnings DO appear for unsafe configs outside monorepo
# (tasks-only configs are safe and load without trust, so use [env] to make
# this one require trust)
mkdir -p ../parent-dir
cat <<EOF >../parent-dir/mise.toml
[env]
FOO = "bar"

[tasks.parent-task]
run = 'echo "parent task"'
EOF
Expand Down
19 changes: 15 additions & 4 deletions e2e/tasks/test_task_untrusted_config_error
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,29 @@

# Ensure we start with a clean slate - no trusted configs
export MISE_TRUSTED_CONFIG_PATHS=""
unset CI GITHUB_ACTIONS GITHUB_ACTION 2>/dev/null || true

# Create a config file with a task
# A config that only defines tasks is safe: tasks execute nothing until the
# user explicitly runs one, so no trust is required
cat <<EOF >mise.toml
[tasks.make]
run = "echo 'hello from task'"
EOF

# Running a task from an untrusted config should give a helpful error
# It should either:
assert_contains "MISE_YES=0 mise run make" "hello from task"

# Unsafe configs (here: [env]) still require trust. Running a task from one
# should give a helpful error message:
# 1. Prompt the user to trust the config (when interactive), or
# 2. Show a clear "config not trusted" error (not "no tasks defined")
#
cat <<EOF >mise.toml
[env]
FOO = "bar"

[tasks.make]
run = "echo 'hello from task'"
EOF

# This test verifies we don't get the misleading "no tasks defined" error
output=$(MISE_YES=0 mise run make 2>&1 || true)

Expand Down
7 changes: 7 additions & 0 deletions man/man1/mise.1
Original file line number Diff line number Diff line change
Expand Up @@ -3402,6 +3402,13 @@ that may execute code or affect the environment. mise checks trust before
parsing `mise.toml`. Without trust, mise may prompt, skip the config in some
discovery paths, fail with an untrusted\-config error when it cannot prompt,
or assume trust in detected CI unless paranoid mode is enabled.

Safe config files do not require trust: files that only contain
`min_version`, `[tools]` entries with plain version strings (or arrays
of them), and `[tasks]` (no templates and no tool options) are loaded
without prompting, since nothing in them executes code at load time —
tools install and tasks run only on explicit commands like `mise install`
or `mise run`.
.PP
\fBUsage:\fR mise trust [OPTIONS] [<CONFIG_FILE>]
.PP
Expand Down
7 changes: 7 additions & 0 deletions mise.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -3503,6 +3503,13 @@ that may execute code or affect the environment. mise checks trust before
parsing `mise.toml`. Without trust, mise may prompt, skip the config in some
discovery paths, fail with an untrusted-config error when it cannot prompt,
or assume trust in detected CI unless paranoid mode is enabled.

Safe config files do not require trust: files that only contain
`min_version`, `[tools]` entries with plain version strings (or arrays
of them), and `[tasks]` (no templates and no tool options) are loaded
without prompting, since nothing in them executes code at load time —
tools install and tasks run only on explicit commands like `mise install`
or `mise run`.
"""#
after_long_help #"""
Examples:
Expand Down
7 changes: 7 additions & 0 deletions src/cli/trust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ use itertools::Itertools;
/// parsing `mise.toml`. Without trust, mise may prompt, skip the config in some
/// discovery paths, fail with an untrusted-config error when it cannot prompt,
/// or assume trust in detected CI unless paranoid mode is enabled.
///
/// Safe config files do not require trust: files that only contain
/// `min_version`, `[tools]` entries with plain version strings (or arrays
/// of them), and `[tasks]` (no templates and no tool options) are loaded
/// without prompting, since nothing in them executes code at load time —
/// tools install and tasks run only on explicit commands like `mise install`
/// or `mise run`.
#[derive(Debug, clap::Args)]
#[clap(verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)]
pub struct Trust {
Expand Down
Loading
Loading