fix(task): resolve sandbox allow_read/allow_write against task dir#9428
fix(task): resolve sandbox allow_read/allow_write against task dir#9428
Conversation
Task-level relative `allow_read` / `allow_write` paths were resolved against the shell's current working directory instead of the task's effective directory, so `dir = "../bar"` + `allow_read = ["."]` allowed reads in the caller's pwd rather than `bar/`. Pre-resolve task-level relative paths against `task.dir(config)` (falling back to `config_root`) before merging with CLI overrides; CLI flags continue to resolve against shell cwd. Fixes #9423 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Greptile SummaryThis PR fixes a sandbox path-resolution bug where task-level Confidence Score: 5/5Safe to merge — the fix is correctly scoped and well-tested. No P0 or P1 issues found. The path-resolution logic handles all three cases correctly (absolute path passthrough, relative joined against task_base, and the rare None-base fallback that degrades to the previous cwd behaviour). The No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant Caller as Caller (shell cwd)
participant TE as TaskExecutor
participant Task
participant SB as SandboxConfig
Caller->>TE: run_task_sched(task, config)
TE->>TE: build_sandbox_for_task(task, config)
TE->>Task: task.dir(config)
Task-->>TE: task_base (e.g. /project/bar)
note over TE: resolve_task_path closure joins relative<br/>allow_read/write against task_base
TE->>SB: SandboxConfig { allow_read: task paths (pre-resolved) + CLI paths (raw) }
TE->>SB: resolve_paths() — canonicalise all, resolve remaining relatives against cwd
SB-->>TE: fully-resolved SandboxConfig
TE-->>Caller: sandbox enforced on task execution
Reviews (3): Last reviewed commit: "docs(sandboxing): drop redundant note on..." | Re-trigger Greptile |
There was a problem hiding this comment.
Code Review
This pull request ensures that relative paths in task-level allow_read and allow_write sandbox settings are resolved against the task's working directory instead of the shell's current working directory. The changes include documentation updates, a new regression test, and modifications to the sandbox configuration logic in task_executor.rs. A review comment points out a redundancy in the path resolution logic where a fallback is already handled by an existing method.
| task: &Task, | ||
| config: &Arc<Config>, | ||
| ) -> Result<SandboxConfig> { | ||
| let task_base = task.dir(config).await?.or_else(|| task.config_root.clone()); |
There was a problem hiding this comment.
The .or_else(|| task.config_root.clone()) call is redundant here because task.dir(config) already falls back to task.config_root if the task's dir is not specified. You can simplify this to just task.dir(config).await?.
| let task_base = task.dir(config).await?.or_else(|| task.config_root.clone()); | |
| let task_base = task.dir(config).await?; |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f3b4dd5. Configure here.
…nding Address PR feedback: - task.dir(config) already returns config_root when dir is unset, so the outer .or_else fallback was dead code. - Rename the local SandboxConfig binding from `config` to `sandbox` so it no longer shadows the `config: &Arc<Config>` parameter. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The behavior is obvious from how `dir` itself works. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.24 x -- echo |
22.6 ± 0.6 | 21.5 | 24.7 | 1.00 |
mise x -- echo |
23.0 ± 1.0 | 21.7 | 30.4 | 1.02 ± 0.05 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.24 env |
22.4 ± 1.1 | 20.8 | 28.4 | 1.00 |
mise env |
23.4 ± 0.6 | 21.6 | 25.2 | 1.04 ± 0.06 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.24 hook-env |
23.2 ± 0.7 | 21.6 | 25.6 | 1.01 ± 0.04 |
mise hook-env |
23.0 ± 0.6 | 22.0 | 25.2 | 1.00 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.24 ls |
23.0 ± 0.7 | 22.1 | 29.1 | 1.00 |
mise ls |
23.4 ± 0.6 | 22.4 | 25.8 | 1.02 ± 0.04 |
xtasks/test/perf
| Command | mise-2026.4.24 | mise | Variance |
|---|---|---|---|
| install (cached) | 155ms | 159ms | -2% |
| ls (cached) | 80ms | 81ms | -1% |
| bin-paths (cached) | 82ms | 84ms | -2% |
| task-ls (cached) | 800ms | 805ms | +0% |
### 🚀 Features - **(task)** add --name-only flag to mise tasks ls by @jdx in [#9435](#9435) ### 🐛 Bug Fixes - **(Dockerfile)** install copr-cli via dnf for better dependency management by @bestagi in [#9421](#9421) - **(aqua)** drop empty-releases fallback to tags by @jdx in [#9443](#9443) - **(docs)** fix theme flicker on docs by @vhespanha in [#9427](#9427) - **(lockfile)** update global lockfile on upgrade by @jdx in [#9442](#9442) - **(ls-remote)** omit rolling/prerelease from JSON when false by @jdx in [#9439](#9439) - **(task)** support usage refs in dependency template tags by @jdx in [#9424](#9424) - **(task)** populate usage.cmd for subcommand-only tasks; share make_usage_ctx by @jdx in [#9431](#9431) - **(task)** resolve sandbox allow_read/allow_write against task dir by @jdx in [#9428](#9428) ### 📚 Documentation - **(site)** add self-hosted page tracker via Cloudflare Worker, drop GoatCounter by @jdx in [#9430](#9430) ### New Contributors - @vhespanha made their first contribution in [#9427](#9427) --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

Summary
allow_read/allow_writepaths inmise.tomlwere resolved against the shell's pwd rather than the task's effective working directory. Withdir = "../bar"andallow_read = ["."], mise allowed reads in the caller's directory but the task itself ran inbar/, so reads got blocked.task.dir(config)(falling back toconfig_root) before merging with CLI overrides. CLI flags (mise run --allow-read=…/mise x --allow-read=…) continue to resolve against shell cwd since those come from the user's shell.e2e/sandbox/and a one-line note indocs/sandboxing.mdclarifying the semantics.Fixes #9423
Test plan
mise run lintmise run test:unitmise run test:e2e test_sandbox_task_dir test_sandbox_task test_sandbox_deny_read test_sandbox_deny_write test_sandbox_deny_env test_sandbox_deny_all test_sandbox_deny_netdir = "../bar"+allow_read = ["."]now works without duplicating the dir🤖 Generated with Claude Code
Note
Medium Risk
Changes how sandbox filesystem allowlists are computed for tasks, which can affect which paths are permitted/denied at runtime. Risk is moderate because it alters security-relevant path resolution logic but is narrowly scoped and covered by a new e2e regression test.
Overview
Fixes task sandboxing so task-level relative
allow_read/allow_writeentries are pre-resolved against the task’s effective working directory (task.dir(...)) before merging with CLI sandbox overrides, ensuring entries like"."refer to where the task actually runs.Adds an e2e regression test (
e2e/sandbox/test_sandbox_task_dir) that runs a task withdir = "../bar"and verifiesallow_write = ["."]correctly permits writes in the task directory outside the default always-writable paths.Reviewed by Cursor Bugbot for commit dac33e0. Bugbot is set up for automated code reviews on this repo. Configure here.