using System.Buffers.Binary; using System.Diagnostics; using System.IO.Pipes; using System.Net; using System.Reflection; using Agent.Admin.Client; using Agent.Windows.Network; using Agent.Windows.Recovery; using Agent.Windows.Security; if (args.Length == 0) return Usage(); try { switch (args[0]) { case "local-peer": await VerifyLocalPeerAsync(); break; case "remote-peer": await VerifyRemotePeerAsync(); break; case "connect": return await ConnectAsync(args); case "wrong-task": case "wrong-version": case "double-apply": case "oversized": Require(args, 2); await VerifyHostRejectsAsync(args[0], Path.GetFullPath(args[1])); break; case "crash-local": Require(args, 4); await LeaveCrashJournalAsync(Path.GetFullPath(args[1]), Path.GetFullPath(args[2]), Path.GetFullPath(args[3])); break; default: return Usage(); } Console.WriteLine($"PASS {args[0]}"); return 0; } catch (Exception ex) { Console.Error.WriteLine(ex); return 1; } static int Usage() { Console.Error.WriteLine("Internal acceptance harness. See scripts/Verify-AdminIpcAttackMatrix.ps1."); return 2; } static void Require(string[] values, int count) { if (values.Length != count) throw new ArgumentException("Invalid acceptance harness arguments."); } static async Task VerifyLocalPeerAsync() { string pipeName = $"AgentSandboxMcp-Acceptance-{Guid.NewGuid():N}"; await using NamedPipeServerStream pipe = CreateAcceptancePipe(pipeName); using Process child = StartSelf("connect", ".", pipeName, "success"); await pipe.WaitForConnectionAsync().WaitAsync(TimeSpan.FromSeconds(10)); uint clientPid = GetClientProcessId(pipe); if (clientPid != child.Id || clientPid == Environment.ProcessId) throw new InvalidOperationException("The actual local Named Pipe peer PID was not authenticated."); await child.WaitForExitAsync().WaitAsync(TimeSpan.FromSeconds(10)); if (child.ExitCode != 0) throw new InvalidOperationException($"Local peer exited with {child.ExitCode}."); } static async Task VerifyRemotePeerAsync() { string pipeName = $"AgentSandboxMcp-Acceptance-{Guid.NewGuid():N}"; await using NamedPipeServerStream pipe = CreateAcceptancePipe(pipeName); using Process child = StartSelf("connect", Environment.MachineName, pipeName, "rejected"); await child.WaitForExitAsync().WaitAsync(TimeSpan.FromSeconds(10)); if (child.ExitCode != 0) throw new InvalidOperationException($"Remote-form peer was not rejected (exit {child.ExitCode})."); if (pipe.IsConnected) throw new InvalidOperationException("PIPE_REJECT_REMOTE_CLIENTS accepted a remote-form connection."); } static async Task ConnectAsync(string[] values) { Require(values, 4); bool expectSuccess = values[3] == "success"; try { await using var pipe = new NamedPipeClientStream(values[1], values[2], PipeDirection.InOut, PipeOptions.Asynchronous); await pipe.ConnectAsync(2_000); return expectSuccess ? 0 : 1; } catch (Exception) when (!expectSuccess) { return 0; } } static Process StartSelf(params string[] arguments) { var start = new ProcessStartInfo { FileName = Environment.ProcessPath!, UseShellExecute = false }; foreach (string argument in arguments) start.ArgumentList.Add(argument); return Process.Start(start) ?? throw new InvalidOperationException("Acceptance child did not start."); } static NamedPipeServerStream CreateAcceptancePipe(string pipeName) => (NamedPipeServerStream)(typeof(ElevatedNetworkSessionFactory).GetMethod("CreateLocalPipeForCurrentLogon", BindingFlags.Static | BindingFlags.NonPublic)?.Invoke(null, [pipeName]) ?? throw new MissingMethodException("Production pipe factory test hook was not found.")); static uint GetClientProcessId(NamedPipeServerStream pipe) { object?[] arguments = [pipe, 0u]; bool success = (bool)(typeof(ElevatedNetworkSessionFactory).GetMethod("TryGetClientProcessId", BindingFlags.Static | BindingFlags.NonPublic)?.Invoke(null, arguments) ?? false); if (!success) throw new InvalidOperationException("GetNamedPipeClientProcessId failed."); return (uint)arguments[1]!; } static NamedPipeServerStream SessionPipe(IElevatedNetworkSession session) => (NamedPipeServerStream)(session.GetType().GetProperty("Pipe", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(session) ?? throw new MissingMemberException("Elevated session pipe test hook was not found.")); static Process SessionHelper(IElevatedNetworkSession session) => (Process)(session.GetType().GetProperty("HelperProcess", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(session) ?? throw new MissingMemberException("Elevated session Process test hook was not found.")); static async Task VerifyHostRejectsAsync(string mode, string helperPath) { Guid taskId = Guid.NewGuid(); var factory = new ElevatedNetworkSessionFactory(helperPath); await using IElevatedNetworkSession abstraction = await factory.StartAsync(taskId, CancellationToken.None); IElevatedNetworkSession session = abstraction; NamedPipeServerStream pipe = SessionPipe(session); Process helper = SessionHelper(session); string sid = AppContainerProfile.EnsureFixed().Sid.Value; if (mode == "double-apply") { await session.ApplyAsync(sid, "none", [], false, CancellationToken.None); await AssertRejectedAsync(pipe, new(AdminProtocol.Version, "apply-network", taskId, sid, "none", [], false)); return; } if (mode == "oversized") { byte[] header = new byte[sizeof(int)]; BinaryPrimitives.WriteInt32LittleEndian(header, AdminProtocol.MaxMessageBytes + 1); await pipe.WriteAsync(header); await pipe.FlushAsync(); await helper.WaitForExitAsync().WaitAsync(TimeSpan.FromSeconds(10)); if (helper.ExitCode != 2) throw new InvalidOperationException("Oversized message did not terminate the helper with its rejection exit code."); return; } AdminRequest request = mode == "wrong-task" ? new(AdminProtocol.Version, "inspect-loopback", Guid.NewGuid(), sid) : new(AdminProtocol.Version + 1, "inspect-loopback", taskId, sid); await AssertRejectedAsync(pipe, request); } static async Task AssertRejectedAsync(NamedPipeServerStream pipe, AdminRequest request) { await AdminProtocol.WriteAsync(pipe, request, CancellationToken.None); AdminResponse response = await AdminProtocol.ReadAsync(pipe, CancellationToken.None); if (response.Success || response.ErrorCode != "sandbox.ipc_rejected") throw new InvalidOperationException("The hostile administrative request was not rejected canonically."); } static async Task LeaveCrashJournalAsync(string helperPath, string journalRoot, string readyPath) { Guid taskId = Guid.NewGuid(); var factory = new ElevatedNetworkSessionFactory(helperPath); IElevatedNetworkSession abstraction = await factory.StartAsync(taskId, CancellationToken.None); IElevatedNetworkSession session = abstraction; Process helper = SessionHelper(session); string sid = AppContainerProfile.EnsureFixed().Sid.Value; bool existing = await session.InspectLoopbackAsync(sid, CancellationToken.None); if (existing) throw new InvalidOperationException("The fixed Application SID already has a loopback exemption; recover it before this scenario."); TaskResourceJournal journal = TaskResourceJournal.Create(journalRoot, taskId); _ = await TaskNetworkPolicy.ApplyAsync(session, journal, AppContainerProfile.EnsureFixed().Sid, "local", [new NetworkEndpoint(IPAddress.Loopback, 11434), new NetworkEndpoint(IPAddress.IPv6Loopback, 11434)], new MemorySecurityAuditSink(), null, CancellationToken.None); await File.WriteAllTextAsync(readyPath, $"task_id={taskId:D}\nhelper_pid={helper.Id}\n"); await helper.WaitForExitAsync(); // Intentionally do not dispose the Task Network Policy. The killed helper owns the // dynamic WFP session; the persistent loopback PREPARE/APPLIED record must survive. }