diff --git a/README.md b/README.md index 5a219e5..a8cc234 100644 --- a/README.md +++ b/README.md @@ -41,16 +41,21 @@ In order to use the antimalware scanning capabilities, ensure you have a ByteGua ### Basic validation ```csharp +// Without antimalware scanner var configuration = new FileValidatorConfiguration { SupportedFileTypes = [FileExtensions.Pdf, FileExtensions.Jpg, FileExtensions.Png], FileSizeLimit = ByteSize.MegaBytes(25), - ThrowExceptionOnInvalidFile = false, - AntimalwareScanner = new SpecificAntimalwareScanner(scannerOptions) + ThrowExceptionOnInvalidFile = false }; var fileValidator = new FileValidator(configuration); var isValid = fileValidator.IsValidFile("example.pdf", fileStream); + +// With antimalware +var antimalwareScanner = AntimalwareScannerImplementation(); +var fileValidator = new FileValidator(configuration, antimalwareScanner); +var isValid = fileValidator.IsValidFile("example.pdf", fileStream); ``` ### Using the fluent builder @@ -60,7 +65,6 @@ var configuration = new FileValidatorConfigurationBuilder() .AllowFileTypes(FileExtensions.Pdf, FileExtensions.Jpg, FileExtensions.Png) .SetFileSizeLimit(ByteSize.MegaBytes(25)) .SetThrowExceptionOnInvalidFile(false) - .AddAntimalwareScanner(new SpecificAntimalwareScanner(scannerOptions)) .Build(); var fileValidator = new FileValidator(configuration); @@ -96,15 +100,16 @@ public async Task Upload(IFormFile file) { using var stream = file.OpenReadStream(); + var antimalwareScanner = AntimalwareScannerImplementation(); + var configuration = new FileValidatorConfiguration { SupportedFileTypes = [FileExtensions.Pdf, FileExtensions.Docx], FileSizeLimit = ByteSize.MegaBytes(10), - ThrowExceptionOnInvalidFile = false, - AntimalwareScanner = new SpecificAntimalwareScanner(scannerOptions) + ThrowExceptionOnInvalidFile = false }; - var validator = new FileValidator(configuration); + var validator = new FileValidator(configuration, antimalwareScanner); if (!validator.IsValidFile(file.FileName, stream)) { @@ -170,7 +175,6 @@ The `FileValidatorConfiguration` supports: | `SupportedFileTypes` | Yes | N/A | A list of allowed file extensions (e.g., `.pdf`, `.jpg`).
Use the predefined constants in `FileExtensions` for supported types. | | `FileSizeLimit` | Yes | N/A | Maximum permitted size of files.
Use the static `ByteSize` class provided with this package, to simplify your limit. | | `ThrowExceptionOnInvalidFile` | No | `true` | Whether to throw an exception on invalid files or return `false`. | -| `AntimalwareScanner` | No | N/A | An antimalware scanner used to scan the given file for potential malware. | ### Exceptions diff --git a/src/ByteGuard.FileValidator/Configuration/FileValidatorConfiguration.cs b/src/ByteGuard.FileValidator/Configuration/FileValidatorConfiguration.cs index 25390af..262cf8e 100644 --- a/src/ByteGuard.FileValidator/Configuration/FileValidatorConfiguration.cs +++ b/src/ByteGuard.FileValidator/Configuration/FileValidatorConfiguration.cs @@ -29,10 +29,5 @@ public class FileValidatorConfiguration /// Whether to throw an exception if an unsupported/invalid file is encountered. Defaults to true. /// public bool ThrowExceptionOnInvalidFile { get; set; } = true; - - /// - /// Optional antimalware scanner to use during file validation. - /// - public IAntimalwareScanner? AntimalwareScanner { get; set; } = null; } } diff --git a/src/ByteGuard.FileValidator/Configuration/FileValidatorConfigurationBuilder.cs b/src/ByteGuard.FileValidator/Configuration/FileValidatorConfigurationBuilder.cs index 2d6060c..670785d 100644 --- a/src/ByteGuard.FileValidator/Configuration/FileValidatorConfigurationBuilder.cs +++ b/src/ByteGuard.FileValidator/Configuration/FileValidatorConfigurationBuilder.cs @@ -10,7 +10,6 @@ public class FileValidatorConfigurationBuilder private readonly List supportedFileTypes = new List(); private bool throwOnInvalidFiles = true; private long fileSizeLimit = ByteSize.MegaBytes(25); - private IAntimalwareScanner? antimalwareScanner = null; /// /// Allow specific file types (extensions) to be validated. @@ -44,22 +43,6 @@ public FileValidatorConfigurationBuilder SetFileSizeLimit(long inFileSizeLimit) return this; } - /// - /// Add an antimalware scanner. - /// - /// Antimalware scanner to use. - /// Thrown when the provided scanner is null. - public FileValidatorConfigurationBuilder AddAntimalwareScanner(IAntimalwareScanner scanner) - { - if (scanner == null) - { - throw new ArgumentNullException(nameof(scanner)); - } - - antimalwareScanner = scanner; - return this; - } - /// /// Build configuration. /// @@ -70,8 +53,7 @@ public FileValidatorConfiguration Build() { SupportedFileTypes = supportedFileTypes, ThrowExceptionOnInvalidFile = throwOnInvalidFiles, - FileSizeLimit = fileSizeLimit, - AntimalwareScanner = antimalwareScanner + FileSizeLimit = fileSizeLimit }; ConfigurationValidator.ThrowIfInvalid(configuration); diff --git a/src/ByteGuard.FileValidator/FileValidator.cs b/src/ByteGuard.FileValidator/FileValidator.cs index 2fe58e2..1618896 100644 --- a/src/ByteGuard.FileValidator/FileValidator.cs +++ b/src/ByteGuard.FileValidator/FileValidator.cs @@ -3,6 +3,7 @@ using ByteGuard.FileValidator.Exceptions; using ByteGuard.FileValidator.Models; using ByteGuard.FileValidator.Validators; +using ByteGuard.FileValidator.Scanners; namespace ByteGuard.FileValidator { @@ -239,10 +240,15 @@ public class FileValidator /// private readonly FileValidatorConfiguration _configuration; + /// + /// Antimalware scanner instance. + /// + private readonly IAntimalwareScanner? _antimalwareScanner; + /// /// Instantiate a new instance of the file validator. /// - /// Configuration including which files should be supported and whether an exception should be thrown when encountering an invalid file. + /// Validator configuration. public FileValidator(FileValidatorConfiguration configuration) { ConfigurationValidator.ThrowIfInvalid(configuration); @@ -250,6 +256,22 @@ public FileValidator(FileValidatorConfiguration configuration) _configuration = configuration; } + /// + /// Instantiate a new instance of the file validator. + /// + /// Validator configuration. + /// Antimalware scanner to use during file validation. + public FileValidator(FileValidatorConfiguration configuration, IAntimalwareScanner antimalwareScanner) + : this(configuration) + { + if (antimalwareScanner == null) + { + throw new ArgumentNullException(nameof(antimalwareScanner), "Antimalware scanner cannot be null."); + } + + _antimalwareScanner = antimalwareScanner; + } + /// /// Get all supported file types based on the current configuration. /// @@ -326,7 +348,7 @@ public bool IsValidFile(string fileName, byte[] content) } // Validate antimalware scan if configured. - if (_configuration.AntimalwareScanner != null) + if (_antimalwareScanner != null) { var isClean = IsMalwareClean(fileName, content); if (!isClean) @@ -988,7 +1010,7 @@ public bool IsValidOpenDocumentFormat(string filePath) /// Thrown if the configured antimalware scanner encountered an error while scanning the file for malware. public bool IsMalwareClean(string fileName, byte[] content) { - if (_configuration.AntimalwareScanner is null) + if (_antimalwareScanner is null) { throw new InvalidOperationException("No antimalware scanner has been configured for the FileValidator."); } @@ -1010,7 +1032,7 @@ public bool IsMalwareClean(string fileName, byte[] content) /// Thrown if the configured antimalware scanner encountered an error while scanning the file for malware. public bool IsMalwareClean(string fileName, Stream stream) { - if (_configuration.AntimalwareScanner is null) + if (_antimalwareScanner is null) { throw new InvalidOperationException("No antimalware scanner has been configured for the FileValidator."); } @@ -1020,7 +1042,7 @@ public bool IsMalwareClean(string fileName, Stream stream) bool isClean; try { - isClean = _configuration.AntimalwareScanner.IsClean(stream, fileName); + isClean = _antimalwareScanner.IsClean(stream, fileName); } catch (Exception ex) { @@ -1051,7 +1073,7 @@ public bool IsMalwareClean(string fileName, Stream stream) /// Thrown if the configured antimalware scanner encountered an error while scanning the file for malware. public bool IsMalwareClean(string filePath) { - if (_configuration.AntimalwareScanner is null) + if (_antimalwareScanner is null) { throw new InvalidOperationException("No antimalware scanner has been configured for the FileValidator."); } diff --git a/tests/ByteGuard.FileValidator.Tests.Unit/FileValidatorTests.cs b/tests/ByteGuard.FileValidator.Tests.Unit/FileValidatorTests.cs index 97644cc..f1cf31d 100644 --- a/tests/ByteGuard.FileValidator.Tests.Unit/FileValidatorTests.cs +++ b/tests/ByteGuard.FileValidator.Tests.Unit/FileValidatorTests.cs @@ -1073,10 +1073,9 @@ public void IsValidFile_AntimalwareScannerDetectsMalwareAndThrowExceptionOnInval { SupportedFileTypes = [".pdf"], FileSizeLimit = ByteSize.MegaBytes(25), - ThrowExceptionOnInvalidFile = true, - AntimalwareScanner = mockAntimalwareScanner + ThrowExceptionOnInvalidFile = true }; - var fileValidator = new FileValidator(config); + var fileValidator = new FileValidator(config, mockAntimalwareScanner); var fileBytes = new byte[] { 0x25, 0x50, 0x44, 0x46, 0x2D }; // Valid PDF signature // Act @@ -1097,10 +1096,9 @@ public void IsValidFile_AntimalwareScannerDetectsMalwareAndThrowExceptionOnInval { SupportedFileTypes = [".pdf"], FileSizeLimit = ByteSize.MegaBytes(25), - ThrowExceptionOnInvalidFile = false, - AntimalwareScanner = mockAntimalwareScanner + ThrowExceptionOnInvalidFile = false }; - var fileValidator = new FileValidator(config); + var fileValidator = new FileValidator(config, mockAntimalwareScanner); var fileBytes = new byte[] { 0x25, 0x50, 0x44, 0x46, 0x2D }; // Valid PDF signature // Act