diff --git a/docs/ValidationRules/RULEID.RULEFRIENDLYNAME.cs b/docs/ValidationRules/RULEID.RULEFRIENDLYNAME.cs index 047318608..4f5448581 100644 --- a/docs/ValidationRules/RULEID.RULEFRIENDLYNAME.cs +++ b/docs/ValidationRules/RULEID.RULEFRIENDLYNAME.cs @@ -87,7 +87,7 @@ public class RULEFRIENDLYNAME : SarifValidationSkimmerBase */ public override string Id => RuleId.RULEFRIENDLYNAME; - protected override IEnumerable MessageResourceNames => new string[] + protected override IEnumerable MessageResourceNames => new string [] { /* * INSTRUCTIONS: diff --git a/src/Sarif.Driver/DriverExtensionMethods.cs b/src/Sarif.Driver/DriverExtensionMethods.cs index 2f6f8743b..6b9ecd15c 100644 --- a/src/Sarif.Driver/DriverExtensionMethods.cs +++ b/src/Sarif.Driver/DriverExtensionMethods.cs @@ -10,7 +10,39 @@ namespace Microsoft.CodeAnalysis.Sarif.Driver { public static class DriverExtensionMethods { - /// + + public static bool IsArchiveByFileExtension(this string extension) + { + if (string.IsNullOrWhiteSpace(extension)) + { + return false; + } + + /* + * Open Packaging Conventions (OPC) files, which are essentially ZIP files with a specific structure. + * + * .zip: Standard ZIP archive files. + * .jar: Java Archive files. + * .nupkg: NuGet package files, which are ZIP files containing .NET libraries and metadata. + * .docx: Microsoft Word documents (Office Open XML format). + * .xlsx: Microsoft Excel spreadsheets (Office Open XML format). + * .pptx: Microsoft PowerPoint presentations (Office Open XML format). + * .appx: Windows application packages. + * .vsix: Visual Studio extension packages + * + */ + + return (extension == ".zip" || + extension == ".jar" || + extension == ".nupkg" || + extension == ".docx" || + extension == ".xlsx" || + extension == ".pptx" || + extension == ".appx" || + extension == ".vsix"); + } + + // /// Ensures the consistency of the command line options related to the location and format /// of the output file, and adjusts the options for ease of use. /// diff --git a/src/Sarif.Driver/Sdk/MultithreadedAnalyzeCommandBase.cs b/src/Sarif.Driver/Sdk/MultithreadedAnalyzeCommandBase.cs index 704dc6291..792b57bd6 100644 --- a/src/Sarif.Driver/Sdk/MultithreadedAnalyzeCommandBase.cs +++ b/src/Sarif.Driver/Sdk/MultithreadedAnalyzeCommandBase.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Diagnostics.Tracing; using System.IO; +using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Http; @@ -583,6 +584,23 @@ private static void LogCachingLogger(TContext globalContext, CachingLogger cachi currentResult = clonedResult; } + + if (globalContext.CurrentTarget.BaseUri != null) + { + currentResult.Locations[0].LogicalLocations = new[] { + new LogicalLocation + { + FullyQualifiedName = globalContext.CurrentTarget.BaseUri.OriginalString, + Kind = LogicalLocationKind.Package, + }, + new LogicalLocation + { + FullyQualifiedName = globalContext.CurrentTarget.Uri.OriginalString, + ParentIndex = 0, + }, + }; + } + globalContext.Logger.FileRegionsCache = cachingLogger.FileRegionsCache; globalContext.Logger.Log(kv.Key, currentResult, tuple.Item2); } @@ -666,6 +684,32 @@ private async Task EnumerateFilesFromArtifactsProvider(TContext globalCont continue; } + if (Path.GetExtension(filePath).IsArchiveByFileExtension()) + { + Stream stream = artifact.Stream; + stream ??= FileSystem.FileOpenRead(filePath); + ZipArchive archive; + + try + { + archive = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: true); + } + catch (InvalidDataException) + { + // TBD log corrupt zip file. + continue; + } + + var archiveArtifactProvider = new ThreadsafeZipArtifactProvider(archive, + globalContext.FileSystem, + artifact.Uri); + + TContext archiveContext = CreateScanTargetContext(globalContext); + archiveContext.TargetsProvider = archiveArtifactProvider; + await EnumerateFilesFromArtifactsProvider(archiveContext); + continue; + } + if (artifact.SizeInBytes == 0) { DriverEventSource.Log.ArtifactNotScanned(filePath, DriverEventNames.EmptyFile, 00, data2: null); diff --git a/src/Sarif/ArtifactProvider.cs b/src/Sarif/ArtifactProvider.cs index ccc9489ba..c2bd2ae17 100644 --- a/src/Sarif/ArtifactProvider.cs +++ b/src/Sarif/ArtifactProvider.cs @@ -1,24 +1,34 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; using System.Collections.Generic; namespace Microsoft.CodeAnalysis.Sarif { public class ArtifactProvider : IArtifactProvider { - internal ArtifactProvider(IFileSystem fileSystem) + private readonly Uri uri; + + internal ArtifactProvider(IFileSystem fileSystem, Uri uri = null) { FileSystem = fileSystem; + this.uri = uri; } - public ArtifactProvider(IEnumerable artifacts) + public ArtifactProvider(IEnumerable artifacts) : this(fileSystem: null, uri: null) { Artifacts = new List(artifacts); } + public Uri Uri => this.uri; + public virtual IEnumerable Artifacts { get; set; } public IFileSystem FileSystem { get; set; } + + public virtual bool Succeeded => UnhandledException == null; + + public virtual Exception UnhandledException { get; set; } } } diff --git a/src/Sarif/EnumeratedArtifact.cs b/src/Sarif/EnumeratedArtifact.cs index 3eb59283c..60a5806cf 100644 --- a/src/Sarif/EnumeratedArtifact.cs +++ b/src/Sarif/EnumeratedArtifact.cs @@ -23,6 +23,8 @@ public EnumeratedArtifact(IFileSystem fileSystem) private Encoding encoding; + public Uri BaseUri { get; set; } + public Uri Uri { get; set; } public bool IsBinary diff --git a/src/Sarif/IEnumeratedArtifact.cs b/src/Sarif/IEnumeratedArtifact.cs index 9044e4e94..e20f508a4 100644 --- a/src/Sarif/IEnumeratedArtifact.cs +++ b/src/Sarif/IEnumeratedArtifact.cs @@ -9,6 +9,8 @@ namespace Microsoft.CodeAnalysis.Sarif { public interface IEnumeratedArtifact { + Uri BaseUri { get; } + Uri Uri { get; } bool IsBinary { get; } diff --git a/src/Sarif/MultithreadedZipArchiveArtifactProvider.cs b/src/Sarif/MultithreadedZipArchiveArtifactProvider.cs deleted file mode 100644 index 4fcb8d6d7..000000000 --- a/src/Sarif/MultithreadedZipArchiveArtifactProvider.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.IO.Compression; - -namespace Microsoft.CodeAnalysis.Sarif -{ - public class MultithreadedZipArchiveArtifactProvider : ArtifactProvider - { - private readonly ZipArchive zipArchive; - private ISet binaryExtensions; - - public ISet BinaryExtensions - { - get - { - this.binaryExtensions ??= CreateDefaultBinaryExtensionsSet(); - return this.binaryExtensions; - } - - set { this.binaryExtensions = value; } - } - - public MultithreadedZipArchiveArtifactProvider(ZipArchive zipArchive, IFileSystem fileSystem) : base(fileSystem) - { - this.zipArchive = zipArchive; - } - - public ISet CreateDefaultBinaryExtensionsSet() - { - - ISet result = new HashSet(StringComparer.OrdinalIgnoreCase); - - result.Add(".bmp"); - result.Add(".cab"); - result.Add(".cer"); - result.Add(".der"); - result.Add(".dll"); - result.Add(".exe"); - result.Add(".gif"); - result.Add(".gz"); - result.Add(".iso"); - result.Add(".jpe"); - result.Add(".jpeg"); - result.Add(".lock"); - result.Add(".p12"); - result.Add(".pack"); - result.Add(".pfx"); - result.Add(".pkcs12"); - result.Add(".png"); - result.Add(".psd"); - result.Add(".rar"); - result.Add(".tar"); - result.Add(".tif"); - result.Add(".tiff"); - result.Add(".xcf"); - result.Add(".zip"); - - return result; - } - - public override IEnumerable Artifacts - { - get - { - foreach (ZipArchiveEntry entry in this.zipArchive.Entries) - { - yield return new ZipArchiveArtifact(this.zipArchive, entry, BinaryExtensions); - } - } - } - } - -} diff --git a/src/Sarif/SinglethreadedZipArchiveArtifactProvider.cs b/src/Sarif/SinglethreadedZipArchiveArtifactProvider.cs deleted file mode 100644 index 922034e91..000000000 --- a/src/Sarif/SinglethreadedZipArchiveArtifactProvider.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.IO.Compression; - -namespace Microsoft.CodeAnalysis.Sarif -{ - public class SinglethreadedZipArchiveArtifactProvider : ArtifactProvider - { - public SinglethreadedZipArchiveArtifactProvider(ZipArchive zipArchive, IFileSystem fileSystem) : base(fileSystem) - { - var artifacts = new List(); - - foreach (ZipArchiveEntry entry in zipArchive.Entries) - { - var artifact = new EnumeratedArtifact(Sarif.FileSystem.Instance) - { - Uri = new Uri(entry.FullName, UriKind.RelativeOrAbsolute), - Stream = entry.Open(), - }; - - // This step will fault in all artifact contents, whether textual - // or binary, and clear the zip stream, which is the point. - artifact.Contents = artifact.Contents; - - artifacts.Add(artifact); - } - - Artifacts = artifacts; - } - } -} diff --git a/src/Sarif/ThreadsafeZipArtifactProvider.cs b/src/Sarif/ThreadsafeZipArtifactProvider.cs new file mode 100644 index 000000000..22764ca6e --- /dev/null +++ b/src/Sarif/ThreadsafeZipArtifactProvider.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; + +namespace Microsoft.CodeAnalysis.Sarif +{ + /// + /// A thread-safe implementation of that can be used to + /// enumerate scan targets from a zip archive. The class is thread-safe in that the + /// containing archive instance is passed to the each constituent archive element. + /// This object is used to synchronize decompressing the archive elements (which + /// itself is not a thread-safe operation). + /// + public class ThreadsafeZipArtifactProvider : ArtifactProvider + { + private readonly ZipArchive zipArchive; + + public ThreadsafeZipArtifactProvider(ZipArchive zipArchive, IFileSystem fileSystem, Uri uri = null) : base(fileSystem, uri) + { + this.zipArchive = zipArchive; + this.FileSystem = fileSystem; + } + + private static readonly ISet s_extensionsDenyList = + new HashSet(StringComparer.OrdinalIgnoreCase) + { + ".bmp", + ".cab", + ".cer", + ".der", + ".dll", + ".exe", + ".gif", + ".gz", + ".iso", + ".jpe", + ".jpeg", + ".lock", + ".p12", + ".pack", + ".pfx", + ".pkcs12", + ".png", + ".psd", + ".rar", + ".tar", + ".tif", + ".tiff", + ".xcf", + "." + }; + + public override IEnumerable Artifacts + { + get + { + System.Collections.ObjectModel.ReadOnlyCollection entries = null; + try + { + entries = this.zipArchive.Entries; + } + catch (InvalidDataException ex) + { + UnhandledException = ex; + } + + if (entries == null) { yield break; } + + foreach (ZipArchiveEntry entry in entries) + { + if (entry.FullName.EndsWith("/") && entry.CompressedLength == 0) + { + // We have a directory entry. We don't want to return these, + // as they can't be analyzed and will simply generate + // 'zero-byte file encountered' warnings. + continue; + } + + yield return new ZipArchiveArtifact(this.zipArchive, entry, s_extensionsDenyList, this.Uri); + } + } + } + } +} diff --git a/src/Sarif/Writers/ConsoleLogger.cs b/src/Sarif/Writers/ConsoleLogger.cs index 3dfb343d3..39dfa072c 100644 --- a/src/Sarif/Writers/ConsoleLogger.cs +++ b/src/Sarif/Writers/ConsoleLogger.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; @@ -117,22 +118,40 @@ public void Log(ReportingDescriptor rule, Result result, int? extensionIndex = n // TODO we need better retrieval for locations than these defaults. // Note that we can potentially emit many messages from a single result. - PhysicalLocation physicalLocation = result.Locations?.First().PhysicalLocation; - - WriteLineToConsole(GetMessageText(_toolName, physicalLocation?.ArtifactLocation?.Uri, physicalLocation?.Region, result.RuleId, message, result.Kind, result.Level)); + Location location = result.Locations?.First(); + PhysicalLocation physicalLocation = location?.PhysicalLocation; + + string path = location.LogicalLocations == null + ? ConstructPathFromUri(physicalLocation?.ArtifactLocation?.Uri) + : ConstructPackagePath(location.LogicalLocations); + + message = GetMessageText(_toolName, + path, + physicalLocation?.Region, + result.RuleId, + message, + result.Kind, + result.Level); + + WriteLineToConsole(message); } - public static string GetMessageText( - string toolName, - Uri uri, - Region region, - string ruleId, - string message, - ResultKind kind, - FailureLevel level) + private string ConstructPackagePath(IList logicalLocations) { - string path = ConstructPathFromUri(uri); + string packageUri = logicalLocations[0].FullyQualifiedName; + string entryUri = logicalLocations[1].FullyQualifiedName; + + return $"[{packageUri}] {entryUri}"; + } + public static string GetMessageText(string toolName, + string path, + Region region, + string ruleId, + string message, + ResultKind kind, + FailureLevel level) + { string issueType = null; switch (level) diff --git a/src/Sarif/Writers/SarifLogger.cs b/src/Sarif/Writers/SarifLogger.cs index 2544991f1..70463d3c7 100644 --- a/src/Sarif/Writers/SarifLogger.cs +++ b/src/Sarif/Writers/SarifLogger.cs @@ -120,7 +120,15 @@ public SarifLogger(TextWriter textWriter, _run.Tool ??= Tool.CreateFromAssemblyData(); _dataToInsert = dataToInsert; _dataToRemove = dataToRemove; - _issueLogJsonWriter.Initialize(_run); + + + if (_issueLogJsonWriter != null) + { + lock (_issueLogJsonWriter) + { + _issueLogJsonWriter?.Initialize(_run); + } + } // Map existing Rules to ensure duplicates aren't created if (_run.Tool.Extensions != null) @@ -295,17 +303,23 @@ public virtual void Dispose() // still needs to be disposed or closed to write the results if (_issueLogJsonWriter != null) { - _issueLogJsonWriter.CloseResults(); - - if (_run?.Invocations?.Count > 0 && _run.Invocations[0].StartTimeUtc != new DateTime() && - !_dataToRemove.HasFlag(OptionallyEmittedData.NondeterministicProperties)) + lock (_issueLogJsonWriter) { - _run.Invocations[0].EndTimeUtc = DateTime.UtcNow; - } + if (_issueLogJsonWriter != null) + { + _issueLogJsonWriter.CloseResults(); + + if (_run?.Invocations?.Count > 0 && _run.Invocations[0].StartTimeUtc != new DateTime() && + !_dataToRemove.HasFlag(OptionallyEmittedData.NondeterministicProperties)) + { + _run.Invocations[0].EndTimeUtc = DateTime.UtcNow; + } - _issueLogJsonWriter.CompleteRun(); - _issueLogJsonWriter.Dispose(); - _issueLogJsonWriter = null; + _issueLogJsonWriter.CompleteRun(); + _issueLogJsonWriter.Dispose(); + _issueLogJsonWriter = null; + } + } } if (_closeWriterOnDispose) @@ -322,7 +336,13 @@ public virtual void Dispose() public void AnalysisStarted() { - _issueLogJsonWriter.OpenResults(); + if (_issueLogJsonWriter != null) + { + lock (_issueLogJsonWriter) + { + _issueLogJsonWriter?.OpenResults(); + } + } } public void AnalysisStopped(RuntimeConditions runtimeConditions) @@ -376,7 +396,13 @@ public void Log(ReportingDescriptor rule, Result result, int? extensionIndex) _insertOptionalDataVisitor?.Visit(result); - _issueLogJsonWriter.WriteResult(result); + if (_issueLogJsonWriter != null) + { + lock (_issueLogJsonWriter) + { + _issueLogJsonWriter?.WriteResult(result); + } + } } private int LogRule(ReportingDescriptor rule) diff --git a/src/Sarif/ZipArchiveArtifact.cs b/src/Sarif/ZipArchiveArtifact.cs index 208b8eec6..c660dccd1 100644 --- a/src/Sarif/ZipArchiveArtifact.cs +++ b/src/Sarif/ZipArchiveArtifact.cs @@ -9,26 +9,28 @@ namespace Microsoft.CodeAnalysis.Sarif { - public class ZipArchiveArtifact : IEnumeratedArtifact { private readonly ISet binaryExtensions; private readonly ZipArchive archive; private ZipArchiveEntry entry; - private readonly Uri uri; private string contents; private byte[] bytes; - public ZipArchiveArtifact(ZipArchive archive, ZipArchiveEntry entry, ISet binaryExtensions = null) + public ZipArchiveArtifact(ZipArchive archive, ZipArchiveEntry entry, ISet binaryExtensions = null, Uri archiveUri = null) { this.entry = entry ?? throw new ArgumentNullException(nameof(entry)); this.archive = archive ?? throw new ArgumentNullException(nameof(archive)); this.binaryExtensions = binaryExtensions ?? new HashSet(); - this.uri = new Uri(entry.FullName, UriKind.RelativeOrAbsolute); + + this.BaseUri = archiveUri; + this.Uri = new Uri(entry.FullName, UriKind.RelativeOrAbsolute); } - public Uri Uri => this.uri; + public Uri BaseUri { get; set; } + + public Uri Uri { get; set; } public bool IsBinary { @@ -50,7 +52,14 @@ public Stream Stream lock (this.archive) { - return entry.Open(); + try + { + return entry.Open(); + } + catch (InvalidDataException) + { + return new MemoryStream(); + } } } set => throw new NotImplementedException(); @@ -95,7 +104,16 @@ public byte[] Bytes var peekable = new PeekableStream(this.Stream, PeekWindowBytes); byte[] header = new byte[PeekWindowBytes]; - int readLength = this.Stream.Read(header, 0, header.Length); + int readLength = 0; + + try + { + readLength = this.Stream.Read(header, 0, header.Length); + } + catch (InvalidDataException) + { + return (string.Empty, Array.Empty()); + } bool isText = FileEncoding.IsTextualData(header, 0, readLength); diff --git a/src/Test.UnitTests.Sarif.Driver/Sdk/AnalyzeCommandBaseTests.cs b/src/Test.UnitTests.Sarif.Driver/Sdk/AnalyzeCommandBaseTests.cs index 3f810208b..93bceb3c9 100644 --- a/src/Test.UnitTests.Sarif.Driver/Sdk/AnalyzeCommandBaseTests.cs +++ b/src/Test.UnitTests.Sarif.Driver/Sdk/AnalyzeCommandBaseTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; @@ -2143,11 +2144,27 @@ private static IFileSystem CreateDefaultFileSystemForResultsCaching(IList x.FileReadAllText(It.Is(f => f == fullyQualifiedName))).Returns(logFileContents); mockFileSystem.Setup(x => x.FileOpenRead(It.Is(f => f == fullyQualifiedName))) - .Returns(new NonDisposingDelegatingStream(new MemoryStream(Encoding.UTF8.GetBytes(generateSameInput ? logFileContents : fileNameWithoutExtension)))); + .Returns(Path.GetExtension(fullyQualifiedName).IsArchiveByFileExtension() + ? OpenTestZipArchiveStream(fileNameWithoutExtension, generateSameInput ? logFileContents : fileNameWithoutExtension) + : new NonDisposingDelegatingStream(new MemoryStream(Encoding.UTF8.GetBytes(generateSameInput ? logFileContents : fileNameWithoutExtension)))); } return mockFileSystem.Object; } + private static Stream OpenTestZipArchiveStream(string fileNameWithoutExtension, string logFileContents) + { + var stream = new MemoryStream(); + using var populateArchive = new ZipArchive(stream, ZipArchiveMode.Create, leaveOpen: true); + + ZipArchiveEntry entry = populateArchive.CreateEntry(fileNameWithoutExtension, CompressionLevel.NoCompression); + using (var errorWriter = new StreamWriter(entry.Open())) + { + errorWriter.WriteLine(logFileContents); + } + + stream.Position = 0; + return stream; + } private static Run RunAnalyzeCommand(TestAnalyzeOptions options, ResultsCachingTestCase testCase) { diff --git a/src/Test.UnitTests.Sarif/ArtifactProviderTests.cs b/src/Test.UnitTests.Sarif/ArtifactProviderTests.cs index 3e14ab2ec..f3cc1d920 100644 --- a/src/Test.UnitTests.Sarif/ArtifactProviderTests.cs +++ b/src/Test.UnitTests.Sarif/ArtifactProviderTests.cs @@ -24,7 +24,7 @@ public void MultithreadedZipArchiveArtifactProvider_RetrieveSizeInBytesBeforeRet { string entryContents = $"{Guid.NewGuid}"; ZipArchive zip = CreateZipArchiveWithTextContents("test.txt", entryContents); - var artifactProvider = new MultithreadedZipArchiveArtifactProvider(zip, FileSystem.Instance); + var artifactProvider = new ThreadsafeZipArtifactProvider(zip, FileSystem.Instance); ValidateTextContents(artifactProvider.Artifacts, entryContents); } @@ -42,7 +42,7 @@ public void MultithreadedZipArchiveArtifactProvider_RetrieveSizeInBytesBeforeRet // Note that even thought we populate an archive with binary contents, the extension // of the archive entry indicates a text file. We still expect binary data on expansion. ZipArchive zip = CreateZipArchiveWithBinaryContents("test.txt", data); - var artifactProvider = new MultithreadedZipArchiveArtifactProvider(zip, FileSystem.Instance); + var artifactProvider = new ThreadsafeZipArtifactProvider(zip, FileSystem.Instance); ValidateBinaryContents(artifactProvider.Artifacts, data); } @@ -52,7 +52,7 @@ public void MultithreadedZipArchiveArtifactProvider_RetrieveSizeInBytesAfterRetr { string entryContents = $"{Guid.NewGuid()}"; ZipArchive zip = CreateZipArchiveWithTextContents("test.txt", entryContents); - var artifactProvider = new MultithreadedZipArchiveArtifactProvider(zip, FileSystem.Instance); + var artifactProvider = new ThreadsafeZipArtifactProvider(zip, FileSystem.Instance); ValidateTextContents(artifactProvider.Artifacts, entryContents); } @@ -68,7 +68,7 @@ public void MultithreadedZipArchiveArtifactProvider_RetrieveSizeInBytesAfterRetr reader.Read(data, 0, data.Length); ZipArchive zip = CreateZipArchiveWithBinaryContents("test.dll", data); - var artifactProvider = new MultithreadedZipArchiveArtifactProvider(zip, FileSystem.Instance); + var artifactProvider = new ThreadsafeZipArtifactProvider(zip, FileSystem.Instance); foreach (IEnumeratedArtifact artifact in artifactProvider.Artifacts) { artifact.Bytes.Should().NotBeNull(); @@ -84,7 +84,7 @@ public void MultithreadedZipArchiveArtifactProvider_SizeInBytesAndContentsAreAva { string entryContents = $"{Guid.NewGuid()}"; ZipArchive zip = CreateZipArchiveWithTextContents("test.csv", entryContents); - var artifactProvider = new MultithreadedZipArchiveArtifactProvider(zip, FileSystem.Instance); + var artifactProvider = new ThreadsafeZipArtifactProvider(zip, FileSystem.Instance); ValidateTextContents(artifactProvider.Artifacts, entryContents); } @@ -97,7 +97,7 @@ public void MultithreadedZipArchiveArtifact_IllegalFileAndPathCharacters() string filePath = $"{Path.GetInvalidPathChars()[0]}{Path.GetInvalidFileNameChars()[1]}MyZippedFile.txt"; ZipArchive zip = EnumeratedArtifactTests.CreateZipArchive(filePath, contents); - foreach (IEnumeratedArtifact entry in new MultithreadedZipArchiveArtifactProvider(zip, new FileSystem()).Artifacts) + foreach (IEnumeratedArtifact entry in new ThreadsafeZipArtifactProvider(zip, new FileSystem()).Artifacts) { entry.IsBinary.Should().BeFalse(); entry.Contents.Should().BeEquivalentTo(text);