757 lines
31 KiB
C#
Executable File
757 lines
31 KiB
C#
Executable File
using MarketAlly.AIPlugin;
|
|
using MarketAlly.AIPlugin.Security.Plugins;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace MarketAlly.AIPlugin.Security.Plugins
|
|
{
|
|
/// <summary>
|
|
/// Reviews authentication and authorization implementation for security best practices
|
|
/// </summary>
|
|
[AIPlugin("AuthenticationAnalyzer", "Reviews authentication and authorization implementation for security best practices")]
|
|
public class AuthenticationAnalyzerPlugin : IAIPlugin
|
|
{
|
|
[AIParameter("Full path to the file or directory to analyze", required: true)]
|
|
public string Path { get; set; }
|
|
|
|
[AIParameter("Check JWT implementation", required: false)]
|
|
public bool CheckJwt { get; set; } = true;
|
|
|
|
[AIParameter("Check OAuth implementation", required: false)]
|
|
public bool CheckOAuth { get; set; } = true;
|
|
|
|
[AIParameter("Check session management", required: false)]
|
|
public bool CheckSessions { get; set; } = true;
|
|
|
|
[AIParameter("Check authorization patterns", required: false)]
|
|
public bool CheckAuthorization { get; set; } = true;
|
|
|
|
[AIParameter("Check password handling", required: false)]
|
|
public bool CheckPasswords { get; set; } = true;
|
|
|
|
public IReadOnlyDictionary<string, Type> SupportedParameters => new Dictionary<string, Type>
|
|
{
|
|
["path"] = typeof(string),
|
|
["checkJwt"] = typeof(bool),
|
|
["checkOAuth"] = typeof(bool),
|
|
["checkSessions"] = typeof(bool),
|
|
["checkAuthorization"] = typeof(bool),
|
|
["checkPasswords"] = typeof(bool)
|
|
};
|
|
|
|
// Authentication security patterns
|
|
private static readonly Dictionary<string, (Regex Pattern, string Type, string Severity, string Description)> AuthPatterns = new()
|
|
{
|
|
// JWT Security Issues
|
|
["jwt_none_algorithm"] = (new Regex(@"(algorithm|alg)\s*[=:]\s*['""]none['""]", RegexOptions.IgnoreCase),
|
|
"JWT", "Critical", "JWT using 'none' algorithm - bypasses signature verification"),
|
|
["jwt_weak_secret"] = (new Regex(@"(jwt|token).*(secret|key)\s*[=:]\s*['""][^'""]{1,15}['""]", RegexOptions.IgnoreCase),
|
|
"JWT", "High", "JWT secret appears too short (less than 16 characters)"),
|
|
["jwt_hardcoded_secret"] = (new Regex(@"(jwt|token).*(secret|key)\s*[=:]\s*['""].*['""]", RegexOptions.IgnoreCase),
|
|
"JWT", "High", "JWT secret appears to be hardcoded"),
|
|
["jwt_no_expiration"] = (new Regex(@"new\s+JwtSecurityToken\([^)]*\)(?!.*exp)", RegexOptions.IgnoreCase),
|
|
"JWT", "Medium", "JWT token created without expiration"),
|
|
["jwt_long_expiration"] = (new Regex(@"(exp|expires|expiry).*(\d{4,}|TimeSpan\.FromDays\(\s*[3-9]\d+)", RegexOptions.IgnoreCase),
|
|
"JWT", "Medium", "JWT token has very long expiration time"),
|
|
|
|
// Password Security Issues
|
|
["plain_text_password"] = (new Regex(@"password\s*[=:]\s*['""][^'""]*['""](?!.*hash|Hash|encrypt|Encrypt)", RegexOptions.IgnoreCase),
|
|
"Password", "Critical", "Password appears to be stored or compared in plain text"),
|
|
["weak_hash_algorithm"] = (new Regex(@"(MD5|SHA1|CRC32)\s*\(", RegexOptions.IgnoreCase),
|
|
"Password", "High", "Using weak hashing algorithm for passwords"),
|
|
["no_salt"] = (new Regex(@"(SHA256|SHA512|MD5|SHA1)\s*\([^)]*password[^)]*\)(?!.*salt)", RegexOptions.IgnoreCase),
|
|
"Password", "High", "Password hashing without salt"),
|
|
["hardcoded_salt"] = (new Regex(@"salt\s*[=:]\s*['""][^'""]*['""]", RegexOptions.IgnoreCase),
|
|
"Password", "High", "Hardcoded salt value"),
|
|
["weak_password_policy"] = (new Regex(@"password.*length.*[1-5]", RegexOptions.IgnoreCase),
|
|
"Password", "Medium", "Weak password length requirement (less than 6 characters)"),
|
|
|
|
// Session Management Issues
|
|
["session_fixation"] = (new Regex(@"Session\[.*\]\s*=.*(?!.*Regenerate|regenerate)", RegexOptions.IgnoreCase),
|
|
"Session", "Medium", "Session value assignment without regeneration"),
|
|
["insecure_session_cookie"] = (new Regex(@"(HttpOnly|Secure)\s*[=:]\s*false", RegexOptions.IgnoreCase),
|
|
"Session", "High", "Session cookie missing security flags"),
|
|
["long_session_timeout"] = (new Regex(@"timeout\s*[=:]\s*['""]?\d{4,}['""]?", RegexOptions.IgnoreCase),
|
|
"Session", "Medium", "Very long session timeout"),
|
|
["session_without_timeout"] = (new Regex(@"session.*(?!.*timeout|Timeout|expire|Expire)", RegexOptions.IgnoreCase),
|
|
"Session", "Low", "Session configuration without explicit timeout"),
|
|
|
|
// OAuth Security Issues
|
|
["oauth_state_missing"] = (new Regex(@"oauth.*authorize.*(?!.*state)", RegexOptions.IgnoreCase),
|
|
"OAuth", "High", "OAuth authorization without state parameter (CSRF vulnerable)"),
|
|
["oauth_pkce_missing"] = (new Regex(@"oauth.*code.*(?!.*code_challenge)", RegexOptions.IgnoreCase),
|
|
"OAuth", "Medium", "OAuth authorization code flow without PKCE"),
|
|
["oauth_implicit_flow"] = (new Regex(@"response_type\s*[=:]\s*['""]token['""]", RegexOptions.IgnoreCase),
|
|
"OAuth", "High", "OAuth implicit flow usage (deprecated and insecure)"),
|
|
["oauth_insecure_redirect"] = (new Regex(@"redirect_uri.*http://", RegexOptions.IgnoreCase),
|
|
"OAuth", "High", "OAuth redirect URI using HTTP instead of HTTPS"),
|
|
|
|
// Authorization Issues
|
|
["missing_authorization"] = (new Regex(@"public\s+\w+\s+\w+\s*\([^)]*\)(?!.*\[Authorize\]|\[AllowAnonymous\])", RegexOptions.IgnoreCase),
|
|
"Authorization", "Medium", "Public action method without authorization attribute"),
|
|
["overly_permissive_auth"] = (new Regex(@"\[AllowAnonymous\]", RegexOptions.IgnoreCase),
|
|
"Authorization", "Low", "Method allows anonymous access - verify if intentional"),
|
|
["role_hardcoded"] = (new Regex(@"Roles?\s*[=:]\s*['""][^'""]*['""]", RegexOptions.IgnoreCase),
|
|
"Authorization", "Medium", "Hardcoded role names in authorization"),
|
|
["privilege_escalation"] = (new Regex(@"(IsInRole|HasClaim|HasPermission).*(?!.*current.*user)", RegexOptions.IgnoreCase),
|
|
"Authorization", "Medium", "Potential privilege escalation - not checking current user context"),
|
|
|
|
// General Authentication Issues
|
|
["insecure_authentication"] = (new Regex(@"(authenticate|login).*http://", RegexOptions.IgnoreCase),
|
|
"Authentication", "High", "Authentication over insecure HTTP connection"),
|
|
["weak_encryption"] = (new Regex(@"(DES|3DES|RC4)\s*\(", RegexOptions.IgnoreCase),
|
|
"Authentication", "High", "Using weak encryption algorithm"),
|
|
["insecure_random"] = (new Regex(@"Random\s*\(\).*password|token|secret", RegexOptions.IgnoreCase),
|
|
"Authentication", "Medium", "Using non-cryptographic random for security tokens"),
|
|
["authentication_bypass"] = (new Regex(@"if\s*\([^)]*debug[^)]*\).*return.*true", RegexOptions.IgnoreCase),
|
|
"Authentication", "Critical", "Potential authentication bypass in debug mode"),
|
|
|
|
// Multi-Factor Authentication
|
|
["missing_2fa"] = (new Regex(@"login|authenticate(?!.*two.*factor|2fa|mfa|totp)", RegexOptions.IgnoreCase),
|
|
"Authentication", "Low", "Authentication without multi-factor authentication"),
|
|
["insecure_2fa"] = (new Regex(@"(sms|email).*token.*authenticate", RegexOptions.IgnoreCase),
|
|
"Authentication", "Medium", "Using SMS/Email for 2FA (less secure than TOTP/hardware keys)")
|
|
};
|
|
|
|
// Positive authentication patterns (good practices)
|
|
private static readonly Dictionary<string, Regex> GoodAuthPatterns = new()
|
|
{
|
|
["bcrypt_usage"] = new Regex(@"BCrypt\.(HashPassword|VerifyPassword)", RegexOptions.IgnoreCase),
|
|
["argon2_usage"] = new Regex(@"Argon2", RegexOptions.IgnoreCase),
|
|
["pbkdf2_usage"] = new Regex(@"PBKDF2|Rfc2898DeriveBytes", RegexOptions.IgnoreCase),
|
|
["secure_jwt"] = new Regex(@"(HS256|RS256|ES256)", RegexOptions.IgnoreCase),
|
|
["jwt_validation"] = new Regex(@"ValidateIssuer|ValidateAudience|ValidateLifetime", RegexOptions.IgnoreCase),
|
|
["authorize_attribute"] = new Regex(@"\[Authorize\]", RegexOptions.IgnoreCase),
|
|
["https_enforcement"] = new Regex(@"RequireHttps|UseHttpsRedirection", RegexOptions.IgnoreCase),
|
|
["secure_cookies"] = new Regex(@"HttpOnly\s*=\s*true|Secure\s*=\s*true", RegexOptions.IgnoreCase),
|
|
["csrf_protection"] = new Regex(@"ValidateAntiForgeryToken|AntiForgeryToken", RegexOptions.IgnoreCase),
|
|
["rate_limiting"] = new Regex(@"RateLimit|Throttle", RegexOptions.IgnoreCase),
|
|
["account_lockout"] = new Regex(@"LockoutEnabled|MaxFailedAttempts", RegexOptions.IgnoreCase),
|
|
["password_complexity"] = new Regex(@"RequiredLength|RequireDigit|RequireUppercase", RegexOptions.IgnoreCase)
|
|
};
|
|
|
|
public async Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters)
|
|
{
|
|
try
|
|
{
|
|
// Extract parameters
|
|
string path = parameters["path"].ToString();
|
|
bool checkJwt = parameters.TryGetValue("checkJwt", out var jwtObj) ? Convert.ToBoolean(jwtObj) : true;
|
|
bool checkOAuth = parameters.TryGetValue("checkOAuth", out var oauthObj) ? Convert.ToBoolean(oauthObj) : true;
|
|
bool checkSessions = parameters.TryGetValue("checkSessions", out var sessionObj) ? Convert.ToBoolean(sessionObj) : true;
|
|
bool checkAuthorization = parameters.TryGetValue("checkAuthorization", out var authzObj) ? Convert.ToBoolean(authzObj) : true;
|
|
bool checkPasswords = parameters.TryGetValue("checkPasswords", out var pwdObj) ? Convert.ToBoolean(pwdObj) : true;
|
|
|
|
// Validate path exists
|
|
if (!File.Exists(path) && !Directory.Exists(path))
|
|
{
|
|
return new AIPluginResult(
|
|
new FileNotFoundException($"Path not found: {path}"),
|
|
"Path not found"
|
|
);
|
|
}
|
|
|
|
// Collect files to analyze
|
|
var filesToAnalyze = GetFilesToAnalyze(path);
|
|
var results = new AuthenticationAnalysisResults
|
|
{
|
|
AnalyzedPath = path,
|
|
FilesAnalyzed = filesToAnalyze.Count
|
|
};
|
|
|
|
// Analyze each file
|
|
foreach (var file in filesToAnalyze)
|
|
{
|
|
var content = await File.ReadAllTextAsync(file);
|
|
var fileName = System.IO.Path.GetFileName(file);
|
|
|
|
if (checkJwt)
|
|
{
|
|
await AnalyzeJwtSecurity(file, content, results);
|
|
}
|
|
|
|
if (checkOAuth)
|
|
{
|
|
await AnalyzeOAuthSecurity(file, content, results);
|
|
}
|
|
|
|
if (checkSessions)
|
|
{
|
|
await AnalyzeSessionManagement(file, content, results);
|
|
}
|
|
|
|
if (checkAuthorization)
|
|
{
|
|
await AnalyzeAuthorizationPatterns(file, content, results);
|
|
}
|
|
|
|
if (checkPasswords)
|
|
{
|
|
await AnalyzePasswordSecurity(file, content, results);
|
|
}
|
|
|
|
// Check for good authentication practices
|
|
await AnalyzeGoodPractices(file, content, results);
|
|
}
|
|
|
|
// Calculate compliance score
|
|
results.ComplianceScore = CalculateComplianceScore(results);
|
|
|
|
// Generate security recommendations
|
|
results.SecurityRecommendations = GenerateSecurityRecommendations(results);
|
|
|
|
return new AIPluginResult(results,
|
|
$"Authentication analysis completed. Found {results.GetTotalIssues()} authentication/authorization issues.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AIPluginResult(ex, $"Authentication analysis failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates security recommendations based on findings
|
|
/// </summary>
|
|
private List<string> GenerateSecurityRecommendations(AuthenticationAnalysisResults results)
|
|
{
|
|
var recommendations = new List<string>();
|
|
|
|
// Critical issues first
|
|
var criticalIssues = results.AuthenticationIssues.Where(i => i.Severity == "Critical").ToList();
|
|
if (criticalIssues.Any())
|
|
{
|
|
recommendations.Add("🚨 **CRITICAL AUTHENTICATION ISSUES**:");
|
|
foreach (var issue in criticalIssues.Take(3))
|
|
{
|
|
recommendations.Add($" • {issue.Description} (Line {issue.LineNumber} in {System.IO.Path.GetFileName(issue.File)})");
|
|
}
|
|
recommendations.Add(" → These issues can lead to complete authentication bypass!");
|
|
}
|
|
|
|
// High severity issues
|
|
var highIssues = results.AuthenticationIssues.Where(i => i.Severity == "High").ToList();
|
|
if (highIssues.Any())
|
|
{
|
|
recommendations.Add("⚠️ **HIGH PRIORITY AUTHENTICATION ISSUES**:");
|
|
recommendations.Add($" • {highIssues.Count} high-severity authentication vulnerabilities found");
|
|
|
|
var issueTypes = highIssues.GroupBy(i => i.Type).ToList();
|
|
foreach (var group in issueTypes)
|
|
{
|
|
recommendations.Add($" • {group.Key}: {group.Count()} issues");
|
|
}
|
|
}
|
|
|
|
// Authorization issues
|
|
if (results.AuthorizationIssues.Any())
|
|
{
|
|
recommendations.Add("🛡️ **AUTHORIZATION IMPROVEMENTS NEEDED**:");
|
|
var authzByType = results.AuthorizationIssues.GroupBy(i => i.Description).ToList();
|
|
foreach (var group in authzByType.Take(3))
|
|
{
|
|
recommendations.Add($" • {group.Key}: {group.Count()} instances");
|
|
}
|
|
}
|
|
|
|
// Specific technology recommendations
|
|
var jwtIssues = results.AuthenticationIssues.Where(i => i.Type.Contains("JWT")).Count();
|
|
if (jwtIssues > 0)
|
|
{
|
|
recommendations.Add("🔑 **JWT SECURITY IMPROVEMENTS**:");
|
|
recommendations.Add(" • Use strong secrets (32+ characters) or RSA keys");
|
|
recommendations.Add(" • Implement short token lifetimes (15-60 minutes)");
|
|
recommendations.Add(" • Add proper token validation and algorithm verification");
|
|
recommendations.Add(" • Consider using refresh tokens for long-lived sessions");
|
|
}
|
|
|
|
var passwordIssues = results.AuthenticationIssues.Where(i => i.Type.Contains("Password")).Count();
|
|
if (passwordIssues > 0)
|
|
{
|
|
recommendations.Add("🔒 **PASSWORD SECURITY IMPROVEMENTS**:");
|
|
recommendations.Add(" • Use bcrypt, Argon2, or PBKDF2 for password hashing");
|
|
recommendations.Add(" • Implement unique salts for each password");
|
|
recommendations.Add(" • Enforce strong password policies (8+ chars, complexity)");
|
|
recommendations.Add(" • Never store passwords in plain text or use weak hashes");
|
|
}
|
|
|
|
var sessionIssues = results.AuthenticationIssues.Where(i => i.Type.Contains("Session")).Count();
|
|
if (sessionIssues > 0)
|
|
{
|
|
recommendations.Add("📝 **SESSION MANAGEMENT IMPROVEMENTS**:");
|
|
recommendations.Add(" • Set HttpOnly and Secure flags on session cookies");
|
|
recommendations.Add(" • Implement appropriate session timeouts");
|
|
recommendations.Add(" • Regenerate session IDs after authentication");
|
|
recommendations.Add(" • Use secure session storage mechanisms");
|
|
}
|
|
|
|
// General security recommendations
|
|
recommendations.Add("📋 **GENERAL AUTHENTICATION BEST PRACTICES**:");
|
|
recommendations.Add(" • Enforce HTTPS for all authentication endpoints");
|
|
recommendations.Add(" • Implement rate limiting on login attempts");
|
|
recommendations.Add(" • Add account lockout protection");
|
|
recommendations.Add(" • Use multi-factor authentication where possible");
|
|
recommendations.Add(" • Implement proper CSRF protection");
|
|
recommendations.Add(" • Log and monitor authentication events");
|
|
|
|
// Positive reinforcement for good practices
|
|
if (results.GoodPractices.Any())
|
|
{
|
|
recommendations.Add("✅ **GOOD SECURITY PRACTICES FOUND**:");
|
|
foreach (var practice in results.GoodPractices.Take(5))
|
|
{
|
|
recommendations.Add($" • {practice}");
|
|
}
|
|
}
|
|
|
|
// Compliance score recommendations
|
|
if (results.ComplianceScore >= 80)
|
|
{
|
|
recommendations.Add("🌟 **EXCELLENT**: Your authentication implementation follows security best practices!");
|
|
recommendations.Add(" • Continue regular security reviews and updates");
|
|
}
|
|
else if (results.ComplianceScore >= 60)
|
|
{
|
|
recommendations.Add("👍 **GOOD**: Address remaining issues to strengthen authentication security");
|
|
recommendations.Add(" • Focus on high and critical severity issues first");
|
|
}
|
|
else if (results.ComplianceScore >= 40)
|
|
{
|
|
recommendations.Add("⚡ **NEEDS IMPROVEMENT**: Significant authentication security gaps found");
|
|
recommendations.Add(" • Prioritize fixing critical and high-severity issues immediately");
|
|
recommendations.Add(" • Consider security training for the development team");
|
|
}
|
|
else
|
|
{
|
|
recommendations.Add("🚨 **CRITICAL**: Authentication security requires immediate attention");
|
|
recommendations.Add(" • Conduct thorough security review and remediation");
|
|
recommendations.Add(" • Consider engaging security experts for assessment");
|
|
recommendations.Add(" • Implement security-first development practices");
|
|
}
|
|
|
|
return recommendations;
|
|
}
|
|
/// Gets list of files to analyze based on path
|
|
/// </summary>
|
|
private List<string> GetFilesToAnalyze(string path)
|
|
{
|
|
var files = new List<string>();
|
|
|
|
if (File.Exists(path))
|
|
{
|
|
files.Add(path);
|
|
}
|
|
else if (Directory.Exists(path))
|
|
{
|
|
// Focus on authentication-related files
|
|
var extensions = new[] { "*.cs", "*.js", "*.ts", "*.py", "*.java", "*.php", "*.config", "*.json" };
|
|
|
|
foreach (var extension in extensions)
|
|
{
|
|
files.AddRange(Directory.GetFiles(path, extension, SearchOption.AllDirectories));
|
|
}
|
|
|
|
// Filter to files likely to contain authentication code
|
|
files = files.Where(f =>
|
|
f.Contains("auth", StringComparison.OrdinalIgnoreCase) ||
|
|
f.Contains("login", StringComparison.OrdinalIgnoreCase) ||
|
|
f.Contains("security", StringComparison.OrdinalIgnoreCase) ||
|
|
f.Contains("jwt", StringComparison.OrdinalIgnoreCase) ||
|
|
f.Contains("oauth", StringComparison.OrdinalIgnoreCase) ||
|
|
f.Contains("controller", StringComparison.OrdinalIgnoreCase) ||
|
|
f.Contains("service", StringComparison.OrdinalIgnoreCase) ||
|
|
System.IO.Path.GetFileName(f).Equals("startup.cs", StringComparison.OrdinalIgnoreCase) ||
|
|
System.IO.Path.GetFileName(f).Equals("program.cs", StringComparison.OrdinalIgnoreCase)
|
|
).ToList();
|
|
|
|
// If no specific auth files found, include all code files (up to 20)
|
|
if (files.Count == 0)
|
|
{
|
|
foreach (var extension in extensions)
|
|
{
|
|
files.AddRange(Directory.GetFiles(path, extension, SearchOption.AllDirectories).Take(20));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Exclude common non-auth directories
|
|
var excludedDirs = new[] { "node_modules", "bin", "obj", ".git", ".vs", "packages", "target", "build" };
|
|
files = files.Where(f => !excludedDirs.Any(dir => f.Contains($"{System.IO.Path.DirectorySeparatorChar}{dir}{System.IO.Path.DirectorySeparatorChar}"))).ToList();
|
|
|
|
return files.Take(50).ToList(); // Limit to 50 files for performance
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes JWT security implementation
|
|
/// </summary>
|
|
private async Task AnalyzeJwtSecurity(string filePath, string content, AuthenticationAnalysisResults results)
|
|
{
|
|
var jwtPatterns = AuthPatterns.Where(p => p.Value.Type == "JWT").ToList();
|
|
|
|
foreach (var pattern in jwtPatterns)
|
|
{
|
|
var matches = pattern.Value.Pattern.Matches(content);
|
|
foreach (Match match in matches)
|
|
{
|
|
results.AuthenticationIssues.Add(new AuthenticationIssue
|
|
{
|
|
Type = "JWT Security",
|
|
Severity = pattern.Value.Severity,
|
|
Description = pattern.Value.Description,
|
|
File = filePath,
|
|
LineNumber = GetLineNumber(content, match.Index),
|
|
Code = GetContextLines(content, match.Index, 2),
|
|
Recommendation = GetJwtRecommendation(pattern.Key)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes OAuth security implementation
|
|
/// </summary>
|
|
private async Task AnalyzeOAuthSecurity(string filePath, string content, AuthenticationAnalysisResults results)
|
|
{
|
|
var oauthPatterns = AuthPatterns.Where(p => p.Value.Type == "OAuth").ToList();
|
|
|
|
foreach (var pattern in oauthPatterns)
|
|
{
|
|
var matches = pattern.Value.Pattern.Matches(content);
|
|
foreach (Match match in matches)
|
|
{
|
|
results.AuthenticationIssues.Add(new AuthenticationIssue
|
|
{
|
|
Type = "OAuth Security",
|
|
Severity = pattern.Value.Severity,
|
|
Description = pattern.Value.Description,
|
|
File = filePath,
|
|
LineNumber = GetLineNumber(content, match.Index),
|
|
Code = GetContextLines(content, match.Index, 2),
|
|
Recommendation = GetOAuthRecommendation(pattern.Key)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes session management security
|
|
/// </summary>
|
|
private async Task AnalyzeSessionManagement(string filePath, string content, AuthenticationAnalysisResults results)
|
|
{
|
|
var sessionPatterns = AuthPatterns.Where(p => p.Value.Type == "Session").ToList();
|
|
|
|
foreach (var pattern in sessionPatterns)
|
|
{
|
|
var matches = pattern.Value.Pattern.Matches(content);
|
|
foreach (Match match in matches)
|
|
{
|
|
results.AuthenticationIssues.Add(new AuthenticationIssue
|
|
{
|
|
Type = "Session Management",
|
|
Severity = pattern.Value.Severity,
|
|
Description = pattern.Value.Description,
|
|
File = filePath,
|
|
LineNumber = GetLineNumber(content, match.Index),
|
|
Code = GetContextLines(content, match.Index, 2),
|
|
Recommendation = GetSessionRecommendation(pattern.Key)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes authorization patterns
|
|
/// </summary>
|
|
private async Task AnalyzeAuthorizationPatterns(string filePath, string content, AuthenticationAnalysisResults results)
|
|
{
|
|
var authzPatterns = AuthPatterns.Where(p => p.Value.Type == "Authorization").ToList();
|
|
|
|
foreach (var pattern in authzPatterns)
|
|
{
|
|
var matches = pattern.Value.Pattern.Matches(content);
|
|
foreach (Match match in matches)
|
|
{
|
|
// Skip if this is a documented exception
|
|
if (IsDocumentedException(content, match.Index))
|
|
continue;
|
|
|
|
results.AuthorizationIssues.Add(new AuthorizationIssue
|
|
{
|
|
Type = "Authorization",
|
|
Severity = pattern.Value.Severity,
|
|
Description = pattern.Value.Description,
|
|
File = filePath,
|
|
LineNumber = GetLineNumber(content, match.Index),
|
|
Code = GetContextLines(content, match.Index, 2),
|
|
Recommendation = GetAuthorizationRecommendation(pattern.Key)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes password security practices
|
|
/// </summary>
|
|
private async Task AnalyzePasswordSecurity(string filePath, string content, AuthenticationAnalysisResults results)
|
|
{
|
|
var passwordPatterns = AuthPatterns.Where(p => p.Value.Type == "Password").ToList();
|
|
|
|
foreach (var pattern in passwordPatterns)
|
|
{
|
|
var matches = pattern.Value.Pattern.Matches(content);
|
|
foreach (Match match in matches)
|
|
{
|
|
results.AuthenticationIssues.Add(new AuthenticationIssue
|
|
{
|
|
Type = "Password Security",
|
|
Severity = pattern.Value.Severity,
|
|
Description = pattern.Value.Description,
|
|
File = filePath,
|
|
LineNumber = GetLineNumber(content, match.Index),
|
|
Code = GetContextLines(content, match.Index, 2),
|
|
Recommendation = GetPasswordRecommendation(pattern.Key)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes good authentication practices
|
|
/// </summary>
|
|
private async Task AnalyzeGoodPractices(string filePath, string content, AuthenticationAnalysisResults results)
|
|
{
|
|
foreach (var pattern in GoodAuthPatterns)
|
|
{
|
|
if (pattern.Value.IsMatch(content))
|
|
{
|
|
var practiceDescription = GetGoodPracticeDescription(pattern.Key);
|
|
if (!results.GoodPractices.Contains(practiceDescription))
|
|
{
|
|
results.GoodPractices.Add(practiceDescription);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if an issue is documented as an exception
|
|
/// </summary>
|
|
private bool IsDocumentedException(string content, int index)
|
|
{
|
|
var contextLines = GetContextLines(content, index, 3);
|
|
return contextLines.Contains("// Security exception:", StringComparison.OrdinalIgnoreCase) ||
|
|
contextLines.Contains("// Allow anonymous:", StringComparison.OrdinalIgnoreCase) ||
|
|
contextLines.Contains("// Public API", StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets line number for a character index
|
|
/// </summary>
|
|
private int GetLineNumber(string content, int index)
|
|
{
|
|
return content.Take(index).Count(c => c == '\n') + 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets context lines around a match
|
|
/// </summary>
|
|
private string GetContextLines(string content, int index, int contextLines)
|
|
{
|
|
var lines = content.Split('\n');
|
|
var targetLine = GetLineNumber(content, index) - 1;
|
|
|
|
var startLine = Math.Max(0, targetLine - contextLines);
|
|
var endLine = Math.Min(lines.Length - 1, targetLine + contextLines);
|
|
|
|
var contextList = new List<string>();
|
|
for (int i = startLine; i <= endLine; i++)
|
|
{
|
|
var marker = i == targetLine ? ">>> " : " ";
|
|
contextList.Add($"{marker}{i + 1}: {lines[i]}");
|
|
}
|
|
|
|
return string.Join("\n", contextList);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets JWT-specific recommendations
|
|
/// </summary>
|
|
private string GetJwtRecommendation(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"jwt_none_algorithm" => "Never use 'none' algorithm for JWT. Use HS256, RS256, or ES256 instead.",
|
|
"jwt_weak_secret" => "Use a strong JWT secret with at least 32 characters. Consider using RSA keys for RS256.",
|
|
"jwt_hardcoded_secret" => "Store JWT secrets in environment variables or secure key management systems.",
|
|
"jwt_no_expiration" => "Always set expiration time for JWT tokens. Use short lifetimes (15-60 minutes) with refresh tokens.",
|
|
"jwt_long_expiration" => "Use shorter JWT expiration times (15-60 minutes) and implement refresh token mechanism.",
|
|
_ => "Follow JWT security best practices: strong secrets, proper algorithms, and reasonable expiration times."
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets OAuth-specific recommendations
|
|
/// </summary>
|
|
private string GetOAuthRecommendation(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"oauth_state_missing" => "Always include and validate the 'state' parameter to prevent CSRF attacks.",
|
|
"oauth_pkce_missing" => "Implement PKCE (Proof Key for Code Exchange) for public clients and mobile apps.",
|
|
"oauth_implicit_flow" => "Replace implicit flow with authorization code flow + PKCE for better security.",
|
|
"oauth_insecure_redirect" => "Use HTTPS for all OAuth redirect URIs to prevent token interception.",
|
|
_ => "Follow OAuth 2.1 security best practices and avoid deprecated flows."
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets session-specific recommendations
|
|
/// </summary>
|
|
private string GetSessionRecommendation(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"session_fixation" => "Regenerate session IDs after authentication to prevent session fixation attacks.",
|
|
"insecure_session_cookie" => "Set HttpOnly=true and Secure=true for session cookies.",
|
|
"long_session_timeout" => "Use shorter session timeouts (30-60 minutes) for better security.",
|
|
"session_without_timeout" => "Configure explicit session timeouts based on your security requirements.",
|
|
_ => "Implement secure session management with proper timeouts and cookie security flags."
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets authorization-specific recommendations
|
|
/// </summary>
|
|
private string GetAuthorizationRecommendation(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"missing_authorization" => "Add [Authorize] attribute to actions that require authentication. Use [AllowAnonymous] only when necessary.",
|
|
"overly_permissive_auth" => "Review [AllowAnonymous] usage. Document why anonymous access is needed.",
|
|
"role_hardcoded" => "Use configuration or constants for role names instead of hardcoding strings.",
|
|
"privilege_escalation" => "Always check authorization against the current user context, not just role existence.",
|
|
_ => "Implement proper authorization with appropriate attributes and user context validation."
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets password-specific recommendations
|
|
/// </summary>
|
|
private string GetPasswordRecommendation(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"plain_text_password" => "Never store or compare passwords in plain text. Use bcrypt, Argon2, or PBKDF2.",
|
|
"weak_hash_algorithm" => "Use strong password hashing algorithms like bcrypt, Argon2, or PBKDF2 instead of MD5/SHA1.",
|
|
"no_salt" => "Always use unique salts for password hashing to prevent rainbow table attacks.",
|
|
"hardcoded_salt" => "Generate unique, random salts for each password instead of using hardcoded values.",
|
|
"weak_password_policy" => "Implement strong password policies: minimum 8 characters, complexity requirements.",
|
|
_ => "Use industry-standard password hashing with salts and implement strong password policies."
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets description for good practices
|
|
/// </summary>
|
|
private string GetGoodPracticeDescription(string patternKey)
|
|
{
|
|
return patternKey switch
|
|
{
|
|
"bcrypt_usage" => "Uses BCrypt for secure password hashing",
|
|
"argon2_usage" => "Uses Argon2 for secure password hashing",
|
|
"pbkdf2_usage" => "Uses PBKDF2 for secure password hashing",
|
|
"secure_jwt" => "Uses secure JWT algorithms (HS256/RS256/ES256)",
|
|
"jwt_validation" => "Implements proper JWT validation",
|
|
"authorize_attribute" => "Uses [Authorize] attributes for access control",
|
|
"https_enforcement" => "Enforces HTTPS for secure communications",
|
|
"secure_cookies" => "Uses secure cookie settings (HttpOnly, Secure)",
|
|
"csrf_protection" => "Implements CSRF protection",
|
|
"rate_limiting" => "Implements rate limiting for authentication",
|
|
"account_lockout" => "Implements account lockout protection",
|
|
"password_complexity" => "Enforces password complexity requirements",
|
|
_ => $"Implements {patternKey} security feature"
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates compliance score based on findings
|
|
/// </summary>
|
|
private int CalculateComplianceScore(AuthenticationAnalysisResults results)
|
|
{
|
|
var baseScore = 100;
|
|
var severityPenalties = new Dictionary<string, int>
|
|
{
|
|
["Critical"] = 30,
|
|
["High"] = 20,
|
|
["Medium"] = 10,
|
|
["Low"] = 5
|
|
};
|
|
|
|
// Deduct points for authentication issues
|
|
foreach (var issue in results.AuthenticationIssues)
|
|
{
|
|
baseScore -= severityPenalties.GetValueOrDefault(issue.Severity, 10);
|
|
}
|
|
|
|
// Deduct points for authorization issues
|
|
foreach (var issue in results.AuthorizationIssues)
|
|
{
|
|
baseScore -= severityPenalties.GetValueOrDefault(issue.Severity, 10);
|
|
}
|
|
|
|
// Add bonus points for good practices (up to 25 points)
|
|
var bonusPoints = Math.Min(25, results.GoodPractices.Count * 3);
|
|
baseScore += bonusPoints;
|
|
|
|
return Math.Max(0, Math.Min(100, baseScore));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Results of authentication analysis
|
|
/// </summary>
|
|
public class AuthenticationAnalysisResults
|
|
{
|
|
public string AnalyzedPath { get; set; }
|
|
public int FilesAnalyzed { get; set; }
|
|
public List<AuthenticationIssue> AuthenticationIssues { get; set; } = new List<AuthenticationIssue>();
|
|
public List<AuthorizationIssue> AuthorizationIssues { get; set; } = new List<AuthorizationIssue>();
|
|
public List<string> GoodPractices { get; set; } = new List<string>();
|
|
public List<string> SecurityRecommendations { get; set; } = new List<string>();
|
|
public int ComplianceScore { get; set; }
|
|
|
|
public int GetTotalIssues() => AuthenticationIssues.Count + AuthorizationIssues.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents an authentication security issue
|
|
/// </summary>
|
|
public class AuthenticationIssue
|
|
{
|
|
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 Code { get; set; }
|
|
public string Recommendation { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents an authorization security issue
|
|
/// </summary>
|
|
public class AuthorizationIssue
|
|
{
|
|
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 Code { get; set; }
|
|
public string Recommendation { get; set; }
|
|
}
|
|
}
|