using MarketAlly.AIPlugin; 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 { /// /// Analyzes input validation patterns and suggests security improvements /// [AIPlugin("InputValidation", "Analyzes input validation patterns and suggests security improvements")] public class InputValidationPlugin : IAIPlugin { [AIParameter("Full path to the file to analyze", required: true)] public string FilePath { get; set; } [AIParameter("Check for SQL injection protection", required: false)] public bool CheckSqlInjection { get; set; } = true; [AIParameter("Check for XSS protection", required: false)] public bool CheckXssProtection { get; set; } = true; [AIParameter("Check for CSRF protection", required: false)] public bool CheckCsrfProtection { get; set; } = true; [AIParameter("Analyze sanitization patterns", required: false)] public bool AnalyzeSanitization { get; set; } = true; [AIParameter("Generate validation suggestions", required: false)] public bool GenerateSuggestions { get; set; } = true; public IReadOnlyDictionary SupportedParameters => new Dictionary { ["filePath"] = typeof(string), ["checkSqlInjection"] = typeof(bool), ["checkXssProtection"] = typeof(bool), ["checkCsrfProtection"] = typeof(bool), ["analyzeSanitization"] = typeof(bool), ["generateSuggestions"] = typeof(bool) }; // Input validation patterns private static readonly Dictionary ValidationPatterns = new() { // SQL Injection Vulnerabilities ["sql_raw_query"] = (new Regex(@"(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER)\s+.*\+.*['""]", RegexOptions.IgnoreCase), "SQLInjection", "High", "Raw SQL query with string concatenation"), ["sql_format_string"] = (new Regex(@"String\.Format\s*\(\s*['""].*?(SELECT|INSERT|UPDATE|DELETE)", RegexOptions.IgnoreCase), "SQLInjection", "High", "SQL query using String.Format"), ["sql_interpolation"] = (new Regex(@"\$['""].*?(SELECT|INSERT|UPDATE|DELETE).*?\{.*?\}", RegexOptions.IgnoreCase), "SQLInjection", "Medium", "SQL query using string interpolation"), ["entity_raw_sql"] = (new Regex(@"(FromSqlRaw|ExecuteSqlRaw|Database\.ExecuteSqlCommand)\s*\(.*\+", RegexOptions.IgnoreCase), "SQLInjection", "High", "Entity Framework raw SQL with concatenation"), // XSS Vulnerabilities ["innerHTML_assignment"] = (new Regex(@"\.innerHTML\s*=.*['""].*\+", RegexOptions.IgnoreCase), "XSS", "High", "Direct innerHTML assignment with concatenation"), ["document_write"] = (new Regex(@"document\.write\s*\(.*\+", RegexOptions.IgnoreCase), "XSS", "High", "Document.write with user input"), ["eval_usage"] = (new Regex(@"eval\s*\(.*['""].*\+", RegexOptions.IgnoreCase), "XSS", "Critical", "eval() function with user input"), ["html_raw"] = (new Regex(@"Html\.Raw\s*\(.*[^\)]", RegexOptions.IgnoreCase), "XSS", "Medium", "Html.Raw without proper encoding"), ["response_write"] = (new Regex(@"Response\.Write\s*\(.*[^\)]", RegexOptions.IgnoreCase), "XSS", "Medium", "Response.Write without encoding"), // Missing Input Validation ["missing_validation"] = (new Regex(@"(Request\.|HttpContext\.Request\.|req\.|params\.|query\.).*(?!\s*(IsValid|Validate|Check|Sanitize))", RegexOptions.IgnoreCase), "Validation", "Medium", "Request parameter without validation"), ["direct_model_binding"] = (new Regex(@"public\s+\w+\s+\w+\s*\([^)]*\bModel\b[^)]*\)(?!.*\[ValidateAntiForgeryToken\])", RegexOptions.IgnoreCase), "Validation", "Medium", "Model binding without validation attributes"), // CSRF Protection ["missing_antiforgery"] = (new Regex(@"(HttpPost|POST).*public.*(?!\[ValidateAntiForgeryToken\])", RegexOptions.IgnoreCase | RegexOptions.Singleline), "CSRF", "High", "POST action without anti-forgery token"), ["form_without_token"] = (new Regex(@"]*method\s*=\s*['""]post['""][^>]*>(?!.*@Html\.AntiForgeryToken)", RegexOptions.IgnoreCase), "CSRF", "High", "POST form without anti-forgery token"), // Path Traversal ["path_traversal"] = (new Regex(@"(File\.|Path\.|Directory\.).*\+.*['""]", RegexOptions.IgnoreCase), "PathTraversal", "High", "File path concatenation vulnerability"), ["unsafe_file_access"] = (new Regex(@"(File\.ReadAllText|File\.WriteAllText|File\.Open)\s*\([^)]*Request\.", RegexOptions.IgnoreCase), "PathTraversal", "High", "Direct file access with user input"), // Command Injection ["process_start"] = (new Regex(@"Process\.Start\s*\([^)]*\+", RegexOptions.IgnoreCase), "CommandInjection", "Critical", "Process execution with concatenated input"), ["cmd_execution"] = (new Regex(@"(cmd\.exe|powershell|bash|sh)\s*.*\+", RegexOptions.IgnoreCase), "CommandInjection", "Critical", "Command execution with user input"), // Weak Validation Patterns ["regex_injection"] = (new Regex(@"new\s+Regex\s*\([^)]*\+", RegexOptions.IgnoreCase), "RegexInjection", "Medium", "Dynamic regex creation with user input"), ["weak_email_validation"] = (new Regex(@"@.*\.\w+", RegexOptions.IgnoreCase), "WeakValidation", "Low", "Weak email validation pattern"), ["missing_length_check"] = (new Regex(@"(string|String)\s+\w+.*(?!.*\.Length)", RegexOptions.IgnoreCase), "WeakValidation", "Low", "String parameter without length validation"), // Deserialization Issues ["unsafe_deserialization"] = (new Regex(@"(JsonConvert\.DeserializeObject|XmlSerializer|BinaryFormatter).*Request\.", RegexOptions.IgnoreCase), "Deserialization", "High", "Unsafe deserialization of user input"), ["xml_external_entities"] = (new Regex(@"XmlDocument.*(?!.*XmlResolver\s*=\s*null)", RegexOptions.IgnoreCase), "XXE", "High", "XML parsing without disabling external entities") }; // Positive validation patterns (good practices) private static readonly Dictionary GoodPatterns = new() { ["parameterized_query"] = new Regex(@"(SqlParameter|DbParameter|@\w+)", RegexOptions.IgnoreCase), ["entity_framework_linq"] = new Regex(@"(\.Where\(|\.Select\(|\.Any\(|\.FirstOrDefault\()", RegexOptions.IgnoreCase), ["html_encode"] = new Regex(@"(Html\.Encode|HttpUtility\.HtmlEncode|AntiXssEncoder)", RegexOptions.IgnoreCase), ["antiforgery_token"] = new Regex(@"(\[ValidateAntiForgeryToken\]|@Html\.AntiForgeryToken)", RegexOptions.IgnoreCase), ["data_annotations"] = new Regex(@"\[(Required|StringLength|Range|RegularExpression|EmailAddress)", RegexOptions.IgnoreCase), ["input_sanitization"] = new Regex(@"(Sanitize|Clean|Validate|Trim|Escape)", RegexOptions.IgnoreCase), ["path_validation"] = new Regex(@"(Path\.GetFullPath|Path\.IsPathRooted|ValidatePath)", RegexOptions.IgnoreCase), ["safe_deserialization"] = new Regex(@"(TypeNameHandling\.None|JsonSerializerSettings.*TypeNameHandling)", RegexOptions.IgnoreCase) }; public async Task ExecuteAsync(IReadOnlyDictionary parameters) { try { // Extract parameters string filePath = parameters["filePath"].ToString(); bool checkSqlInjection = parameters.TryGetValue("checkSqlInjection", out var sqlObj) ? Convert.ToBoolean(sqlObj) : true; bool checkXssProtection = parameters.TryGetValue("checkXssProtection", out var xssObj) ? Convert.ToBoolean(xssObj) : true; bool checkCsrfProtection = parameters.TryGetValue("checkCsrfProtection", out var csrfObj) ? Convert.ToBoolean(csrfObj) : true; bool analyzeSanitization = parameters.TryGetValue("analyzeSanitization", out var sanitObj) ? Convert.ToBoolean(sanitObj) : true; bool generateSuggestions = parameters.TryGetValue("generateSuggestions", out var suggestObj) ? Convert.ToBoolean(suggestObj) : true; // Validate path exists if (!File.Exists(filePath) && !Directory.Exists(filePath)) { return new AIPluginResult( new FileNotFoundException($"Path not found: {filePath}"), "Path not found" ); } // Collect files to analyze var filesToAnalyze = GetFilesToAnalyze(filePath); var results = new InputValidationResults { FilePath = filePath, FileName = Path.GetFileName(filePath) ?? "Multiple Files", FileType = Directory.Exists(filePath) ? "Directory" : GetFileType(Path.GetExtension(filePath).ToLower()) }; // Perform analysis on all files foreach (var file in filesToAnalyze) { var content = await File.ReadAllTextAsync(file); if (checkSqlInjection) { await AnalyzeSqlInjectionProtection(content, results); } if (checkXssProtection) { await AnalyzeXssProtection(content, results); } if (checkCsrfProtection) { await AnalyzeCsrfProtection(content, results); } if (analyzeSanitization) { await AnalyzeSanitizationMethod(content, results); } // Analyze general validation patterns await AnalyzeGeneralValidation(content, results); } // Calculate security score results.SecurityScore = CalculateSecurityScore(results); // Generate suggestions if requested if (generateSuggestions) { results.Suggestions = GenerateSecuritySuggestions(results); } return new AIPluginResult(results, $"Input validation analysis completed. Found {results.GetTotalIssues()} security issues with score {results.SecurityScore}/100."); } catch (Exception ex) { return new AIPluginResult(ex, $"Input validation analysis failed: {ex.Message}"); } } /// /// Determines file type based on extension /// private string GetFileType(string extension) { return extension switch { ".cs" => "C#", ".vb" => "VB.NET", ".js" => "JavaScript", ".ts" => "TypeScript", ".py" => "Python", ".java" => "Java", ".php" => "PHP", ".rb" => "Ruby", ".go" => "Go", ".html" => "HTML", ".aspx" => "ASP.NET", ".razor" => "Razor", _ => "Unknown" }; } /// /// Analyzes SQL injection protection patterns /// private async Task AnalyzeSqlInjectionProtection(string content, InputValidationResults results) { var sqlPatterns = ValidationPatterns.Where(p => p.Value.Type == "SQLInjection").ToList(); foreach (var pattern in sqlPatterns) { var matches = pattern.Value.Pattern.Matches(content); foreach (Match match in matches) { // Check if this area has proper protection if (!HasSqlProtection(content, match.Index)) { results.ValidationIssues.Add(new ValidationIssue { Type = "SQL Injection Vulnerability", Severity = pattern.Value.Severity, Description = pattern.Value.Description, LineNumber = GetLineNumber(content, match.Index), Code = GetContextLines(content, match.Index, 2), Recommendation = GetSqlInjectionRecommendation(pattern.Key) }); } } } // Check for positive SQL protection patterns CheckPositiveSqlPatterns(content, results); } /// /// Analyzes XSS protection patterns /// private async Task AnalyzeXssProtection(string content, InputValidationResults results) { var xssPatterns = ValidationPatterns.Where(p => p.Value.Type == "XSS").ToList(); foreach (var pattern in xssPatterns) { var matches = pattern.Value.Pattern.Matches(content); foreach (Match match in matches) { if (!HasXssProtection(content, match.Index)) { results.ValidationIssues.Add(new ValidationIssue { Type = "Cross-Site Scripting (XSS) Vulnerability", Severity = pattern.Value.Severity, Description = pattern.Value.Description, LineNumber = GetLineNumber(content, match.Index), Code = GetContextLines(content, match.Index, 2), Recommendation = GetXssRecommendation(pattern.Key) }); } } } // Check for positive XSS protection patterns CheckPositiveXssPatterns(content, results); } /// /// Analyzes CSRF protection patterns /// private async Task AnalyzeCsrfProtection(string content, InputValidationResults results) { var csrfPatterns = ValidationPatterns.Where(p => p.Value.Type == "CSRF").ToList(); foreach (var pattern in csrfPatterns) { var matches = pattern.Value.Pattern.Matches(content); foreach (Match match in matches) { results.MissingProtections.Add(new MissingProtection { Type = "CSRF Protection", Description = pattern.Value.Description, LineNumber = GetLineNumber(content, match.Index), Code = GetContextLines(content, match.Index, 1), Recommendation = "Add [ValidateAntiForgeryToken] attribute to POST actions and @Html.AntiForgeryToken() to forms" }); } } // Check for existing CSRF protection CheckCsrfProtectionMethod(content, results); } /// /// Analyzes input sanitization patterns /// private async Task AnalyzeSanitizationMethod(string content, InputValidationResults results) { var sanitizationPatterns = ValidationPatterns.Where(p => p.Value.Type == "PathTraversal" || p.Value.Type == "CommandInjection" || p.Value.Type == "Deserialization" || p.Value.Type == "XXE").ToList(); foreach (var pattern in sanitizationPatterns) { var matches = pattern.Value.Pattern.Matches(content); foreach (Match match in matches) { results.ValidationIssues.Add(new ValidationIssue { Type = pattern.Value.Type, Severity = pattern.Value.Severity, Description = pattern.Value.Description, LineNumber = GetLineNumber(content, match.Index), Code = GetContextLines(content, match.Index, 2), Recommendation = GetSanitizationRecommendation(pattern.Key) }); } } // Check for positive sanitization patterns CheckSanitizationPatterns(content, results); } /// /// Analyzes general validation patterns /// private async Task AnalyzeGeneralValidation(string content, InputValidationResults results) { var validationPatterns = ValidationPatterns.Where(p => p.Value.Type == "Validation" || p.Value.Type == "WeakValidation" || p.Value.Type == "RegexInjection").ToList(); foreach (var pattern in validationPatterns) { var matches = pattern.Value.Pattern.Matches(content); foreach (Match match in matches) { results.ValidationIssues.Add(new ValidationIssue { Type = "Input Validation Issue", Severity = pattern.Value.Severity, Description = pattern.Value.Description, LineNumber = GetLineNumber(content, match.Index), Code = GetContextLines(content, match.Index, 1), Recommendation = GetValidationRecommendation(pattern.Key) }); } } // Check for data annotation usage CheckDataAnnotations(content, results); } /// /// Checks if SQL protection is present near a vulnerability /// private bool HasSqlProtection(string content, int index) { var contextArea = GetContextArea(content, index, 300); return GoodPatterns["parameterized_query"].IsMatch(contextArea) || GoodPatterns["entity_framework_linq"].IsMatch(contextArea); } /// /// Checks if XSS protection is present near a vulnerability /// private bool HasXssProtection(string content, int index) { var contextArea = GetContextArea(content, index, 200); return GoodPatterns["html_encode"].IsMatch(contextArea); } /// /// Checks for positive SQL protection patterns /// private void CheckPositiveSqlPatterns(string content, InputValidationResults results) { if (GoodPatterns["parameterized_query"].IsMatch(content)) { results.GoodPractices.Add("Uses parameterized queries for SQL operations"); } if (GoodPatterns["entity_framework_linq"].IsMatch(content)) { results.GoodPractices.Add("Uses Entity Framework LINQ queries (safe from SQL injection)"); } } /// /// Checks for positive XSS protection patterns /// private void CheckPositiveXssPatterns(string content, InputValidationResults results) { if (GoodPatterns["html_encode"].IsMatch(content)) { results.GoodPractices.Add("Uses HTML encoding for output"); } } /// /// Checks for CSRF protection implementation /// private void CheckCsrfProtectionMethod(string content, InputValidationResults results) { if (GoodPatterns["antiforgery_token"].IsMatch(content)) { results.GoodPractices.Add("Implements anti-forgery token protection"); } } /// /// Checks for input sanitization patterns /// private void CheckSanitizationPatterns(string content, InputValidationResults results) { if (GoodPatterns["input_sanitization"].IsMatch(content)) { results.GoodPractices.Add("Uses input sanitization methods"); } if (GoodPatterns["path_validation"].IsMatch(content)) { results.GoodPractices.Add("Uses path validation for file operations"); } if (GoodPatterns["safe_deserialization"].IsMatch(content)) { results.GoodPractices.Add("Uses safe deserialization settings"); } } /// /// Checks for data annotation usage /// private void CheckDataAnnotations(string content, InputValidationResults results) { if (GoodPatterns["data_annotations"].IsMatch(content)) { results.GoodPractices.Add("Uses data annotations for model validation"); } } /// /// Gets context area around a position /// private string GetContextArea(string content, int index, int contextSize) { var start = Math.Max(0, index - contextSize); var end = Math.Min(content.Length, index + contextSize); return content.Substring(start, end - start); } /// /// Gets line number for a character index /// private int GetLineNumber(string content, int index) { return content.Take(index).Count(c => c == '\n') + 1; } /// /// Gets context lines around a match /// 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(); for (int i = startLine; i <= endLine; i++) { var marker = i == targetLine ? ">>> " : " "; contextList.Add($"{marker}{i + 1}: {lines[i]}"); } return string.Join("\n", contextList); } /// /// Gets SQL injection recommendation /// private string GetSqlInjectionRecommendation(string patternKey) { return patternKey switch { "sql_raw_query" => "Use parameterized queries instead of string concatenation. Replace with SqlParameter or similar.", "sql_format_string" => "Replace String.Format with parameterized queries using SqlParameter objects.", "sql_interpolation" => "Avoid string interpolation in SQL queries. Use parameterized queries or Entity Framework LINQ.", "entity_raw_sql" => "Use FromSqlInterpolated() or parameterized versions of raw SQL methods in Entity Framework.", _ => "Use parameterized queries or ORM methods to prevent SQL injection attacks." }; } /// /// Gets XSS recommendation /// private string GetXssRecommendation(string patternKey) { return patternKey switch { "innerHTML_assignment" => "Use textContent instead of innerHTML, or encode the content with Html.Encode().", "document_write" => "Avoid document.write(). Use safer DOM manipulation methods and encode user input.", "eval_usage" => "Never use eval() with user input. Use JSON.parse() for data or safer alternatives.", "html_raw" => "Use Html.Encode() instead of Html.Raw() unless absolutely necessary and input is trusted.", "response_write" => "Use Html.Encode() before writing to response to prevent XSS attacks.", _ => "Encode all user input before output and use safe DOM manipulation methods." }; } /// /// Gets sanitization recommendation /// private string GetSanitizationRecommendation(string patternKey) { return patternKey switch { "path_traversal" => "Validate and sanitize file paths. Use Path.GetFullPath() and check against allowed directories.", "unsafe_file_access" => "Validate file paths and restrict access to allowed directories only.", "process_start" => "Avoid executing processes with user input. If necessary, use allow-lists and input validation.", "cmd_execution" => "Never execute commands with user input. Use safer alternatives or strict input validation.", "unsafe_deserialization" => "Use safe deserialization settings and validate input types before deserialization.", "xml_external_entities" => "Set XmlResolver to null and disable external entity processing in XML parsers.", "regex_injection" => "Use static regex patterns or validate regex input against a safe pattern allow-list.", _ => "Sanitize and validate all user input before processing." }; } /// /// Gets validation recommendation /// private string GetValidationRecommendation(string patternKey) { return patternKey switch { "missing_validation" => "Add input validation using data annotations or custom validation logic.", "direct_model_binding" => "Add validation attributes like [Required], [StringLength], etc. to model properties.", "weak_email_validation" => "Use [EmailAddress] data annotation or a proper email validation library.", "missing_length_check" => "Add length validation to prevent buffer overflow or DoS attacks.", _ => "Implement proper input validation using data annotations and custom validation logic." }; } /// /// Calculates security score based on findings /// private int CalculateSecurityScore(InputValidationResults results) { var baseScore = 100; var severityPenalties = new Dictionary { ["Critical"] = 25, ["High"] = 15, ["Medium"] = 8, ["Low"] = 3 }; // Deduct points for issues foreach (var issue in results.ValidationIssues) { baseScore -= severityPenalties.GetValueOrDefault(issue.Severity, 5); } foreach (var protection in results.MissingProtections) { baseScore -= 10; } // Add points for good practices (up to 20 bonus points) var bonusPoints = Math.Min(20, results.GoodPractices.Count * 5); baseScore += bonusPoints; return Math.Max(0, Math.Min(100, baseScore)); } /// /// Generates security improvement suggestions /// private List GenerateSecuritySuggestions(InputValidationResults results) { var suggestions = new List(); // Critical issues first var criticalIssues = results.ValidationIssues.Where(i => i.Severity == "Critical").ToList(); if (criticalIssues.Any()) { suggestions.Add("🚨 **CRITICAL**: Address critical security vulnerabilities immediately:"); foreach (var issue in criticalIssues.Take(3)) { suggestions.Add($" • Line {issue.LineNumber}: {issue.Description}"); } } // High severity issues var highIssues = results.ValidationIssues.Where(i => i.Severity == "High").ToList(); if (highIssues.Any()) { suggestions.Add("⚠️ **HIGH PRIORITY**: Fix high-severity vulnerabilities:"); suggestions.Add($" • {highIssues.Count} high-severity issues found"); suggestions.Add(" • Focus on SQL injection and XSS prevention"); } // Missing protections if (results.MissingProtections.Any()) { suggestions.Add("🛡️ **MISSING PROTECTIONS**: Implement security measures:"); var protectionTypes = results.MissingProtections.GroupBy(p => p.Type).ToList(); foreach (var group in protectionTypes) { suggestions.Add($" • {group.Key}: {group.Count()} instances"); } } // General recommendations suggestions.Add("📋 **GENERAL RECOMMENDATIONS**:"); suggestions.Add(" • Use parameterized queries for all database operations"); suggestions.Add(" • Implement input validation with data annotations"); suggestions.Add(" • Encode all output to prevent XSS attacks"); suggestions.Add(" • Add CSRF protection to state-changing operations"); suggestions.Add(" • Validate and sanitize file paths and external input"); // Positive reinforcement if (results.GoodPractices.Any()) { suggestions.Add("✅ **GOOD PRACTICES FOUND**:"); foreach (var practice in results.GoodPractices.Take(5)) { suggestions.Add($" • {practice}"); } } // Score-based recommendations if (results.SecurityScore >= 80) { suggestions.Add("🌟 **EXCELLENT**: Your code follows good security practices!"); } else if (results.SecurityScore >= 60) { suggestions.Add("👍 **GOOD**: Address remaining issues to improve security posture"); } else { suggestions.Add("⚡ **ACTION NEEDED**: Significant security improvements required"); } return suggestions; } /// /// Gets list of files to analyze based on path /// private List GetFilesToAnalyze(string path) { var files = new List(); if (File.Exists(path)) { files.Add(path); } else if (Directory.Exists(path)) { // Focus on code files that likely contain validation logic var extensions = new[] { "*.cs", "*.js", "*.ts", "*.py", "*.java", "*.php", "*.rb", "*.go" }; foreach (var extension in extensions) { files.AddRange(Directory.GetFiles(path, extension, SearchOption.AllDirectories)); } // Filter out common excluded 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(); // Limit to reasonable number of files to avoid performance issues files = files.Take(50).ToList(); } return files; } } /// /// Results of input validation analysis /// public class InputValidationResults { public string FilePath { get; set; } public string FileName { get; set; } public string FileType { get; set; } public List ValidationIssues { get; set; } = new List(); public List MissingProtections { get; set; } = new List(); public List GoodPractices { get; set; } = new List(); public List Suggestions { get; set; } = new List(); public int SecurityScore { get; set; } public int GetTotalIssues() => ValidationIssues.Count + MissingProtections.Count; } /// /// Represents a validation issue /// public class ValidationIssue { public string Type { get; set; } public string Severity { get; set; } public string Description { get; set; } public int LineNumber { get; set; } public string Code { get; set; } public string Recommendation { get; set; } } /// /// Represents a missing protection /// public class MissingProtection { public string Type { get; set; } public string Description { get; set; } public int LineNumber { get; set; } public string Code { get; set; } public string Recommendation { get; set; } } }