Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
28 changes: 28 additions & 0 deletions docs/dev-tools/mise-lock.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,34 @@ mise install
mise use node@$(jq -r '.engines.node' package.json)
```

## Provenance and Security

When `mise lock` generates a lockfile, it records a provenance type (e.g., `slsa`, `cosign`, `minisign`, `github-attestations`) for each tool. For the **current platform**, mise downloads the artifact and performs full cryptographic verification at lock time -- ensuring the provenance entry in the lockfile is backed by actual verification, not just registry metadata. This applies to both the aqua and github backends. For cross-platform entries, provenance is detected from registry metadata without verification (since the artifact may not be runnable on the current machine).

By default, when `mise install` sees a lockfile with both a checksum and a provenance entry, it trusts the lockfile and skips re-verification. This avoids redundant API calls (e.g., GitHub attestation queries) which can cause rate limit issues in CI. Since the current platform's provenance was already verified during `mise lock`, this is safe.

For additional security, you can force provenance re-verification at install time on every install:

```toml
[settings]
locked_verify_provenance = true
```

Or via environment variable:

```sh
MISE_LOCKED_VERIFY_PROVENANCE=1 mise install
```

This is also automatically enabled in [paranoid mode](/paranoid.html):

```toml
[settings]
paranoid = true
```

When enabled, every `mise install` will cryptographically verify provenance regardless of what the lockfile contains, ensuring the artifact was built by a trusted CI pipeline.

## Minimum Release Age

In addition to lockfiles, mise supports the [`install_before`](/configuration/settings.html#install_before) setting to limit supply chain risk by only installing versions that have been available for a minimum amount of time:
Expand Down
15 changes: 15 additions & 0 deletions docs/paranoid.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ Normally mise uses HTTP because loading the TLS module takes about 10ms and this
affects commonly used commands so it is a noticeably delay.
In paranoid mode, all endpoints will be fetched over HTTPS.

## Provenance re-verification

Normally, when a lockfile contains both a checksum and a provenance entry for a tool,
`mise install` trusts the lockfile and skips provenance re-verification to avoid
redundant API calls (e.g., to GitHub). This is safe when you trust the lockfile was
generated correctly.

In paranoid mode, `mise install` always re-verifies provenance (SLSA, cosign, minisign,
GitHub artifact attestations) at install time, even when the lockfile already has a
provenance entry. This ensures that cryptographic verification happens on every install,
not just when the lockfile is first generated.

This behavior can also be enabled independently via the
[`locked_verify_provenance`](/configuration/settings.html#locked_verify_provenance) setting.

## More?

If you have suggestions for more that could be added to paranoid, please let
Expand Down
4 changes: 2 additions & 2 deletions e2e-win/npm_backend.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ Describe 'npm_backend' {
mise x node@24.4.1 npm:prettier@3.6.2 -- prettier --version | Should -be "3.6.2"
}
It 'installs npm:cowsay 1.6.0 with bun' {
$env:MISE_NPM_BUN = "true"
$env:MISE_NPM_PACKAGE_MANAGER = "bun"
mise x node@24.4.1 bun@1.2.19 npm:cowsay@1.6.0 -- cowsay --version | Should -be "1.6.0"
Remove-Item Env:MISE_NPM_BUN
Remove-Item Env:MISE_NPM_PACKAGE_MANAGER
}
}
17 changes: 10 additions & 7 deletions e2e/backend/test_aqua_cosign
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
#!/usr/bin/env bash
# Test native Cosign verification for aqua packages
# Uses fork-cleaner which has bundle-based cosign (native verification),
# unlike sops which only has opts-based cosign (CLI pass-through).

set -euo pipefail

export MISE_EXPERIMENTAL=1
export MISE_AQUA_COSIGN=true
export MISE_AQUA_SLSA=false
export MISE_AQUA_GITHUB_ATTESTATIONS=false

echo "=== Testing Native Cosign Verification ==="

# Test: Install sops which has cosign signatures configured (v3.8.0+)
echo "Installing sops with native Cosign verification..."
# Test: Install fork-cleaner which has cosign bundle verification configured
echo "Installing fork-cleaner with native Cosign verification..."

# Capture the installation output to verify the native verification is being used
output=$(mise install aqua:getsops/sops@3.9.0 2>&1)
output=$(mise install aqua:caarlos0/fork-cleaner@2.4.0 2>&1)
echo "$output"

# Verify the native Cosign verification was used
if echo "$output" | grep -q "verify checksums with cosign"; then
if echo "$output" | grep -q "Cosign"; then
echo "✅ Native Cosign verification was used"
else
echo "❌ ERROR: Cosign verification message not found in output"
Expand All @@ -27,11 +30,11 @@ else
fi

# Verify the tool works
assert_contains "mise x aqua:getsops/sops@3.9.0 -- sops --version" "3.9.0"
echo "✓ sops installed and working correctly"
assert_contains "mise x aqua:caarlos0/fork-cleaner@2.4.0 -- fork-cleaner --version" "2.4.0"
echo "✓ fork-cleaner installed and working correctly"

# Cleanup
mise uninstall aqua:getsops/sops@3.9.0 || true
mise uninstall aqua:caarlos0/fork-cleaner@2.4.0 || true

echo ""
echo "=== Native Cosign Verification Test Passed ✓ ==="
4 changes: 2 additions & 2 deletions e2e/backend/test_backend_missing_deps
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ test_npm() {

# Test with bun mode enabled but npm still missing
echo "Testing npm backend with bun mode enabled but npm missing..."
export MISE_NPM_BUN=true
export MISE_NPM_PACKAGE_MANAGER=bun
test_backend_warning "npm (bun mode)" "mise ls-remote npm:test-package" "npm may be required but was not found"
output=$(mise ls-remote npm:test-package 2>&1 || true)
test_error_message "npm (bun mode) shows npm is required for queries" "$output" "npm is required for querying package information"
unset MISE_NPM_BUN
unset MISE_NPM_PACKAGE_MANAGER
}

# Test cargo backend
Expand Down
3 changes: 2 additions & 1 deletion e2e/lockfile/test_lockfile_provenance
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ EOF
mise lock --platform "$PLATFORM"
assert "test -f mise.lock"
# sops has SLSA provenance configured in the aqua registry
assert_contains "cat mise.lock" 'provenance = "slsa"'
# Lock-time verification records the SLSA provenance URL (intoto.jsonl)
assert_contains "cat mise.lock" 'provenance.slsa'

echo "=== Testing provenance downgrade attack detection ==="
rm -f mise.lock mise.toml
Expand Down
5 changes: 5 additions & 0 deletions schema/mise.json
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,11 @@
"description": "Require lockfile URLs to be present during installation.",
"type": "boolean"
},
"locked_verify_provenance": {
"default": false,
"description": "Re-verify provenance at install time even when the lockfile already has provenance.",
"type": "boolean"
},
"lockfile": {
"description": "Create and read lockfiles for tool versions.",
"type": "boolean"
Expand Down
19 changes: 19 additions & 0 deletions settings.toml
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,25 @@ Equivalent to passing `--locked` to `mise install`.
env = "MISE_LOCKED"
type = "Bool"

[locked_verify_provenance]
default = false
description = "Re-verify provenance at install time even when the lockfile already has provenance."
docs = """
When enabled, `mise install` will re-verify provenance (SLSA, cosign, minisign,
GitHub artifact attestations) at install time even when the lockfile already
contains both a checksum and a provenance entry.

By default, when a lockfile has a checksum and provenance type recorded,
`mise install` trusts the lockfile and skips re-verification to avoid redundant
API calls (e.g., to GitHub). Enabling this setting forces re-verification every
time, which provides stronger security guarantees at the cost of additional
network requests.

This is automatically enabled when `paranoid` is set to `true`.
"""
env = "MISE_LOCKED_VERIFY_PROVENANCE"
type = "Bool"

[lockfile]
description = "Create and read lockfiles for tool versions."
docs = """
Expand Down
Loading
Loading