File: SignToolTests.cs
Web Access
Project: src\src\Microsoft.DotNet.SignTool.Tests\Microsoft.DotNet.SignTool.Tests.csproj (Microsoft.DotNet.SignTool.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using FluentAssertions;
using Microsoft.Arcade.Test.Common;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Xunit;
using Xunit.Abstractions;
using Microsoft.DotNet.Build.Tasks.Installers;
 
namespace Microsoft.DotNet.SignTool.Tests
{
    public class SignToolTests : IDisposable
    {
        private readonly string _tmpDir;
        private readonly ITestOutputHelper _output;
 
        // Default extension based signing information
        private static readonly Dictionary<string, List<SignInfo>> s_fileExtensionSignInfo = new Dictionary<string, List<SignInfo>>()
        {
            {".js", new List<SignInfo>{ new SignInfo("JSCertificate") } },
            {".jar",  new List<SignInfo>{ new SignInfo("JARCertificate") } },
            {".ps1",  new List<SignInfo>{ new SignInfo("PSCertificate") } },
            {".psd1",  new List<SignInfo>{ new SignInfo("PSDCertificate") } },
            {".psm1",  new List<SignInfo>{ new SignInfo("PSMCertificate") } },
            {".psc1",   new List<SignInfo>{ new SignInfo("PSCCertificate") } },
            {".dylib", new List<SignInfo>{ new SignInfo("DylibCertificate") } },
            {".deb", new List<SignInfo>{ new SignInfo("LinuxSign") } },
            {".rpm", new List<SignInfo>{ new SignInfo("LinuxSign") } },
            {".dll",  new List<SignInfo>{ new SignInfo("Microsoft400") } }, // lgtm [cs/common-default-passwords] Safe, these are certificate names
            {".exe",  new List<SignInfo>{ new SignInfo("Microsoft400") } }, // lgtm [cs/common-default-passwords] Safe, these are certificate names
            {".msi",  new List<SignInfo>{ new SignInfo("Microsoft400") } }, // lgtm [cs/common-default-passwords] Safe, these are certificate names
            {".vsix",  new List<SignInfo>{ new SignInfo("VsixSHA2") } },
            {".zip",  new List<SignInfo>{ SignInfo.Ignore } },
            {".tgz",  new List<SignInfo>{ SignInfo.Ignore } },
            {".pkg",  new List<SignInfo>{ new SignInfo("MacDeveloperHarden") } }, // lgtm [cs/common-default-passwords] Safe, these are certificate names
            {".app",  new List<SignInfo>{ new SignInfo("MacDeveloperHarden") } }, // lgtm [cs/common-default-passwords] Safe, these are certificate names
            {".py",  new List<SignInfo>{ new SignInfo("Microsoft400") } }, // lgtm [cs/common-default-passwords] Safe, these are certificate names
            {".nupkg",  new List<SignInfo>{ new SignInfo("NuGet") } },
            {".symbols.nupkg",  new List<SignInfo>{ SignInfo.Ignore } },
        };
 
        private static readonly Dictionary<string, List<SignInfo>> s_fileExtensionSignInfoWithCollisionId = 
            new Dictionary<string, List<SignInfo>>()
        {
            {".js", new List<SignInfo>{ new SignInfo("JSCertificate", collisionPriorityId: "123") } },
            {".jar", new List<SignInfo>{ new SignInfo("JARCertificate", collisionPriorityId: "123") } },
            { ".ps1", new List<SignInfo>{ new SignInfo("PSCertificate", collisionPriorityId: "123") } },
            { ".psd1", new List<SignInfo>{ new SignInfo("PSDCertificate", collisionPriorityId: "123") } },
            { ".psm1", new List<SignInfo>{ new SignInfo("PSMCertificate", collisionPriorityId: "123") } },
            { ".psc1", new List<SignInfo>{ new SignInfo("PSCCertificate", collisionPriorityId: "123") } },
            { ".dylib", new List<SignInfo>{ new SignInfo("DylibCertificate", collisionPriorityId: "123") } },
            { ".deb", new List<SignInfo>{ new SignInfo("LinuxSign", collisionPriorityId: "123") } },
            { ".dll", new List<SignInfo>
                { 
                    new SignInfo("Microsoft400", collisionPriorityId: "123"), // lgtm [cs/common-default-passwords] Safe, these are certificate names
                    new SignInfo("FakeOne", collisionPriorityId: "456")
                } 
             },
            { ".exe", new List<SignInfo>{ new SignInfo("Microsoft400", collisionPriorityId:  "123") } }, // lgtm [cs/common-default-passwords] Safe, these are certificate names
            { ".msi", new List<SignInfo>{ new SignInfo("Microsoft400", collisionPriorityId:  "123") } }, // lgtm [cs/common-default-passwords] Safe, these are certificate names
            { ".vsix", new List<SignInfo>{ new SignInfo("VsixSHA2", collisionPriorityId: "123") } },
            { ".zip", new List<SignInfo>{ SignInfo.Ignore } },
            { ".tgz", new List<SignInfo>{ SignInfo.Ignore } },
            { ".pkg", new List<SignInfo>{ new SignInfo("Microsoft400", collisionPriorityId:  "123") } },
            { ".app",  new List<SignInfo>{ new SignInfo("Microsoft400", collisionPriorityId:  "123") } },
            { ".nupkg", new List<SignInfo>{ new SignInfo("NuGet", collisionPriorityId: "123") } },
            { ".symbols.nupkg",  new List<SignInfo>{ SignInfo.Ignore } },
        };
 
        // Default extension based signing information post build
        private static readonly ITaskItem[] s_fileExtensionSignInfoPostBuild = new ITaskItem[]
        {
            new TaskItem(".js", new Dictionary<string, string> {
                { "CertificateName", "JSCertificate" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".jar", new Dictionary<string, string> {
                { "CertificateName", "JARCertificate" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".ps1", new Dictionary<string, string> {
                { "CertificateName", "PSCertificate" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".psd1", new Dictionary<string, string> {
                { "CertificateName", "PSDCertificate" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".psm1", new Dictionary<string, string> {
                { "CertificateName", "PSMCertificate" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".psc1", new Dictionary<string, string> {
                { "CertificateName", "PSCCertificate" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".dylib", new Dictionary<string, string> {
                { "CertificateName", "DylibCertificate" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".deb", new Dictionary<string, string> {
                { "CertificateName", "LinuxSign" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".dll", new Dictionary<string, string> {
                { "CertificateName", "Microsoft400" }, // lgtm [cs/common-default-passwords] Safe, these are certificate names
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".exe", new Dictionary<string, string> {
                { "CertificateName", "Microsoft400" }, // lgtm [cs/common-default-passwords] Safe, these are certificate names
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".zip", new Dictionary<string, string> {
                { "CertificateName", "None" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".tgz", new Dictionary<string, string> {
                { "CertificateName", "None" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".pkg", new Dictionary<string, string> {
                { "CertificateName", "Microsoft400" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".app", new Dictionary<string, string> {
                { "CertificateName", "Microsoft400" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".nupkg", new Dictionary<string, string> {
                { "CertificateName", "NuGet" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".vsix", new Dictionary<string, string> {
                { "CertificateName", "VsixSHA2" },
                { SignToolConstants.CollisionPriorityId, "123" }
            }),
            new TaskItem(".js", new Dictionary<string, string> {
                { "CertificateName", "JSCertificate" },
                { SignToolConstants.CollisionPriorityId, "234" }
            }),
            new TaskItem(".jar", new Dictionary<string, string> {
                { "CertificateName", "JARCertificate" },
                { SignToolConstants.CollisionPriorityId, "234" }
            }),
            new TaskItem(".ps1", new Dictionary<string, string> {
                { "CertificateName", "PSCertificate" },
                { SignToolConstants.CollisionPriorityId, "234" }
            }),
            new TaskItem(".psd1", new Dictionary<string, string> {
                { "CertificateName", "PSDCertificate" },
                { SignToolConstants.CollisionPriorityId, "234" }
            }),
            new TaskItem(".dll", new Dictionary<string, string> {
                { "CertificateName", "Microsoft400" }, // lgtm [cs/common-default-passwords] Safe, these are certificate names
                { SignToolConstants.CollisionPriorityId, "234" }
            }),
            new TaskItem(".nupkg", new Dictionary<string, string> {
                { "CertificateName", "NuGet" },
                { SignToolConstants.CollisionPriorityId, "234" }
            }),
            new TaskItem(".vsix", new Dictionary<string, string> {
                { "CertificateName", "VsixSHA2" },
                { SignToolConstants.CollisionPriorityId, "234" }
            })
        };
 
        /// <summary>
        /// List of known signable extensions. Copied, removing duplicates, from here:
        /// https://microsoft.sharepoint.com/teams/codesigninfo/Wiki/Signable%20Files.aspx
        /// </summary>
        public static readonly string[] SignableExtensions =
        {
            ".exe",
            ".dll",
            ".rll",
            ".olb",
            ".ocx",
 
            ".cab",
 
            ".cat",
 
            ".vbs",
            ".js",
            ".wfs",
 
            ".msi",
            ".mui",
            ".msp",
            ".msu",
            ".psf",
            ".mpb",
            ".mp",
            ".msm",
 
            ".doc",
            ".xls",
            ".ppt",
            ".xla",
            ".vdx",
            ".xsn",
            ".mpp",
 
            ".xlam",
            ".xlsb",
            ".xlsm",
            ".xltm",
            ".potm",
            ".ppsm",
            ".pptm",
            ".docm",
            ".dotm",
 
            ".ttf",
            ".otf",
 
            ".ps1",
            ".ps1xml",
            ".psm1",
            ".psd1",
            ".psc1",
            ".cdxml",
            ".wsf",
            ".mof",
 
            ".sft",
            ".dsft",
 
            ".vsi",
 
            ".xap",
 
            ".efi",
 
            ".vsix",
 
            ".jar",
 
            ".winmd",
 
            ".appx",
            ".appxbundle",
 
            ".esd",
 
            ".py",
            ".pyd",
#if !NETFRAMEWORK
            ".deb",
#endif
        };
 
        public static IEnumerable<object[]> GetSignableExtensions()
        {
            foreach (var extension in SignableExtensions)
            {
                yield return new object[] { extension };
            }
        }
 
        public SignToolTests(ITestOutputHelper output)
        {
            _tmpDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
            Directory.CreateDirectory(_tmpDir);
            _output = output;
        }
 
        private string GetWixToolPath()
        {
            return Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "wix");
        }
 
        private static string s_snPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "sn", "sn.exe");
        private static string s_tarToolPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "tar", "Microsoft.Dotnet.Tar.dll");
        private static string s_pkgToolPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "pkg", "Microsoft.Dotnet.MacOsPkg.dll");
 
        private string GetResourcePath(string name, string relativePath = null)
        {
            var srcPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "Resources", name);
 
            var dstDir = _tmpDir;
 
            if (relativePath != null)
            {
                dstDir = Path.Combine(dstDir, relativePath);
                Directory.CreateDirectory(dstDir);
            }
 
            var dstPath = Path.Combine(dstDir, name);
 
            if (!File.Exists(dstPath))
            {
                File.Copy(srcPath, dstPath);
            }
 
            return dstPath;
        }
 
        private string CreateTestResource(string name)
        {
            var dstPath = Path.Combine(_tmpDir, name);
 
            File.WriteAllText(dstPath, "This is a test file content");
 
            return dstPath;
        }
 
        public void Dispose()
        {
            try
            {
                Directory.Delete(_tmpDir, recursive: true);
            }
            catch
            {
            }
        }
 
        private void ValidateGeneratedProject(
            List<ItemToSign> itemsToSign,
            Dictionary<string, List<SignInfo>> strongNameSignInfo,
            Dictionary<ExplicitCertificateKey, string> fileSignInfo,
            Dictionary<string, List<SignInfo>> extensionsSignInfo,
            string[] expectedXmlElementsPerSigningRound,
            Dictionary<string, List<AdditionalCertificateInformation>> additionalCertificateInfo = null,
            string wixToolsPath = null)
        {
            var buildEngine = new FakeBuildEngine();
 
            var task = new SignToolTask { BuildEngine = buildEngine };
 
            // The path to DotNet will always be null in these tests, this will force
            // the signing logic to call our FakeBuildEngine.BuildProjectFile with a path
            // to the XML that store the content of the would be Microbuild sign request.
            var signToolArgs = new SignToolArgs(_tmpDir, microBuildCorePath: "MicroBuildCorePath", testSign: true, dotnetPath: null, msbuildVerbosity: "quiet", _tmpDir, enclosingDir: "", "", wixToolsPath: wixToolsPath, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, dotnetTimeout: -1);
 
            var signTool = new FakeSignTool(signToolArgs, task.Log);
            var configuration = new Configuration(signToolArgs.TempDir, itemsToSign, strongNameSignInfo, fileSignInfo, extensionsSignInfo, additionalCertificateInfo, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, snPath: s_snPath, task.Log);
            var signingInput = configuration.GenerateListOfFiles();
            var util = new BatchSignUtil(
                task.BuildEngine,
                task.Log,
                signTool,
                signingInput,
                new string[] { },
                configuration._hashToCollisionIdMap);
 
            var beforeSigningEngineFilesList = Directory.GetFiles(signToolArgs.TempDir, "*-engine.exe", SearchOption.AllDirectories);
            util.Go(doStrongNameCheck: true);
            var afterSigningEngineFilesList = Directory.GetFiles(signToolArgs.TempDir, "*-engine.exe", SearchOption.AllDirectories);
 
            // validate no intermediate msi engine files have populated the drop (they fail signing validation).
            beforeSigningEngineFilesList.SequenceEqual(afterSigningEngineFilesList).Should().BeTrue();
 
            // The list of files that would be signed was captured inside the FakeBuildEngine,
            // here we check if that matches what we expected
            var actualXmlElementsPerSigningRound = buildEngine.FilesToSign.Select(round => string.Join(Environment.NewLine, round));
            actualXmlElementsPerSigningRound.Count().Should().Be(expectedXmlElementsPerSigningRound.Length);
            int i = 0;
            foreach (var actual in actualXmlElementsPerSigningRound)
            {
                var actualXml = AssertEx.NormalizeWhitespace(actual);
                var expectedXml = AssertEx.NormalizeWhitespace(expectedXmlElementsPerSigningRound[i]);
                actualXml.Should().Be(expectedXml);
                i++;
            }
 
            task.Log.HasLoggedErrors.Should().BeFalse();
        }
 
        private void ValidateFileSignInfos(
            List<ItemToSign> itemsToSign,
            Dictionary<string, List<SignInfo>> strongNameSignInfo,
            Dictionary<ExplicitCertificateKey, string> fileSignInfo,
            Dictionary<string, List<SignInfo>> extensionsSignInfo,
            string[] expected,
            string[] expectedCopyFiles = null,
            Dictionary<string, List<AdditionalCertificateInformation>> additionalCertificateInfo = null,
            string[] expectedErrors = null,
            string[] expectedWarnings = null)
        {
            var engine = new FakeBuildEngine();
            var task = new SignToolTask { BuildEngine = engine };
            var signingInput = new Configuration(_tmpDir, itemsToSign, strongNameSignInfo, fileSignInfo, extensionsSignInfo, additionalCertificateInfo, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, snPath: s_snPath, task.Log).GenerateListOfFiles();
 
            signingInput.FilesToSign.Select(f => f.ToString()).Should().BeEquivalentTo(expected);
            signingInput.FilesToCopy.Select(f => $"{f.Key} -> {f.Value}").Should().BeEquivalentTo(expectedCopyFiles ?? Array.Empty<string>());
            engine.LogErrorEvents.Select(w => w.Message).Should().BeEquivalentTo(expectedErrors ?? Array.Empty<string>());
            engine.LogWarningEvents.Select(w => $"{w.Code}: {w.Message}").Should().BeEquivalentTo(expectedWarnings ?? Array.Empty<string>());
        }
 
#if !NETFRAMEWORK
        private void ValidateProducedDebContent(
            string debianPackage,
            (string, string)[] expectedFilesOriginalHashes,
            string[] signableFiles,
            string expectedControlFileContent)
        {
            string tempDir = Path.Combine(_tmpDir, "verification");
            Directory.CreateDirectory(tempDir);
 
            string controlArchive = ExtractArchiveFromDebPackage(debianPackage, "control.tar", tempDir);
            string dataArchive = ExtractArchiveFromDebPackage(debianPackage, "data.tar", tempDir);
 
            string controlLayout = Path.Combine(tempDir, "control");
            string dataLayout = Path.Combine(tempDir, "data");
 
            Directory.CreateDirectory(controlLayout);
            Directory.CreateDirectory(dataLayout);
 
            ZipData.ExtractTarballContents(dataArchive, dataLayout, skipSymlinks: false);
            ZipData.ExtractTarballContents(controlArchive, controlLayout);
 
            string md5sumsContents = File.ReadAllText(Path.Combine(controlLayout, "md5sums"));
 
            // Checks:
            // Expected files are present
            // Signed files have hashes different than original
            // md5sums file contains the correct hashes of all files
            // md5sums file does not contain the original hashes of signable files
            foreach ((string targetSystemFilePath, string originalHash) in expectedFilesOriginalHashes)
            {
                string layoutFilePath = Path.Combine(dataLayout, targetSystemFilePath);
                File.Exists(layoutFilePath).Should().BeTrue();
 
                using MD5 md5 = MD5.Create();
                using FileStream fileStream = File.OpenRead(layoutFilePath);
                string newHash = Convert.ToHexString(md5.ComputeHash(fileStream));
 
                if (signableFiles.Contains(targetSystemFilePath))
                {
                    newHash.Should().NotBe(originalHash);
                    md5sumsContents.Should().Contain($"{newHash} {targetSystemFilePath}");
                    md5sumsContents.Should().NotContain($"{originalHash} {targetSystemFilePath}");
                }
                else
                {
                    newHash.Should().Be(originalHash);
                    md5sumsContents.Should().Contain($"{originalHash} {targetSystemFilePath}");
                }
            }
 
            // Check: control file contents matches the expected contents
            string controlFileContents = File.ReadAllText(Path.Combine(controlLayout, "control"));
            controlFileContents.Should().Be(expectedControlFileContent);
        }
 
        private string ExtractArchiveFromDebPackage(string debianPackage, string archiveName, string destinationFolder)
        {
            var (relativePath, content, contentSize) = ZipData.ReadDebContainerEntries(debianPackage, archiveName).Single();
            string archive = Path.Combine(destinationFolder, relativePath);
            File.WriteAllBytes(archive, ((MemoryStream)content).ToArray());
            return archive;
        }
 
        private void ValidateProducedRpmContent(
            string rpmPackage,
            (string, string)[] expectedFilesOriginalHashes,
            string[] signableFiles,
            string originalUncompressedPayloadChecksum)
        {
            string tempDir = Path.Combine(_tmpDir, "verification");
            Directory.CreateDirectory(tempDir);
 
            string layout = Path.Combine(tempDir, "layout");
            Directory.CreateDirectory(layout);
 
            ZipData.ExtractRpmPayloadContents(rpmPackage, layout);
 
            // Checks:
            // Expected files are present
            // Signed files have hashes different than original
            foreach ((string targetSystemFilePath, string originalHash) in expectedFilesOriginalHashes)
            {
                string layoutFilePath = Path.Combine(layout, targetSystemFilePath);
                File.Exists(layoutFilePath).Should().BeTrue();
 
                using MD5 md5 = MD5.Create(); // lgtm [cs/weak-crypto] Azure Storage specifies use of MD5
                using FileStream fileStream = File.OpenRead(layoutFilePath);
                string newHash = Convert.ToHexString(md5.ComputeHash(fileStream));
 
                if (signableFiles.Contains(targetSystemFilePath))
                {
                    newHash.Should().NotBe(originalHash);
                }
                else
                {
                    newHash.Should().Be(originalHash);
                }
            }
 
            // Checks:
            // Header payload digest matches the hash of the payload
            // Header payload digest is different than the hash of the original payload
            IReadOnlyList<RpmHeader<RpmHeaderTag>.Entry> headerEntries = ZipData.GetRpmHeaderEntries(rpmPackage);
            string uncompressedPayloadDigest = ((string[])headerEntries.FirstOrDefault(e => e.Tag == RpmHeaderTag.UncompressedPayloadDigest).Value)[0].ToString();
            uncompressedPayloadDigest.Should().NotBe(originalUncompressedPayloadChecksum);
 
            using var stream = File.Open(rpmPackage, FileMode.Open);
            using RpmPackage package = RpmPackage.Read(stream);
            package.ArchiveStream.Seek(0, SeekOrigin.Begin);
            using (SHA256 sha256 = SHA256.Create())
            {
                byte[] hash = sha256.ComputeHash(package.ArchiveStream);
                string checksum = Convert.ToHexString(hash).ToLower();
                checksum.Should().Be(uncompressedPayloadDigest);
            }
        }
#endif
        [Fact]
        public void EmptySigningList()
        {
            var itemsToSign = new List<ItemToSign>();
 
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>();
 
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            var task = new SignToolTask { BuildEngine = new FakeBuildEngine() };
            var signingInput = new Configuration(_tmpDir, itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, null, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, snPath: s_snPath, task.Log).GenerateListOfFiles();
 
            signingInput.FilesToSign.Should().BeEmpty();
            signingInput.ZipDataMap.Should().BeEmpty();
            task.Log.HasLoggedErrors.Should().BeFalse();
        }
 
        [Fact]
        public void EmptySigningListForTask()
        {
            var task = new SignToolTask
            {
                BuildEngine = new FakeBuildEngine(),
                ItemsToSign = Array.Empty<ITaskItem>(),
                StrongNameSignInfo = Array.Empty<ITaskItem>(),
                LogDir = "LogDir",
                TempDir = "TempDir",
                DryRun = false,
                TestSign = true,
                DotNetPath = CreateTestResource("dotnet.fake"),
                SNBinaryPath = CreateTestResource("fake.sn.exe"),
                PkgToolPath = s_pkgToolPath,
            };
 
            task.Execute().Should().BeTrue();
        }
 
        [Fact]
        public void SignWhenSnExeIsNotRequired()
        {
            var task = new SignToolTask
            {
                BuildEngine = new FakeBuildEngine(_output),
                ItemsToSign = Array.Empty<ITaskItem>(),
                StrongNameSignInfo = Array.Empty<ITaskItem>(),
                LogDir = "LogDir",
                TempDir = "TempDir",
                DryRun = false,
                TestSign = true,
                DotNetPath = CreateTestResource("dotnet.fake"),
                DoStrongNameCheck = false,
                SNBinaryPath = null,
                PkgToolPath = s_pkgToolPath,
            };
 
            task.Execute().Should().BeTrue();
        }
 
        [Fact]
        public void OnlyContainer()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("ContainerOne.1.0.0.nupkg"), "")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo> {new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.1.0.0.nupkg' Certificate='NuGet'",
            }/*,
            Reenable after https://github.com/dotnet/arcade/issues/10293,
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "2", "lib/native/NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
            }*/);
        }
 
        [Fact]
        public void SkipSigning()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("ContainerOne.1.0.0.nupkg"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo> {new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>
            {
                { new ExplicitCertificateKey("NativeLibrary.dll"), "None" },
                { new ExplicitCertificateKey("ProjectOne.dll", publicKeyToken: "581d91ccdfc4ea9c", targetFramework: ".NETCoreApp,Version=v2.1"), "None" }
            };
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.1.0.0.nupkg' Certificate='NuGet'"
            });
        }
 
        [Fact]
        public void SkipStrongNamingForAlreadyStrongNamedBinary()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("SignedLibrary.dll")),
                new ItemToSign(GetResourcePath("StrongNamedWithEcmaKey.dll"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "31bf3856ad364e35", new List<SignInfo> {new SignInfo(certificate: "FooCert", strongName: "Blah.snk") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, Array.Empty<string>());
        }
 
        [Fact]
        public void DoNotSkipStrongNamingForDelaySignedBinary()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("DelaySigned.dll"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "b03f5f7f11d50a3a", new List<SignInfo> {new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] 
            {
                "File 'DelaySigned.dll' TargetFramework='.NETCoreApp,Version=v9.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'"
            });
        }
 
        [Fact]
        public void SkipStrongNamingForCrossGennedBinary()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("Crossgenned.exe"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "b03f5f7f11d50a3a", new List<SignInfo> {new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'Crossgenned.exe' Certificate='Microsoft400'"
            });
        }
 
        [Fact]
        public void SkipStrongNamingBinaryButDontSkipAuthenticode()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("OpenSigned.dll"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "cc7b13ffcd2ddd51", new List<SignInfo> {new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'OpenSigned.dll' TargetFramework='.NETCoreApp,Version=v9.0' Certificate='3PartySHA2'"
            });
        }
 
        [Fact]
        public void OnlyAuthenticodeSignByPKT()
        {
            var fileToTest = "ProjectOne.dll";
            var pktToTest = "581d91ccdfc4ea9c";
            var certificateToTest = "3PartySHA2";
 
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath(fileToTest), "123")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { pktToTest, new List<SignInfo> { new SignInfo(certificateToTest, collisionPriorityId: "123") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, new Dictionary<string, List<SignInfo>>(), new[]
            {
                $"File '{fileToTest}' TargetFramework='.NETStandard,Version=v2.0' Certificate='{certificateToTest}'",
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, new Dictionary<string, List<SignInfo>>(), new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, fileToTest))}"">
  <Authenticode>{certificateToTest}</Authenticode>
</FilesToSign>
"
            });
        }
 
        [Fact]
        public void OnlyContainerAndOverridingByPKT()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath(GetResourcePath("ContainerOne.1.0.0.nupkg")))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo> { new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>()
            {
                { new ExplicitCertificateKey("ProjectOne.dll", "581d91ccdfc4ea9c"), "OverriddenCertificate" }
            };
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='OverriddenCertificate' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='OverriddenCertificate' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='OverriddenCertificate' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='OverriddenCertificate' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.1.0.0.nupkg' Certificate='NuGet'"
            },
            expectedWarnings: new[]
            {
                // Reenable after https://github.com/dotnet/arcade/issues/10293
                // $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "2", "lib/native/NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "3", "lib/net461/ProjectOne.dll")}' with Microsoft certificate 'OverriddenCertificate'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "5", "lib/netcoreapp2.0/ProjectOne.dll")}' with Microsoft certificate 'OverriddenCertificate'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "6", "lib/netcoreapp2.1/ProjectOne.dll")}' with Microsoft certificate 'OverriddenCertificate'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "7", "lib/netstandard2.0/ProjectOne.dll")}' with Microsoft certificate 'OverriddenCertificate'. The library is considered 3rd party library due to its copyright: ''."
            });
        }
 
        [Fact]
        public void OnlyContainerAndOverridingByFileName()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("ContainerOne.1.0.0.nupkg"), "123")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo> { new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>()
            {
                { new ExplicitCertificateKey("NativeLibrary.dll", collisionPriorityId: "123"), "OverriddenCertificate1" },
                { new ExplicitCertificateKey("ProjectOne.dll", collisionPriorityId: "123"), "3PartySHA2" }
            };
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'NativeLibrary.dll' Certificate='OverriddenCertificate1'",
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='ArcadeCertTest' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.1.0.0.nupkg' Certificate='NuGet'"
            },
            expectedWarnings: new[]
            {
                // Reenable after https://github.com/dotnet/arcade/issues/10293
                // $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "2", "lib/native/NativeLibrary.dll")}' with Microsoft certificate 'OverriddenCertificate1'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "4", "lib/netcoreapp2.0/ContainerOne.dll")}' with Microsoft certificate 'ArcadeCertTest'. The library is considered 3rd party library due to its copyright: ''."
            });
        }
 
        [Fact]
        public void EmptyPKT()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("EmptyPKT.dll"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo("ArcadeCertTest", "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>()
            {
                { new ExplicitCertificateKey("EmptyPKT.dll"), "3PartySHA2" }
            };
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'EmptyPKT.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='3PartySHA2'",
            });
        }
 
        [Fact]
        public void CrossGenerated()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("CoreLibCrossARM.dll"), "123"),
                new ItemToSign(GetResourcePath("AspNetCoreCrossLib.dll"), "123")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "7cec85d7bea7798e", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } },
                { "adb9793829ddae60", new List<SignInfo>{ new SignInfo(certificate: "Microsoft400", strongName: "AspNetCore", collisionPriorityId: "123") } } // lgtm [cs/common-default-passwords] Safe, these are certificate names
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>()
            {
                { new ExplicitCertificateKey("EmptyPKT.dll", collisionPriorityId: "123"), "3PartySHA2" }
            };
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, new Dictionary<string, List<SignInfo>>(), new[]
            {
                "File 'CoreLibCrossARM.dll' Certificate='ArcadeCertTest'",
                "File 'AspNetCoreCrossLib.dll' TargetFramework='.NETCoreApp,Version=v3.0' Certificate='Microsoft400'",
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, new Dictionary<string, List<SignInfo>>(), new[]
            {
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "CoreLibCrossARM.dll"))}"">
  <Authenticode>ArcadeCertTest</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "AspNetCoreCrossLib.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>",
            });
        }
 
        [Fact]
        public void DefaultCertificateForAssemblyWithoutStrongName()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("EmptyPKT.dll"), "123")
            };
 
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "", new List<SignInfo>{ new SignInfo("3PartySHA2", collisionPriorityId: "123") } }
            };
 
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>() { };
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'EmptyPKT.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='3PartySHA2'",
            });
        }
 
        [Fact]
        public void CustomTargetFrameworkAttribute()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("CustomTargetFrameworkAttribute.dll"), "123")
            };
 
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                {  "", new List<SignInfo>{ new SignInfo("DefaultCertificate", collisionPriorityId: "123") } }
            };
 
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>()
            {
                { new ExplicitCertificateKey("CustomTargetFrameworkAttribute.dll", targetFramework: ".NETFramework,Version=v2.0", collisionPriorityId: "123"), "3PartySHA2" }
            };
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'CustomTargetFrameworkAttribute.dll' TargetFramework='.NETFramework,Version=v2.0' Certificate='3PartySHA2'",
            });
        }
 
        [Fact]
        public void ThirdPartyLibraryMicrosoftCertificate()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("EmptyPKT.dll"))
            };
 
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>() { };
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>() { };
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'EmptyPKT.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='Microsoft400'",
            },
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "EmptyPKT.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''."
            });
        }
 
        [WindowsOnlyFact]
        [Trait("Category", "SkipWhenLiveUnitTesting")]
        public void DoubleNestedContainer()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("PackageWithWix.nupkg"), "123"),
                new ItemToSign(GetResourcePath("MsiBootstrapper.exe.wixpack.zip"), "123")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo("3PartySHA2", "ArcadeStrongTest", "123") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'MsiSetup.msi' Certificate='Microsoft400'",
                "File 'MsiBootstrapper.exe' Certificate='Microsoft400'",
                "File 'PackageWithWix.nupkg' Certificate='NuGet'"
            }/*,
            Reenable after https://github.com/dotnet/arcade/issues/10293
            expectedWarnings: new[]
            {
                
                // $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "4", "MsiBootstrapper.exe")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: 'Copyright (c). All rights reserved.'."
            }*/);
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "4", "ABCDEFG/MsiSetup.msi"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>",
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "engines\\0\\MsiBootstrapper.exe-engine.exe"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>",
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "4", "MsiBootstrapper.exe"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>",
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "PackageWithWix.nupkg"))}"">
  <Authenticode>NuGet</Authenticode>
</FilesToSign>"
            },
            wixToolsPath: GetWixToolPath());
        }
 
 
        [Fact]
        public void NestedContainer()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("NestedContainer.1.0.0.nupkg"), "123")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerTwo.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.1.0.0.nupkg' Certificate='NuGet'",
                "File 'NestedContainer.1.0.0.nupkg' Certificate='NuGet'"
            }/*,
            Reenable after https://github.com/dotnet/arcade/issues/10293,
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "2", "lib/native/NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''."
            }*/);
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "lib/native/NativeLibrary.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "3", "lib/net461/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "4", "lib/netcoreapp2.0/ContainerTwo.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "5", "lib/netcoreapp2.0/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "6", "lib/netcoreapp2.1/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "7", "lib/netstandard2.0/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "9", "lib/netcoreapp2.0/ContainerOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "8", "ContainerOne.1.0.0.nupkg"))}"">
  <Authenticode>NuGet</Authenticode>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(GetResourcePath("NestedContainer.1.0.0.nupkg"))}"">
  <Authenticode>NuGet</Authenticode>
</FilesToSign>"
            });
        }
 
        [Fact]
        public void NestedContainerWithCollisions()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("NestedContainer.1.0.0.nupkg"), "123")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } }
            };
 
            // Overriding information. Since ContainerOne.dll collides with ContainerTwo.dll already in the hash mapping
            // table with collition id 123, we end up using ArcadeStrongTest instead of OverriddenCertificate1
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>()
            {
                { new ExplicitCertificateKey("ContainerOne.dll", collisionPriorityId: "456"), "OverriddenCertificate1" }
            };
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerTwo.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.1.0.0.nupkg' Certificate='NuGet'",
                "File 'NestedContainer.1.0.0.nupkg' Certificate='NuGet'"
            }/*,
            Reenable after https://github.com/dotnet/arcade/issues/10293,
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "2", "lib/native/NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''."
            }*/);
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "lib/native/NativeLibrary.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "3", "lib/net461/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "4", "lib/netcoreapp2.0/ContainerTwo.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "5", "lib/netcoreapp2.0/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "6", "lib/netcoreapp2.1/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "7", "lib/netstandard2.0/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "9", "lib/netcoreapp2.0/ContainerOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "8", "ContainerOne.1.0.0.nupkg"))}"">
  <Authenticode>NuGet</Authenticode>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(GetResourcePath("NestedContainer.1.0.0.nupkg"))}"">
  <Authenticode>NuGet</Authenticode>
</FilesToSign>"
            });
        }
 
        [Fact]
        public void SignZipFile()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("test.zip"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'Nested.NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'Nested.SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'test.zip'",
            }/*,
            Reenable after https://github.com/dotnet/arcade/issues/10293,
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "0", "NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "2", "this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
            }*/);
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "0", "NativeLibrary.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "1", "SOS.NETCore.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.NativeLibrary.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "3", "this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.SOS.NETCore.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
"
            });
        }
 
        /// <summary>
        /// Verifies that signing of pkgs can be done on Windows, even though
        /// we will not unpack or repack them.
        /// </summary>
        [WindowsOnlyFact]
        public void SignJustPkgWithoutUnpack()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("test.pkg"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'test.pkg' Certificate='MacDeveloperHarden'",
            });
 
            // OSX files need to be zipped first before being signed
            // This is why the .pkgs are listed as .zip files below
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "test.pkg.zip"))}"">
                <Authenticode>MacDeveloperHarden</Authenticode>
                </FilesToSign>",
            });
        }
 
        [MacOSOnlyFact]
        public void UnpackAndSignPkg()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("test.pkg"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'Nested.NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'Nested.SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'NestedPkg.pkg' Certificate='MacDeveloperHarden'",
                "File 'test.pkg' Certificate='MacDeveloperHarden'",
            });
 
            // OSX files need to be zipped first before being signed
            // This is why the .pkgs are listed as .zip files below
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "3", "Payload/SOS.NETCore.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "4", "Payload/NativeLibrary.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "5", "Payload/this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.SOS.NETCore.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "6", "Payload/this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.NativeLibrary.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                ",
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "1", "NestedPkg.pkg.zip"))}"">
                <Authenticode>MacDeveloperHarden</Authenticode>
                </FilesToSign>",
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "test.pkg.zip"))}"">
                <Authenticode>MacDeveloperHarden</Authenticode>
                </FilesToSign>",
            });
        }
 
        [MacOSOnlyFact]
        public void SignAndNotarizePkgFile()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("test.pkg"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } }
            };
 
            // Set up the cert to allow for signing and notarization.
            var additionalCertificateInfo = new Dictionary<string, List<AdditionalCertificateInformation>>()
            {
                {  "MacDeveloperHardenWithNotarization",
                    new List<AdditionalCertificateInformation>() {
                        new AdditionalCertificateInformation() { MacNotarizationAppName = "dotnet", MacSigningOperation = "MacDeveloperHarden" }
                    } 
                }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>()
            {
                { new ExplicitCertificateKey("test.pkg"), "MacDeveloperHardenWithNotarization" }
            };
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'Nested.NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'Nested.SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'NestedPkg.pkg' Certificate='MacDeveloperHarden'",
                "File 'test.pkg' Certificate='MacDeveloperHarden' NotarizationAppName='com.microsoft.dotnet'",
            }, additionalCertificateInfo: additionalCertificateInfo);
 
            // OSX files need to be zipped first before being signed
            // This is why the .pkgs are listed as .zip files below
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "3", "Payload/SOS.NETCore.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "4", "Payload/NativeLibrary.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "5", "Payload/this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.SOS.NETCore.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "6", "Payload/this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.NativeLibrary.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                ",
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "1", "NestedPkg.pkg.zip"))}"">
                <Authenticode>MacDeveloperHarden</Authenticode>
                </FilesToSign>",
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "test.pkg.zip"))}"">
                <Authenticode>MacDeveloperHarden</Authenticode>
                </FilesToSign>
                ",
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "test.pkg.zip"))}"">
                <Authenticode>8020</Authenticode>
                <MacAppName>com.microsoft.dotnet</MacAppName>
                </FilesToSign>",
            }, additionalCertificateInfo: additionalCertificateInfo);
        }
 
        [MacOSOnlyFact]
        public void SignNestedPkgFile()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign( GetResourcePath("NestedPkg.pkg"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'Nested.SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'Nested.NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'NestedPkg.pkg' Certificate='MacDeveloperHarden'",
            });
 
            // OSX files need to be zipped first before being signed
            // This is why the .pkgs and .apps are listed as .zip files below
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "Payload/SOS.NETCore.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "3", "Payload/NativeLibrary.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "4", "Payload/this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.SOS.NETCore.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "5", "Payload/this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.NativeLibrary.dll"))}"">
                <Authenticode>Microsoft400</Authenticode>
                </FilesToSign>
                ",
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "NestedPkg.pkg.zip"))}"">
                <Authenticode>MacDeveloperHarden</Authenticode>
                </FilesToSign>"
            });
        }
 
        [MacOSOnlyFact]
        public void SignPkgFileWithApp()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign( GetResourcePath("WithApp.pkg"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            // When .apps are unpacked from .pkgs, they get zipped so they can be signed
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'libexample.dylib' Certificate='DylibCertificate'",
                "File 'test.app' Certificate='MacDeveloperHarden'",
                "File 'WithApp.pkg' Certificate='MacDeveloperHarden'",
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                // This dylib does not go to a zip file because the cert chosen is DylibCertificate.
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "4", "Contents/Resources/libexample.dylib"))}"">
                <Authenticode>DylibCertificate</Authenticode>
                </FilesToSign>
                ",
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "Payload", "test.app.zip"))}"">
                <Authenticode>MacDeveloperHarden</Authenticode>
                </FilesToSign>
                ",
                $@"
                <FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "WithApp.pkg.zip"))}"">
                <Authenticode>MacDeveloperHarden</Authenticode>
                </FilesToSign>"
            });
        }
 
        [Fact]
        public void SignTarGZipFile()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("test.tgz"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'Nested.NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'Nested.SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'test.tgz'",
            }/*,
            Reenable after https://github.com/dotnet/arcade/issues/10293,
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "0", "test/NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "2", "test/this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
            }*/);
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "0", "test/NativeLibrary.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "1", "test/SOS.NETCore.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "test/this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.NativeLibrary.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "3", "test/this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.SOS.NETCore.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
"
            });
        }
 
        [Fact]
        public void SymbolsNupkg()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("test.symbols.nupkg"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'Nested.NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'Nested.SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'test.symbols.nupkg'",
            }/*,
            Reenable after https://github.com/dotnet/arcade/issues/10293,
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "0", "NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "2", "this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
            }*/);
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "0", "NativeLibrary.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "1", "SOS.NETCore.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.NativeLibrary.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "3", "this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.SOS.NETCore.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
"
            });
        }
 
        [Fact]
        public void SignedSymbolsNupkg()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("test.symbols.nupkg"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
            var tempFileExtensionSignInfo = s_fileExtensionSignInfo.Where(s => s.Key != ".symbols.nupkg").ToDictionary(e => e.Key, e => e.Value);
            
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, tempFileExtensionSignInfo, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'Nested.NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'Nested.SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'test.symbols.nupkg' Certificate='NuGet'",
            }/*,
            Reenable after https://github.com/dotnet/arcade/issues/10293,
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "0", "NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "2", "this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
            }*/);
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "0", "NativeLibrary.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "1", "SOS.NETCore.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.NativeLibrary.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "3", "this_is_a_big_folder_name_look/this_is_an_even_more_longer_folder_name/but_this_one_is_ever_longer_than_the_previous_other_two/Nested.SOS.NETCore.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>
"
            });
        }
 
#if !NETFRAMEWORK
        [Fact]
        public void CheckDebSigning()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>
            {
                new ItemToSign(GetResourcePath("test.deb"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>();
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'mscorlib.dll' TargetFramework='.NETCoreApp,Version=v10.0' Certificate='Microsoft400'",
                "File 'data.tar.gz'",
                "File 'test.deb' Certificate='LinuxSign'"
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "1", "./usr/local/bin/mscorlib.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>",
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "test.deb"))}"">
  <Authenticode>LinuxSign</Authenticode>
</FilesToSign>"
            });
 
            var expectedFilesOriginalHashes = new (string, string)[]
            {
                ("usr/local/bin/hello", "644981BBD6F4ED1B3CF68CD0F47981AA"),
                ("usr/local/bin/mscorlib.dll", "B80EEBA2B8616B7C37E49B004D69BBB7")
            };
            string[] signableFiles = ["usr/local/bin/mscorlib.dll"];
            string expectedControlFileContent = "Package: test\nVersion: 1.0\nSection: base\nPriority: optional\nArchitecture: all\n";
            expectedControlFileContent +="Maintainer: Arcade <test@example.com>\nInstalled-Size: 49697\nDescription: A simple test package\n This is a simple generated .deb package for testing purposes.\n";
 
            ValidateProducedDebContent(Path.Combine(_tmpDir, "test.deb"), expectedFilesOriginalHashes, signableFiles, expectedControlFileContent);
        }
 
        [WindowsOnlyFact]
        public void CheckRpmSigningOnWindows()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>
            {
                new ItemToSign(GetResourcePath("test.rpm"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>();
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'test.rpm' Certificate='LinuxSign'"
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "test.rpm"))}"">
  <Authenticode>LinuxSign</Authenticode>
</FilesToSign>"
            });
        }
 
        [LinuxOnlyFact]
        public void CheckRpmSigning()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>
            {
                new ItemToSign(GetResourcePath("test.rpm"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>();
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'mscorlib.dll' TargetFramework='.NETCoreApp,Version=v10.0' Certificate='Microsoft400'",
                "File 'test.rpm' Certificate='LinuxSign'"
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "1", "./usr/local/bin/mscorlib.dll"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>",
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "test.rpm"))}"">
  <Authenticode>LinuxSign</Authenticode>
</FilesToSign>"
            });
 
            var expectedFilesOriginalHashes = new (string, string)[]
            {
                ("usr/local/bin/hello", "644981BBD6F4ED1B3CF68CD0F47981AA"),
                ("usr/local/bin/mscorlib.dll", "B80EEBA2B8616B7C37E49B004D69BBB7")
            };
            string[] signableFiles = ["usr/local/bin/mscorlib.dll"];
            string originalUncompressedPayloadChecksum = "216c2a99006d2e14d28a40c0f14a63f6462f533e89789a6f294186e0a0aad3fd";
 
            ValidateProducedRpmContent(Path.Combine(_tmpDir, "test.rpm"), expectedFilesOriginalHashes, signableFiles, originalUncompressedPayloadChecksum);
        }
 
        [Fact]
        public void VerifyDebIntegrity()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>
            {
                new ItemToSign(GetResourcePath("SignedDeb.deb")),
                new ItemToSign(GetResourcePath("IncorrectlySignedDeb.deb"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>();
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            var expectedFilesToBeSigned = new List<string>
            {
                "File 'IncorrectlySignedDeb.deb' Certificate='LinuxSign'"
            };
 
            // If on windows, both packages will be submitted for signing
            // because the CL verification tool (gpg) is not available on Windows.
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                expectedFilesToBeSigned.Add("File 'SignedDeb.deb' Certificate='LinuxSign'");
            }
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, expectedFilesToBeSigned.ToArray());
        }
 
        [Fact]
        public void VerifyRpmIntegrity()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>
            {
                new ItemToSign(GetResourcePath("SignedRpm.rpm")),
                new ItemToSign(GetResourcePath("IncorrectlySignedRpm.rpm"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>();
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            var expectedFilesToBeSigned = new List<string>
            {
                "File 'IncorrectlySignedRpm.rpm' Certificate='LinuxSign'"
            };
 
            // If on windows, both packages will be submitted for signing
            // because the CL verification tool (gpg) is not available on Windows.
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                expectedFilesToBeSigned.Add("File 'SignedRpm.rpm' Certificate='LinuxSign'");
            }
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, expectedFilesToBeSigned.ToArray());
        }
#endif
 
        [Fact]
        public void CheckPowershellSigning()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("SignedScript.ps1")),
                new ItemToSign(GetResourcePath("UnsignedScript.ps1"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>();
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'UnsignedScript.ps1' Certificate='PSCertificate'"
            });
        }
 
        /* These tests return different results on netcoreapp. ie, we can only truly validate nuget integrity when running on framework.
         * NuGet behaves differently on core vs framework 
         * - https://github.com/NuGet/NuGet.Client/blob/e88a5a03a1b26099f8be225d3ee3a897b2edb1d0/build/common.targets#L18-L25
         */
#if NETFRAMEWORK
        [Fact]
        public void VerifyNupkgIntegrity()
        {
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("SignedPackage.1.0.0.nupkg")),
                new ItemToSign(GetResourcePath("IncorrectlySignedPackage.1.0.0.nupkg"))
            };
 
            ValidateFileSignInfos(itemsToSign,
                                  new Dictionary<string, List<SignInfo>>(),
                                  new Dictionary<ExplicitCertificateKey, string>(),
                                  s_fileExtensionSignInfo,
                                  new[] { "File 'IncorrectlySignedPackage.1.0.0.nupkg' Certificate='NuGet'" });
        }
 
        [Fact]
        public void SignNupkgWithUnsignedContents()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("UnsignedContents.nupkg")),
                new ItemToSign(GetResourcePath("FakeSignedContents.nupkg"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>();
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'UnsignedScript.ps1' Certificate='PSCertificate'",
                "File 'UnsignedContents.nupkg' Certificate='NuGet'",
                "File 'FakeSignedContents.nupkg' Certificate='NuGet'"
            });
        }
#endif
        [WindowsOnlyFact]
        [Trait("Category", "SkipWhenLiveUnitTesting")]
        public void SignMsiEngine()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("MsiBootstrapper.exe")),
                new ItemToSign(GetResourcePath("MsiBootstrapper.exe.wixpack.zip"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'MsiSetup.msi' Certificate='Microsoft400'",
                "File 'MsiBootstrapper.exe' Certificate='Microsoft400'"
            }/*,
            Reenable after https://github.com/dotnet/arcade/issues/10293,
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "MsiBootstrapper.exe")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: 'Copyright (c). All rights reserved.'."
            }*/);
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
{
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "0", "ABCDEFG/MsiSetup.msi"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>",
 $@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "engines", "0", "MsiBootstrapper.exe-engine.exe"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>",
 $@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "MsiBootstrapper.exe"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>"
            },
            wixToolsPath: GetWixToolPath());
 
        }
 
        [WindowsOnlyFact]
        [Trait("Category", "SkipWhenLiveUnitTesting")]
        public void MsiWithWixpack()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("MsiSetup.msi"), "123"),
                new ItemToSign(GetResourcePath("MsiSetup.msi.wixpack.zip"), "123")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'MsiApplication.exe' TargetFramework='.NETFramework,Version=v4.7.2' Certificate='Microsoft400'",
                "File 'MsiSetup.msi' Certificate='Microsoft400'"
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
$@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "0", "ABCDEFG/MsiApplication.exe"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>",
 $@"<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "MsiSetup.msi"))}"">
  <Authenticode>Microsoft400</Authenticode>
</FilesToSign>"
            },
            wixToolsPath: GetWixToolPath());
        }
 
        /// <summary>
        /// Validate that an invalid wix toolset path causes an error
        /// </summary>
        [WindowsOnlyFact]
        public void BadWixToolsetPath()
        {
            var badPath = Path.Combine(GetWixToolPath(), "badpath");
 
            var fakeBuildEngine = new FakeBuildEngine(_output);
            var task = new SignToolTask
            {
                BuildEngine = fakeBuildEngine,
                ItemsToSign =  Array.Empty<ITaskItem>(),
                StrongNameSignInfo = Array.Empty<ITaskItem>(),
                FileExtensionSignInfo = Array.Empty<ITaskItem>(),
                LogDir = "LogDir",
                TempDir = "TempDir",
                DryRun = true,
                DotNetPath = CreateTestResource("dotnet.fake"),
                DoStrongNameCheck = false,
                SNBinaryPath = null,
                WixToolsPath = badPath
            };
 
            task.Execute().Should().BeFalse();
            task.Log.HasLoggedErrors.Should().BeTrue();
            fakeBuildEngine.LogErrorEvents.ForEach(a => a.Message.Should().EndWithEquivalent(" does not exist." ));
        }
 
        [Fact]
        public void MPackFile()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("test.mpack"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "3PartySHA2") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'VisualStudio.Mac.Banana.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='3PartySHA2'",
                "File 'test.mpack'",
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "1", "VisualStudio.Mac.Banana.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
</FilesToSign>
"
            });
        }
 
        [Fact]
        public void VsixPackage_DuplicateVsixAfter()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("test.vsix"), "123"),
                new ItemToSign(GetResourcePath("PackageWithRelationships.vsix"), "123")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'PackageWithRelationships.vsix' Certificate='VsixSHA2'",
                "File 'test.vsix' Certificate='VsixSHA2'",
            },
            new[]
            {
                $"{Path.Combine(_tmpDir, "ContainerSigning", "6", "PackageWithRelationships.vsix")} -> {Path.Combine(_tmpDir, "PackageWithRelationships.vsix")}"
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "1", "lib/net461/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "lib/netstandard2.0/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "8", "Contents/Common7/IDE/PrivateAssemblies/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "6", "PackageWithRelationships.vsix"))}"">
  <Authenticode>VsixSHA2</Authenticode>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "test.vsix"))}"">
  <Authenticode>VsixSHA2</Authenticode>
</FilesToSign>
"
            });
        }
 
        [Fact]
        public void VsixPackage_WithSpaces()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("TestSpaces.vsix"), "123"),
                new ItemToSign(GetResourcePath("PackageWithRelationships.vsix"), "123")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'PackageWithRelationships.vsix' Certificate='VsixSHA2'",
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",                            
                "File 'TestSpaces.vsix' Certificate='VsixSHA2'"
            },
            new[]
            {
                $"{Path.Combine(_tmpDir, "ContainerSigning", "4", "PackageWithRelationships.vsix")} -> {Path.Combine(_tmpDir, "PackageWithRelationships.vsix")}"
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "6", "Contents/Common7/IDE/PrivateAssemblies/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "10", "Team%20Tools/Dynamic Code Coverage/net461/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "11", "Team%20Tools/Dynamic Code Coverage/netstandard2.0/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "4", "PackageWithRelationships.vsix"))}"">
  <Authenticode>VsixSHA2</Authenticode>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "TestSpaces.vsix"))}"">
  <Authenticode>VsixSHA2</Authenticode>
</FilesToSign>
"
            });
        }
 
        [Fact]
        public void VsixPackage_DuplicateVsixBefore()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("PackageWithRelationships.vsix")),
                new ItemToSign(GetResourcePath("test.vsix"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'PackageWithRelationships.vsix' Certificate='VsixSHA2'",
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'test.vsix' Certificate='VsixSHA2'",
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "Contents/Common7/IDE/PrivateAssemblies/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "7", "lib/net461/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "8", "lib/netstandard2.0/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "PackageWithRelationships.vsix"))}"">
  <Authenticode>VsixSHA2</Authenticode>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "test.vsix"))}"">
  <Authenticode>VsixSHA2</Authenticode>
</FilesToSign>
"
            });
        }
 
        [Fact]
        public void VsixPackage_DuplicateVsixBeforeAndAfter()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("PackageWithRelationships.vsix", relativePath: "A"), "123"),
                new ItemToSign(GetResourcePath("test.vsix"), "123"),
                new ItemToSign(GetResourcePath("PackageWithRelationships.vsix", relativePath: "B"), "123")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'PackageWithRelationships.vsix' Certificate='VsixSHA2'",
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'test.vsix' Certificate='VsixSHA2'",
            },
            new[]
            {
                $"{Path.Combine(_tmpDir, "A", "PackageWithRelationships.vsix")} -> {Path.Combine(_tmpDir, "B", "PackageWithRelationships.vsix")}"
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "Contents/Common7/IDE/PrivateAssemblies/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "7", "lib/net461/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "8", "lib/netstandard2.0/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "A", "PackageWithRelationships.vsix"))}"">
  <Authenticode>VsixSHA2</Authenticode>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "test.vsix"))}"">
  <Authenticode>VsixSHA2</Authenticode>
</FilesToSign>
"
            });
        }
 
        [Fact]
        public void VsixPackageWithRelationships()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("PackageWithRelationships.vsix"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'PackageWithRelationships.vsix' Certificate='VsixSHA2'"
            });
 
            ValidateGeneratedProject(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "ContainerSigning", "2", "Contents/Common7/IDE/PrivateAssemblies/ProjectOne.dll"))}"">
  <Authenticode>3PartySHA2</Authenticode>
  <StrongName>ArcadeStrongTest</StrongName>
</FilesToSign>",
 
$@"
<FilesToSign Include=""{Uri.EscapeDataString(Path.Combine(_tmpDir, "PackageWithRelationships.vsix"))}"">
  <Authenticode>VsixSHA2</Authenticode>
</FilesToSign>"
            });
        }
 
        [Fact]
        public void ZeroLengthFilesShouldNotBeSigned()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("ZeroLengthPythonFile.py"))
            };
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>();
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>()
            {
                { new ExplicitCertificateKey("ZeroLengthPythonFile.py"), "3PartySHA2" }
            };
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, Array.Empty<string>());
        }
 
        [Fact]
        public void CheckFileExtensionSignInfo()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(CreateTestResource("dynalib.dylib"), "123"),
                new ItemToSign(CreateTestResource("javascript.js"), "123"),
                new ItemToSign(CreateTestResource("javatest.jar"), "123"),
                new ItemToSign(CreateTestResource("power.ps1"), "123"),
                new ItemToSign(CreateTestResource("powerc.psc1"), "123"),
                new ItemToSign(CreateTestResource("powerd.psd1"), "123"),
                new ItemToSign(CreateTestResource("powerm.psm1"), "123"),
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>();
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'dynalib.dylib' Certificate='DylibCertificate'",
                "File 'javascript.js' Certificate='JSCertificate'",
                "File 'javatest.jar' Certificate='JARCertificate'",
                "File 'power.ps1' Certificate='PSCertificate'",
                "File 'powerc.psc1' Certificate='PSCCertificate'",
                "File 'powerd.psd1' Certificate='PSDCertificate'",
                "File 'powerm.psm1' Certificate='PSMCertificate'",
            });
        }
 
        [Fact]
        public void ValidateParseFileExtensionEntriesForSameCollisionPriorityIdFails()
        {
            var fileExtensionSignInfo = new List<ITaskItem>();
 
            // Validate that multiple entries will collide and fail
            fileExtensionSignInfo.Add(new TaskItem(".js", new Dictionary<string, string>
            {
                { "CertificateName", "JSCertificate" },
                { "CollisionPriorityId", "123" }
            }));
            fileExtensionSignInfo.Add(new TaskItem(".js", new Dictionary<string, string>{
                { "CertificateName", "None" },
                { "CollisionPriorityId", "123" }
            }));
 
            runTask(fileExtensionSignInfo: fileExtensionSignInfo.ToArray()).Should().BeFalse();
        }
 
        [Fact]
        public void ValidateParseFileExtensionEntriesForDifferentCollisionPriorityIdSucceeds()
        {
            var fileExtensionSignInfo = new List<ITaskItem>();
 
            // Validate that multiple entries will collide and fail
            fileExtensionSignInfo.Add(new TaskItem(".js", new Dictionary<string, string>
            {
                { "CertificateName", "JSCertificate" },
                { "CollisionPriorityId", "123" }
            }));
            fileExtensionSignInfo.Add(new TaskItem(".js", new Dictionary<string, string>{
                { "CertificateName", "None" }
            }));
            fileExtensionSignInfo.Add(new TaskItem(".js", new Dictionary<string, string>
            {
                { "CertificateName", "JSCertificate" },
                { "CollisionPriorityId", "456" }
            }));
 
            runTask(fileExtensionSignInfo: fileExtensionSignInfo.ToArray()).Should().BeTrue();
        }
 
        [Fact]
        public void ValidateParseFileExtensionEntriesForTarGzExtensionPasses()
        {
            var fileExtensionSignInfo = new List<ITaskItem>();
 
            fileExtensionSignInfo.Add(new TaskItem(".tar.gz", new Dictionary<string, string>
            {
                { "CertificateName", "None" }
            }));
 
            runTask(fileExtensionSignInfo: fileExtensionSignInfo.ToArray()).Should().BeTrue();
        }
 
        // Given:
        // - "SameFiles1.zip" contains "Simple1.exe" and "Simple2.exe"
        // - "SameFiles2.zip" contains "Simple1.exe"
        // - "Simple1.exe" and "Simple2.exe" have identical contents
        // This test shows that:
        // - even though Simple1 and Simple2 have identical contents, they are treated as unique files
        // - Simple1 from SameFiles1.zip and Simple1 from SameFiles2.zip are treated as the same files because they have the
        //   same content and the same name
        [Fact]
        public void FilesAreUniqueByName()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("SameFiles1.zip"), "123"),
                new ItemToSign(GetResourcePath("SameFiles2.zip"), "123"),
            };
 
            ValidateFileSignInfos(itemsToSign, new Dictionary<string, List<SignInfo>>(), new Dictionary<ExplicitCertificateKey, string>(), s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'Simple1.exe' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='Microsoft400'",
                "File 'Simple2.exe' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='Microsoft400'",
                "File 'SameFiles1.zip'",
                "File 'SameFiles2.zip'",
            },
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "0", "Simple1.exe")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "1", "Simple2.exe")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''."
            });
        }
 
        /// <summary>
        /// This test is intended to validate that the argument parsing which occurs
        /// in the SignToolTask class are properly parsed before they are passed
        /// to sign tool.
        /// </summary>
        [Fact]
        public void ValidateSignToolTaskParsing()
        {
            // List of files to be considered for signing
            var itemsToSign = new ITaskItem[]
            {
                // Unsigned package
                new TaskItem(GetResourcePath("ContainerOne.1.0.0.nupkg"), new Dictionary<string, string>
                {
                    { SignToolConstants.CollisionPriorityId, "123" }
                }),
                // Signed pe file
                new TaskItem(GetResourcePath("SignedLibrary.dll"), new Dictionary<string, string>
                {
                    { SignToolConstants.CollisionPriorityId, "123" }
                })
            };
 
            var strongNameSignInfo = new ITaskItem[]
            {
                new TaskItem("ArcadeStrongTest", new Dictionary<string, string>
                {
                    { "CertificateName", "3PartySHA2" },
                    { "PublicKeyToken", "581d91ccdfc4ea9c" },
                    { "CollisionPriorityId", "123" }
                })
            };
 
            // Overriding file signing information
            var fileSignInfo = new ITaskItem[]
            {
                new TaskItem("ProjectOne.dll", new Dictionary<string, string>
                {
                    { "TargetFramework", ".NETStandard,Version=v2.0" },
                    { "CertificateName", "OverrideCertificateName" },
                    { "PublicKeyToken", "581d91ccdfc4ea9c" },
                    { "CollisionPriorityId", "123" }
                }),
                new TaskItem("SignedLibrary.dll", new Dictionary<string, string>
                {
                    { "TargetFramework", ".NETCoreApp,Version=v2.0" },
                    { "CertificateName", "DualSignCertificate" },
                    { "PublicKeyToken", "31bf3856ad364e35" },
                    { "CollisionPriorityId", "123" }
                })
            };
 
            // Enable dual signing for signed library
            var certificatesSignInfo = new ITaskItem[]
            {
                new TaskItem("DualSignCertificate", new Dictionary<string, string>
                {
                    { "DualSigningAllowed", "true" },
                    { "CollisionPriorityId", "123" }
                }),
                new TaskItem("MacDeveloperHardenWithNotarization", new Dictionary<string, string>
                {
                    { "MacCertificate", "MacDeveloperHarden" },
                    { "MacNotarizationAppName", "com.microsoft.dotnet" },
                    { "CollisionPriorityId", "123" }
                })
            };
 
            var task = new SignToolTask
            {
                BuildEngine = new FakeBuildEngine(_output),
                ItemsToSign = itemsToSign,
                StrongNameSignInfo = strongNameSignInfo,
                FileExtensionSignInfo = s_fileExtensionSignInfoPostBuild,
                FileSignInfo = fileSignInfo,
                CertificatesSignInfo = certificatesSignInfo,
                LogDir = "LogDir",
                TempDir = "TempDir",
                DryRun = true,
                DotNetPath = CreateTestResource("dotnet.fake"),
                MicroBuildCorePath = "MicroBuildCorePath",
                DoStrongNameCheck = false,
                SNBinaryPath = null,
                TarToolPath = s_tarToolPath,
                PkgToolPath = s_pkgToolPath,
            };
 
            task.Execute().Should().BeTrue();
 
            var expected = new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='OverrideCertificateName' StrongName='ArcadeStrongTest'",
                "File 'ContainerOne.1.0.0.nupkg' Certificate='NuGet'",
                "File 'SignedLibrary.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='DualSignCertificate'"
            };
            task.ParsedSigningInput.FilesToSign.Select(f => f.ToString()).Should().BeEquivalentTo(expected);
        }
 
        private bool runTask(ITaskItem[] itemsToSign = null, ITaskItem[] strongNameSignInfo = null, ITaskItem[] fileExtensionSignInfo = null)
        {
            var task = new SignToolTask
            {
                BuildEngine = new FakeBuildEngine(_output),
                ItemsToSign = itemsToSign ?? Array.Empty<ITaskItem>(),
                StrongNameSignInfo = strongNameSignInfo ?? Array.Empty<ITaskItem>(),
                FileExtensionSignInfo = fileExtensionSignInfo ?? Array.Empty<ITaskItem>(),
                LogDir = "LogDir",
                TempDir = "TempDir",
                DryRun = true,
                DotNetPath = CreateTestResource("dotnet.fake"),
                DoStrongNameCheck = false,
                SNBinaryPath = null,
            };
 
            return task.Execute();
        }
 
        [Fact]
        public void ValidateAppendingCertificate()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("SignedLibrary.dll")),
            };
 
            const string dualCertName = "DualCertificateName";
            var additionalCertInfo = new Dictionary<string, List<AdditionalCertificateInformation>>()
            {
                {dualCertName, new List<AdditionalCertificateInformation>(){new AdditionalCertificateInformation() { DualSigningAllowed = true } } },
            };
 
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "31bf3856ad364e35", new List<SignInfo>{ new SignInfo(certificate: dualCertName, strongName: null) } }
            };
 
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                $"File 'SignedLibrary.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='{dualCertName}'",
            },
            additionalCertificateInfo: additionalCertInfo);
        }
 
        [Fact]
        public void ValidateCertNotAppendedWithNonMatchingCollisionId()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("SignedLibrary.dll")),
            };
 
            const string dualCertName = "DualCertificateName";
            var additionalCertInfo = new Dictionary<string, List<AdditionalCertificateInformation>>()
            {
                { dualCertName, new List<AdditionalCertificateInformation>(){new AdditionalCertificateInformation()
                {
                    DualSigningAllowed = true,
                    CollisionPriorityId = "123"
                } } },
            };
 
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "31bf3856ad364e35", new List<SignInfo>{ new SignInfo(certificate: dualCertName, strongName: null) } }
            };
 
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new string[] { }, additionalCertificateInfo: additionalCertInfo);
        }
 
        [Fact]
        public void PackageWithZipFile()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign( GetResourcePath("PackageWithZip.nupkg"), "123")
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'test.zip'",
                "File 'PackageWithZip.nupkg' Certificate='NuGet'",
            }/*,
            Reenable after https://github.com/dotnet/arcade/issues/10293,
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "2", "NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''."
            }*/);
        }
 
        [Fact]
        public void NestedZipFile()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign( GetResourcePath("NestedZip.zip"))
            };
 
            // Default signing information
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo>{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } }
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>();
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'NativeLibrary.dll' Certificate='Microsoft400'",
                "File 'SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'InnerZipFile.zip'",
                "File 'Mid.SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'",
                "File 'MidNativeLibrary.dll' Certificate='Microsoft400'",
                "File 'NestedZip.zip'",
            }/*,
            Reenable after https://github.com/dotnet/arcade/issues/10293,
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "0", "NativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "4", "MidNativeLibrary.dll")}' with Microsoft certificate 'Microsoft400'. The library is considered 3rd party library due to its copyright: ''."
            }*/);
        }
 
        [Fact]
        public void SpecificFileSignInfos()
        {
            // List of files to be considered for signing
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(CreateTestResource("test.js"), "123"),
                new ItemToSign(CreateTestResource("test.jar"), "123"),
                new ItemToSign(CreateTestResource("test.ps1"), "123"),
                new ItemToSign(CreateTestResource("test.psd1"), "123"),
                new ItemToSign(CreateTestResource("test.psm1"), "123"),
                new ItemToSign(CreateTestResource("test.psc1"), "123"),
                new ItemToSign(CreateTestResource("test.dylib"), "123"),
                new ItemToSign(GetResourcePath("EmptyPKT.dll"), "123"),
                new ItemToSign(GetResourcePath("test.vsix"), "123"),
                new ItemToSign(GetResourcePath("Simple.nupkg"), "123"),
                // This symbols nupkg has the same hash as Simple.nupkg.
                // It should still get signed with a different signature.
                new ItemToSign(GetResourcePath("Simple.symbols.nupkg"), "123"),
                // A few extra interesting cases. This has no file extension
                new ItemToSign(GetResourcePath("filewithoutextension"), "123"),
                // This will be marked as not having any cert.
                new ItemToSign(GetResourcePath("SPCNoPKT.dll"), "123"),
                // This will be marked to have hardening and notarization
                new ItemToSign(GetResourcePath("Simple.exe"), "1234")
            };
 
            var strongNameSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { "581d91ccdfc4ea9c", new List<SignInfo> {new SignInfo(certificate: "ArcadeCertTest", strongName: "StrongNameValue", collisionPriorityId: "123") } },
            };
 
            // Overriding information
            var fileSignInfo = new Dictionary<ExplicitCertificateKey, string>()
            {
                { new ExplicitCertificateKey("test.js", collisionPriorityId: "123"), "JSCertificate" },
                { new ExplicitCertificateKey("test.jar", collisionPriorityId: "123"), "JARCertificate" },
                { new ExplicitCertificateKey("test.ps1", collisionPriorityId: "123"), "PS1Certificate" },
                { new ExplicitCertificateKey("test.psd1", collisionPriorityId: "123"), "PSD1Certificate" },
                { new ExplicitCertificateKey("test.psm1", collisionPriorityId: "123"), "PSM1Certificate" },
                { new ExplicitCertificateKey("test.psc1", collisionPriorityId: "123"), "PSC1Certificate" },
                { new ExplicitCertificateKey("test.dylib", collisionPriorityId: "123"), "DYLIBCertificate" },
                { new ExplicitCertificateKey("EmptyPKT.dll", collisionPriorityId: "123"), "DLLCertificate" },
                { new ExplicitCertificateKey("test.vsix", collisionPriorityId: "123"), "VSIXCertificate" },
                { new ExplicitCertificateKey("PackageWithRelationships.vsix", collisionPriorityId: "123"), "VSIXCertificate2" },
                { new ExplicitCertificateKey("Simple.dll", collisionPriorityId: "123"), "DLLCertificate2" },
                { new ExplicitCertificateKey("Simple.nupkg", collisionPriorityId: "123"), "NUPKGCertificate" },
                { new ExplicitCertificateKey("Simple.symbols.nupkg", collisionPriorityId: "123"), "NUPKGCertificate2" },
                { new ExplicitCertificateKey("ProjectOne.dll", "581d91ccdfc4ea9c", ".NETFramework,Version=v4.6.1", "123"), "DLLCertificate3" },
                { new ExplicitCertificateKey("ProjectOne.dll", "581d91ccdfc4ea9c", ".NETStandard,Version=v2.0", "123"), "DLLCertificate4" },
                { new ExplicitCertificateKey("ProjectOne.dll", "581d91ccdfc4ea9c", ".NETCoreApp,Version=v2.0", "123"), "DLLCertificate5" },
                { new ExplicitCertificateKey("filewithoutextension", collisionPriorityId: "123"), "MacDeveloperHarden" },
                { new ExplicitCertificateKey("SPCNoPKT.dll", collisionPriorityId: "123"), "None" },
                { new ExplicitCertificateKey("Simple.exe", collisionPriorityId: "1234"), "MacDeveloperHardenWithNotarization" },
            };
 
            // Set up the cert to allow for signing and notarization.
            var certificatesSignInfo = new Dictionary<string, List<AdditionalCertificateInformation>>()
            {
                {  "MacDeveloperHardenWithNotarization",
                    new List<AdditionalCertificateInformation>() {
                        new AdditionalCertificateInformation() { MacNotarizationAppName = "dotnet", MacSigningOperation = "MacDeveloperHarden" }
                    }
                }
            };
 
            ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[]
            {
                "File 'test.js' Certificate='JSCertificate'",
                "File 'test.jar' Certificate='JARCertificate'",
                "File 'test.ps1' Certificate='PS1Certificate'",
                "File 'test.psd1' Certificate='PSD1Certificate'",
                "File 'test.psm1' Certificate='PSM1Certificate'",
                "File 'test.psc1' Certificate='PSC1Certificate'",
                "File 'test.dylib' Certificate='DYLIBCertificate'",
                "File 'EmptyPKT.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='DLLCertificate'",
                "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='DLLCertificate3' StrongName='StrongNameValue'",
                "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='DLLCertificate4' StrongName='StrongNameValue'",
                "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='DLLCertificate5' StrongName='StrongNameValue'",
                "File 'PackageWithRelationships.vsix' Certificate='VSIXCertificate2'",
                "File 'test.vsix' Certificate='VSIXCertificate'",
                "File 'Simple.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='DLLCertificate2'",
                "File 'Simple.nupkg' Certificate='NUPKGCertificate'",
                "File 'Simple.symbols.nupkg' Certificate='NUPKGCertificate2'",
                "File 'filewithoutextension' Certificate='MacDeveloperHarden'",
                "File 'Simple.exe' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='MacDeveloperHarden' NotarizationAppName='dotnet'",
            },
            additionalCertificateInfo: certificatesSignInfo,
            expectedWarnings: new[]
            {
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "EmptyPKT.dll")}' with Microsoft certificate 'DLLCertificate'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "9", "lib/net461/ProjectOne.dll")}' with Microsoft certificate 'DLLCertificate3'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "10", "lib/netstandard2.0/ProjectOne.dll")}' with Microsoft certificate 'DLLCertificate4'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "16", "Contents/Common7/IDE/PrivateAssemblies/ProjectOne.dll")}' with Microsoft certificate 'DLLCertificate5'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "ContainerSigning", "23", "Simple.dll")}' with Microsoft certificate 'DLLCertificate2'. The library is considered 3rd party library due to its copyright: ''.",
                $@"SIGN004: Signing 3rd party library '{Path.Combine(_tmpDir, "Simple.exe")}' with Microsoft certificate 'MacDeveloperHarden'. The library is considered 3rd party library due to its copyright: ''."
            });
        }
 
        [Theory]
        [MemberData(nameof(GetSignableExtensions))]
        public void MissingCertificateName(string extension)
        {
            var needContent = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
            {
                { ".dll", "EmptyPKT.dll" },
                { ".vsix", "Simple.vsix" },
                { ".nupkg", "Simple.nupkg" },
                { ".exe", "Simple.exe" },
                { ".deb", "test.deb" }
            };
 
            var task = new SignToolTask { BuildEngine = new FakeBuildEngine() };
 
            var inputFilePath = needContent.TryGetValue(extension, out var resourcePath) ?
                GetResourcePath(resourcePath) :
                CreateTestResource("test" + extension);
            
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(inputFilePath)
            };
 
            new Configuration(_tmpDir,
                itemsToSign,
                new Dictionary<string, List<SignInfo>>(),
                new Dictionary<ExplicitCertificateKey, string>(),
                new Dictionary<string, List<SignInfo>>(),
                new(),
                tarToolPath: s_tarToolPath,
                pkgToolPath: s_pkgToolPath,
                snPath: s_snPath,
                task.Log)
                .GenerateListOfFiles();
 
            task.Log.HasLoggedErrors.Should().BeTrue();
        }
 
        [Theory]
        [MemberData(nameof(GetSignableExtensions))]
        public void MissingCertificateNameButExtensionIsIgnored(string extension)
        {
            var needContent = new Dictionary<string, (string, string[])>(StringComparer.OrdinalIgnoreCase)
            {
                { ".dll", ("EmptyPKT.dll", []) },
                { ".vsix", ("Simple.vsix", []) },
                { ".nupkg", ("Simple.nupkg", []) },
                { ".exe", ("Simple.exe", []) },
                { ".deb", ("test.deb", [".dll"]) }
            };
 
            var task = new SignToolTask { BuildEngine = new FakeBuildEngine() };
 
            needContent.TryGetValue(extension, out (string ResourcePath, string[] AdditionalExtensions) value);
            var inputFilePath = value.ResourcePath != null ?
                GetResourcePath(value.ResourcePath) :
                CreateTestResource("test" + extension);
 
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(inputFilePath)
            };
 
            var extensionSignInfo = new Dictionary<string, List<SignInfo>>()
            {
                { extension, new List<SignInfo> { SignInfo.Ignore } }
            };
 
            foreach (var additionalExtension in value.AdditionalExtensions ?? [])
            {
                extensionSignInfo.Add(additionalExtension, new List<SignInfo> { SignInfo.Ignore });
            }
 
            new Configuration(_tmpDir,
                itemsToSign,
                new Dictionary<string, List<SignInfo>>(),
                new Dictionary<ExplicitCertificateKey, string>(),
                extensionSignInfo,
                new(),
                tarToolPath: s_tarToolPath,
                pkgToolPath: s_pkgToolPath,
                snPath: s_snPath,
                task.Log)
                .GenerateListOfFiles();
 
            task.Log.HasLoggedErrors.Should().BeFalse();
        }
 
        [Fact]
        public void CrossGeneratedLibraryWithoutPKT()
        {
            var itemsToSign = new List<ItemToSign>()
            {
                new ItemToSign(GetResourcePath("SPCNoPKT.dll"), "123")
            };
 
            ValidateFileSignInfos(
                itemsToSign, 
                new Dictionary<string, List<SignInfo>>(), 
                new Dictionary<ExplicitCertificateKey, 
                string>(), 
                s_fileExtensionSignInfoWithCollisionId, 
                new string[0]);
 
            ValidateGeneratedProject(
                itemsToSign, 
                new Dictionary<string, List<SignInfo>>(), 
                new Dictionary<ExplicitCertificateKey, string>(), 
                s_fileExtensionSignInfoWithCollisionId, 
                new string[0]);
        }
 
        /// <summary>
        /// Verify that running the wixpack returns passing result and that the expected output file
        /// is created, or a negative result if the wix tool fails.
        /// </summary>
        [WindowsOnlyTheory]
        [InlineData(true)]
        [InlineData(false)]
        [Trait("Category", "SkipWhenLiveUnitTesting")]
        public void RunWixToolRunsOrFailsProperly(bool deleteWixobjBeforeRunningTool)
        {
            var task = new SignToolTask { BuildEngine = new FakeBuildEngine() };
 
            const string expectedExe = "MsiBootstrapper.exe";
            const string wixPack = "MsiBootstrapper.exe.wixpack.zip";
            var wixToolsPath = GetWixToolPath();
            var wixpackPath = GetResourcePath(wixPack);
            var tempDir = Path.GetTempPath();
            string workingDir = Path.Combine(tempDir, "extract", Guid.NewGuid().ToString());
            string outputDir = Path.Combine(tempDir, "output", Guid.NewGuid().ToString());
            string createFileName = Path.Combine(workingDir, "create.cmd");
            string outputFileName = Path.Combine(outputDir, expectedExe);
            Directory.CreateDirectory(outputDir);
 
            try
            {
                // Unzip the wixpack zip, run the tool, and check the exit code
                ZipFile.ExtractToDirectory(wixpackPath, workingDir);
 
                if (deleteWixobjBeforeRunningTool)
                {
                    File.Delete(Path.Combine(workingDir, "Bundle.wixobj"));
                }
 
                BatchSignUtil.RunWixTool(createFileName, outputDir, workingDir, wixToolsPath, task.Log).Should().Be(!deleteWixobjBeforeRunningTool);
                File.Exists(outputFileName).Should().Be(!deleteWixobjBeforeRunningTool);
            }
            finally
            {
                Directory.Delete(workingDir, true);
                Directory.Delete(outputDir, true);
            }
        }
 
        /// <summary>
        /// Run a wix tool, but with an empty wix path.
        /// </summary>
        [Fact]
        public void RunWixToolThrowsErrorIfNoWixToolsProvided()
        {
            var fakeBuildEngine = new FakeBuildEngine();
            var task = new SignToolTask { BuildEngine = fakeBuildEngine };
 
            BatchSignUtil.RunWixTool("create.cmd", "foodir", "bardir", null, task.Log).Should().BeFalse();
            task.Log.HasLoggedErrors.Should().BeTrue();
            fakeBuildEngine.LogErrorEvents.Should().Contain(e => e.Message.Contains("WixToolsPath must be defined to run WiX tooling"));
        }
 
        /// <summary>
        /// If attempting to repack a wix container, but a wix path was not
        /// provided
        /// </summary>
        [Fact]
        public void RunWixToolThrowsErrorIfWixToolsProvidedButDirDoesNotExist()
        {
            const string totalWixToolDir = "totally/wix/tools";
            var fakeBuildEngine = new FakeBuildEngine();
            var task = new SignToolTask { BuildEngine = fakeBuildEngine };
 
            BatchSignUtil.RunWixTool("create.cmd", "foodir", "bardir", "totally/wix/tools", task.Log).Should().BeFalse();
            task.Log.HasLoggedErrors.Should().BeTrue();
            fakeBuildEngine.LogErrorEvents.Should().Contain(e => e.Message.Contains($"WixToolsPath '{totalWixToolDir}' not found."));
        }
 
        [Fact]
        public void MissingStrongNameSignaturesDoNotValidate()
        {
            StrongName.IsSigned(GetResourcePath("AspNetCoreCrossLib.dll")).Should().BeFalse();
            StrongName.IsSigned(GetResourcePath("CoreLibCrossARM.dll")).Should().BeFalse();
            StrongName.IsSigned(GetResourcePath("EmptyPKT.dll")).Should().BeFalse();
            StrongName.IsSigned(GetResourcePath("DelaySigned.dll")).Should().BeFalse();
            StrongName.IsSigned(GetResourcePath("ProjectOne.dll")).Should().BeFalse();
        }
 
        /// <summary>
        /// Add one to a byte in the input stream and write to the output stream. Both streams are left open.
        /// </summary>
        /// <param name="inputStream"></param>
        /// <param name="outputStream"></param>
        /// <param name="offset"></param>
        private void FlipBit(Stream inputStream, Stream outputStream, int offset, byte flipz)
        {
            using BinaryReader reader = new BinaryReader(inputStream, System.Text.Encoding.Default, true);
            using BinaryWriter writer = new BinaryWriter(outputStream, System.Text.Encoding.Default, true);
 
            // Read up to the checksum and write to the binary
            var bytesBeforeOffset = reader.ReadBytes(offset);
            writer.Write(bytesBeforeOffset);
 
            // Toggle a bit and write it out
            // Cast to byte explicitly to avoid writing an int.
            byte b = reader.ReadByte();
            byte f = (byte)(b ^ flipz);
            writer.Write(f);
 
            // Then write the read
            var bytesAfterChecksum = reader.ReadBytes((int)inputStream.Length - offset - 1);
            writer.Write(bytesAfterChecksum);
 
            outputStream.Position = 0;
            inputStream.Position = 0;
        }
 
        private void WriteBytesToLocation(Stream inputStream, Stream outputStream, int offset, uint bytez)
        {
            using BinaryReader reader = new BinaryReader(inputStream, System.Text.Encoding.Default, true);
            using BinaryWriter writer = new BinaryWriter(outputStream, System.Text.Encoding.Default, true);
 
            // Read up to the checksum and write to the binary
            var bytesBeforeOffset = reader.ReadBytes(offset);
            writer.Write(bytesBeforeOffset);
 
            // Toggle a bit and write it out
            // Cast to byte explicitly to avoid writing an int.
            writer.Write(bytez);
            // Advance the reader.
            reader.ReadUInt32();
 
            // Then write the read
            var bytesAfterChecksum = reader.ReadBytes((int)inputStream.Length - offset - sizeof(uint));
            writer.Write(bytesAfterChecksum);
 
            outputStream.Position = 0;
            inputStream.Position = 0;
        }
 
        /// <summary>
        /// Verify that flipbit works properly by flipping twice.
        /// </summary>
        [Fact]
        public void NoFlipButWriteShouldVerify()
        {
            // We're going to open the file and flip a bit in the checksum
            using var inputStream = File.OpenRead(GetResourcePath("SignedLibrary.dll"));
            using MemoryStream outputStream = new();
 
            PEHeaders peHeaders = new PEHeaders(inputStream);
            inputStream.Position = 0;
            int checksumStart = peHeaders.PEHeaderStartOffset + StrongName.ChecksumOffsetInPEHeader;
            WriteBytesToLocation(inputStream, outputStream, checksumStart, peHeaders.PEHeader.CheckSum);
            StrongName.IsSigned(outputStream).Should().BeTrue();
        }
 
        [Fact]
        public void IncorrectChecksumsDoNotValidate()
        {
            // We're going to open the file and flip a bit in the checksum
            using var inputStream = File.OpenRead(GetResourcePath("SignedLibrary.dll"));
            using MemoryStream outputStream = new();
 
            PEHeaders peHeaders = new PEHeaders(inputStream);
            inputStream.Position = 0;
            int checksumStart = peHeaders.PEHeaderStartOffset + StrongName.ChecksumOffsetInPEHeader;
            WriteBytesToLocation(inputStream, outputStream, checksumStart, peHeaders.PEHeader.CheckSum ^ 0x1);
            StrongName.IsSigned(outputStream).Should().BeFalse();
        }
 
        // This binary has had a resource added after it was strong name. This invalidated the checksum too,
        // so we write the expected checksum.
        [Fact]
        public void InvalidatedSNSignatureDoesNotValidate()
        {
            using var inputStream = File.OpenRead(GetResourcePath("InvalidatedStrongName.dll"));
            using MemoryStream outputStream = new();
 
            PEHeaders peHeaders = new PEHeaders(inputStream);
            inputStream.Position = 0;
 
            int checksumStart = peHeaders.PEHeaderStartOffset + StrongName.ChecksumOffsetInPEHeader;
            // Write the checksum that would be expected after editing the binary.
            WriteBytesToLocation(inputStream, outputStream, checksumStart, 110286);
 
            StrongName.IsSigned(outputStream).Should().BeFalse();
        }
 
        [Fact]
        public void ValidStrongNameSignaturesValidate()
        {
            StrongName.IsSigned(GetResourcePath("SignedLibrary.dll")).Should().BeTrue();
            StrongName.IsSigned(GetResourcePath("StrongNamedWithEcmaKey.dll")).Should().BeTrue();
        }
 
        [WindowsOnlyFact]
        public void ValidStrongNameSignaturesValidateWithFallback()
        {
            StrongName.IsSigned_Legacy(GetResourcePath("SignedLibrary.dll"), s_snPath).Should().BeTrue();
            StrongName.IsSigned_Legacy(GetResourcePath("StrongNamedWithEcmaKey.dll"), s_snPath).Should().BeTrue();
        }
 
        [Theory]
        [InlineData("OpenSigned.dll", "OpenSignedCorrespondingKey.snk", true)]
        [InlineData("DelaySignedWithOpen.dll", "OpenSignedCorrespondingKey.snk", false)]
        public void SigningSignsAsExpected(string file, string key, bool initiallySigned)
        {
            // Make sure this is unique
            string resourcePath = GetResourcePath(file, Guid.NewGuid().ToString());
            StrongName.IsSigned(resourcePath).Should().Be(initiallySigned);
            StrongName.Sign(resourcePath, GetResourcePath(key));
            StrongName.IsSigned(resourcePath).Should().BeTrue();
 
            // Legacy sn verification works on on Windows only
            StrongName.IsSigned_Legacy(resourcePath, s_snPath).Should().Be(
                RuntimeInformation.IsOSPlatform(OSPlatform.Windows));
        }
 
        [WindowsOnlyTheory]
        [InlineData("OpenSigned.dll", "OpenSignedCorrespondingKey.snk", true)]
        [InlineData("DelaySignedWithOpen.dll", "OpenSignedCorrespondingKey.snk", false)]
        [Trait("Category", "SkipWhenLiveUnitTesting")]
        public void SigningSignsAsExpectedWithLegacyAndVerifiesWithNonLegacy(string file, string key, bool initiallySigned)
        {
            // Make sure this is unique
            string resourcePath = GetResourcePath(file, Guid.NewGuid().ToString());
            StrongName.IsSigned_Legacy(resourcePath, s_snPath).Should().Be(initiallySigned);
            // Unset the strong name bit first
            StrongName.ClearStrongNameSignedBit(resourcePath);
            StrongName.Sign_Legacy(resourcePath, GetResourcePath(key), s_snPath).Should().BeTrue();
            StrongName.IsSigned(resourcePath).Should().BeTrue();
        }
 
        [Fact]
        public void CannotSignWithTheEcmaKey()
        {
            // Using stream variant so that legacy fallback doesn't swallow the exception.
            using (var inputStream = File.OpenRead(GetResourcePath("StrongNamedWithEcmaKey.dll")))
            {
                Action shouldFail = () => StrongName.Sign(inputStream, GetResourcePath("OpenSignedCorrespondingKey.snk"));
                shouldFail.Should().Throw<NotImplementedException>();
            }
        }
 
        [Fact]
        public void DelaySignedBinaryFailsToSignWithDifferentKey()
        {
            // Using stream variant so that legacy fallback doesn't swallow the exception.
            using (var inputStream = File.OpenRead(GetResourcePath("DelaySigned.dll")))
            {
                Action shouldFail = () => StrongName.Sign(inputStream, GetResourcePath("OpenSignedCorrespondingKey.snk"));
                shouldFail.Should().Throw<InvalidOperationException>();
            }
        }
    }
}