summaryrefslogtreecommitdiff
path: root/tests/Agent.Windows.Tests/SecurityPolicyTests.cs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/Agent.Windows.Tests/SecurityPolicyTests.cs')
-rw-r--r--tests/Agent.Windows.Tests/SecurityPolicyTests.cs74
1 files changed, 54 insertions, 20 deletions
diff --git a/tests/Agent.Windows.Tests/SecurityPolicyTests.cs b/tests/Agent.Windows.Tests/SecurityPolicyTests.cs
index 6dbc9ab..5b10417 100644
--- a/tests/Agent.Windows.Tests/SecurityPolicyTests.cs
+++ b/tests/Agent.Windows.Tests/SecurityPolicyTests.cs
@@ -11,6 +11,8 @@ using Agent.Core.Tools;
using System.Text.Json;
using Agent.App.Sandbox;
using Agent.App.Configuration;
+using LoopbackExemption = Agent.Admin.Host.LoopbackExemption;
+using WfpDynamicSession = Agent.Admin.Host.WfpDynamicSession;
namespace Agent.Windows.Tests;
@@ -793,7 +795,7 @@ public sealed class SecurityPolicyTests
try
{
- RecoveryResult recovered = new RecoveryManager(journals, audit).RecoverAll();
+ RecoveryResult recovered = new RecoveryManager(journals, audit, new DirectElevatedNetworkSessionFactory()).RecoverAll();
Assert.True(recovered.IsClean);
Assert.Equal(1, recovered.JournalsRecovered);
@@ -957,7 +959,8 @@ public sealed class SecurityPolicyTests
session.ProbeWriteTransaction();
IReadOnlyList<ulong> ids = session.ApplyDefaultBlock(profile.Sid,
- [new NetworkEndpoint(IPAddress.Loopback, 11434), new NetworkEndpoint(IPAddress.IPv6Loopback, 11434)]);
+ [new Agent.Admin.Host.NetworkEndpoint(IPAddress.Loopback, 11434),
+ new Agent.Admin.Host.NetworkEndpoint(IPAddress.IPv6Loopback, 11434)]);
Assert.Equal(4, ids.Count);
Assert.All(ids, id => Assert.NotEqual(0UL, id));
@@ -965,7 +968,7 @@ public sealed class SecurityPolicyTests
}
[Fact]
- public async Task Windows_backend_applies_file_effect_and_cleans_acl_wfp_temp_and_journal()
+ public async Task Windows_backend_file_only_effect_does_not_apply_acl_or_network_resources()
{
using TestDirectory directory = new();
string workspace = Path.Combine(directory.Path, "workspace");
@@ -978,7 +981,8 @@ public sealed class SecurityPolicyTests
[new FilePolicyEntry(workspace, FileCapability.Read | FileCapability.Write | FileCapability.Delete)], [], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000);
- var backend = new WindowsToolBackend(settings, audit);
+ var elevated = new DirectElevatedNetworkSessionFactory();
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: elevated);
Guid taskId = Guid.NewGuid();
Guid sessionId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, sessionId, TestContext.Current.CancellationToken);
@@ -995,13 +999,13 @@ public sealed class SecurityPolicyTests
Assert.Empty(Directory.EnumerateFiles(Path.Combine(directory.Path, "journals")));
string backups = Path.Combine(directory.Path, "backups");
Assert.True(!Directory.Exists(backups) || !Directory.EnumerateFileSystemEntries(backups).Any());
- Assert.Contains(audit.Events, item => item.Event == "network.rule.remove" && item.Data["residual_filters"] == "0");
+ Assert.Equal(0, elevated.Starts);
+ Assert.DoesNotContain(audit.Events, item => item.Event is "network.rule.apply" or "network.rule.remove");
Assert.Contains(audit.Events, item => item.Event == "task.start" && item.SessionId == sessionId);
Assert.Contains(audit.Events, item => item.Event == "tool.call" && item.SessionId == sessionId);
Assert.Contains(audit.Events, item => item.Event == "file.write" && item.SessionId == sessionId);
Assert.Contains(audit.Events, item => item.Event == "task.end" && item.SessionId == sessionId);
- Assert.Single(audit.Events, item => item.Event == "acl.apply");
- Assert.Single(audit.Events, item => item.Event == "acl.restore");
+ Assert.DoesNotContain(audit.Events, item => item.Event is "acl.apply" or "acl.restore");
}
[Fact]
@@ -1023,7 +1027,7 @@ public sealed class SecurityPolicyTests
[new ApprovedEntryExecutable("cmd", cmd, EntryExecutablePolicy.ComputeSha256(cmd), true, null)], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000, BaselineCapabilities: ["registryRead", "lpacInstrumentation"]);
- var backend = new WindowsToolBackend(settings, audit);
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: new DirectElevatedNetworkSessionFactory());
Guid taskId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
@@ -1042,6 +1046,34 @@ public sealed class SecurityPolicyTests
}
[Fact]
+ public async Task File_only_task_does_not_start_the_elevated_helper()
+ {
+ using TestDirectory directory = new();
+ string workspace = Path.Combine(directory.Path, "workspace");
+ Directory.CreateDirectory(workspace);
+ string input = Path.Combine(workspace, "input.txt");
+ File.WriteAllText(input, "read-only operation");
+ var factory = new NoOpElevatedNetworkSessionFactory();
+ var settings = new EffectiveSandboxPolicy(
+ [new FilePolicyEntry(workspace, FileCapability.Read | FileCapability.Write | FileCapability.Delete | FileCapability.Execute, "workspace")],
+ [], [], [workspace], new SandboxJobLimits(), "none", [],
+ Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
+ 16 * 1024 * 1024, 1024 * 1024, 1000, 10000,
+ BaselineCapabilities: ["registryRead", "lpacInstrumentation"]);
+ var backend = new WindowsToolBackend(settings, new MemorySecurityAuditSink(), elevatedNetwork: factory);
+ Guid taskId = Guid.NewGuid();
+
+ await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
+ using JsonDocument read = JsonDocument.Parse($$"""{"path":{{JsonSerializer.Serialize(input)}},"mode":"whole"}""");
+ ToolExecutionResult result = await backend.ExecuteAsync(taskId, "file_read", read.RootElement,
+ TestContext.Current.CancellationToken);
+ await backend.EndTaskAsync(taskId, result.Success, TestContext.Current.CancellationToken);
+
+ Assert.True(result.Success);
+ Assert.Equal(0, factory.Starts);
+ }
+
+ [Fact]
public async Task Dir_mkdir_creates_directory_within_approved_root_and_cleans_up()
{
using TestDirectory directory = new();
@@ -1054,7 +1086,7 @@ public sealed class SecurityPolicyTests
[new FilePolicyEntry(workspace, FileCapability.Read | FileCapability.Write | FileCapability.Delete)], [], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000);
- var backend = new WindowsToolBackend(settings, audit);
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: new DirectElevatedNetworkSessionFactory());
Guid taskId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
using JsonDocument mkdir = JsonDocument.Parse($$"""{"path":{{JsonSerializer.Serialize(target)}},"operation":"mkdir"}""");
@@ -1085,7 +1117,7 @@ public sealed class SecurityPolicyTests
[new FilePolicyEntry(workspace, FileCapability.Read | FileCapability.Write | FileCapability.Delete)], [], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000);
- var backend = new WindowsToolBackend(settings, audit);
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: new DirectElevatedNetworkSessionFactory());
Guid taskId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
using JsonDocument rmdir = JsonDocument.Parse($$"""{"path":{{JsonSerializer.Serialize(target)}},"operation":"rmdir"}""");
@@ -1113,7 +1145,7 @@ public sealed class SecurityPolicyTests
[new FilePolicyEntry(workspace, FileCapability.Read | FileCapability.Write | FileCapability.Delete)], [], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000);
- var backend = new WindowsToolBackend(settings, audit);
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: new DirectElevatedNetworkSessionFactory());
Guid taskId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
using JsonDocument rmdir = JsonDocument.Parse($$"""{"path":{{JsonSerializer.Serialize(target)}},"operation":"rmdir"}""");
@@ -1140,7 +1172,7 @@ public sealed class SecurityPolicyTests
[new FilePolicyEntry(workspace, FileCapability.Read)], [], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000);
- var backend = new WindowsToolBackend(settings, audit);
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: new DirectElevatedNetworkSessionFactory());
Guid taskId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
using JsonDocument list = JsonDocument.Parse($$"""{"path":{{JsonSerializer.Serialize(workspace)}},"operation":"list"}""");
@@ -1166,7 +1198,7 @@ public sealed class SecurityPolicyTests
[new FilePolicyEntry(workspace, FileCapability.Read | FileCapability.Write)], [], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000);
- var backend = new WindowsToolBackend(settings, audit);
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: new DirectElevatedNetworkSessionFactory());
Guid taskId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
string badTarget = Path.Combine(outside.Path, "escaped");
@@ -1192,7 +1224,7 @@ public sealed class SecurityPolicyTests
[new FilePolicyEntry(workspace, FileCapability.Read)], [], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000);
- var backend = new WindowsToolBackend(settings, audit);
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: new DirectElevatedNetworkSessionFactory());
Guid taskId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
using JsonDocument tail = JsonDocument.Parse($$"""{"root":{{JsonSerializer.Serialize(binaryFile)}},"mode":"tail","query":"","tail_lines":10}""");
@@ -1231,7 +1263,7 @@ public sealed class SecurityPolicyTests
[new FilePolicyEntry(workspace, FileCapability.Read)], [], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000);
- var backend = new WindowsToolBackend(settings, audit);
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: new DirectElevatedNetworkSessionFactory());
Guid taskId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
using JsonDocument tail = JsonDocument.Parse($$"""{"root":{{JsonSerializer.Serialize(textFile)}},"mode":"tail","query":"","tail_lines":10}""");
@@ -1256,7 +1288,7 @@ public sealed class SecurityPolicyTests
[new FilePolicyEntry(workspace, FileCapability.Read | FileCapability.Write | FileCapability.Delete)], [], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000);
- var backend = new WindowsToolBackend(settings, audit);
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: new DirectElevatedNetworkSessionFactory());
Guid taskId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
using JsonDocument rename = JsonDocument.Parse($$"""{"path":{{JsonSerializer.Serialize(source)}},"operation":"rename","new_path":{{JsonSerializer.Serialize(dest)}}}""");
@@ -1287,7 +1319,7 @@ public sealed class SecurityPolicyTests
[new FilePolicyEntry(workspace, FileCapability.Read | FileCapability.Write)], [], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000);
- var backend = new WindowsToolBackend(settings, audit);
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: new DirectElevatedNetworkSessionFactory());
Guid taskId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
using JsonDocument rename = JsonDocument.Parse($$"""{"path":{{JsonSerializer.Serialize(source)}},"operation":"rename","new_path":{{JsonSerializer.Serialize(dest)}}}""");
@@ -1317,7 +1349,7 @@ public sealed class SecurityPolicyTests
[new FilePolicyEntry(workspace, FileCapability.Read | FileCapability.Write)], [], [], [],
new SandboxJobLimits(), "none", [], Path.Combine(directory.Path, "journals"), Path.Combine(directory.Path, "backups"),
16 * 1024 * 1024, 1024 * 1024, 1000, 10000);
- var backend = new WindowsToolBackend(settings, audit);
+ var backend = new WindowsToolBackend(settings, audit, elevatedNetwork: new DirectElevatedNetworkSessionFactory());
Guid taskId = Guid.NewGuid();
await backend.BeginTaskAsync(taskId, Guid.NewGuid(), TestContext.Current.CancellationToken);
using JsonDocument rename = JsonDocument.Parse($$"""{"path":{{JsonSerializer.Serialize(source)}},"operation":"rename","new_path":{{JsonSerializer.Serialize(dest)}}}""");
@@ -1358,7 +1390,8 @@ public sealed class SecurityPolicyTests
{
Guid taskId = Guid.NewGuid();
TaskResourceJournal journal = TaskResourceJournal.Create(Path.Combine(directory.Path, "deny-journal"), taskId);
- using (TaskNetworkPolicy.Apply(journal, profile.Sid, [], audit))
+ await using (await TaskNetworkPolicy.ApplyAsync(new DirectElevatedNetworkSession(), journal, profile.Sid,
+ "none", [], audit, null, TestContext.Current.CancellationToken))
{
RestrictedProcessResult denied = Curl(launcher, taskId, curl, deniedServer.Url, Environment.SystemDirectory);
Assert.NotEqual(0u, denied.ExitCode);
@@ -1383,7 +1416,8 @@ public sealed class SecurityPolicyTests
[
new(allowedAddress, checked((ushort)allowedV4.Port))
];
- using (TaskNetworkPolicy.Apply(allowJournal, profile.Sid, allowances, audit))
+ await using (await TaskNetworkPolicy.ApplyAsync(new DirectElevatedNetworkSession(), allowJournal, profile.Sid,
+ "local", allowances, audit, null, TestContext.Current.CancellationToken))
{
RestrictedProcessResult v4 = Curl(launcher, allowTaskId, curl, allowedV4.Url, Environment.SystemDirectory);
RestrictedProcessResult wrongPort = Curl(launcher, allowTaskId, curl, deniedPort.Url, Environment.SystemDirectory);