From 8e37bfc43affcbe2aa1298f1f869f372d80733b5 Mon Sep 17 00:00:00 2001 From: Homer Quan Date: Mon, 18 May 2026 14:29:25 -0400 Subject: [PATCH] fix: enforce execution profile sandbox settings --- README.md | 2 +- lib/mirror_neuron/execution/profile.ex | 26 ++++++++++++-- tests/unit/execution_profile_test.exs | 48 +++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2569174..c77bf8c 100644 --- a/README.md +++ b/README.md @@ -260,7 +260,7 @@ mix run --no-halt } ``` -The BEAM runtime keeps orchestration, leases, placement, reconnect, and manual recovery. OpenCV, ffmpeg, model runtimes, and similar heavy dependencies stay in the profile image or a prewarmed node cache. +The BEAM runtime keeps orchestration, leases, placement, reconnect, and manual recovery. OpenCV, ffmpeg, model runtimes, and similar heavy dependencies stay in the profile image or a prewarmed node cache. When a manifest selects an execution profile, the profile owns OpenShell security settings such as image, policy, remote access, SSH key, workspace reuse, upload path, pool, GPU, and capability settings; manifest values for those fields are ignored. Relative `policy` and `ssh_key` paths in a profile are expanded on the runtime node, not against a submitted payload bundle. ### Inspect Jobs and Events diff --git a/lib/mirror_neuron/execution/profile.ex b/lib/mirror_neuron/execution/profile.ex index bdb27ec..ce9be9b 100644 --- a/lib/mirror_neuron/execution/profile.ex +++ b/lib/mirror_neuron/execution/profile.ex @@ -29,6 +29,7 @@ defmodule MirrorNeuron.Execution.Profile do "video_codecs", "required_capabilities" ] + @profile_local_cli_path_keys ["policy", "ssh_key"] def apply_to_config(config) when is_map(config) do case profile_name(config) do @@ -38,9 +39,9 @@ defmodule MirrorNeuron.Execution.Profile do name -> case fetch(name) do {:ok, profile} -> - profile - |> config_from_profile() - |> Map.merge(stringify_map(config)) + config + |> manifest_config_for_profile() + |> Map.merge(config_from_profile(profile)) |> Map.put("execution_profile", name) {:error, _reason} -> @@ -158,6 +159,25 @@ defmodule MirrorNeuron.Execution.Profile do |> stringify_map() |> normalize_openshell_aliases() |> Map.merge(openshell) + |> resolve_profile_local_cli_paths() + end + + defp manifest_config_for_profile(config) do + config + |> stringify_map() + |> Map.drop(@openshell_keys) + end + + defp resolve_profile_local_cli_paths(config) do + Enum.reduce(@profile_local_cli_path_keys, config, fn key, acc -> + case Map.get(acc, key) do + value when is_binary(value) and value != "" -> + Map.put(acc, key, Path.expand(value)) + + _other -> + acc + end + end) end defp normalize_openshell_aliases(config) do diff --git a/tests/unit/execution_profile_test.exs b/tests/unit/execution_profile_test.exs index b0c7044..8c12d0f 100644 --- a/tests/unit/execution_profile_test.exs +++ b/tests/unit/execution_profile_test.exs @@ -67,11 +67,57 @@ defmodule MirrorNeuron.Execution.ProfileTest do assert config["pool"] == "opencv_gpu" assert config["pool_slots"] == 1 assert config["gpu"] == true - assert config["policy"] == "policies/video-egress.yaml" + assert config["policy"] == Path.expand("policies/video-egress.yaml") assert config["reuse_shared_sandbox"] == true assert config["persistent_workspace"] == true end + test "profile OpenShell settings override manifest supplied sandbox settings" do + Application.put_env(:mirror_neuron, :execution_profiles, %{ + "privileged-video" => %{ + "image" => "registry.local/video-guardian:stable", + "pool" => "opencv_gpu", + "pool_slots" => 2, + "policy" => "operator/policies/video.yaml", + "ssh_key" => "operator/keys/video.pem", + "remote" => %{"host" => "gpu-runtime.internal"}, + "reuse_shared_sandbox" => true, + "persistent_workspace" => false, + "sandbox_upload_path" => "/srv/mirror-neuron/uploads" + } + }) + + config = + Profile.apply_to_config(%{ + "execution_profile" => "privileged-video", + "from" => "attacker.local/unsafe:latest", + "image" => "attacker.local/unsafe-alias:latest", + "pool" => "default", + "pool_slots" => 99, + "policy" => "payloads/attacker-policy.yaml", + "ssh_key" => "payloads/attacker-key.pem", + "remote" => %{"host" => "attacker.example"}, + "reuse_shared_sandbox" => false, + "persistent_workspace" => true, + "sandbox_upload_path" => "/tmp/attacker", + "runner_module" => ConfigEchoRunner, + "output_message_type" => nil + }) + + assert config["execution_profile"] == "privileged-video" + assert config["from"] == "registry.local/video-guardian:stable" + assert config["pool"] == "opencv_gpu" + assert config["pool_slots"] == 2 + assert config["policy"] == Path.expand("operator/policies/video.yaml") + assert config["ssh_key"] == Path.expand("operator/keys/video.pem") + assert config["remote"] == %{"host" => "gpu-runtime.internal"} + assert config["reuse_shared_sandbox"] == true + assert config["persistent_workspace"] == false + assert config["sandbox_upload_path"] == "/srv/mirror-neuron/uploads" + assert config["runner_module"] == ConfigEchoRunner + assert Map.has_key?(config, "output_message_type") + end + test "capability matching accepts only healthy nodes that advertise the required profile" do Application.put_env(:mirror_neuron, :execution_profiles, %{ "opencv-video-guardian" => %{