273 lines
8.2 KiB
C#
273 lines
8.2 KiB
C#
using Xunit;
|
|
|
|
namespace StellaOps.Scanner.Analyzers.OS.Windows.WinSxS.Tests;
|
|
|
|
public class WinSxSManifestParserTests
|
|
{
|
|
private readonly WinSxSManifestParser _parser = new();
|
|
|
|
[Fact]
|
|
public void Parse_WithValidManifest_ExtractsMetadata()
|
|
{
|
|
// Arrange
|
|
var manifestPath = CreateTestManifest(@"<?xml version=""1.0"" encoding=""UTF-8""?>
|
|
<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
|
|
<assemblyIdentity
|
|
name=""Microsoft.Windows.Common-Controls""
|
|
version=""6.0.0.0""
|
|
processorArchitecture=""x86""
|
|
publicKeyToken=""6595b64144ccf1df""
|
|
language=""*""
|
|
type=""win32"" />
|
|
<file name=""comctl32.dll"" hash=""abcd1234"" hashalg=""SHA256"" size=""12345"" />
|
|
</assembly>");
|
|
|
|
try
|
|
{
|
|
// Act
|
|
var result = _parser.Parse(manifestPath, CancellationToken.None);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal("Microsoft.Windows.Common-Controls", result.Name);
|
|
Assert.Equal("6.0.0.0", result.Version);
|
|
Assert.Equal("x86", result.ProcessorArchitecture);
|
|
Assert.Equal("6595b64144ccf1df", result.PublicKeyToken);
|
|
Assert.Equal("*", result.Language);
|
|
Assert.Equal("win32", result.Type);
|
|
Assert.Single(result.Files);
|
|
Assert.Equal("comctl32.dll", result.Files[0].Name);
|
|
}
|
|
finally
|
|
{
|
|
CleanupTestManifest(manifestPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_WithAmd64Architecture_ExtractsCorrectly()
|
|
{
|
|
// Arrange
|
|
var manifestPath = CreateTestManifest(@"<?xml version=""1.0"" encoding=""UTF-8""?>
|
|
<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
|
|
<assemblyIdentity
|
|
name=""Microsoft.Windows.SystemCompatible""
|
|
version=""10.0.19041.1""
|
|
processorArchitecture=""amd64""
|
|
publicKeyToken=""31bf3856ad364e35"" />
|
|
</assembly>");
|
|
|
|
try
|
|
{
|
|
// Act
|
|
var result = _parser.Parse(manifestPath, CancellationToken.None);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal("Microsoft.Windows.SystemCompatible", result.Name);
|
|
Assert.Equal("amd64", result.ProcessorArchitecture);
|
|
Assert.Equal("31bf3856ad364e35", result.PublicKeyToken);
|
|
}
|
|
finally
|
|
{
|
|
CleanupTestManifest(manifestPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_WithMultipleFiles_ExtractsAllFiles()
|
|
{
|
|
// Arrange
|
|
var manifestPath = CreateTestManifest(@"<?xml version=""1.0"" encoding=""UTF-8""?>
|
|
<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
|
|
<assemblyIdentity name=""TestAssembly"" version=""1.0.0.0"" processorArchitecture=""x86"" />
|
|
<file name=""file1.dll"" />
|
|
<file name=""file2.dll"" />
|
|
<file name=""file3.config"" />
|
|
</assembly>");
|
|
|
|
try
|
|
{
|
|
// Act
|
|
var result = _parser.Parse(manifestPath, CancellationToken.None);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal(3, result.Files.Count);
|
|
Assert.Contains(result.Files, f => f.Name == "file1.dll");
|
|
Assert.Contains(result.Files, f => f.Name == "file2.dll");
|
|
Assert.Contains(result.Files, f => f.Name == "file3.config");
|
|
}
|
|
finally
|
|
{
|
|
CleanupTestManifest(manifestPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_WithKbReferenceInFilename_ExtractsKbReference()
|
|
{
|
|
// Arrange
|
|
var manifestPath = CreateTestManifestWithName(
|
|
"amd64_microsoft-windows-security_31bf3856ad364e35_10.0.19041.4170_kb5034441.manifest",
|
|
@"<?xml version=""1.0"" encoding=""UTF-8""?>
|
|
<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
|
|
<assemblyIdentity name=""TestSecurity"" version=""10.0.19041.4170"" processorArchitecture=""amd64"" />
|
|
</assembly>");
|
|
|
|
try
|
|
{
|
|
// Act
|
|
var result = _parser.Parse(manifestPath, CancellationToken.None);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal("KB5034441", result.KbReference);
|
|
}
|
|
finally
|
|
{
|
|
CleanupTestManifest(manifestPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_WithNonExistentFile_ReturnsNull()
|
|
{
|
|
// Act
|
|
var result = _parser.Parse("/nonexistent/path/manifest.manifest", CancellationToken.None);
|
|
|
|
// Assert
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_WithInvalidXml_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
var manifestPath = CreateTestManifest("not valid xml content");
|
|
|
|
try
|
|
{
|
|
// Act
|
|
var result = _parser.Parse(manifestPath, CancellationToken.None);
|
|
|
|
// Assert
|
|
Assert.Null(result);
|
|
}
|
|
finally
|
|
{
|
|
CleanupTestManifest(manifestPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_WithMissingAssemblyIdentity_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
var manifestPath = CreateTestManifest(@"<?xml version=""1.0"" encoding=""UTF-8""?>
|
|
<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
|
|
<file name=""something.dll"" />
|
|
</assembly>");
|
|
|
|
try
|
|
{
|
|
// Act
|
|
var result = _parser.Parse(manifestPath, CancellationToken.None);
|
|
|
|
// Assert
|
|
Assert.Null(result);
|
|
}
|
|
finally
|
|
{
|
|
CleanupTestManifest(manifestPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_WithEmptyPath_ReturnsNull()
|
|
{
|
|
// Act
|
|
var result = _parser.Parse(string.Empty, CancellationToken.None);
|
|
|
|
// Assert
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildAssemblyIdentityString_BuildsCorrectFormat()
|
|
{
|
|
// Arrange
|
|
var metadata = new WinSxSAssemblyMetadata(
|
|
Name: "Microsoft.Windows.Common-Controls",
|
|
Version: "6.0.0.0",
|
|
ProcessorArchitecture: "x86",
|
|
PublicKeyToken: "6595b64144ccf1df",
|
|
Language: "en-us",
|
|
Type: "win32",
|
|
VersionScope: null,
|
|
ManifestPath: "/test/manifest.manifest",
|
|
CatalogPath: null,
|
|
CatalogThumbprint: null,
|
|
KbReference: null,
|
|
Files: []);
|
|
|
|
// Act
|
|
var result = WinSxSManifestParser.BuildAssemblyIdentityString(metadata);
|
|
|
|
// Assert
|
|
Assert.Equal("microsoft.windows.common-controls_6.0.0.0_x86_6595b64144ccf1df_en-us", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildAssemblyIdentityString_WithNeutralLanguage_OmitsLanguage()
|
|
{
|
|
// Arrange
|
|
var metadata = new WinSxSAssemblyMetadata(
|
|
Name: "TestAssembly",
|
|
Version: "1.0.0.0",
|
|
ProcessorArchitecture: "amd64",
|
|
PublicKeyToken: null,
|
|
Language: "*",
|
|
Type: null,
|
|
VersionScope: null,
|
|
ManifestPath: "/test/manifest.manifest",
|
|
CatalogPath: null,
|
|
CatalogThumbprint: null,
|
|
KbReference: null,
|
|
Files: []);
|
|
|
|
// Act
|
|
var result = WinSxSManifestParser.BuildAssemblyIdentityString(metadata);
|
|
|
|
// Assert
|
|
Assert.Equal("testassembly_1.0.0.0_amd64", result);
|
|
}
|
|
|
|
private static string CreateTestManifest(string content)
|
|
{
|
|
var tempDir = Path.Combine(Path.GetTempPath(), "winsxs-test-" + Guid.NewGuid().ToString("N")[..8]);
|
|
Directory.CreateDirectory(tempDir);
|
|
var manifestPath = Path.Combine(tempDir, "test.manifest");
|
|
File.WriteAllText(manifestPath, content);
|
|
return manifestPath;
|
|
}
|
|
|
|
private static string CreateTestManifestWithName(string fileName, string content)
|
|
{
|
|
var tempDir = Path.Combine(Path.GetTempPath(), "winsxs-test-" + Guid.NewGuid().ToString("N")[..8]);
|
|
Directory.CreateDirectory(tempDir);
|
|
var manifestPath = Path.Combine(tempDir, fileName);
|
|
File.WriteAllText(manifestPath, content);
|
|
return manifestPath;
|
|
}
|
|
|
|
private static void CleanupTestManifest(string manifestPath)
|
|
{
|
|
var directory = Path.GetDirectoryName(manifestPath);
|
|
if (!string.IsNullOrEmpty(directory) && Directory.Exists(directory))
|
|
{
|
|
Directory.Delete(directory, recursive: true);
|
|
}
|
|
}
|
|
}
|