SGuard.ConfigValidation 0.1.0

dotnet add package SGuard.ConfigValidation --version 0.1.0
                    
NuGet\Install-Package SGuard.ConfigValidation -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SGuard.ConfigValidation" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SGuard.ConfigValidation" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="SGuard.ConfigValidation" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SGuard.ConfigValidation --version 0.1.0
                    
#r "nuget: SGuard.ConfigValidation, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SGuard.ConfigValidation@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SGuard.ConfigValidation&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=SGuard.ConfigValidation&version=0.1.0
                    
Install as a Cake Tool

SGuard.ConfigValidation

A powerful yet lightweight tool to catch critical configuration issues before runtime, ensuring your .NET applications run reliably across all environments.

✨ Description

SGuard.ConfigValidation is designed to help developers and DevOps teams detect missing, invalid, or misconfigured settings in configuration files before the application runs—either at startup or (ideally) automatically during your CI/CD pipelines. The library supports both JSON (default) and YAML configuration formats and fits seamlessly into any .NET project. It offers a highly extensible architecture, making it easy to add custom validators or integrate with existing tools. Whether you use it as a CLI tool or programmatically inside your code, it helps to ensure your configuration files (like appsettings.json, environment variables, and custom config files) are complete and valid for all specified environments.

Use cases include:

  • Early detection of missing connection strings, API keys, or URLs
  • Preventing production outages caused by configuration errors
  • Automated configuration validation in build/deploy pipelines
  • Enforcing security and size limits for configuration files
  • Supporting both small projects and large enterprise-grade solutions by handling thousands of environments or rules efficiently

With SGuard.ConfigValidation, you reduce the risk of runtime failures, improve deployment reliability, and enforce best practices around configuration management—leading to more robust applications and happier teams.

✨ Why?

Misconfigurations are among the most common sources of production incidents, outages, and security vulnerabilities. Mistyped keys, missing secrets, or incorrect environment variables often go unnoticed until the application is started or deployed—sometimes resulting in downtime, failed deployments, or even data loss.

SGuard.ConfigValidation helps you:

  • Shift left: Catch configuration errors as early as possible, ideally before deploying to production.
  • Automate checks: Integrate config validation into your CI/CD pipeline so no bad configs slip through unnoticed.
  • Support all environments: Validate against every environment (development, staging, production, etc.) and ensure consistent configuration health everywhere.
  • Extend easily: Create and plug in your own custom validation rules, tailored to your application's needs.
  • Increase security & reliability: Enforce size, depth, and complexity limits to prevent resource exhaustion, and protect against common configuration issues like missing or weak secrets.
  • Save time and resources: Debugging runtime issues caused by misconfiguration can be costly. By detecting these problems before deployment, you reduce downtime, support costs, and stress for both developers and operations teams.

In short, SGuard.ConfigValidation gives you confidence that your application will work as expected, in every environment, every time.

🚀 Features

Supported Validators

  • required → Ensures a specific key must exist in the target config file
  • min_len → Validates minimum string length
  • max_len → Validates maximum string length
  • eq → Checks if value equals a specified value
  • ne → Checks if value does not equal a specified value
  • gt → Checks if value is greater than a specified value
  • gte → Checks if value is greater than or equal to a specified value
  • lt → Checks if value is less than a specified value
  • lte → Checks if value is less than or equal to a specified value
  • in → Checks if value is in a specified array

Framework Support

  • .NET 8.0 (LTS)
  • .NET 9.0
  • .NET 10.0

Additional Features

  • JSON Configuration Support: Load configuration and app settings from JSON files (.json). JSON is the default format for both sguard.json configuration files and appsettings.json files.
  • YAML Configuration Support: Load configuration and app settings from YAML files (.yaml, .yml) when YAML loader is provided
  • JSON Schema Validation: Validate sguard.json against JSON Schema for structure validation
  • Custom Validator Plugins: Extend validation capabilities with custom validator plugins
  • Performance Optimizations: Built-in caching for path resolution, schema validation, and reflection operations

📖 Usage

CLI Commands

Validate Command (Default)
# Validate all environments
dotnet run -- validate

# Validate specific environment
dotnet run -- validate --env dev

# Validate specific environment
dotnet run -- validate --env prod

# Validate with custom config file
dotnet run -- validate --config custom-sguard.json

# Validate all environments explicitly
dotnet run -- validate --all

# Output as JSON
dotnet run -- validate --output json

# Write results to JSON file
dotnet run -- validate --output json --output-file results.json

# Write results to text file
dotnet run -- validate --output text --output-file results.txt

# Enable verbose logging
dotnet run -- validate --verbose
Command Options
  • --config, -c - Path to the configuration file (default: sguard.json)
  • --env, -e - Environment ID to validate (if not specified, all environments are validated)
  • --all, -a - Validate all environments
  • --output, -o - Output format: json, text, or console (default: console)
  • --output-file, -f - Path to output file. If specified, results will be written to this file instead of console. Works with both json and text formats.
  • --verbose, -v - Enable verbose logging

📂 Configuration Format

SGuard.ConfigValidation supports JSON (default) and YAML configuration formats. JSON is the default format and is used for both sguard.json configuration files and appsettings.json files.

Example sguard.json (JSON Format)

{
  "version": "1",
  "environments": [
    {
      "id": "dev",
      "name": "Development",
      "path": "appsettings.Development.json",
      "description": "Development environment"
    },
    {
      "id": "stag",
      "name": "Staging",
      "path": "appsettings.Staging.json",
      "description": "Staging environment"
    },
    {
      "id": "prod",
      "name": "Production",
      "path": "appsettings.Production.json",
      "description": "Production environment"
    }
  ],
  "rules": [
    {
      "id": "common-rules",
      "environments": ["dev", "stag", "prod"],
      "rule": {
        "id": "connection-string-rule",
        "conditions": [
          {
            "key": "ConnectionStrings:DefaultConnection",
            "condition": [
              {
                "validator": "required",
                "message": "Connection string is required"
              },
              {
                "validator": "min_len",
                "value": 10,
                "message": "Connection string must be at least 10 characters long"
              },
              {
                "validator": "max_len",
                "value": 200,
                "message": "Connection string must be at most 200 characters long"
              }
            ]
          }
        ]
      }
    },
    {
      "id": "production-rules",
      "environments": ["prod"],
      "rule": {
        "id": "api-key-rule",
        "conditions": [
          {
            "key": "ApiKeys:ExternalService",
            "condition": [
              {
                "validator": "required",
                "message": "External service API key is required in production"
              },
              {
                "validator": "min_len",
                "value": 32,
                "message": "API key must be at least 32 characters"
              }
            ]
          }
        ]
      }
    }
  ]
}

Configuration Schema

  • version (string, required): Configuration version. Must not be null or empty.
  • environments (array, required): List of environment definitions. Must contain at least one environment.
    • id (string, required): Unique environment identifier. Must not be null or empty. Must be unique across all environments.
    • name (string, required): Environment display name. Must not be null or empty.
    • path (string, required): Path to the appsettings file for this environment. Must not be null or empty.
    • description (string, optional): Environment description. Can be null or empty.
  • rules (array, required): List of validation rules. Must contain at least one rule. Cannot be empty.
    • id (string, required): Unique rule identifier. Must not be null or empty. Must be unique across all rules.
    • environments (array, required): List of environment IDs where this rule applies. Must contain at least one environment ID. All environment IDs must exist in the environments list.
    • rule (object, required): Rule definition. Must not be null.
      • id (string, required): Rule detail identifier. Must not be null or empty.
      • conditions (array, required): List of validation conditions. Must contain at least one condition.

🔧 Programmatic Usage

Using the Library

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using SGuard.ConfigValidation.Common;
using SGuard.ConfigValidation.Services;
using SGuard.ConfigValidation.Validators;

// Setup security options
var securityOptions = Options.Create(new SecurityOptions());

// Setup services with logging
var loggerFactory = NullLoggerFactory.Instance;
var validatorFactoryLogger = NullLogger<ValidatorFactory>.Instance;
var configLoaderLogger = NullLogger<ConfigLoader>.Instance;
var fileValidatorLogger = NullLogger<FileValidator>.Instance;
var ruleEngineLogger = NullLogger<RuleEngine>.Instance;

var validatorFactory = new ValidatorFactory(validatorFactoryLogger);
var configLoader = new ConfigLoader(configLoaderLogger, securityOptions);
var fileValidator = new FileValidator(validatorFactory, fileValidatorLogger);
var ruleEngine = new RuleEngine(
    configLoader, 
    fileValidator, 
    validatorFactory, 
    ruleEngineLogger, 
    securityOptions);

// Validate environment
var result = await ruleEngine.ValidateEnvironmentAsync("sguard.json", "prod");

if (result.IsSuccess && !result.HasValidationErrors)
{
    Console.WriteLine("Validation passed!");
}
else
{
    if (!string.IsNullOrEmpty(result.ErrorMessage))
    {
        Console.WriteLine($"Validation failed: {result.ErrorMessage}");
    }
    
    foreach (var validationResult in result.ValidationResults)
    {
        foreach (var error in validationResult.Errors)
        {
            Console.WriteLine($"  - {error.Key}: {error.Message}");
        }
    }
}

Using the extension methods for easy registration:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SGuard.ConfigValidation.Extensions;

var services = new ServiceCollection();

// Register logging (required)
services.AddLogging(builder => builder.AddConsole());

// Build configuration
var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", optional: true)
    .AddEnvironmentVariables()
    .Build();

// Register all SGuard.ConfigValidation services with one line
services.AddSGuardConfigValidation(configuration);

// Or specify a logging level directly (logging will be automatically configured)
services.AddSGuardConfigValidation(configuration, logLevel: LogLevel.Debug);

var serviceProvider = services.BuildServiceProvider();
var ruleEngine = serviceProvider.GetRequiredService<IRuleEngine>();

// Example: Validate all environments
var result = await ruleEngine.ValidateAllEnvironmentsAsync("sguard.json");
if (result.IsSuccess)
{
    Console.WriteLine($"Validated {result.ValidationResults.Count} environment(s)");
}

Manual Dependency Injection Registration

If you prefer manual registration or need more control:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SGuard.ConfigValidation.Common;
using SGuard.ConfigValidation.Services;
using SGuard.ConfigValidation.Validators;
using SGuard.ConfigValidation.Validators.Plugin;

var services = new ServiceCollection();

// Register logging (required)
services.AddLogging(builder => builder.AddConsole());

// Register configuration and security options
var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", optional: true)
    .AddEnvironmentVariables()
    .Build();

services.Configure<SecurityOptions>(configuration.GetSection("Security"));

// Register core services
services.AddSingleton<IValidatorFactory, ValidatorFactory>();
services.AddSingleton<IConfigLoader, ConfigLoader>();
services.AddSingleton<IFileValidator, FileValidator>();
services.AddSingleton<IRuleEngine, RuleEngine>();

// Register optional services
services.AddSingleton<IYamlLoader, YamlLoader>();
services.AddSingleton<ISchemaValidator, JsonSchemaValidator>();
services.AddSingleton<ValidatorPluginDiscovery>();

var serviceProvider = services.BuildServiceProvider();
var ruleEngine = serviceProvider.GetRequiredService<IRuleEngine>();

// Example: Validate all environments
var result = await ruleEngine.ValidateAllEnvironmentsAsync("sguard.json");
if (result.IsSuccess)
{
    Console.WriteLine($"Validated {result.ValidationResults.Count} environment(s)");
}

Logging Level Configuration

You can specify a logging level directly when registering services. This will automatically configure logging for SGuard namespaces:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SGuard.ConfigValidation.Extensions;

var services = new ServiceCollection();

// Register with Debug level (verbose logging)
services.AddSGuardConfigValidation(logLevel: LogLevel.Debug);

// Or for production, use Warning level (only warnings and errors)
services.AddSGuardConfigValidation(logLevel: LogLevel.Warning);

// Or for development, use Trace level (most verbose)
services.AddSGuardConfigValidation(logLevel: LogLevel.Trace);

var serviceProvider = services.BuildServiceProvider();
var ruleEngine = serviceProvider.GetRequiredService<IRuleEngine>();

Note: When logLevel is specified, logging is automatically configured. You don't need to call AddLogging() separately. However, you may still want to add a logging provider (e.g., AddConsole()) if you need console output.

Available Log Levels:

  • LogLevel.Trace - Most verbose, includes all log messages
  • LogLevel.Debug - Debug information for troubleshooting
  • LogLevel.Information - General informational messages (default)
  • LogLevel.Warning - Warning messages and above
  • LogLevel.Error - Error messages and above
  • LogLevel.Critical - Only critical errors

Core Services Only

If you only need core services without YAML or schema validation:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SGuard.ConfigValidation.Extensions;

var services = new ServiceCollection();

// Register logging
services.AddLogging(builder => builder.AddConsole());

// Register only core services
services.AddSGuardConfigValidationCore();

// Or specify a logging level directly
services.AddSGuardConfigValidationCore(logLevel: LogLevel.Debug);

// Optionally add YAML support later
// services.AddSingleton<IYamlLoader, YamlLoader>();

var serviceProvider = services.BuildServiceProvider();
var ruleEngine = serviceProvider.GetRequiredService<IRuleEngine>();

YAML Configuration Support

The library supports loading configuration and app settings from YAML files:

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using SGuard.ConfigValidation.Common;
using SGuard.ConfigValidation.Services;

// Setup security options
var securityOptions = Options.Create(new SecurityOptions());
var logger = NullLogger<YamlLoader>.Instance;
var yamlLoader = new YamlLoader(logger, securityOptions);

// Load configuration from YAML
var config = yamlLoader.LoadConfig("sguard.yaml");

// Load app settings from YAML
var appSettings = yamlLoader.LoadAppSettings("appsettings.yaml");

// Example: Access loaded configuration
Console.WriteLine($"Loaded {config.Environments.Count} environment(s)");
Console.WriteLine($"Loaded {config.Rules.Count} rule(s)");

JSON Schema Validation

Validate your sguard.json configuration against a JSON Schema:

using SGuard.ConfigValidation.Services;
using System.Text.Json;

var schemaValidator = new JsonSchemaValidator();

// Example 1: Validate JSON content against schema content
var jsonContent = """
{
  "version": "1",
  "environments": [{"id": "dev", "name": "Development", "path": "appsettings.Dev.json"}],
  "rules": []
}
""";

var schemaContent = """
{
  "type": "object",
  "properties": {
    "version": {"type": "string"},
    "environments": {"type": "array"},
    "rules": {"type": "array"}
  },
  "required": ["version", "environments"]
}
""";

var result = schemaValidator.Validate(jsonContent, schemaContent);

if (!result.IsValid)
{
    Console.WriteLine("Schema validation failed:");
    foreach (var error in result.Errors)
    {
        Console.WriteLine($"  - {error}");
    }
}
else
{
    Console.WriteLine("Schema validation passed!");
}

// Example 2: Validate against a schema file
var fileResult = schemaValidator.ValidateAgainstFile(jsonContent, "sguard.schema.json");
if (!fileResult.IsValid)
{
    foreach (var error in fileResult.Errors)
    {
        Console.WriteLine($"Schema validation error: {error}");
    }
}

Custom Validator Plugins

Create custom validators by implementing IValidatorPlugin:

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using SGuard.ConfigValidation.Models;
using SGuard.ConfigValidation.Validators;
using SGuard.ConfigValidation.Validators.Plugin;
using System.Text.RegularExpressions;

// Step 1: Create a custom validator
public class EmailValidator : IValidator<object>
{
    private static readonly Regex EmailRegex = new(@"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.Compiled);

    public string ValidatorType => "email";

    public ValidationResult Validate(object value, ValidatorCondition condition)
    {
        if (value == null)
        {
            return ValidationResult.Failure(condition.Message ?? "Email is required");
        }

        var email = value.ToString();
        if (string.IsNullOrWhiteSpace(email))
        {
            return ValidationResult.Failure(condition.Message ?? "Email cannot be empty");
        }

        if (!EmailRegex.IsMatch(email))
        {
            return ValidationResult.Failure(
                condition.Message ?? $"Invalid email format. Actual value: '{email}'");
        }

        return ValidationResult.Success();
    }
}

// Step 2: Create a plugin wrapper
public class EmailValidatorPlugin : IValidatorPlugin
{
    public string ValidatorType => "email";
    
    public IValidator<object> Validator => new EmailValidator();
}

// Step 3: Discover and use plugins
var logger = NullLogger<ValidatorPluginDiscovery>.Instance;
var pluginDiscovery = new ValidatorPluginDiscovery(logger);
var plugins = pluginDiscovery.DiscoverValidators(new[] { "./plugins" });

// Step 4: Register plugins with ValidatorFactory
var validatorFactory = new ValidatorFactory(logger);
foreach (var plugin in plugins)
{
    validatorFactory.RegisterValidator(plugin.ValidatorType, plugin.Validator);
}

Error Handling

Handle validation errors gracefully:

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using SGuard.ConfigValidation.Common;
using SGuard.ConfigValidation.Exceptions;
using SGuard.ConfigValidation.Services;
using SGuard.ConfigValidation.Validators;

var securityOptions = Options.Create(new SecurityOptions());
var logger = NullLogger<RuleEngine>.Instance;
var validatorFactory = new ValidatorFactory(NullLogger<ValidatorFactory>.Instance);
var configLoader = new ConfigLoader(NullLogger<ConfigLoader>.Instance, securityOptions);
var fileValidator = new FileValidator(validatorFactory, NullLogger<FileValidator>.Instance);
var ruleEngine = new RuleEngine(
    configLoader, 
    fileValidator, 
    validatorFactory, 
    logger, 
    securityOptions);

try
{
    var result = await ruleEngine.ValidateEnvironmentAsync("sguard.json", "prod");
    
    if (result.IsSuccess)
    {
        if (result.HasValidationErrors)
        {
            Console.WriteLine("Validation completed with errors:");
            var fileResult = result.SingleResult ?? result.ValidationResults.FirstOrDefault();
            if (fileResult != null)
            {
                Console.WriteLine($"File: {fileResult.Path}");
                foreach (var error in fileResult.Errors)
                {
                    Console.WriteLine($"  Key: {error.Key}");
                    Console.WriteLine($"  Error: {error.Message}");
                }
            }
        }
        else
        {
            Console.WriteLine("All validations passed!");
        }
    }
    else
    {
        Console.WriteLine($"Validation failed: {result.ErrorMessage}");
    }
}
catch (FileNotFoundException ex)
{
    Console.WriteLine($"Configuration file not found: {ex.FileName}");
}
catch (ConfigurationException ex)
{
    Console.WriteLine($"Configuration error: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"Unexpected error: {ex.Message}");
}

Advanced Usage: Validating Multiple Environments

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using SGuard.ConfigValidation.Common;
using SGuard.ConfigValidation.Services;
using SGuard.ConfigValidation.Validators;

var securityOptions = Options.Create(new SecurityOptions());
var logger = NullLogger<RuleEngine>.Instance;
var validatorFactory = new ValidatorFactory(NullLogger<ValidatorFactory>.Instance);
var configLoader = new ConfigLoader(NullLogger<ConfigLoader>.Instance, securityOptions);
var fileValidator = new FileValidator(validatorFactory, NullLogger<FileValidator>.Instance);
var ruleEngine = new RuleEngine(
    configLoader, 
    fileValidator, 
    validatorFactory, 
    logger, 
    securityOptions);

// Validate all environments
var allResults = await ruleEngine.ValidateAllEnvironmentsAsync("sguard.json");

var successCount = allResults.ValidationResults.Count(r => r.IsValid);
var failureCount = allResults.ValidationResults.Count - successCount;

Console.WriteLine($"Validated {allResults.ValidationResults.Count} environment(s)");
Console.WriteLine($"  ✅ Passed: {successCount}");
Console.WriteLine($"  ❌ Failed: {failureCount}");

// Validate specific environments
var environmentsToValidate = new[] { "dev", "stag", "prod" };
foreach (var envId in environmentsToValidate)
{
    var result = await ruleEngine.ValidateEnvironmentAsync("sguard.json", envId);
    Console.WriteLine($"Environment '{envId}': {(result.IsSuccess && !result.HasValidationErrors ? "✅ Pass" : "❌ Fail")}");
}

CI/CD Integration Example

Example GitHub Actions workflow:

name: Validate Configuration

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  validate-config:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup .NET
        uses: actions/setup-dotnet@v3
        with:
          dotnet-version: '8.0.x'
      
      - name: Restore dependencies
        run: dotnet restore
      
      - name: Validate configuration
        run: |
          dotnet run --project SGuard.ConfigValidation.Console -- validate --all --output json > validation-results.json
      
      - name: Check validation results
        run: |
          if grep -q '"HasValidationErrors":true' validation-results.json; then
            echo "❌ Configuration validation failed!"
            cat validation-results.json
            exit 1
          else
            echo "✅ All configurations are valid!"
          fi

Example Azure DevOps pipeline:

trigger:
  branches:
    include:
      - main
      - develop

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: UseDotNet@2
    inputs:
      packageType: 'sdk'
      version: '8.0.x'
  
  - task: DotNetCoreCLI@2
    displayName: 'Restore dependencies'
    inputs:
      command: 'restore'
  
  - task: DotNetCoreCLI@2
    displayName: 'Validate configuration'
    inputs:
      command: 'run'
      projects: 'SGuard.ConfigValidation.Console/SGuard.ConfigValidation.Console.csproj'
      arguments: '-- validate --all --output json'
    continueOnError: false

📊 Output Formats

Console Output (Default)

🔍 Validating Environments:

📁 Environment: Development
   File: appsettings.Development.json
   Status: ✅ PASS
   Validated 2 rule(s)

📁 Environment: Production
   File: appsettings.Production.json
   Status: ❌ FAIL
   Errors (1):
     🔑 ConnectionStrings:DefaultConnection
        ✖ required: Connection string is required
        💡 Current value: 

✅ All validations passed successfully!

JSON Output (Console)

{
  "Success": true,
  "ErrorMessage": "",
  "HasValidationErrors": false,
  "Results": [
    {
      "Path": "appsettings.Development.json",
      "IsValid": true,
      "ErrorCount": 0,
      "Results": [...],
      "Errors": []
    }
  ]
}

File Output

You can write validation results to a file in either text or JSON format:

Text File Output
using SGuard.ConfigValidation.Output;
using Microsoft.Extensions.Logging;

var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());

// Write text output to file
var textFormatter = OutputFormatterFactory.Create("text", loggerFactory, "validation-results.txt");
await textFormatter.FormatAsync(result);
JSON File Output
using SGuard.ConfigValidation.Output;
using Microsoft.Extensions.Logging;

var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());

// Write JSON output to file
var jsonFormatter = OutputFormatterFactory.Create("json", loggerFactory, "validation-results.json");
await jsonFormatter.FormatAsync(result);
Direct Formatter Usage

You can also use the formatters directly:

using SGuard.ConfigValidation.Output;

// Text file formatter
var fileFormatter = new FileOutputFormatter("results.txt");
await fileFormatter.FormatAsync(result);

// JSON file formatter
var jsonFileFormatter = new JsonFileOutputFormatter("results.json");
await jsonFileFormatter.FormatAsync(result);

🔒 Security Configuration

SGuard.ConfigValidation includes built-in security limits to prevent DoS (Denial of Service) attacks. These limits can be configured via appsettings.json or environment variables.

Default Security Limits

Setting Default Value Hard Limit (Maximum) Description
MaxFileSizeBytes 104857600 (100 MB) 524288000 (500 MB) Maximum allowed file size for configuration and app settings files
MaxEnvironmentsCount 1000 5000 Maximum number of environments allowed in a configuration file
MaxRulesCount 10000 50000 Maximum number of rules allowed in a configuration file
MaxConditionsPerRule 1000 5000 Maximum number of conditions allowed per rule
MaxValidatorsPerCondition 100 500 Maximum number of validators allowed per condition
MaxPathCacheSize 10000 100000 Maximum number of entries in the path resolver cache
MaxPathLength 4096 16384 Maximum length for a single path string (characters)
MaxJsonDepth 64 256 Maximum depth for nested JSON/YAML structures
MaxParallelEnvironments Environment.ProcessorCount 100 Maximum number of environments that can be validated in parallel
StreamingThresholdBytes 524288 (512 KB) 10485760 (10 MB) File size threshold for using streaming when loading app settings

Note: Hard limits are absolute maximums that cannot be exceeded even if configured higher. If a configuration value exceeds the hard limit, it will be automatically clamped to the hard limit and a warning will be logged.

Configuring Security Limits

Add a Security section to your appsettings.json:

{
  "Security": {
    "MaxFileSizeBytes": 104857600,
    "MaxEnvironmentsCount": 1000,
    "MaxRulesCount": 10000,
    "MaxConditionsPerRule": 1000,
    "MaxValidatorsPerCondition": 100,
    "MaxPathCacheSize": 10000,
    "MaxPathLength": 4096,
    "MaxJsonDepth": 64,
    "MaxParallelEnvironments": 4,
    "StreamingThresholdBytes": 524288
  }
}

Environment Variables

You can also override security limits using environment variables:

# Linux/Mac
export Security__MaxFileSizeBytes=209715200  # 200 MB
export Security__MaxEnvironmentsCount=2000

# Windows
set Security__MaxFileSizeBytes=209715200
set Security__MaxEnvironmentsCount=2000

Security Features

  • Path Traversal Protection: Prevents access to files outside the base directory
  • Symlink Attack Protection: Validates symlinks to prevent unauthorized file access
  • DoS Protection: Resource limits prevent memory exhaustion and excessive processing
  • Cache Poisoning Protection: Sanitizes cache keys to prevent injection attacks

🧪 Testing

Run tests with:

dotnet test

Test Coverage

The project maintains comprehensive test coverage across all supported .NET frameworks. Coverage reports are generated automatically in the CI/CD pipeline.

Coverage Summary
Framework Line Coverage Branch Coverage
.NET 8.0 ~85%+ ~80%+
.NET 9.0 ~85%+ ~80%+
.NET 10.0 ~85%+ ~80%+

Coverage percentages are approximate and may vary. Detailed coverage reports are available in CI/CD artifacts.

Running Tests with Coverage

To generate coverage reports locally:

dotnet test --collect:"XPlat Code Coverage" --results-directory:"./TestResults"

Coverage reports will be generated in the TestResults directory in Cobertura XML format.

Performance Benchmarks

The project includes BenchmarkDotNet benchmarks for performance-critical operations. Run benchmarks with:

cd SGuard.ConfigValidation.Tests
dotnet run --configuration Release --project SGuard.ConfigValidation.Tests.csproj

This will execute benchmarks for:

  • Large file loading performance
  • Validator factory lookup performance
  • File validation performance
  • Memory allocation tracking

🗺️ Roadmap

  • JSON config support
  • Multiple validator types
  • Environment-based validation
  • CLI tool with System.CommandLine
  • JSON and console output formats
  • Structured logging
  • Dependency injection support
  • YAML config support
  • Custom validator plugins
  • Schema validation for sguard.json
  • Performance optimizations (caching, memory allocation improvements)
  • CI/CD pipeline integration (GitHub Actions with test coverage)
  • NuGet package publishing and distribution

🔌 Using as a Library (DLL)

The SGuard.ConfigValidation project is a standalone library that can be used in any .NET application without requiring the console application.

Installation

<PackageReference Include="SGuard.ConfigValidation" Version="0.0.1" />
Project Reference
<ProjectReference Include="path/to/SGuard.ConfigValidation/SGuard.ConfigValidation.csproj" />

Quick Start

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SGuard.ConfigValidation.Extensions;
using SGuard.ConfigValidation.Services.Abstract;

var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole());
services.AddSGuardConfigValidation();

var serviceProvider = services.BuildServiceProvider();
var ruleEngine = serviceProvider.GetRequiredService<IRuleEngine>();

var result = await ruleEngine.ValidateEnvironmentAsync("sguard.json", "prod");

Use Cases

1. Web API Integration

Validate configurations during application startup:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using SGuard.ConfigValidation.Extensions;
using SGuard.ConfigValidation.Output;
using SGuard.ConfigValidation.Services.Abstract;

var builder = WebApplication.CreateBuilder(args);

// Register SGuard services
builder.Services.AddLogging();
builder.Services.AddSGuardConfigValidation(builder.Configuration);

var app = builder.Build();

// Validate configurations at startup
var ruleEngine = app.Services.GetRequiredService<IRuleEngine>();
var loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
var result = await ruleEngine.ValidateAllEnvironmentsAsync("sguard.json");

if (!result.IsSuccess || result.HasValidationErrors)
{
    app.Logger.LogError("Configuration validation failed");
    
    // Optionally write results to file for debugging
    var formatter = OutputFormatterFactory.Create("json", loggerFactory, "validation-errors.json");
    await formatter.FormatAsync(result);
    
    // Handle error appropriately
}

app.Run();
2. Worker Service Integration

Validate configurations in a background worker:

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using SGuard.ConfigValidation.Extensions;
using SGuard.ConfigValidation.Output;
using SGuard.ConfigValidation.Services.Abstract;

var builder = Host.CreateApplicationBuilder(args);

// Register SGuard services
builder.Services.AddSGuardConfigValidation(builder.Configuration);

// Register worker
builder.Services.AddHostedService<ConfigValidationWorker>();

var host = builder.Build();
host.Run();

public class ConfigValidationWorker : BackgroundService
{
    private readonly IRuleEngine _ruleEngine;
    private readonly ILogger<ConfigValidationWorker> _logger;
    private readonly ILoggerFactory _loggerFactory;

    public ConfigValidationWorker(
        IRuleEngine ruleEngine, 
        ILogger<ConfigValidationWorker> logger,
        ILoggerFactory loggerFactory)
    {
        _ruleEngine = ruleEngine;
        _logger = logger;
        _loggerFactory = loggerFactory;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var result = await _ruleEngine.ValidateAllEnvironmentsAsync("sguard.json", stoppingToken);
            
            if (result.IsSuccess && !result.HasValidationErrors)
            {
                _logger.LogInformation("All configurations are valid");
            }
            else
            {
                _logger.LogError("Configuration validation failed");
                
                // Write validation results to file for analysis
                var formatter = OutputFormatterFactory.Create("json", _loggerFactory, 
                    $"validation-results-{DateTime.UtcNow:yyyyMMddHHmmss}.json");
                await formatter.FormatAsync(result);
            }

            await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
        }
    }
}
3. Unit Testing

Use the library in your test projects:

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using SGuard.ConfigValidation.Common;
using SGuard.ConfigValidation.Services;
using SGuard.ConfigValidation.Validators;
using Xunit;

public class MyServiceTests
{
    [Fact]
    public void TestConfigurationValidation()
    {
        var securityOptions = Options.Create(new SecurityOptions());
        var logger = NullLogger<RuleEngine>.Instance;
        var validatorFactory = new ValidatorFactory(NullLogger<ValidatorFactory>.Instance);
        var configLoader = new ConfigLoader(NullLogger<ConfigLoader>.Instance, securityOptions);
        var fileValidator = new FileValidator(validatorFactory, NullLogger<FileValidator>.Instance);
        var ruleEngine = new RuleEngine(configLoader, fileValidator, validatorFactory, logger, securityOptions);

        var result = await ruleEngine.ValidateEnvironmentAsync("test-sguard.json", "test-env");
        
        Assert.True(result.IsSuccess);
    }
}
4. Custom Console Application

Create your own console application using the library:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SGuard.ConfigValidation.Extensions;
using SGuard.ConfigValidation.Output;
using SGuard.ConfigValidation.Services.Abstract;

var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole());
services.AddSGuardConfigValidation();

var serviceProvider = services.BuildServiceProvider();
var ruleEngine = serviceProvider.GetRequiredService<IRuleEngine>();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();

// Your custom logic here
var result = await ruleEngine.ValidateAllEnvironmentsAsync("sguard.json");

if (result.IsSuccess && !result.HasValidationErrors)
{
    Console.WriteLine("✅ All configurations are valid!");
    
    // Optionally save results to file
    var formatter = OutputFormatterFactory.Create("json", loggerFactory, "validation-success.json");
    await formatter.FormatAsync(result);
    
    Environment.Exit(0);
}
else
{
    Console.WriteLine("❌ Configuration validation failed!");
    
    // Save error details to file
    var formatter = OutputFormatterFactory.Create("json", loggerFactory, "validation-errors.json");
    await formatter.FormatAsync(result);
    
    Environment.Exit(1);
}

📝 License

This project is licensed under the MIT License - see the LICENSE file for details.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


For questions or contributions, feel free to open an issue or pull request!

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 95 1/21/2026