Files
MBN_STOCK_WEBVIEW/tests/MBN_STOCK_WEBVIEW.Playout.Tests/TrustedPlayoutAssetPathTests.cs

123 lines
4.3 KiB
C#

using System.Runtime.InteropServices;
namespace MBN_STOCK_WEBVIEW.Playout.Tests;
public sealed class TrustedPlayoutAssetPathTests
{
private static readonly IReadOnlySet<string> Allowed = new HashSet<string>(
[".vrv", ".png"],
StringComparer.OrdinalIgnoreCase);
[Fact]
public void ResolveRelativeFile_AcceptsOneInternalRegularFile()
{
using var root = TemporarySceneDirectory.Create("studio.vrv");
var resolved = TrustedPlayoutAssetPath.ResolveRelativeFile(
root.Path,
"studio.vrv",
Allowed);
Assert.Equal(Path.Combine(root.Path, "studio.vrv"), resolved, ignoreCase: true);
}
[Theory]
[InlineData("..\\outside.vrv")]
[InlineData("C:\\outside.vrv")]
public void ResolveRelativeFile_RejectsRootEscape(string input)
{
using var root = TemporarySceneDirectory.Create("studio.vrv");
Assert.Throws<TrustedPlayoutAssetException>(() =>
TrustedPlayoutAssetPath.ResolveRelativeFile(root.Path, input, Allowed));
}
[Fact]
public void ResolveRelativeFile_RejectsMissingFile()
{
using var root = TemporarySceneDirectory.Create();
Assert.Throws<TrustedPlayoutAssetException>(() =>
TrustedPlayoutAssetPath.ResolveRelativeFile(root.Path, "missing.vrv", Allowed));
}
[Fact]
public void ResolveAbsoluteFile_RejectsPickerSelectionOutsideTheRoot()
{
using var root = TemporarySceneDirectory.Create();
using var outside = TemporarySceneDirectory.Create("outside.vrv");
Assert.Throws<TrustedPlayoutAssetException>(() =>
TrustedPlayoutAssetPath.ResolveAbsoluteFile(
root.Path,
Path.Combine(outside.Path, "outside.vrv"),
Allowed));
}
[Fact]
public void ResolveRelativeFile_RejectsHardLinkedFile()
{
using var root = TemporarySceneDirectory.Create();
var parent = Directory.GetParent(root.Path)!.FullName;
var outside = Path.Combine(parent, "outside.vrv");
File.WriteAllText(outside, "outside trusted-looking bytes");
var link = Path.Combine(root.Path, "linked.vrv");
Assert.True(CreateHardLinkW(link, outside, IntPtr.Zero));
Assert.Throws<TrustedPlayoutAssetException>(() =>
TrustedPlayoutAssetPath.ResolveRelativeFile(root.Path, "linked.vrv", Allowed));
}
[Fact]
public void ResolveRelativeFile_RejectsReparseDirectory()
{
using var root = TemporarySceneDirectory.Create();
var parent = Directory.GetParent(root.Path)!.FullName;
var outside = Path.Combine(parent, "outside");
Directory.CreateDirectory(outside);
File.WriteAllText(Path.Combine(outside, "studio.vrv"), "outside bytes");
var link = Path.Combine(root.Path, "linked");
try
{
Directory.CreateSymbolicLink(link, outside);
}
catch (Exception exception) when (
exception is UnauthorizedAccessException or IOException or PlatformNotSupportedException)
{
throw Xunit.Sdk.SkipException.ForSkip(
"The Windows test host does not permit creating a symbolic link.");
}
Assert.Throws<TrustedPlayoutAssetException>(() =>
TrustedPlayoutAssetPath.ResolveRelativeFile(
root.Path,
"linked\\studio.vrv",
Allowed));
}
[Fact]
public void EnsureOpenedRegularFile_BindsTheHandleToTheExpectedPath()
{
using var root = TemporarySceneDirectory.Create("expected.vrv", "other.vrv");
var expected = Path.Combine(root.Path, "expected.vrv");
var other = Path.Combine(root.Path, "other.vrv");
using var stream = new FileStream(
other,
FileMode.Open,
FileAccess.Read,
FileShare.Read);
Assert.Throws<TrustedPlayoutAssetException>(() =>
TrustedPlayoutAssetPath.EnsureOpenedRegularFile(
stream.SafeFileHandle,
expected));
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CreateHardLinkW(
string fileName,
string existingFileName,
IntPtr securityAttributes);
}