Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="9.0.4" />
<PackageVersion Include="System.Security.Cryptography.Xml" Version="9.0.4" />
<PackageVersion Include="System.Text.Json" Version="9.0.4" />
<PackageVersion Include="WixToolset.Dtf.Compression.Cab" Version="6.0.0" />
</ItemGroup>
</Project>
2 changes: 2 additions & 0 deletions NuGet.Config
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<add key="dotnet-libraries" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries/nuget/v3/index.json" />
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
<add key="dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
<!-- TODO: Mirror WixToolset.Dtf.Compression.Cab and dependencies -->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR should not be merged before this has been resolved.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are the instructions for mirroring a package. Also be sure to revert this file.

<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<disabledPackageSources />
</configuration>
84 changes: 84 additions & 0 deletions src/Sign.Core/Containers/CabContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE.txt file in the project root for more information.

using Microsoft.Extensions.Logging;
using WixToolset.Dtf.Compression;
using WixToolset.Dtf.Compression.Cab;

namespace Sign.Core
{
internal class CabContainer : Container
{
private readonly IDirectoryService _directoryService;
private readonly ILogger _logger;
private readonly FileInfo _cabFile;

internal CabContainer(
FileInfo cabFile,
IDirectoryService directoryService,
IFileMatcher fileMatcher,
ILogger logger)
: base(fileMatcher)
{
ArgumentNullException.ThrowIfNull(cabFile, nameof(cabFile));
ArgumentNullException.ThrowIfNull(directoryService, nameof(directoryService));
ArgumentNullException.ThrowIfNull(logger, nameof(logger));

_directoryService = directoryService;
_logger = logger;
_cabFile = cabFile;
}

public override ValueTask OpenAsync()
{
if (TemporaryDirectory is not null)
{
throw new InvalidOperationException();
}

TemporaryDirectory = new TemporaryDirectory(_directoryService);

_logger.LogInformation(
Resources.OpeningContainer,
_cabFile.FullName,
TemporaryDirectory.Directory.FullName);

new CabInfo(_cabFile.FullName).Unpack(TemporaryDirectory.Directory.FullName);

return ValueTask.CompletedTask;
}

public override ValueTask SaveAsync()
{
if (TemporaryDirectory is null)
{
throw new InvalidOperationException();
}

_logger.LogInformation(
Resources.SavingContainer,
_cabFile.FullName,
TemporaryDirectory.Directory.FullName);

using (TemporaryDirectory temporaryDirectory = new(_directoryService))
{
string destinationFilePath = Path.Combine(temporaryDirectory.Directory.FullName, _cabFile.Name);

new CabInfo(destinationFilePath).Pack(
TemporaryDirectory.Directory.FullName,
includeSubdirectories: true,
CompressionLevel.Max,
progressHandler: null);

_cabFile.Delete();

File.Move(destinationFilePath, _cabFile.FullName, overwrite: true);

_cabFile.Refresh();
}

return ValueTask.CompletedTask;
}
}
}
13 changes: 13 additions & 0 deletions src/Sign.Core/Containers/ContainerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal sealed class ContainerProvider : IContainerProvider
private readonly IMakeAppxCli _makeAppxCli;
private readonly HashSet<string> _nuGetExtensions;
private readonly HashSet<string> _zipExtensions;
private const string _cabExtension = ".cab";

// Dependency injection requires a public constructor.
public ContainerProvider(
Expand Down Expand Up @@ -98,6 +99,13 @@ public bool IsZipContainer(FileInfo file)
return _zipExtensions.Contains(file.Extension);
}

public bool IsCabContainer(FileInfo file)
{
ArgumentNullException.ThrowIfNull(file, nameof(file));

return string.Equals(file.Extension, _cabExtension, StringComparison.OrdinalIgnoreCase);
}

public IContainer? GetContainer(FileInfo file)
{
ArgumentNullException.ThrowIfNull(file, nameof(file));
Expand All @@ -122,6 +130,11 @@ public bool IsZipContainer(FileInfo file)
return new NuGetContainer(file, _directoryService, _fileMatcher, _logger);
}

if (IsCabContainer(file))
{
return new CabContainer(file, _directoryService, _fileMatcher, _logger);
}

return null;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Sign.Core/Containers/IContainerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ internal interface IContainerProvider
bool IsAppxContainer(FileInfo file);
bool IsNuGetContainer(FileInfo file);
bool IsZipContainer(FileInfo file);
bool IsCabContainer(FileInfo file);
IContainer? GetContainer(FileInfo file);
}
}
30 changes: 30 additions & 0 deletions src/Sign.Core/DataFormatSigners/AggregatingSigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,36 @@ where _containerProvider.IsAppxBundleContainer(file)
containers.Clear();
}

List<FileInfo> cabs = (from file in files
where _containerProvider.IsCabContainer(file)
select file).ToList();

try
{
foreach (FileInfo cab in cabs)
{
IContainer container = _containerProvider.GetContainer(cab)!;

await container.OpenAsync();

containers.Add(container);
}

List<FileInfo> allFiles = containers.SelectMany(c => c.GetFiles()).ToList();

if (allFiles.Count > 0)
{
await SignAsync(allFiles, options);

await Parallel.ForEachAsync(containers, (container, cancellationToken) => container.SaveAsync());
}
}
finally
{
containers.ForEach(c => c.Dispose());
containers.Clear();
}

// split by code sign service and fallback to default

var grouped = (from signer in _signers
Expand Down
1 change: 1 addition & 0 deletions src/Sign.Core/Sign.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<PackageReference Include="System.Security.Cryptography.Pkcs" PrivateAssets="analyzers;build;compile;contentfiles" />
<PackageReference Include="System.Security.Cryptography.Xml" PrivateAssets="analyzers;build;compile;contentfiles" />
<PackageReference Include="System.Text.Json" PrivateAssets="analyzers;build;compile;contentfiles" />
<PackageReference Include="WixToolset.Dtf.Compression.Cab" />
</ItemGroup>

<ItemGroup>
Expand Down
77 changes: 77 additions & 0 deletions test/Sign.Core.Test/Containers/CabContainerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE.txt file in the project root for more information.

using System.Text;
using Microsoft.Extensions.Logging;
using Moq;
using WixToolset.Dtf.Compression.Cab;

namespace Sign.Core.Test
{
public class CabContainerTests
{
[Fact]
public async Task OpenAsync_ExtractsCabToDirectory()
{
string[] expectedFileNames = [".a", "b", "c.d"];
FileInfo cabFile = CreateCabFile(expectedFileNames);

using (DirectoryServiceStub directoryService = new())
using (CabContainer container = new(cabFile, directoryService, Mock.Of<IFileMatcher>(), Mock.Of<ILogger>()))
{
await container.OpenAsync();

FileInfo[] actualFiles = directoryService.Directories[0].GetFiles("*", SearchOption.AllDirectories);
string[] actualFileNames = actualFiles
.Select(file => file.FullName.Substring(directoryService.Directories[0].FullName.Length + 1))
.ToArray();

Assert.Equal(expectedFileNames, actualFileNames);
}
}

[Fact]
public async Task SaveAsync_CompressesCabFromDirectory()
{
string[] fileNames = ["a"];
FileInfo cabFile = CreateCabFile(fileNames);

using (DirectoryServiceStub directoryService = new())
using (CabContainer container = new(cabFile, directoryService, Mock.Of<IFileMatcher>(), Mock.Of<ILogger>()))
{
await container.OpenAsync();

File.WriteAllText(Path.Combine(directoryService.Directories[0].FullName, "b"), "b");

await container.SaveAsync();
}

var cab = new CabInfo(cabFile.FullName);
var files = cab.GetFiles();
Assert.Equal(2, files.Count);
Assert.Contains(files, e => e.Name == "a");
Assert.Contains(files, e => e.Name == "b");
}

private static FileInfo CreateCabFile(params string[] entryNames)
{
FileInfo file = new(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()));

var cab = new CabInfo(file.FullName);

var sourceFiles = new List<string>();
foreach (string entryName in entryNames)
{
var sourceFile = Path.GetTempFileName();
File.WriteAllBytes(sourceFile, Encoding.UTF8.GetBytes(entryName));

sourceFiles.Add(sourceFile);
}

cab.PackFiles(sourceDirectory: null, sourceFiles, entryNames);

return file;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public bool IsZipContainer(FileInfo file)
return _containerProvider.IsZipContainer(file);
}

public bool IsCabContainer(FileInfo file)
{
return _containerProvider.IsCabContainer(file);
}

public IContainer? GetContainer(FileInfo file)
{
ArgumentNullException.ThrowIfNull(file, nameof(file));
Expand Down