Skip to content

Security

mise includes several supply-chain controls for installing and managing tools. These controls have different coverage depending on the backend and the metadata available from upstream.

Software verification

mise provides native software verification without requiring external dependencies. For aqua tools, Cosign/Minisign signatures, SLSA provenance, and GitHub artifact attestations are verified automatically using mise's built-in implementation. OpenPGP signature verification for Node.js and Swift downloads is likewise built in and requires no external gpg binary.

To configure aqua verification, which is enabled by default:

sh
# Disable specific verification methods if needed
export MISE_AQUA_COSIGN=false
export MISE_AQUA_SLSA=false
export MISE_AQUA_GITHUB_ATTESTATIONS=false
export MISE_AQUA_MINISIGN=false

For lockfile checksum and provenance behavior, see mise.lock.

Safe mode

Safe mode (MISE_SAFE=1, or the safe setting) is a hard boundary against project configuration executing code. It is intended for running mise against configuration you do not control — most notably automation that refreshes mise.lock on pull request branches, such as a scheduled mise lock --bump job (see Bumping Locked Versions).

sh
# resolve tool versions from untrusted config without executing any of it
MISE_SAFE=1 mise lock --bump --dry-run --json

When enabled, mise refuses with an error (never a silent fallback) to:

  • run exec() or read_file() in config templates
  • run hooks (suppressed like --no-hooks, since hooks fire ambiently from mise env/hook-env)
  • run tasks
  • execute asdf plugin scripts
  • install plugins

It also ignores environment and shell configuration from project (non-global) config[env] values, _.path, _.file, and [shell_alias] entries. These would otherwise be applied to your shell environment (env vars and aliases are emitted through hook-env) and to the subprocesses mise spawns during version resolution (for example go list or a vfox plugin hook), which is an indirect code-execution vector (PATH, LD_PRELOAD, DYLD_INSERT_LIBRARIES, NODE_OPTIONS, …) that safe mode is meant to close. Global and system config (e.g. ~/.config/mise/config.toml) is operator-owned and still applies, mirroring the trust model.

_.source is treated as code execution rather than environment injection, so it is ignored in safe mode regardless of where it is defined — including operator-owned global config.

[settings] from project (non-global) config are also ignored, so an untrusted repo cannot change mise's behavior during resolution (for example disabling verification or redirecting a backend). Global/system settings still apply. Specific project settings may be allowlisted in the future if they are both safe and necessary.

Because a config loaded in safe mode is inert — it can neither execute code nor inject environment — safe mode does not require configs to be trusted. mise loads untrusted configs without a trust prompt or error when safe is set, since there is nothing a config can do to compromise the host. This is what lets automation run mise lock against pull-request config without a preceding mise trust.

Version resolution still works for every HTTP-based backend — core, aqua, github, gitlab, http, cargo, pipx, gem, dotnet, and npm — as well as go (which runs with GOTOOLCHAIN=local so a project go.mod cannot trigger a toolchain download). Refreshing mise.lock and listing installed tools work normally.

Already-installed and embedded vfox plugins also keep working: their code was chosen by the operator, not by the repository being processed, and version resolution short-circuits on plugins that are not installed without executing anything.

TIP

Safe mode is a code-execution boundary; it does not replace the trust system. Untrusted configs still require mise trust (or a trusted config path). Safe mode limits what a config can do; trust limits which configs are loaded.

MISE_SAFE is global_only, so it can only be set via the environment or global config — a project mise.toml cannot turn it off for itself.

Minimum release age

To limit supply-chain risk, you can restrict mise to only install versions released before a certain date or duration. This is similar to Renovate's minimum release age concept: newly published versions are ignored until they have been available for a configurable amount of time.

toml
# mise.toml
[settings]
minimum_release_age = "7d"  # only install versions released more than 7 days ago

Supports relative durations (7d, 6mo, 1y) and absolute dates (2024-06-01). For most backends, this only affects fuzzy version resolution, such as node@20 or latest. Explicitly pinned versions like [email protected] bypass the filter. During ordinary toolset resolution, already-installed fuzzy matches remain eligible: minimum_release_age limits remote version selection and does not make an installed version inactive. Lockfile generation may re-check fuzzy installed matches against release metadata.

Capability depends on the backend:

CapabilityBackends
Top-level version filteringBackends that provide release timestamps, such as aqua:, cargo:, github:, gitlab:, go:, npm:, pipx:, and many core tools
Transitive dependency filtering during installnpm: and pipx:

Versions without timestamps are included by default. Backends without transitive dependency support may still select an older top-level tool version, but they do not constrain dependencies fetched by the tool's installer/compiler.

For npm: and pipx: transitive dependency support details, refer to the npm backend docs and pipx backend docs.

You can also set minimum_release_age per-tool to override the global setting:

toml
# mise.toml
[settings]
minimum_release_age = "7d"  # default for all tools

[tools.trivy]
version = "latest"
minimum_release_age = "1d"  # trivy updates are time-sensitive, use a shorter window

Precedence: --minimum-release-age CLI flag > per-tool minimum_release_age > global minimum_release_age setting.

Use minimum_release_age_excludes to exclude tools or backends from the global/default setting:

toml
[settings]
minimum_release_age = "7d"
minimum_release_age_excludes = ["trivy", "npm:*"]

Exclusions can match backend wildcards like npm:*, tool shorthands like trivy, or full backend IDs like npm:prettier. Matching tools skip the global setting and built-in default. Per-tool minimum_release_age options and the CLI flag still apply even when a tool matches the exclusion list.

See minimum_release_age for the setting reference.

MIT LicenseCopyright © 2026jdx.dev