Skip to content

Make Linux sandboxing bwrap-first for managed profiles#150

Merged
t-kalinowski merged 25 commits into
mainfrom
linux-sandbox-update
Jun 27, 2026
Merged

Make Linux sandboxing bwrap-first for managed profiles#150
t-kalinowski merged 25 commits into
mainfrom
linux-sandbox-update

Conversation

@t-kalinowski

@t-kalinowski t-kalinowski commented Jun 26, 2026

Copy link
Copy Markdown
Member

Summary

Rework Linux sandbox enforcement so managed filesystem profiles use bubblewrap by default, with legacy Landlock retained as an explicit fallback.

Changes

  • Accept managed filesystem profiles on Linux instead of rejecting them as macOS-only.
  • Render restricted reads, :minimal, project-root entries, temp special paths, deny paths/globs, and protected .git/.codex/.agents paths into the Linux bubblewrap mount plan.
  • Preserve symlinked and missing writable roots in the bwrap mount plan, and fail closed when legacy Landlock cannot represent managed writable-root carveouts.
  • Keep client-provided sandbox helper paths ignored; mcp-repl continues to launch its own internal helper.
  • Preserve seccomp-based network and process restrictions, while continuing to fail closed for unsupported managed domain allowlists on Linux.
  • Route Linux bwrap interrupts to sandboxed worker descendants instead of the bwrap monitor.
  • Preserve split UTF-8 tails across input-wait/request-boundary drains.
  • Update sandbox docs and add an active follow-up plan for remaining Linux sandbox parity work.

Validation

  • cargo check
  • cargo build
  • python3 tests/run_integration_tests.py --binary target/debug/mcp-repl
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test --quiet
  • cargo +nightly fmt

Follow-ups

  • Bundle or probe bubblewrap more explicitly so behavior is less dependent on the host bwrap.
  • Add Linux managed-network proxy routing for domain allowlists.
  • Add full protected-create monitoring for missing metadata paths.

@t-kalinowski t-kalinowski marked this pull request as ready for review June 26, 2026 12:16
@t-kalinowski t-kalinowski force-pushed the linux-sandbox-update branch from b42e994 to 25d96b3 Compare June 26, 2026 12:56
@t-kalinowski t-kalinowski marked this pull request as draft June 26, 2026 14:13
@t-kalinowski t-kalinowski marked this pull request as ready for review June 26, 2026 14:23
@t-kalinowski t-kalinowski force-pushed the linux-sandbox-update branch 7 times, most recently from d0f8c01 to 8ccf5f2 Compare June 26, 2026 17:07
Finding 1:
<<<FINDING 1 START>>>
[P2] Preserve the new Linux bwrap default when env is unset — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:1752: With the normal startup path (`sandbox_state_defaults_with_environment`) and no `MCP_REPL_USE_LINUX_BWRAP` set, the new default is immediately overwritten to `false` by `env_var_truthy`, so Linux still starts on the legacy Landlock path by default. If bwrap is meant to be the default, the environment override needs to distinguish an unset variable from an explicit false value.
<<<FINDING 1 END>>>

Finding 2:
<<<FINDING 2 START>>>
[P2] Honor local bwrap disablement for inherited sandbox metadata — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:1238: When a Linux user disables bwrap locally via env/config but uses `--sandbox inherit`, normal Codex metadata with `useLegacyLandlock: false` produces `Some(true)`, and `apply_update` overwrites the disabled default back to bwrap. This makes the local opt-out ineffective for inherited sandbox calls unless the client also sends `useLegacyLandlock: true`.
<<<FINDING 2 END>>>
[P2] Gate Linux-only env helper from non-Linux builds — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:918-923: On macOS and Windows this new helper is compiled but only referenced inside a #[cfg(target_os = "linux")] block, so cross-platform cargo clippy --all-targets --all-features -- -D warnings will fail with an unused dead_code warning. Put the helper behind the same Linux cfg or allow dead code so non-Linux CI remains clean.
Finding 1:
<<<FINDING 1 START>>>
[P1] Enforce deny globs for future paths — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:4237-4249: On Linux, managed unreadable globs are converted into a one-time list of existing files via Makefile
Cargo.lock
Cargo.toml
AGENTS.md
r/mcp_repl.R
LICENSE
mcp-repl.Rproj
pyproject.toml
scripts/install.ps1
scripts/test_codex_stdio_trace_win.py
scripts/test_diff_composition.py
scripts/codex-stdio-trace-win.py
scripts/install.sh
scripts/codex-traced-mcp-repl.cmd
scripts/mcp-stdio-trace.py
scripts/diff_composition.py
README.md
src/oversized_output.rs
src/python_worker.rs
src/output_stream.rs
src/backend.rs
docs/output_timeline.md
docs/sandbox.md
docs/testing.md
docs/releasing.md
docs/worker_sideband_protocol.md
src/pager/mod.rs
src/pager/presentation.rs
src/pager/ranges.rs
src/pager/command.rs
src/pager/search.rs
src/r_controls.rs
src/windows_sandbox_setup.rs
src/server.rs
src/python_runtime.rs
src/output_snapshot.rs
docs/tool-descriptions/repl_tool_r_pager.md
docs/tool-descriptions/repl_tool_python.md
docs/tool-descriptions/repl_tool_r.md
docs/tool-descriptions/repl_tool_python_pager.md
docs/tool-descriptions/repl_tool.md
docs/tool-descriptions/repl_reset_tool.md
docs/index.md
docs/architecture.md
src/worker_process/write_preflight.rs
src/worker_process/test_support.rs
src/worker_process/backend_driver.rs
src/worker_process/request_reply.rs
src/worker_process/pending_poll.rs
src/worker_process/write_flow.rs
src/worker_process/request_lifecycle.rs
src/worker_process/output_state.rs
src/worker_process/interrupt.rs
src/worker_process/reply_state.rs
src/worker_process/control_prefix.rs
src/worker_process/session_reset_reply.rs
src/worker_process/worker_launch.rs
src/worker_process/write_dispatch.rs
src/worker_process/restart.rs
src/worker_process/session_lifecycle.rs
src/worker_process/sandbox_state.rs
src/python_input_queue.rs
src/input_state.rs
src/windows_sandbox.rs
src/r_htmd.rs
src/worker_process.rs
src/completion_reply.rs
src/main.rs
src/stdin_payload.rs
src/ipc.rs
src/pending_output_tape.rs
src/windows_sandbox_test_support.rs
src/worker_supervisor.rs
src/windows_conpty.rs
src/debug_logs.rs
tests/run_integration_tests.py
tests/pager_hits_seek.rs
tests/client_config_dual_backend.rs
tests/worker_ipc_disconnect.rs
tests/python_help_snapshots.rs
tests/pager_seek.rs
tests/r_manuals.rs
tests/test_run_integration_tests.py
tests/manage_session_behavior.rs
tests/r_startup.rs
tests/claude_integration.rs
tests/debug_events_tool_calls.rs
tests/debug_repl_prompt.rs
tests/python_snapshots.rs
tests/oversized_output_cli.rs
tests/r_help.rs
tests/docs_contracts.rs
tests/pager_page_size.rs
tests/sandbox.rs
tests/debug_events_env.rs
tests/pager_skip.rs
docs/futurework/worker-session-tempdir-rotation.md
tests/repl_surface.rs
docs/futurework/advisory-worker-write-observations.md
tests/write_stdin_edge_cases.rs
docs/futurework/per-turn-history-bundles.md
tests/refactor_coverage.rs
docs/futurework/claude-session-lifecycle-and-integration.md
tests/pager_where.rs
docs/futurework/claude-sandbox-inherit.md
tests/python_backend.rs
docs/futurework/managed-network-follow-up.md
tests/r_file_show.rs
docs/futurework/mcp-client-install-managed-network-defaults.md
tests/plot_images.rs
docs/futurework/repl-tool-description-extras.md
docs/futurework/stronger-worker-child-containment.md
tests/reticulate_py_help.rs
docs/futurework/project-local-mcp-repl-config.md
docs/futurework/server-backend-boundary.md
docs/futurework/r-embedding-minimal-callbacks.md
docs/futurework/repl-interaction-rough-edges.md
docs/futurework/offline-manual-surfaces.md
docs/futurework/sidecar-viewer-observability.md
docs/futurework/portable-plugin-skill-mcp-bundle.md
docs/futurework/stdin-transport-single-owner.md
docs/futurework/r-graphics-device-for-incremental-plot-emission.md
docs/futurework/composable-tool-descriptions.md
tests/pager.rs
src/server/timeouts.rs
src/server/tests.rs
src/server/response.rs
src/managed_network.rs
src/resolved_output.rs
src/sandbox.rs
src/event_log.rs
src/reply_presentation.rs
src/python_ffi.rs
src/worker.rs
src/diagnostics.rs
src/r_graphics.rs
src/debug_repl.rs
src/output_capture.rs
src/ipc/test_support.rs
src/ipc/emit.rs
src/ipc/worker_connection.rs
src/ipc/transport.rs
src/ipc/server_connection.rs
src/ipc/protocol.rs
src/sandbox_cli.rs
src/install.rs
src/python_session.rs
src/html_to_markdown.rs
src/r_session.rs
tests/write_stdin_behavior.rs
tests/windows_suite_server_lock.rs
tests/mcp_transcripts.rs
tests/r_console_encoding.rs
tests/interrupt.rs
tests/write_stdin_batch.rs
tests/release_script.rs
tests/zod_protocol.rs
tests/sandbox_state_meta.rs
tests/python_plot_images.rs
tests/pager_flags.rs
tests/README.md
tests/r_vignettes.rs
tests/codex_integration.rs
tests/session_endings.rs
tests/fixtures/zod-worker.rs
tests/server_smoke.rs
tests/python_program_selection.rs
docs/plans/tech-debt.md
docs/plans/AGENTS.md
docs/plans/active/linux-sandbox-managed-profiles.md
docs/plans/active/template.md
docs/plans/active/windows-test-parity.md
docs/plans/active/public-api-runner.md
docs/plans/active/managed-network-proxy.md
src/worker_protocol.rs
src/python_session/stdio.rs
src/python_session/windows_stdin.rs
src/python_session/unix_stdin.rs
src/python_session/state.rs
src/sandbox/seatbelt_base_policy.sbpl
src/sandbox/seatbelt_network_policy.sbpl
src/sandbox/restricted_read_only_platform_defaults.sbpl
docs/debugging.md
python/embedded.py
docs/plans/completed/oversized-output-previews.md
docs/plans/completed/sideband-v3-cleanup.md
docs/plans/completed/dev-binary-release.md
docs/plans/completed/windows-sandbox-setup-refresh.md
docs/plans/completed/python-embedded-backend.md
docs/plans/completed/windows-python-conpty-turn-protocol.md
tests/common/mod.rs
docs/plans/completed/r-timeline-ordering-and-completion.md
docs/plans/completed/python-pty-readline.md
docs/plans/completed/python-cell-execution.md
docs/plans/completed/codex-sandbox-state-meta-migration.md
docs/plans/completed/image-output-bundles.md
docs/plans/completed/inherit-sandbox-restart-contract.md
docs/plans/completed/python-fifo-cell-execution.md
docs/plans/completed/codex-sandbox-alignment.md
docs/plans/completed/ipc-module-split.md
docs/plans/completed/python-help-contract.md
docs/plans/completed/remove-pager-stage1.md
docs/plans/completed/unified-output-timeline-pipeline.md
docs/plans/completed/one-way-output-bundle-previews.md
tests/snapshots/plot_images__grid_plots_emit_stable_images_for_repeats@transcript__macos.snap
tests/snapshots/plot_images__grid_plots_emit_stable_images_for_repeats@transcript.snap
tests/snapshots/claude_integration__claude_live_install_integration.snap
tests/snapshots/pager__pager_matches_with_headings@transcript.snap
tests/snapshots/plot_images__plots_emit_stable_images_for_repeats@transcript.snap
tests/snapshots/plot_images__plots_emit_stable_images_for_repeats@macos.snap
tests/snapshots/pager__pager_hits_mode@transcript.snap
tests/snapshots/python_snapshots__snapshots_sys_stdin_followup_without_prompt.snap
tests/snapshots/write_stdin_batch__write_stdin_drives_browser@transcript.snap
tests/snapshots/server_smoke__sends_input_to_r_console@transcript.snap
docs/notes/eval_suite_ideas.md
tests/snapshots/plot_images__plots_emit_images_and_updates@macos.snap
tests/snapshots/refactor_coverage__snapshots_truncation_notice_tail.snap
tests/snapshots/pager__pager_search_and_counts.snap
tests/snapshots/mcp_transcripts__snapshots_support_multiple_calls_and_sessions@transcript.snap
tests/snapshots/refactor_coverage__snapshots_browser_prompt_and_continue@transcript.snap
tests/snapshots/pager__pager_search_case_insensitive_prefix_parsing@transcript.snap
tests/snapshots/mcp_transcripts__snapshots_tempdir_session_restart.snap
tests/snapshots/plot_images__multi_panel_plots_emit_single_image@transcript__macos.snap
tests/snapshots/plot_images__plots_emit_stable_images_for_repeats@transcript__macos.snap
tests/snapshots/python_help_snapshots__python_help_contract.snap
tests/snapshots/python_snapshots__snapshots_buffered_input_prompt_matching_primary_prompt@transcript.snap
tests/snapshots/claude_integration__claude_live_integration.snap
tests/snapshots/mcp_transcripts__snapshots_interrupt_handler_output@transcript.snap
tests/snapshots/codex_integration__linux__codex_exec_wire_sandbox_state_meta.snap
tests/snapshots/plot_images__multi_panel_plots_emit_single_image@transcript.snap
tests/snapshots/pager__pager_dedup_on_seek@transcript.snap
tests/snapshots/pager__pager_search_case_insensitive_prefix_parsing.snap
tests/snapshots/pager__pager_search_preserves_whitespace@transcript.snap
tests/snapshots/python_snapshots__snapshots_multiline_cell_no_continuation_prompt.snap
tests/snapshots/python_snapshots__snapshots_sys_stdin_followup_without_prompt@transcript.snap
tests/snapshots/plot_images__plots_emit_images_and_updates@transcript.snap
tests/snapshots/session_endings__snapshots_session_endings.snap
tests/snapshots/write_stdin_batch__write_stdin_pager_hits@transcript.snap
tests/snapshots/plot_images__grid_plots_emit_images_and_updates@transcript.snap
tests/snapshots/write_stdin_batch__write_stdin_pager_search.snap
tests/snapshots/pager__pager_whitespace_only_input_advances_page@transcript.snap
tests/snapshots/refactor_coverage__snapshots_pager_hits_with_images@transcript.snap
tests/snapshots/python_help_snapshots__python_help_contract@windows.snap
tests/snapshots/write_stdin_batch__write_stdin_pager_hits.snap
tests/snapshots/plot_images__multi_panel_plots_emit_single_image@macos.snap
tests/snapshots/plot_images__grid_plots_emit_stable_images_for_repeats@macos.snap
tests/snapshots/claude_integration__claude_live_install_integration@transcript.snap
tests/snapshots/codex_integration__linux__codex_exec_initial_sandbox_state.snap
tests/snapshots/plot_images__grid_plots_emit_images_and_updates@macos.snap
tests/snapshots/pager__pager_dedup_on_seek.snap
tests/snapshots/codex_integration__macos__codex_exec_initial_sandbox_state_plain.snap
tests/snapshots/refactor_coverage__snapshots_browser_prompt_and_continue.snap
tests/snapshots/mcp_transcripts__snapshots_support_multiple_calls_and_sessions.snap
tests/snapshots/refactor_coverage__snapshots_restart_and_interrupt_with_plots.snap
tests/snapshots/codex_integration__linux__codex_exec_initial_sandbox_state_plain.snap
tests/snapshots/python_snapshots__snapshots_input_followup_prompt_once.snap
tests/snapshots/refactor_coverage__snapshots_truncation_notice_tail@transcript.snap
tests/snapshots/refactor_coverage__snapshots_restart_and_interrupt_with_plots@transcript.snap
tests/snapshots/python_snapshots__snapshots_multiline_cell_no_continuation_prompt@transcript.snap
tests/snapshots/write_stdin_batch__write_stdin_pager_search@transcript.snap
tests/snapshots/codex_integration__macos__codex_exec_initial_sandbox_state.snap
tests/snapshots/plot_images__grid_plots_emit_images_and_updates@transcript__macos.snap
tests/snapshots/plot_images__plots_emit_stable_images_for_repeats.snap
tests/snapshots/python_snapshots__snapshots_input_followup_prompt_once@transcript.snap
tests/snapshots/mcp_transcripts__snapshots_interrupt_handler_output.snap
tests/snapshots/pager__pager_search_and_counts@transcript.snap
tests/snapshots/mcp_transcripts__snapshots_tempdir_session_restart@transcript.snap
tests/snapshots/python_snapshots__snapshots_buffered_input_prompt_matching_primary_prompt.snap
tests/snapshots/plot_images__grid_plots_emit_images_and_updates.snap
tests/snapshots/plot_images__plots_emit_images_and_updates.snap
tests/snapshots/session_endings__snapshots_session_endings@transcript.snap
tests/snapshots/pager__pager_matches_with_headings.snap
tests/snapshots/write_stdin_batch__write_stdin_drives_browser.snap
tests/snapshots/write_stdin_batch__write_stdin_accepts_multiple_calls@transcript.snap
tests/snapshots/write_stdin_batch__write_stdin_accepts_multiple_calls.snap
tests/snapshots/claude_integration__claude_live_integration@transcript.snap
tests/snapshots/pager__pager_hits_mode.snap
tests/snapshots/plot_images__multi_panel_plots_emit_single_image.snap
tests/snapshots/python_help_snapshots__python_help_contract@windows-transcript.snap
tests/snapshots/pager__pager_search_preserves_whitespace.snap
tests/snapshots/pager__paginates_large_output.snap
tests/snapshots/plot_images__grid_plots_emit_stable_images_for_repeats.snap
tests/snapshots/plot_images__plots_emit_images_and_updates@transcript__macos.snap
tests/snapshots/pager__pager_whitespace_only_input_advances_page.snap
tests/snapshots/server_smoke__sends_input_to_r_console.snap
tests/snapshots/refactor_coverage__snapshots_pager_hits_with_images.snap
tests/snapshots/python_help_snapshots__python_help_contract@transcript.snap
tests/snapshots/codex_integration__macos__codex_exec_wire_sandbox_state_meta.snap
tests/snapshots/pager__paginates_large_output@transcript.snap before constructing the bwrap mounts. If a denied pattern matches a path that does not exist when the worker starts, no mask is installed, so sandboxed code can create or write that matching file inside an otherwise writable root; this bypasses the documented/Codex glob-deny semantics for newly-created secrets or artifacts.
<<<FINDING 1 END>>>
Finding 1:
<<<FINDING 1 START>>>
[P2] Preserve missing writable roots in bwrap policy — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:3405-3409: When a managed profile grants write access to a path that does not exist yet, such as a :project-roots subpath intended for new build/output files under an otherwise read-only tree, this filter drops that writable root before constructing the bwrap mounts. The worker then cannot create the granted path, so the Linux managed profile is silently narrowed instead of honoring or explicitly rejecting the requested permission.
<<<FINDING 1 END>>>
[P2] Preserve symlinked writable-root aliases in bwrap — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:3544-3548: When a Linux managed restricted-read profile grants write access to a symlinked directory, only the canonical target path is mounted while the requested writable-root path remains the synthetic parent directory. Writes through the policy path itself, such as when the sandbox cwd or project root is a symlink, fail even though the profile explicitly allows that root because the alias path is never bound to the writable target inside the tmpfs namespace.
[P1] Preserve managed read-only carveouts on Landlock fallback — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:4480-4484: When Linux bwrap is disabled via `MCP_REPL_USE_LINUX_BWRAP=0`, `useLegacyLandlock`, or a startup fallback, this path converts each managed `WritableRoot` to only `root`, dropping `read_only_subpaths` and `protected_metadata_names`. For managed profiles with explicit writable roots, the legacy Landlock launch then allows writes to protected `.git`, `.agents`, and `.codex` paths that the bwrap path blocks, silently widening the inherited sandbox instead of failing closed or preserving those exclusions.
[P2] Keep runtime libraries readable for minimal deny profiles — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:3453-3460: On Linux managed profiles that combine restricted reads such as `:minimal` with any deny path/glob, `include_platform_defaults()` is false, so this branch omits `/lib`, `/lib64`, `/usr`, etc. from the tmpfs-based bwrap root. The helper/current executable is still bind-mounted, but a dynamically linked binary cannot exec without its ELF loader and shared libraries, so otherwise valid minimal+deny profiles fail before the worker starts.
Finding 1:
[P1] Deny directory glob matches in fallback scanner — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:4412-4414: When `rg` is not installed, the internal glob scanner only records files and symlinks that match the deny glob. If a Linux managed profile denies a glob that matches a directory, such as `secrets*` or `.ssh*`, the directory itself is never added to `unreadable_roots`, so its children remain readable inside the bubblewrap sandbox. This leaves deny-read globs partially unenforced in environments using the fallback scanner.

Finding 2:
[P2] Preserve macOS platform defaults for restricted profiles — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:389-390: On macOS, this now adds the restricted-read platform defaults only when the managed profile includes `:minimal`. For any restricted managed profile that grants project or explicit paths without full-disk read and without `:minimal`, the generated seatbelt policy no longer includes the runtime/system read allowances that were previously applied to all restricted non-full-read profiles, which can break worker startup or common system/library probes under those profiles.
Finding 1:
[P1] Mount runtime roots for all restricted-read Linux profiles: When Linux receives a managed restricted-read profile that does not include `:minimal` or full root read, such as a project-roots read profile, this branch skips mounting `/lib`, `/lib64`, `/usr`, etc. The inner helper is then executed inside bwrap with only the project/current executable/R home visible, so dynamically linked builds cannot load the ELF interpreter/libc and fail before `worker_ready`. The platform runtime roots should not be gated only on `has_minimal_read_entry()`.

Finding 2:
[P2] Include matching directories when expanding Linux deny globs: On systems with `rg` installed, this fast path uses `rg --files`, which only emits files, while the internal fallback includes matching directories. For a deny-read glob that matches a directory name outside writable roots, e.g. `secrets*`, the directory itself is omitted from `unreadable_roots`, so its children remain readable instead of being masked.
Finding 1:
<<<FINDING 1 START>>>
[P1] Avoid granting platform roots to all restricted profiles — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:3462-3468
On Linux bwrap, any managed profile without full-disk read now gets `/bin`, `/usr`, `/etc`, `/lib`, etc. mounted read-only, even when the profile only grants a specific readable path and does not include `:minimal`. This widens restricted-read policies and lets code read host files such as `/etc/*` that the inherited permission profile did not allow; these defaults should be conditional on the minimal/default-runtime profile rather than all restricted profiles.
<<<FINDING 1 END>>>
Finding 1:
<<<FINDING 1 START>>>
[P2] Mount runtime roots for project-only profiles — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:394-396: On Linux, a managed restricted-read profile that grants only project roots and temp writes, without `:minimal` or root read, now skips platform default mounts. The bwrap command still execs the inner `mcp-repl` helper and worker inside that namespace, so dynamically linked runtime dependencies under `/lib`, `/lib64`, or `/usr` are unavailable and startup can fail before `worker_ready`. This breaks valid project-only restricted-read profiles; the implementation needs to mount the helper/runtime dependencies even when the client did not request `:minimal`.
<<<FINDING 1 END>>>
Finding 1:
[P2] Restore bwrap after non-legacy inherited metadata — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:1255-1259: When an inherited Linux tool call sets `useLegacyLandlock: true`, this stores `use_linux_sandbox_bwrap = false`; a later tool call with `useLegacyLandlock: false` maps to `None`, so `apply_update` leaves bwrap disabled. In inherit mode this makes the legacy Landlock choice sticky across per-call metadata, so later managed/restricted profiles that require bwrap can fail or run with the wrong backend until the server is restarted.
Finding 1:
<<<FINDING 1 START>>>
[P2] Honor explicit Linux bwrap disable on inherited metadata — /home/tomasz/github/posit-dev/mcp-repl/src/sandbox.rs:1748-1749: When `--sandbox inherit` receives Codex metadata with the normal `useLegacyLandlock: false`, this branch flips `use_linux_sandbox_bwrap` back to `true` even if the user explicitly disabled bwrap via `MCP_REPL_USE_LINUX_BWRAP=0` or `features.use_linux_sandbox_bwrap=false`. In environments where bwrap is unavailable or intentionally disabled, the next inherited tool call will unexpectedly try the bwrap path instead of staying on legacy Landlock.
<<<FINDING 1 END>>>
@t-kalinowski t-kalinowski force-pushed the linux-sandbox-update branch from 8ccf5f2 to b8a83c2 Compare June 26, 2026 19:24
@t-kalinowski t-kalinowski force-pushed the linux-sandbox-update branch from b8a83c2 to 94508d8 Compare June 26, 2026 19:58
@t-kalinowski t-kalinowski merged commit 08a5c0a into main Jun 27, 2026
3 checks passed
@t-kalinowski t-kalinowski deleted the linux-sandbox-update branch June 27, 2026 00:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant