diff --git a/Directory.Version.props b/Directory.Version.props
index 317e204..191820c 100644
--- a/Directory.Version.props
+++ b/Directory.Version.props
@@ -1,6 +1,6 @@
- 10.0.0
+ 10.1.0
\ No newline at end of file
diff --git a/README.md b/README.md
index 8bee196..29a3008 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
It gives you:
- Extension methods to register the file validator in the DI container
-- Easy configuration via appsettings.json or fluent configuration in code
+- Easy configuration via `appsettings.json` or fluent configuration in code
> This package is the `Microsoft.Extensions.DependencyInjection` integration layer.
> The core validation logic lives in [`ByteGuard.FileValidator`](https://github.com/ByteGuard-HQ/byteguard-file-validator-net).
@@ -12,7 +12,7 @@ It gives you:
## Getting Started
### Installation
-This package is published and installed via NuGet.
+This package is published and installed via [NuGet](https://www.nuget.org/packages/ByteGuard.FileValidator.Extensions.DependencyInjection).
Reference the package in your project:
```bash
@@ -24,20 +24,31 @@ dotnet add package ByteGuard.FileValidator.Extensions.DependencyInjection
### Add to DI container
In your `Program.cs` (or `Startup.cs` in older projects), register the validator:
+**Using inline configuration**
```csharp
-using ByteGuard.FileValidator;
-using ByteGuard.FileValidator.Extensions.DependencyInjection;
-
-// Using inline configuration
builder.Services.AddFileValidator(options =>
{
options.AllowFileTypes(FileExtensions.Pdf, FileExtensions.Jpg, FileExtensions.Png);
options.FileSizeLimit = ByteSize.MegaBytes(25);
options.ThrowOnInvalidFiles(false);
+
+ // If an antimalware package has been added
+ options.Scanner = ScannerRegistration.Create(opts =>
+ {
+ // Refer to the individual scanner implementations for ScannerType value and possible options.
+ // ...
+ })
});
+```
-// Using configuration from appsettings.json
-builder.Services.AddFileValidator(options => configuration.GetSection("FileValidatorConfiguration").Bind(options));
+**Using configuration from appsettings.json with default "FileValidator" section name**
+```csharp
+builder.Services.AddFileValidator(builder.Configuration);
+```
+
+**Using configuration from appsettings.json with custom section name**
+```csharp
+builder.Services.AddFileValidator(builder.Configuration, "MySection");
```
### Injection & Usage
@@ -79,5 +90,28 @@ It's possible to configure the `FileValidator` through `appsettings.json`.
}
```
+**With antimalware scanner**
+It's possible to configure an antimalware scanner directly through `appsettings.json`.
+
+> ℹ️ _Refer to the individual scanner implementations for `ScannerType` value and possible options._
+
+```json
+{
+ "FileValidatorConfiguration": {
+ "SupportedFileTypes": [ ".pdf", ".jpg", ".png" ],
+ "FileSizeLimit": 26214400,
+ "UnitFileSizeLimit": "25MB",
+ "ThrowExceptionOnInvalidFile": true,
+ "Scanner": {
+ "ScannerType": "...",
+ "Options": {
+ "OptionA": "...",
+ "OptionB": "..."
+ }
+ }
+ }
+}
+```
+
## License
_ByteGuard.FileValidator.Extensions.DpendencyInjection is Copyright © ByteGuard Contributors - Provided under the MIT license._
\ No newline at end of file
diff --git a/assets/icon.png b/assets/icon.png
new file mode 100644
index 0000000..8cc0687
Binary files /dev/null and b/assets/icon.png differ
diff --git a/src/ByteGuard.FileValidator.Extensions.DependencyInjection/ByteGuard.FileValidator.Extensions.DependencyInjection.csproj b/src/ByteGuard.FileValidator.Extensions.DependencyInjection/ByteGuard.FileValidator.Extensions.DependencyInjection.csproj
index 7ea2af2..7fa6b5c 100644
--- a/src/ByteGuard.FileValidator.Extensions.DependencyInjection/ByteGuard.FileValidator.Extensions.DependencyInjection.csproj
+++ b/src/ByteGuard.FileValidator.Extensions.DependencyInjection/ByteGuard.FileValidator.Extensions.DependencyInjection.csproj
@@ -9,18 +9,22 @@
git
byteguard file-validator extensions dependency injection
README.md
+ icon.png
Copyright © ByteGuard Contributors
MIT
-
+
+
+
+
diff --git a/src/ByteGuard.FileValidator.Extensions.DependencyInjection/Configuration/FileValidatorSettingsConfiguration.cs b/src/ByteGuard.FileValidator.Extensions.DependencyInjection/Configuration/FileValidatorSettingsConfiguration.cs
index d368d9d..f16d7d7 100644
--- a/src/ByteGuard.FileValidator.Extensions.DependencyInjection/Configuration/FileValidatorSettingsConfiguration.cs
+++ b/src/ByteGuard.FileValidator.Extensions.DependencyInjection/Configuration/FileValidatorSettingsConfiguration.cs
@@ -44,4 +44,9 @@ public class FileValidatorSettingsConfiguration
/// Whether to throw an exception if an unsupported/invalid file is encountered. Defaults to true.
///
public bool ThrowExceptionOnInvalidFile { get; set; } = true;
+
+ ///
+ /// Configuration for the antimalware scanner to use.
+ ///
+ public ScannerRegistration? Scanner { get; set; }
}
diff --git a/src/ByteGuard.FileValidator.Extensions.DependencyInjection/Configuration/ScannerRegistration.cs b/src/ByteGuard.FileValidator.Extensions.DependencyInjection/Configuration/ScannerRegistration.cs
new file mode 100644
index 0000000..9585361
--- /dev/null
+++ b/src/ByteGuard.FileValidator.Extensions.DependencyInjection/Configuration/ScannerRegistration.cs
@@ -0,0 +1,84 @@
+using ByteGuard.FileValidator.Scanners;
+using Microsoft.Extensions.Configuration;
+
+namespace ByteGuard.FileValidator.Extensions.DependencyInjection.Configuration;
+
+///
+/// Registration information for an antimalware scanner.
+///
+public sealed class ScannerRegistration
+{
+ ///
+ /// Type of the scanner to register. Must be derived from IAntimalwareScanner.
+ ///
+ public string ScannerType { get; set; } = default!;
+
+ private Type? _scannerType;
+
+ ///
+ /// Gets or sets the of the scanner.
+ ///
+ public Type Type
+ {
+ get
+ {
+ if (_scannerType is not null)
+ return _scannerType;
+
+ _scannerType = Type.GetType(ScannerType, throwOnError: false)!;
+ return _scannerType;
+ }
+ set
+ {
+ _scannerType = value;
+ ScannerType = value.AssemblyQualifiedName!;
+ }
+ }
+
+ ///
+ /// Options for the scanner.
+ ///
+ public object? OptionsInstance { get; set; }
+
+ ///
+ /// Raw configuration section for options (for appsettings registration).
+ ///
+ public IConfigurationSection? OptionsConfiguration { get; set; }
+
+ ///
+ /// Creates a new instance for the specified scanner type and options.
+ ///
+ /// Scanner options.
+ /// Scanner implementation inheriting from IAntimalwareScanner.
+ /// Scanner options.
+ public static ScannerRegistration Create(Action options)
+ where TScanner : IAntimalwareScanner
+ where TOptions : class, new()
+ {
+ var opts = new TOptions();
+ options?.Invoke(opts);
+
+ return new ScannerRegistration
+ {
+ Type = typeof(TScanner),
+ OptionsInstance = opts
+ };
+ }
+
+ ///
+ /// Creates a new instance for the specified scanner type and options.
+ ///
+ /// Scanner options.
+ /// Scanner implementation inheriting from IAntimalwareScanner.
+ /// Scanner options.
+ public static ScannerRegistration Create(TOptions options)
+ where TScanner : IAntimalwareScanner
+ where TOptions : class, new()
+ {
+ return new ScannerRegistration
+ {
+ Type = typeof(TScanner),
+ OptionsInstance = options
+ };
+ }
+}
diff --git a/src/ByteGuard.FileValidator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/ByteGuard.FileValidator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs
index b755b12..0fd9abb 100644
--- a/src/ByteGuard.FileValidator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs
+++ b/src/ByteGuard.FileValidator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs
@@ -1,5 +1,7 @@
using ByteGuard.FileValidator.Configuration;
using ByteGuard.FileValidator.Extensions.DependencyInjection.Configuration;
+using ByteGuard.FileValidator.Scanners;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@@ -10,44 +12,189 @@ namespace ByteGuard.FileValidator.Extensions.DependencyInjection;
///
public static class ServiceCollectionExtensions
{
+ ///
+ /// The default configuration section name for File Validator settings.
+ ///
+ public const string DefaultSectionName = "FileValidator";
+
+ ///
+ /// Adds the File Validator services to the specified with configuration from the provided .
+ ///
+ ///
+ /// This method binds the configuration section named "FileValidator" by default.
+ ///
+ /// Service collection.
+ /// Configuration.
+ public static IServiceCollection AddFileValidator(this IServiceCollection services, IConfiguration configuration)
+ {
+ return services.AddFileValidator(configuration, DefaultSectionName);
+ }
+
+ ///
+ /// Adds the File Validator services to the specified with configuration from the provided .
+ ///
+ /// Service collection.
+ /// Configuration.
+ /// Section name.
+ public static IServiceCollection AddFileValidator(this IServiceCollection services, IConfiguration configuration, string sectionName)
+ {
+ ArgumentNullException.ThrowIfNull(configuration);
+ ArgumentException.ThrowIfNullOrEmpty(sectionName);
+
+ var section = configuration.GetSection(sectionName);
+ if (section is null)
+ {
+ throw new InvalidOperationException($"Configuration section '{sectionName}' not found.");
+ }
+
+ var settings = new FileValidatorSettingsConfiguration();
+ section.Bind(settings);
+
+ var scannerSection = section.GetSection("Scanner");
+ if (scannerSection.Exists())
+ {
+ var scannerType = scannerSection["ScannerType"];
+ if (string.IsNullOrWhiteSpace(scannerType))
+ {
+ throw new InvalidOperationException("ScannerType must be specified in the configuration section.");
+ }
+
+ settings.Scanner = new ScannerRegistration();
+ settings.Scanner.ScannerType = scannerType;
+ settings.Scanner.OptionsConfiguration = scannerSection.GetSection("Options");
+ }
+
+ ConfigureFromSettings(services, settings);
+ return services;
+ }
+
///
/// Adds the File Validator services to the specified with custom configuration options.
///
/// Service collection.
- /// Configuration options.
+ /// File validator configuration.
public static IServiceCollection AddFileValidator(this IServiceCollection services, Action options)
{
- // Validate and setup configuration options.
- services.AddSingleton,
- FileValidatorConfigurationOptionsValidator>();
+ ArgumentNullException.ThrowIfNull(options);
+
+ var settings = new FileValidatorSettingsConfiguration();
+ options(settings);
- services.Configure(options);
+ ConfigureFromSettings(services, settings);
+ return services;
+ }
+
+ ///
+ /// Configures services from settings.
+ ///
+ /// Service collection.
+ /// File valiator settings.
+ private static void ConfigureFromSettings(IServiceCollection services, FileValidatorSettingsConfiguration settings)
+ {
+ ArgumentNullException.ThrowIfNull(settings);
services.AddOptions()
- .Configure>((cfg, settings) =>
+ .Configure(config =>
{
// Convert from FileValidatorSettingsConfiguration to FileValidatorConfiguration.
- cfg.SupportedFileTypes = settings.Value.SupportedFileTypes;
- cfg.ThrowExceptionOnInvalidFile = settings.Value.ThrowExceptionOnInvalidFile;
+ config.SupportedFileTypes = settings.SupportedFileTypes;
+ config.ThrowExceptionOnInvalidFile = settings.ThrowExceptionOnInvalidFile;
- if (settings.Value.FileSizeLimit != -1)
+ if (settings.FileSizeLimit != -1)
{
- cfg.FileSizeLimit = settings.Value.FileSizeLimit;
+ config.FileSizeLimit = settings.FileSizeLimit;
}
- else if (!string.IsNullOrWhiteSpace(settings.Value.UnitFileSizeLimit))
+ else if (!string.IsNullOrWhiteSpace(settings.UnitFileSizeLimit))
{
- cfg.FileSizeLimit = ByteSize.Parse(settings.Value.UnitFileSizeLimit);
+ config.FileSizeLimit = ByteSize.Parse(settings.UnitFileSizeLimit);
}
})
.ValidateOnStart();
+ // Register antimalware scanner (if any).
+ RegisterConfiguredScanner(services, settings.Scanner);
+
// Register the FileValidator service.
services.AddSingleton(serviceProvider =>
{
var configuration = serviceProvider.GetRequiredService>().Value;
+
+ // If an antimalware scanner is registered, resolve it and pass it to the FileValidator.
+ var antimalwareScanner = serviceProvider.GetService();
+ if (antimalwareScanner is not null)
+ {
+ return new FileValidator(configuration, antimalwareScanner);
+ }
+
+ // No antimalware scanner registered.
return new FileValidator(configuration);
});
+ }
- return services;
+ ///
+ /// Registers the configured antimalware scanner.
+ ///
+ /// Service collection.
+ /// Scanner registration.
+ private static void RegisterConfiguredScanner(IServiceCollection services, ScannerRegistration? scanner)
+ {
+ // If no scanner has been registerered.
+ if (scanner is null) return;
+
+ var scannerType = scanner.Type;
+ if (scannerType is null)
+ {
+ throw new InvalidOperationException("The specified scanner type could not be resolved.");
+ }
+
+ if (!typeof(IAntimalwareScanner).IsAssignableFrom(scanner.Type))
+ {
+ throw new InvalidOperationException($"The specified scanner type '{scanner.Type.FullName}' does not implement the '{nameof(IAntimalwareScanner)}' interface.");
+ }
+
+ var genericScannerInterface = scanner.Type
+ .GetInterfaces()
+ .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IAntimalwareScanner<>));
+
+ var optionsType = genericScannerInterface?.GetGenericArguments()[0];
+
+ // Register scanner as IAntimalwareScanner implementation.
+ services.AddSingleton(_ =>
+ {
+ object? optionsInstance = scanner.OptionsInstance;
+ if (optionsInstance is null && scanner.OptionsConfiguration is not null)
+ {
+ if (optionsType is null)
+ {
+ throw new InvalidOperationException($"Scanner '{scannerType.FullName}' must implement IAntimalwareScanner to be configured from appsettings.");
+ }
+
+ optionsInstance = Activator.CreateInstance(optionsType);
+ if (optionsInstance is null)
+ {
+ throw new InvalidOperationException($"Could not create options instance of type '{optionsType.FullName}'.");
+ }
+
+ scanner.OptionsConfiguration.Bind(optionsInstance);
+ }
+
+ object? impl;
+
+ if (optionsInstance is not null)
+ {
+ impl = Activator.CreateInstance(scanner.Type, optionsInstance);
+ }
+ else
+ {
+ impl = Activator.CreateInstance(scanner.Type);
+ }
+
+ if (impl is not IAntimalwareScanner typedScanner)
+ {
+ throw new InvalidOperationException($"Scanner type '{scanner.Type.FullName}' does not implement the '{nameof(IAntimalwareScanner)}' interface.");
+ }
+
+ return typedScanner;
+ });
}
}
diff --git a/tests/FileValidator.Extensions.DependencyInjection.Tests.Unit/MockAntimalwareScanner.cs b/tests/FileValidator.Extensions.DependencyInjection.Tests.Unit/MockAntimalwareScanner.cs
new file mode 100644
index 0000000..dba1301
--- /dev/null
+++ b/tests/FileValidator.Extensions.DependencyInjection.Tests.Unit/MockAntimalwareScanner.cs
@@ -0,0 +1,29 @@
+using ByteGuard.FileValidator.Scanners;
+
+namespace FileValidator.Extensions.DependencyInjection.Tests.Unit;
+
+public class MockAntimalwareScanner : IAntimalwareScanner
+{
+ public readonly MockAntimalwareScannerOptions Options;
+
+ public MockAntimalwareScanner(MockAntimalwareScannerOptions options)
+ {
+ Options = options;
+ }
+
+ public MockAntimalwareScanner()
+ {
+ Options = new MockAntimalwareScannerOptions();
+ }
+
+ public bool IsClean(Stream contentStream, string fileName)
+ {
+ return true;
+ }
+}
+
+public class MockAntimalwareScannerOptions
+{
+ public string OptionA { get; set; } = string.Empty;
+ public int? OptionB { get; set; }
+}
diff --git a/tests/FileValidator.Extensions.DependencyInjection.Tests.Unit/ServiceCollectionExtensionsTests.cs b/tests/FileValidator.Extensions.DependencyInjection.Tests.Unit/ServiceCollectionExtensionsTests.cs
index 092c16a..7544ca6 100644
--- a/tests/FileValidator.Extensions.DependencyInjection.Tests.Unit/ServiceCollectionExtensionsTests.cs
+++ b/tests/FileValidator.Extensions.DependencyInjection.Tests.Unit/ServiceCollectionExtensionsTests.cs
@@ -1,5 +1,8 @@
using ByteGuard.FileValidator.Configuration;
using ByteGuard.FileValidator.Extensions.DependencyInjection.Configuration;
+using ByteGuard.FileValidator.Scanners;
+using FileValidator.Extensions.DependencyInjection.Tests.Unit;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@@ -85,4 +88,205 @@ public void AddFileValidator_FileSizeLimitAndFriendlyFileSizeLimitBothSet_Should
var options = sp.GetRequiredService>();
Assert.Equal(ByteSize.MegaBytes(10), options.Value.FileSizeLimit);
}
+
+ [Fact(DisplayName = "AddFileValidator should register the configured antimalware scanner")]
+ public void AddFileValidator_ConfiguredAntimalwareScanner_ShouldRegisterScanner()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+ Action configAction = options =>
+ {
+ options.SupportedFileTypes = [".pdf"];
+ options.FileSizeLimit = ByteSize.MegaBytes(15);
+ options.Scanner = ScannerRegistration.Create(opts =>
+ {
+ opts.OptionA = "TestOption";
+ opts.OptionB = 42;
+ });
+ };
+
+ // Act
+ services.AddFileValidator(configAction);
+
+ // Assert
+ var sp = services.BuildServiceProvider();
+ var scanner = sp.GetRequiredService();
+ Assert.IsType(scanner);
+ }
+
+ [Fact(DisplayName = "AddFileValidator should register the configured antimalware scanner without options")]
+ public void AddFileValidator_ConfiguredAntimalwareScannerWithoutOptions_ShouldRegisterScanner()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+ Action configAction = options =>
+ {
+ options.SupportedFileTypes = [".pdf"];
+ options.FileSizeLimit = ByteSize.MegaBytes(15);
+ options.Scanner = ScannerRegistration.Create(_ => { });
+ };
+
+ // Act
+ services.AddFileValidator(configAction);
+
+ // Assert
+ var sp = services.BuildServiceProvider();
+ var scanner = sp.GetRequiredService();
+ Assert.IsType(scanner);
+ }
+
+ [Fact(DisplayName = "AddFileValidator should not register an antimalware scanner when none is configured")]
+ public void AddFileValidator_NoConfiguredAntimalwareScanner_ShouldNotRegisterScanner()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+ Action configAction = options =>
+ {
+ options.SupportedFileTypes = [".pdf"];
+ options.FileSizeLimit = ByteSize.MegaBytes(15);
+ options.Scanner = null; // No scanner configured
+ };
+
+ // Act
+ services.AddFileValidator(configAction);
+
+ // Assert
+ var sp = services.BuildServiceProvider();
+ var scanner = sp.GetService();
+ Assert.Null(scanner);
+ }
+
+ [Fact(DisplayName = "AddFileValidator should throw exception when configured antimalware scanner type is invalid")]
+ public void AddFileValidator_ConfiguredAntimalwareScannerWithInvalidType_ShouldThrowException()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+ Action configAction = options =>
+ {
+ options.SupportedFileTypes = [".pdf"];
+ options.FileSizeLimit = ByteSize.MegaBytes(15);
+ options.Scanner = new ScannerRegistration
+ {
+ ScannerType = "Unknown.Namespace.ImplementationScanner, ImplementationScanner", // Invalid type, does not implement IAntimalwareScanner
+ OptionsInstance = null
+ };
+ };
+
+ // Act & Assert
+ Assert.Throws(() => services.AddFileValidator(configAction));
+ }
+
+ [Fact(DisplayName = "AddFileValidator should inject the antimalware scanner into the FileValidator")]
+ public void AddFileValidator_ConfiguredAntimalwareScanner_ShouldInjectScannerIntoFileValidator()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+ Action configAction = options =>
+ {
+ options.SupportedFileTypes = [".pdf"];
+ options.FileSizeLimit = ByteSize.MegaBytes(15);
+ options.Scanner = ScannerRegistration.Create(opts =>
+ {
+ opts.OptionA = "InjectedOption";
+ opts.OptionB = 99;
+ });
+ };
+
+ // Act
+ services.AddFileValidator(configAction);
+
+ // Assert
+ var sp = services.BuildServiceProvider();
+ var fileValidator = sp.GetRequiredService();
+ Assert.NotNull(fileValidator);
+
+ var scannerField = typeof(FileValidator).GetField("_antimalwareScanner", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ Assert.NotNull(scannerField);
+
+ var scannerInstance = scannerField.GetValue(fileValidator) as MockAntimalwareScanner;
+ Assert.NotNull(scannerInstance);
+ }
+
+ [Fact(DisplayName = "AddFileValidator should create FileValidator without antimalware scanner when none is configured")]
+ public void AddFileValidator_NoConfiguredAntimalwareScanner_ShouldCreateFileValidatorWithoutScanner()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+ Action configAction = options =>
+ {
+ options.SupportedFileTypes = [".pdf"];
+ options.FileSizeLimit = ByteSize.MegaBytes(15);
+ options.Scanner = null; // No scanner configured
+ };
+
+ // Act
+ services.AddFileValidator(configAction);
+
+ // Assert
+ var sp = services.BuildServiceProvider();
+ var fileValidator = sp.GetRequiredService();
+ Assert.NotNull(fileValidator);
+
+ var scannerField = typeof(FileValidator).GetField("_antimalwareScanner", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ Assert.NotNull(scannerField);
+
+ var scannerInstance = scannerField.GetValue(fileValidator);
+ Assert.Null(scannerInstance);
+ }
+
+ [Fact(DisplayName = "AddFileValidator should register the scanner when configuration has been provided through appsettings.json")]
+ public void AddFileValidator_ConfigurationFromAppSettings_ShouldRegisterScanner()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+
+ var inMemorySettings = new Dictionary
+ {
+ {"FileValidator:SupportedFileTypes:0", ".pdf"},
+ {"FileValidator:UnitFileSizeLimit", "15MB"},
+ {"FileValidator:Scanner:ScannerType", "FileValidator.Extensions.DependencyInjection.Tests.Unit.MockAntimalwareScanner, FileValidator.Extensions.DependencyInjection.Tests.Unit"},
+ {"FileValidator:Scanner:Options:OptionA", "ConfigOption"},
+ {"FileValidator:Scanner:Options:OptionB", "123"}
+ };
+
+ IConfiguration configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(inMemorySettings.AsEnumerable())
+ .Build();
+
+ // Act
+ services.AddFileValidator(configuration);
+
+ // Assert
+ var sp = services.BuildServiceProvider();
+ var scanner = sp.GetRequiredService();
+ Assert.IsType(scanner);
+ }
+
+ [Fact(DisplayName = "AddFileValidator should register the scanner when configuration has been provided through appsettings.json")]
+ public void AddFileValidator_ConfigurationFromAppSettingsCustomSectionName_ShouldRegisterScanner()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+
+ var inMemorySettings = new Dictionary
+ {
+ {"CustomName:SupportedFileTypes:0", ".pdf"},
+ {"CustomName:UnitFileSizeLimit", "15MB"},
+ {"CustomName:Scanner:ScannerType", "FileValidator.Extensions.DependencyInjection.Tests.Unit.MockAntimalwareScanner, FileValidator.Extensions.DependencyInjection.Tests.Unit"},
+ {"CustomName:Scanner:Options:OptionA", "ConfigOption"},
+ {"CustomName:Scanner:Options:OptionB", "123"}
+ };
+
+ IConfiguration configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(inMemorySettings.AsEnumerable())
+ .Build();
+
+ // Act
+ services.AddFileValidator(configuration, "CustomName");
+
+ // Assert
+ var sp = services.BuildServiceProvider();
+ var scanner = sp.GetRequiredService();
+ Assert.IsType(scanner);
+ }
}