Files

128 lines
4.1 KiB
C#

using System.Security.AccessControl;
using System.Security.Principal;
using System.Runtime.Versioning;
using MBN_STOCK_WEBVIEW.Infrastructure;
namespace MBN_STOCK_WEBVIEW.Infrastructure.Tests;
public sealed class TrustedManualDirectoryTests
{
[Fact]
public void Prepare_creates_nested_private_directory_with_protected_acl()
{
using var parent = new TemporaryDirectory();
var target = Path.Combine(parent.Path, "operator", "private");
var prepared = TrustedManualDirectory.PreparePrivateWritableDirectory(target);
Assert.Equal(Path.GetFullPath(target), prepared);
Assert.True(Directory.Exists(prepared));
if (OperatingSystem.IsWindows())
{
AssertPrivateAcl(prepared);
}
}
[SupportedOSPlatform("windows")]
private static void AssertPrivateAcl(string directory)
{
var security = new DirectoryInfo(directory).GetAccessControl(
AccessControlSections.Access);
Assert.True(security.AreAccessRulesProtected);
Assert.DoesNotContain(
security.GetAccessRules(true, true, typeof(SecurityIdentifier))
.Cast<FileSystemAccessRule>(),
rule => rule.IsInherited);
}
[Fact]
public void Writable_store_rejects_acl_expanded_to_world_readable()
{
if (!OperatingSystem.IsWindows())
{
return;
}
using var directory = new TemporaryDirectory();
TrustedManualDirectory.PreparePrivateWritableDirectory(directory.Path);
var info = new DirectoryInfo(directory.Path);
var security = info.GetAccessControl();
security.AddAccessRule(new FileSystemAccessRule(
new SecurityIdentifier(WellKnownSidType.WorldSid, null),
FileSystemRights.Read,
AccessControlType.Allow));
info.SetAccessControl(security);
Assert.Throws<S5025TrustedManualDataException>(() =>
new S5025TrustedManualFileStore(directory.Path));
}
[Fact]
public void Writable_lease_pins_directory_against_rename_race()
{
if (!OperatingSystem.IsWindows())
{
return;
}
using var parent = new TemporaryDirectory();
var directory = Path.Combine(parent.Path, "operator");
TrustedManualDirectory.PreparePrivateWritableDirectory(directory);
using (var store = new S5025TrustedManualFileStore(directory))
{
Assert.ThrowsAny<IOException>(() =>
Directory.Move(directory, Path.Combine(parent.Path, "redirected")));
}
Directory.Move(directory, Path.Combine(parent.Path, "released"));
}
[Fact]
public void Prepare_rejects_reparse_ancestor_before_creating_target()
{
using var parent = new TemporaryDirectory();
using var outside = new TemporaryDirectory();
var link = Path.Combine(parent.Path, "linked");
try
{
Directory.CreateSymbolicLink(link, outside.Path);
}
catch (Exception exception) when (exception is
UnauthorizedAccessException or IOException or PlatformNotSupportedException)
{
return;
}
Assert.Throws<S5025TrustedManualDataException>(() =>
TrustedManualDirectory.PreparePrivateWritableDirectory(
Path.Combine(link, "must-not-be-created")));
Assert.False(Directory.Exists(Path.Combine(outside.Path, "must-not-be-created")));
}
private sealed class TemporaryDirectory : IDisposable
{
public TemporaryDirectory()
{
Path = System.IO.Path.Combine(
System.IO.Path.GetTempPath(),
Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(Path);
}
public string Path { get; }
public void Dispose()
{
try
{
Directory.Delete(Path, recursive: true);
}
catch (Exception exception) when (exception is
IOException or UnauthorizedAccessException)
{
// Cleanup must not hide the test result.
}
}
}
}