943 lines
35 KiB
C#
Executable File
943 lines
35 KiB
C#
Executable File
using MarketAlly.AIPlugin;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using System.Xml.Linq;
|
|
|
|
namespace MarketAlly.AIPlugin.Security.Plugins
|
|
{
|
|
/// <summary>
|
|
/// Validates configuration files for security best practices and sensitive data exposure
|
|
/// </summary>
|
|
[AIPlugin("SecureConfiguration", "Validates configuration files for security best practices and sensitive data exposure")]
|
|
public class SecureConfigurationPlugin : IAIPlugin
|
|
{
|
|
[AIParameter("Full path to the configuration file or directory", required: true)]
|
|
public string ConfigPath { get; set; }
|
|
|
|
[AIParameter("Check for exposed secrets in config files", required: false)]
|
|
public bool CheckSecrets { get; set; } = true;
|
|
|
|
[AIParameter("Validate HTTPS/TLS configurations", required: false)]
|
|
public bool CheckTls { get; set; } = true;
|
|
|
|
[AIParameter("Check database connection security", required: false)]
|
|
public bool CheckDatabase { get; set; } = true;
|
|
|
|
[AIParameter("Validate CORS settings", required: false)]
|
|
public bool CheckCors { get; set; } = true;
|
|
|
|
[AIParameter("Check environment-specific configurations", required: false)]
|
|
public bool CheckEnvironments { get; set; } = true;
|
|
|
|
public IReadOnlyDictionary<string, Type> SupportedParameters => new Dictionary<string, Type>
|
|
{
|
|
["configPath"] = typeof(string),
|
|
["checkSecrets"] = typeof(bool),
|
|
["checkTls"] = typeof(bool),
|
|
["checkDatabase"] = typeof(bool),
|
|
["checkCors"] = typeof(bool),
|
|
["checkEnvironments"] = typeof(bool)
|
|
};
|
|
|
|
// Configuration security patterns
|
|
private static readonly Dictionary<string, (Regex Pattern, string Type, string Severity, string Description)> ConfigPatterns = new()
|
|
{
|
|
// Exposed Secrets
|
|
["password_in_config"] = (new Regex(@"(password|pwd|pass|secret|key|token)\s*[=:]\s*['""][^'""]{3,}['""]", RegexOptions.IgnoreCase),
|
|
"Secret", "High", "Password or secret in configuration file"),
|
|
["connection_string_password"] = (new Regex(@"password\s*=\s*[^;]{3,}", RegexOptions.IgnoreCase),
|
|
"Secret", "High", "Database password in connection string"),
|
|
["api_key_exposed"] = (new Regex(@"(api.?key|apikey|clientsecret)\s*[=:]\s*['""][^'""]{10,}['""]", RegexOptions.IgnoreCase),
|
|
"Secret", "Critical", "API key or client secret exposed in configuration"),
|
|
["jwt_secret_exposed"] = (new Regex(@"(jwt|token).*(secret|key)\s*[=:]\s*['""][^'""]{1,}['""]", RegexOptions.IgnoreCase),
|
|
"Secret", "Critical", "JWT secret exposed in configuration"),
|
|
["encryption_key_exposed"] = (new Regex(@"(encryption|cipher).*(key|secret)\s*[=:]\s*['""][^'""]{1,}['""]", RegexOptions.IgnoreCase),
|
|
"Secret", "Critical", "Encryption key exposed in configuration"),
|
|
|
|
// TLS/HTTPS Issues
|
|
["ssl_disabled"] = (new Regex(@"(ssl|https|tls)\s*[=:]\s*(false|disabled|no)", RegexOptions.IgnoreCase),
|
|
"TLS", "High", "SSL/TLS disabled in configuration"),
|
|
["weak_tls_version"] = (new Regex(@"(tls|ssl).*(version|protocol)\s*[=:]\s*['""]?(1\.0|1\.1|ssl)['""]?", RegexOptions.IgnoreCase),
|
|
"TLS", "High", "Weak TLS version configured (TLS 1.0/1.1 or SSL)"),
|
|
["insecure_ciphers"] = (new Regex(@"(cipher|encryption).*(rc4|des|md5|sha1)", RegexOptions.IgnoreCase),
|
|
"TLS", "Medium", "Weak cipher suites configured"),
|
|
["certificate_validation_disabled"] = (new Regex(@"(certificate|cert).*(validation|verify|check)\s*[=:]\s*(false|disabled|no)", RegexOptions.IgnoreCase),
|
|
"TLS", "High", "Certificate validation disabled"),
|
|
|
|
// Database Security
|
|
["integrated_security_disabled"] = (new Regex(@"integrated.?security\s*=\s*(false|no)", RegexOptions.IgnoreCase),
|
|
"Database", "Medium", "Integrated security disabled for database connection"),
|
|
["sql_server_trust_cert"] = (new Regex(@"trustservercertificate\s*=\s*true", RegexOptions.IgnoreCase),
|
|
"Database", "Medium", "SQL Server certificate trust enabled (potential MITM risk)"),
|
|
["database_timeout_too_long"] = (new Regex(@"(timeout|commandtimeout)\s*=\s*([5-9]\d{2,}|\d{4,})", RegexOptions.IgnoreCase),
|
|
"Database", "Low", "Very long database timeout configured"),
|
|
["database_no_encryption"] = (new Regex(@"encrypt\s*=\s*(false|no)", RegexOptions.IgnoreCase),
|
|
"Database", "Medium", "Database connection encryption disabled"),
|
|
|
|
// CORS Issues
|
|
["cors_allow_all_origins"] = (new Regex(@"(allowedorigins|origins)\s*.*\*", RegexOptions.IgnoreCase),
|
|
"CORS", "High", "CORS allows all origins (*)"),
|
|
["cors_allow_credentials_with_wildcard"] = (new Regex(@"(allowcredentials|credentials)\s*[=:]\s*true.*\*", RegexOptions.IgnoreCase),
|
|
"CORS", "High", "CORS allows credentials with wildcard origin"),
|
|
["cors_unsafe_headers"] = (new Regex(@"(allowedheaders|headers).*\*", RegexOptions.IgnoreCase),
|
|
"CORS", "Medium", "CORS allows all headers (*)"),
|
|
["cors_unsafe_methods"] = (new Regex(@"(allowedmethods|methods).*(delete|put|patch)", RegexOptions.IgnoreCase),
|
|
"CORS", "Low", "CORS allows potentially unsafe HTTP methods"),
|
|
|
|
// General Security Settings
|
|
["debug_mode_enabled"] = (new Regex(@"(debug|development)\s*[=:]\s*true", RegexOptions.IgnoreCase),
|
|
"General", "Medium", "Debug mode enabled"),
|
|
["detailed_errors_enabled"] = (new Regex(@"(detailederrors|customerrors)\s*[=:]\s*(on|true|detailed)", RegexOptions.IgnoreCase),
|
|
"General", "Medium", "Detailed error information enabled"),
|
|
["insecure_cookies"] = (new Regex(@"(httponly|secure)\s*[=:]\s*(false|no)", RegexOptions.IgnoreCase),
|
|
"General", "Medium", "Insecure cookie settings"),
|
|
["weak_session_timeout"] = (new Regex(@"(session|timeout)\s*[=:]\s*['""]?\d{1,2}['""]?", RegexOptions.IgnoreCase),
|
|
"General", "Low", "Very short session timeout"),
|
|
|
|
// Environment-Specific Issues
|
|
["prod_using_dev_settings"] = (new Regex(@"(environment|env)\s*[=:]\s*['""]?(development|dev)['""]?", RegexOptions.IgnoreCase),
|
|
"Environment", "High", "Production environment using development settings"),
|
|
["hardcoded_environment"] = (new Regex(@"(server|host|endpoint)\s*[=:]\s*['""]localhost['""]", RegexOptions.IgnoreCase),
|
|
"Environment", "Medium", "Hardcoded localhost/development server"),
|
|
["missing_environment_config"] = (new Regex(@"\{\{.*\}\}|\$\{.*\}", RegexOptions.IgnoreCase),
|
|
"Environment", "Low", "Environment variable placeholder found"),
|
|
|
|
// Logging Security
|
|
["sensitive_data_logging"] = (new Regex(@"(log|logging).*(password|secret|key|token)", RegexOptions.IgnoreCase),
|
|
"Logging", "High", "Sensitive data may be logged"),
|
|
["excessive_logging"] = (new Regex(@"(loglevel|level)\s*[=:]\s*['""]?(debug|trace|verbose)['""]?", RegexOptions.IgnoreCase),
|
|
"Logging", "Low", "Excessive logging level in production"),
|
|
["log_injection_risk"] = (new Regex(@"log.*user.*input", RegexOptions.IgnoreCase),
|
|
"Logging", "Medium", "Potential log injection from user input")
|
|
};
|
|
|
|
// Positive configuration patterns (good practices)
|
|
private static readonly Dictionary<string, Regex> GoodConfigPatterns = new()
|
|
{
|
|
["environment_variables"] = new Regex(@"\$\{[^}]+\}|%[^%]+%", RegexOptions.IgnoreCase),
|
|
["https_enforced"] = new Regex(@"(https|ssl|tls)\s*[=:]\s*(true|enabled|yes)", RegexOptions.IgnoreCase),
|
|
["secure_cookies"] = new Regex(@"(httponly|secure)\s*[=:]\s*(true|yes)", RegexOptions.IgnoreCase),
|
|
["strong_tls"] = new Regex(@"(tls|ssl).*(1\.2|1\.3)", RegexOptions.IgnoreCase),
|
|
["security_headers"] = new Regex(@"(hsts|csp|x-frame-options|x-content-type-options)", RegexOptions.IgnoreCase),
|
|
["encrypted_connections"] = new Regex(@"encrypt\s*=\s*true", RegexOptions.IgnoreCase),
|
|
["certificate_validation"] = new Regex(@"(certificate|cert).*(validation|verify)\s*[=:]\s*(true|enabled)", RegexOptions.IgnoreCase)
|
|
};
|
|
|
|
public async Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters)
|
|
{
|
|
try
|
|
{
|
|
// Extract parameters
|
|
string configPath = parameters["configPath"].ToString();
|
|
bool checkSecrets = parameters.TryGetValue("checkSecrets", out var secretsObj) ? Convert.ToBoolean(secretsObj) : true;
|
|
bool checkTls = parameters.TryGetValue("checkTls", out var tlsObj) ? Convert.ToBoolean(tlsObj) : true;
|
|
bool checkDatabase = parameters.TryGetValue("checkDatabase", out var dbObj) ? Convert.ToBoolean(dbObj) : true;
|
|
bool checkCors = parameters.TryGetValue("checkCors", out var corsObj) ? Convert.ToBoolean(corsObj) : true;
|
|
bool checkEnvironments = parameters.TryGetValue("checkEnvironments", out var envObj) ? Convert.ToBoolean(envObj) : true;
|
|
|
|
// Validate path exists
|
|
if (!File.Exists(configPath) && !Directory.Exists(configPath))
|
|
{
|
|
return new AIPluginResult(
|
|
new FileNotFoundException($"Configuration path not found: {configPath}"),
|
|
"Configuration path not found"
|
|
);
|
|
}
|
|
|
|
// Find configuration files to analyze
|
|
var configFiles = GetConfigurationFiles(configPath);
|
|
if (!configFiles.Any())
|
|
{
|
|
return new AIPluginResult(
|
|
new FileNotFoundException($"No configuration files found in: {configPath}"),
|
|
"No configuration files found"
|
|
);
|
|
}
|
|
|
|
var results = new SecureConfigurationResults
|
|
{
|
|
ConfigPath = configPath,
|
|
FilesAnalyzed = configFiles.Count
|
|
};
|
|
|
|
// Analyze each configuration file
|
|
foreach (var configFile in configFiles)
|
|
{
|
|
await AnalyzeConfigurationFile(configFile, results, checkSecrets, checkTls, checkDatabase, checkCors, checkEnvironments);
|
|
}
|
|
|
|
// Calculate security rating
|
|
results.SecurityRating = CalculateSecurityRating(results);
|
|
|
|
// Generate configuration recommendations
|
|
results.ConfigurationRecommendations = GenerateConfigurationRecommendations(results);
|
|
|
|
return new AIPluginResult(results,
|
|
$"Configuration security analysis completed. Found {results.GetTotalIssues()} security issues.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AIPluginResult(ex, $"Configuration security analysis failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds configuration files to analyze
|
|
/// </summary>
|
|
private List<string> GetConfigurationFiles(string path)
|
|
{
|
|
var configFiles = new List<string>();
|
|
|
|
if (File.Exists(path))
|
|
{
|
|
if (IsConfigurationFile(path))
|
|
{
|
|
configFiles.Add(path);
|
|
}
|
|
}
|
|
else if (Directory.Exists(path))
|
|
{
|
|
// Look for common configuration file patterns
|
|
var patterns = new[]
|
|
{
|
|
"*.json", "*.config", "*.xml", "*.yml", "*.yaml",
|
|
"*.ini", "*.properties", "*.conf", "*.cfg", "*.env"
|
|
};
|
|
|
|
foreach (var pattern in patterns)
|
|
{
|
|
configFiles.AddRange(Directory.GetFiles(path, pattern, SearchOption.AllDirectories));
|
|
}
|
|
|
|
// Filter to actual configuration files
|
|
configFiles = configFiles.Where(IsConfigurationFile).ToList();
|
|
}
|
|
|
|
// Exclude common non-config directories
|
|
var excludedDirs = new[] { "node_modules", "bin", "obj", ".git", ".vs", "packages", "target", "build" };
|
|
configFiles = configFiles.Where(f => !excludedDirs.Any(dir => f.Contains($"{Path.DirectorySeparatorChar}{dir}{Path.DirectorySeparatorChar}"))).ToList();
|
|
|
|
return configFiles.Take(20).ToList(); // Limit for performance
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if a file is a configuration file
|
|
/// </summary>
|
|
private bool IsConfigurationFile(string filePath)
|
|
{
|
|
var fileName = Path.GetFileName(filePath).ToLower();
|
|
var configNames = new[]
|
|
{
|
|
"appsettings", "web.config", "app.config", "config", "settings",
|
|
"environment", ".env", "database", "connection", "cors", "security"
|
|
};
|
|
|
|
return configNames.Any(name => fileName.Contains(name)) ||
|
|
fileName.EndsWith(".json") ||
|
|
fileName.EndsWith(".config") ||
|
|
fileName.EndsWith(".xml") ||
|
|
fileName.EndsWith(".yml") ||
|
|
fileName.EndsWith(".yaml") ||
|
|
fileName.EndsWith(".ini") ||
|
|
fileName.EndsWith(".properties");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes a single configuration file
|
|
/// </summary>
|
|
private async Task AnalyzeConfigurationFile(string configFile, SecureConfigurationResults results,
|
|
bool checkSecrets, bool checkTls, bool checkDatabase, bool checkCors, bool checkEnvironments)
|
|
{
|
|
try
|
|
{
|
|
var content = await File.ReadAllTextAsync(configFile);
|
|
var fileName = Path.GetFileName(configFile);
|
|
var extension = Path.GetExtension(configFile).ToLower();
|
|
|
|
// Parse configuration based on file type
|
|
var configData = ParseConfigurationFile(content, extension);
|
|
|
|
// Perform security checks
|
|
if (checkSecrets)
|
|
{
|
|
await AnalyzeSecretsExposure(configFile, content, configData, results);
|
|
}
|
|
|
|
if (checkTls)
|
|
{
|
|
await AnalyzeTlsConfiguration(configFile, content, configData, results);
|
|
}
|
|
|
|
if (checkDatabase)
|
|
{
|
|
await AnalyzeDatabaseSecurity(configFile, content, configData, results);
|
|
}
|
|
|
|
if (checkCors)
|
|
{
|
|
await AnalyzeCorsConfiguration(configFile, content, configData, results);
|
|
}
|
|
|
|
if (checkEnvironments)
|
|
{
|
|
await AnalyzeEnvironmentConfiguration(configFile, content, configData, results);
|
|
}
|
|
|
|
// Check for good practices
|
|
await AnalyzeGoodPractices(configFile, content, results);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
results.AnalysisErrors.Add($"Error analyzing {configFile}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses configuration file content based on format
|
|
/// </summary>
|
|
private Dictionary<string, object> ParseConfigurationFile(string content, string extension)
|
|
{
|
|
var configData = new Dictionary<string, object>();
|
|
|
|
try
|
|
{
|
|
switch (extension)
|
|
{
|
|
case ".json":
|
|
if (content.Trim().StartsWith("{"))
|
|
{
|
|
var jsonDoc = JsonDocument.Parse(content);
|
|
configData = JsonElementToDictionary(jsonDoc.RootElement);
|
|
}
|
|
break;
|
|
|
|
case ".xml":
|
|
case ".config":
|
|
var xmlDoc = XDocument.Parse(content);
|
|
configData = XmlToDictionary(xmlDoc);
|
|
break;
|
|
|
|
case ".yml":
|
|
case ".yaml":
|
|
// Simple YAML parsing (key: value pairs)
|
|
configData = ParseSimpleYaml(content);
|
|
break;
|
|
|
|
case ".ini":
|
|
case ".properties":
|
|
configData = ParseKeyValueFile(content);
|
|
break;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// If parsing fails, fall back to regex analysis on raw content
|
|
}
|
|
|
|
return configData;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts JsonElement to Dictionary
|
|
/// </summary>
|
|
private Dictionary<string, object> JsonElementToDictionary(JsonElement element)
|
|
{
|
|
var dict = new Dictionary<string, object>();
|
|
|
|
if (element.ValueKind == JsonValueKind.Object)
|
|
{
|
|
foreach (var property in element.EnumerateObject())
|
|
{
|
|
dict[property.Name] = property.Value.ValueKind == JsonValueKind.Object
|
|
? JsonElementToDictionary(property.Value)
|
|
: property.Value.ToString();
|
|
}
|
|
}
|
|
|
|
return dict;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts XML to Dictionary (simplified)
|
|
/// </summary>
|
|
private Dictionary<string, object> XmlToDictionary(XDocument doc)
|
|
{
|
|
var dict = new Dictionary<string, object>();
|
|
|
|
foreach (var element in doc.Descendants())
|
|
{
|
|
if (!element.HasElements)
|
|
{
|
|
dict[element.Name.LocalName] = element.Value;
|
|
}
|
|
}
|
|
|
|
return dict;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses simple YAML (key: value pairs)
|
|
/// </summary>
|
|
private Dictionary<string, object> ParseSimpleYaml(string content)
|
|
{
|
|
var dict = new Dictionary<string, object>();
|
|
var lines = content.Split('\n');
|
|
|
|
foreach (var line in lines)
|
|
{
|
|
var trimmed = line.Trim();
|
|
if (trimmed.Contains(':') && !trimmed.StartsWith('#'))
|
|
{
|
|
var parts = trimmed.Split(':', 2);
|
|
if (parts.Length == 2)
|
|
{
|
|
dict[parts[0].Trim()] = parts[1].Trim();
|
|
}
|
|
}
|
|
}
|
|
|
|
return dict;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses key=value format files
|
|
/// </summary>
|
|
private Dictionary<string, object> ParseKeyValueFile(string content)
|
|
{
|
|
var dict = new Dictionary<string, object>();
|
|
var lines = content.Split('\n');
|
|
|
|
foreach (var line in lines)
|
|
{
|
|
var trimmed = line.Trim();
|
|
if (trimmed.Contains('=') && !trimmed.StartsWith('#') && !trimmed.StartsWith(';'))
|
|
{
|
|
var parts = trimmed.Split('=', 2);
|
|
if (parts.Length == 2)
|
|
{
|
|
dict[parts[0].Trim()] = parts[1].Trim();
|
|
}
|
|
}
|
|
}
|
|
|
|
return dict;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes configuration for exposed secrets
|
|
/// </summary>
|
|
private async Task AnalyzeSecretsExposure(string filePath, string content, Dictionary<string, object> configData, SecureConfigurationResults results)
|
|
{
|
|
var secretPatterns = ConfigPatterns.Where(p => p.Value.Type == "Secret").ToList();
|
|
|
|
foreach (var pattern in secretPatterns)
|
|
{
|
|
var matches = pattern.Value.Pattern.Matches(content);
|
|
foreach (Match match in matches)
|
|
{
|
|
// Additional validation for false positives
|
|
if (!IsLikelySecret(match.Value))
|
|
continue;
|
|
|
|
results.ExposedSecrets.Add(new ConfigurationIssue
|
|
{
|
|
Type = "Exposed Secret",
|
|
Severity = pattern.Value.Severity,
|
|
Description = pattern.Value.Description,
|
|
File = filePath,
|
|
LineNumber = GetLineNumber(content, match.Index),
|
|
ConfigKey = ExtractConfigKey(content, match.Index),
|
|
Value = MaskSensitiveValue(match.Value),
|
|
Recommendation = GetSecretsRecommendation(pattern.Key)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes TLS/HTTPS configuration
|
|
/// </summary>
|
|
private async Task AnalyzeTlsConfiguration(string filePath, string content, Dictionary<string, object> configData, SecureConfigurationResults results)
|
|
{
|
|
var tlsPatterns = ConfigPatterns.Where(p => p.Value.Type == "TLS").ToList();
|
|
|
|
foreach (var pattern in tlsPatterns)
|
|
{
|
|
var matches = pattern.Value.Pattern.Matches(content);
|
|
foreach (Match match in matches)
|
|
{
|
|
results.SecurityIssues.Add(new ConfigurationIssue
|
|
{
|
|
Type = "TLS/HTTPS Security",
|
|
Severity = pattern.Value.Severity,
|
|
Description = pattern.Value.Description,
|
|
File = filePath,
|
|
LineNumber = GetLineNumber(content, match.Index),
|
|
ConfigKey = ExtractConfigKey(content, match.Index),
|
|
Value = match.Value,
|
|
Recommendation = GetTlsRecommendation(pattern.Key)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes database security configuration
|
|
/// </summary>
|
|
private async Task AnalyzeDatabaseSecurity(string filePath, string content, Dictionary<string, object> configData, SecureConfigurationResults results)
|
|
{
|
|
var dbPatterns = ConfigPatterns.Where(p => p.Value.Type == "Database").ToList();
|
|
|
|
foreach (var pattern in dbPatterns)
|
|
{
|
|
var matches = pattern.Value.Pattern.Matches(content);
|
|
foreach (Match match in matches)
|
|
{
|
|
results.SecurityIssues.Add(new ConfigurationIssue
|
|
{
|
|
Type = "Database Security",
|
|
Severity = pattern.Value.Severity,
|
|
Description = pattern.Value.Description,
|
|
File = filePath,
|
|
LineNumber = GetLineNumber(content, match.Index),
|
|
ConfigKey = ExtractConfigKey(content, match.Index),
|
|
Value = MaskSensitiveValue(match.Value),
|
|
Recommendation = GetDatabaseRecommendation(pattern.Key)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes CORS configuration
|
|
/// </summary>
|
|
private async Task AnalyzeCorsConfiguration(string filePath, string content, Dictionary<string, object> configData, SecureConfigurationResults results)
|
|
{
|
|
var corsPatterns = ConfigPatterns.Where(p => p.Value.Type == "CORS").ToList();
|
|
|
|
foreach (var pattern in corsPatterns)
|
|
{
|
|
var matches = pattern.Value.Pattern.Matches(content);
|
|
foreach (Match match in matches)
|
|
{
|
|
results.SecurityIssues.Add(new ConfigurationIssue
|
|
{
|
|
Type = "CORS Security",
|
|
Severity = pattern.Value.Severity,
|
|
Description = pattern.Value.Description,
|
|
File = filePath,
|
|
LineNumber = GetLineNumber(content, match.Index),
|
|
ConfigKey = ExtractConfigKey(content, match.Index),
|
|
Value = match.Value,
|
|
Recommendation = GetCorsRecommendation(pattern.Key)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes environment-specific configuration
|
|
/// </summary>
|
|
private async Task AnalyzeEnvironmentConfiguration(string filePath, string content, Dictionary<string, object> configData, SecureConfigurationResults results)
|
|
{
|
|
var envPatterns = ConfigPatterns.Where(p => p.Value.Type == "Environment" || p.Value.Type == "General" || p.Value.Type == "Logging").ToList();
|
|
|
|
foreach (var pattern in envPatterns)
|
|
{
|
|
var matches = pattern.Value.Pattern.Matches(content);
|
|
foreach (Match match in matches)
|
|
{
|
|
var severity = pattern.Value.Severity;
|
|
|
|
// Adjust severity for environment-specific issues
|
|
if (pattern.Value.Type == "Environment" && IsProductionConfig(filePath))
|
|
{
|
|
severity = severity == "Medium" ? "High" : severity;
|
|
}
|
|
|
|
results.EnvironmentIssues.Add(new ConfigurationIssue
|
|
{
|
|
Type = pattern.Value.Type,
|
|
Severity = severity,
|
|
Description = pattern.Value.Description,
|
|
File = filePath,
|
|
LineNumber = GetLineNumber(content, match.Index),
|
|
ConfigKey = ExtractConfigKey(content, match.Index),
|
|
Value = match.Value,
|
|
Recommendation = GetEnvironmentRecommendation(pattern.Key)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes good security practices in configuration
|
|
/// </summary>
|
|
private async Task AnalyzeGoodPractices(string filePath, string content, SecureConfigurationResults results)
|
|
{
|
|
foreach (var pattern in GoodConfigPatterns)
|
|
{
|
|
if (pattern.Value.IsMatch(content))
|
|
{
|
|
var practiceDescription = GetGoodPracticeDescription(pattern.Key);
|
|
if (!results.GoodPractices.Contains(practiceDescription))
|
|
{
|
|
results.GoodPractices.Add(practiceDescription);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if a match is likely a real secret
|
|
/// </summary>
|
|
private bool IsLikelySecret(string value)
|
|
{
|
|
// Filter out common non-secrets
|
|
var nonSecrets = new[] { "password", "secret", "key", "token", "example", "placeholder", "your_", "xxx", "***", "localhost", "127.0.0.1" };
|
|
var lowerValue = value.ToLower();
|
|
|
|
if (nonSecrets.Any(ns => lowerValue.Contains(ns)))
|
|
return false;
|
|
|
|
// Check for reasonable length and complexity
|
|
var cleanValue = value.Replace("\"", "").Replace("'", "").Trim();
|
|
return cleanValue.Length >= 8 && cleanValue.Any(char.IsLetterOrDigit);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if configuration file is for production environment
|
|
/// </summary>
|
|
private bool IsProductionConfig(string filePath)
|
|
{
|
|
var fileName = Path.GetFileName(filePath).ToLower();
|
|
return fileName.Contains("prod") || fileName.Contains("production") || fileName.Contains("release");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracts configuration key from context
|
|
/// </summary>
|
|
private string ExtractConfigKey(string content, int index)
|
|
{
|
|
var lines = content.Split('\n');
|
|
var lineNumber = GetLineNumber(content, index) - 1;
|
|
|
|
if (lineNumber >= 0 && lineNumber < lines.Length)
|
|
{
|
|
var line = lines[lineNumber];
|
|
var colonIndex = line.IndexOf(':');
|
|
var equalsIndex = line.IndexOf('=');
|
|
|
|
if (colonIndex > 0 && (equalsIndex < 0 || colonIndex < equalsIndex))
|
|
{
|
|
return line.Substring(0, colonIndex).Trim().Trim('"', '\'');
|
|
}
|
|
else if (equalsIndex > 0)
|
|
{
|
|
return line.Substring(0, equalsIndex).Trim().Trim('"', '\'');
|
|
}
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Masks sensitive values for display
|
|
/// </summary>
|
|
private string MaskSensitiveValue(string value)
|
|
{
|
|
if (value.Length <= 4)
|
|
return "***";
|
|
|
|
return value.Substring(0, 2) + "***" + value.Substring(value.Length - 2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets line number for character index
|
|
/// </summary>
|
|
private int GetLineNumber(string content, int index)
|
|
{
|
|
return content.Take(index).Count(c => c == '\n') + 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets secrets-specific recommendations
|
|
/// </summary>
|
|
private string GetSecretsRecommendation(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"password_in_config" => "Move passwords to environment variables or secure key management systems like Azure Key Vault or AWS Secrets Manager.",
|
|
"connection_string_password" => "Use integrated authentication or move connection strings to secure configuration providers.",
|
|
"api_key_exposed" => "Store API keys in environment variables and use secure configuration providers.",
|
|
"jwt_secret_exposed" => "Move JWT secrets to environment variables and ensure they are cryptographically strong.",
|
|
"encryption_key_exposed" => "Store encryption keys in dedicated key management systems, never in configuration files.",
|
|
_ => "Move all secrets to environment variables or dedicated secret management systems."
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets TLS-specific recommendations
|
|
/// </summary>
|
|
private string GetTlsRecommendation(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"ssl_disabled" => "Enable HTTPS/TLS for all production environments to protect data in transit.",
|
|
"weak_tls_version" => "Use TLS 1.2 or 1.3. Disable older protocols (TLS 1.0/1.1, SSL 2.0/3.0).",
|
|
"insecure_ciphers" => "Configure strong cipher suites and disable weak algorithms like RC4, DES, MD5.",
|
|
"certificate_validation_disabled" => "Enable certificate validation to prevent man-in-the-middle attacks.",
|
|
_ => "Implement strong TLS configuration with modern protocols and cipher suites."
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets database-specific recommendations
|
|
/// </summary>
|
|
private string GetDatabaseRecommendation(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"integrated_security_disabled" => "Use integrated security (Windows Authentication) when possible for better security.",
|
|
"sql_server_trust_cert" => "Verify server certificates properly instead of blindly trusting them.",
|
|
"database_timeout_too_long" => "Use reasonable database timeouts to prevent resource exhaustion.",
|
|
"database_no_encryption" => "Enable encryption for database connections, especially over networks.",
|
|
_ => "Follow database security best practices including encrypted connections and proper authentication."
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets CORS-specific recommendations
|
|
/// </summary>
|
|
private string GetCorsRecommendation(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"cors_allow_all_origins" => "Specify explicit allowed origins instead of using wildcards (*) in production.",
|
|
"cors_allow_credentials_with_wildcard" => "Never allow credentials with wildcard origins - specify exact domains.",
|
|
"cors_unsafe_headers" => "Specify only required headers instead of allowing all headers (*).",
|
|
"cors_unsafe_methods" => "Review if unsafe HTTP methods (DELETE, PUT, PATCH) are necessary for CORS.",
|
|
_ => "Configure CORS with specific, minimal permissions based on actual requirements."
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets environment-specific recommendations
|
|
/// </summary>
|
|
private string GetEnvironmentRecommendation(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"prod_using_dev_settings" => "Ensure production environments use production-specific configuration, not development settings.",
|
|
"debug_mode_enabled" => "Disable debug mode in production to prevent information disclosure.",
|
|
"detailed_errors_enabled" => "Disable detailed error messages in production to prevent information leakage.",
|
|
"hardcoded_environment" => "Use environment-specific configuration instead of hardcoded development values.",
|
|
"sensitive_data_logging" => "Ensure sensitive data is not logged. Use structured logging with data classification.",
|
|
"excessive_logging" => "Use appropriate logging levels in production (Info/Warning/Error, not Debug/Trace).",
|
|
_ => "Review environment-specific settings and ensure appropriate configuration for each environment."
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets description for good practices
|
|
/// </summary>
|
|
private string GetGoodPracticeDescription(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"environment_variables" => "Uses environment variables for configuration",
|
|
"https_enforced" => "Enforces HTTPS/TLS encryption",
|
|
"secure_cookies" => "Uses secure cookie settings",
|
|
"strong_tls" => "Uses modern TLS versions (1.2/1.3)",
|
|
"security_headers" => "Implements security headers",
|
|
"encrypted_connections" => "Uses encrypted database connections",
|
|
"certificate_validation" => "Enables proper certificate validation",
|
|
_ => $"Implements {patternKey} security feature"
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates overall security rating
|
|
/// </summary>
|
|
private string CalculateSecurityRating(SecureConfigurationResults results)
|
|
{
|
|
var totalIssues = results.GetTotalIssues();
|
|
var criticalIssues = results.ExposedSecrets.Count(s => s.Severity == "Critical") +
|
|
results.SecurityIssues.Count(s => s.Severity == "Critical") +
|
|
results.EnvironmentIssues.Count(s => s.Severity == "Critical");
|
|
|
|
var highIssues = results.ExposedSecrets.Count(s => s.Severity == "High") +
|
|
results.SecurityIssues.Count(s => s.Severity == "High") +
|
|
results.EnvironmentIssues.Count(s => s.Severity == "High");
|
|
|
|
if (criticalIssues > 0)
|
|
return "Critical Risk";
|
|
else if (highIssues >= 3)
|
|
return "High Risk";
|
|
else if (totalIssues >= 5)
|
|
return "Medium Risk";
|
|
else if (totalIssues > 0)
|
|
return "Low Risk";
|
|
else
|
|
return "Secure";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates configuration recommendations
|
|
/// </summary>
|
|
private List<string> GenerateConfigurationRecommendations(SecureConfigurationResults results)
|
|
{
|
|
var recommendations = new List<string>();
|
|
|
|
// Critical secrets exposure
|
|
var criticalSecrets = results.ExposedSecrets.Where(s => s.Severity == "Critical").ToList();
|
|
if (criticalSecrets.Any())
|
|
{
|
|
recommendations.Add("🚨 **CRITICAL: SECRETS EXPOSED**:");
|
|
recommendations.Add($" • {criticalSecrets.Count} critical secrets found in configuration files");
|
|
recommendations.Add(" • Immediately move secrets to environment variables or key vaults");
|
|
recommendations.Add(" • Rotate any exposed secrets/keys immediately");
|
|
recommendations.Add(" • Review commit history for accidentally committed secrets");
|
|
}
|
|
|
|
// High severity issues
|
|
var highSecrets = results.ExposedSecrets.Where(s => s.Severity == "High").ToList();
|
|
if (highSecrets.Any())
|
|
{
|
|
recommendations.Add("⚠️ **HIGH PRIORITY: SECRET MANAGEMENT**:");
|
|
recommendations.Add($" • {highSecrets.Count} high-risk secrets found");
|
|
recommendations.Add(" • Implement proper secret management strategy");
|
|
recommendations.Add(" • Use Azure Key Vault, AWS Secrets Manager, or similar services");
|
|
}
|
|
|
|
// TLS/HTTPS issues
|
|
var tlsIssues = results.SecurityIssues.Where(i => i.Type.Contains("TLS")).ToList();
|
|
if (tlsIssues.Any())
|
|
{
|
|
recommendations.Add("🔒 **TLS/HTTPS SECURITY**:");
|
|
recommendations.Add($" • {tlsIssues.Count} TLS security issues found");
|
|
recommendations.Add(" • Enable HTTPS enforcement for all environments");
|
|
recommendations.Add(" • Use TLS 1.2 or 1.3, disable older protocols");
|
|
recommendations.Add(" • Configure strong cipher suites");
|
|
}
|
|
|
|
// Database security
|
|
var dbIssues = results.SecurityIssues.Where(i => i.Type.Contains("Database")).ToList();
|
|
if (dbIssues.Any())
|
|
{
|
|
recommendations.Add("🗄️ **DATABASE SECURITY**:");
|
|
recommendations.Add($" • {dbIssues.Count} database security issues found");
|
|
recommendations.Add(" • Enable connection encryption");
|
|
recommendations.Add(" • Use integrated authentication where possible");
|
|
recommendations.Add(" • Move connection strings to secure configuration");
|
|
}
|
|
|
|
// CORS issues
|
|
var corsIssues = results.SecurityIssues.Where(i => i.Type.Contains("CORS")).ToList();
|
|
if (corsIssues.Any())
|
|
{
|
|
recommendations.Add("🌐 **CORS CONFIGURATION**:");
|
|
recommendations.Add($" • {corsIssues.Count} CORS security issues found");
|
|
recommendations.Add(" • Specify explicit allowed origins (avoid wildcards)");
|
|
recommendations.Add(" • Review allowed headers and methods");
|
|
recommendations.Add(" • Never allow credentials with wildcard origins");
|
|
}
|
|
|
|
// Environment issues
|
|
if (results.EnvironmentIssues.Any())
|
|
{
|
|
recommendations.Add("⚙️ **ENVIRONMENT CONFIGURATION**:");
|
|
var envByType = results.EnvironmentIssues.GroupBy(i => i.Type).ToList();
|
|
foreach (var group in envByType.Take(3))
|
|
{
|
|
recommendations.Add($" • {group.Key}: {group.Count()} issues");
|
|
}
|
|
recommendations.Add(" • Ensure production uses production-specific settings");
|
|
recommendations.Add(" • Disable debug mode and detailed errors in production");
|
|
}
|
|
|
|
// General best practices
|
|
recommendations.Add("📋 **CONFIGURATION BEST PRACTICES**:");
|
|
recommendations.Add(" • Use environment variables for all sensitive values");
|
|
recommendations.Add(" • Implement configuration validation on startup");
|
|
recommendations.Add(" • Use different configuration files for each environment");
|
|
recommendations.Add(" • Enable security headers (HSTS, CSP, X-Frame-Options)");
|
|
recommendations.Add(" • Implement proper logging without sensitive data");
|
|
recommendations.Add(" • Regular security configuration reviews");
|
|
|
|
// Positive reinforcement
|
|
if (results.GoodPractices.Any())
|
|
{
|
|
recommendations.Add("✅ **GOOD PRACTICES FOUND**:");
|
|
foreach (var practice in results.GoodPractices.Take(5))
|
|
{
|
|
recommendations.Add($" • {practice}");
|
|
}
|
|
}
|
|
|
|
// Overall security rating recommendations
|
|
switch (results.SecurityRating)
|
|
{
|
|
case "Critical Risk":
|
|
recommendations.Add("🚨 **IMMEDIATE ACTION REQUIRED**: Critical security vulnerabilities found");
|
|
recommendations.Add(" • Stop deployment until critical issues are resolved");
|
|
recommendations.Add(" • Conduct emergency security review");
|
|
break;
|
|
case "High Risk":
|
|
recommendations.Add("⚠️ **HIGH PRIORITY**: Significant security improvements needed");
|
|
recommendations.Add(" • Address high-severity issues before next deployment");
|
|
break;
|
|
case "Medium Risk":
|
|
recommendations.Add("👍 **GOOD**: Address remaining issues to improve security posture");
|
|
break;
|
|
case "Low Risk":
|
|
recommendations.Add("🌟 **EXCELLENT**: Minor improvements needed");
|
|
break;
|
|
case "Secure":
|
|
recommendations.Add("🏆 **OUTSTANDING**: Configuration follows security best practices!");
|
|
recommendations.Add(" • Continue regular security reviews");
|
|
break;
|
|
}
|
|
|
|
return recommendations;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Results of secure configuration analysis
|
|
/// </summary>
|
|
public class SecureConfigurationResults
|
|
{
|
|
public string ConfigPath { get; set; }
|
|
public int FilesAnalyzed { get; set; }
|
|
public List<ConfigurationIssue> ExposedSecrets { get; set; } = new List<ConfigurationIssue>();
|
|
public List<ConfigurationIssue> SecurityIssues { get; set; } = new List<ConfigurationIssue>();
|
|
public List<ConfigurationIssue> EnvironmentIssues { get; set; } = new List<ConfigurationIssue>();
|
|
public List<string> GoodPractices { get; set; } = new List<string>();
|
|
public List<string> ConfigurationRecommendations { get; set; } = new List<string>();
|
|
public List<string> AnalysisErrors { get; set; } = new List<string>();
|
|
public string SecurityRating { get; set; }
|
|
|
|
public int GetTotalIssues() => ExposedSecrets.Count + SecurityIssues.Count + EnvironmentIssues.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a configuration security issue
|
|
/// </summary>
|
|
public class ConfigurationIssue
|
|
{
|
|
public string Type { get; set; }
|
|
public string Severity { get; set; }
|
|
public string Description { get; set; }
|
|
public string File { get; set; }
|
|
public int LineNumber { get; set; }
|
|
public string ConfigKey { get; set; }
|
|
public string Value { get; set; }
|
|
public string Recommendation { get; set; }
|
|
}
|
|
} |