751 lines
23 KiB
C#
Executable File
751 lines
23 KiB
C#
Executable File
using MarketAlly.AIPlugin;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Text.Json;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using System.Xml.Linq;
|
|
|
|
namespace MarketAlly.AIPlugin.Security.Plugins
|
|
{
|
|
/// <summary>
|
|
/// Analyzes project dependencies for known vulnerabilities and outdated packages
|
|
/// </summary>
|
|
[AIPlugin("VulnerabilityAnalyzer", "Analyzes project dependencies for known vulnerabilities and outdated packages")]
|
|
public class VulnerabilityAnalyzerPlugin : IAIPlugin
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
|
|
public VulnerabilityAnalyzerPlugin(HttpClient httpClient = null)
|
|
{
|
|
_httpClient = httpClient ?? new HttpClient();
|
|
}
|
|
|
|
[AIParameter("Full path to the project file or directory", required: true)]
|
|
public string ProjectPath { get; set; }
|
|
|
|
[AIParameter("Include transitive dependencies in analysis", required: false)]
|
|
public bool IncludeTransitive { get; set; } = true;
|
|
|
|
[AIParameter("Check for outdated packages", required: false)]
|
|
public bool CheckOutdated { get; set; } = true;
|
|
|
|
[AIParameter("Vulnerability database source: nvd, github, snyk", required: false)]
|
|
public string VulnerabilitySource { get; set; } = "nvd";
|
|
|
|
[AIParameter("Generate upgrade recommendations", required: false)]
|
|
public bool GenerateRecommendations { get; set; } = true;
|
|
|
|
public IReadOnlyDictionary<string, Type> SupportedParameters => new Dictionary<string, Type>
|
|
{
|
|
["projectPath"] = typeof(string),
|
|
["includeTransitive"] = typeof(bool),
|
|
["checkOutdated"] = typeof(bool),
|
|
["vulnerabilitySource"] = typeof(string),
|
|
["generateRecommendations"] = typeof(bool)
|
|
};
|
|
|
|
public async Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters)
|
|
{
|
|
try
|
|
{
|
|
// Extract parameters
|
|
string projectPath = parameters["projectPath"].ToString();
|
|
bool includeTransitive = parameters.TryGetValue("includeTransitive", out var transObj) ? Convert.ToBoolean(transObj) : true;
|
|
bool checkOutdated = parameters.TryGetValue("checkOutdated", out var outdatedObj) ? Convert.ToBoolean(outdatedObj) : true;
|
|
string vulnSource = parameters.TryGetValue("vulnerabilitySource", out var sourceObj) ? sourceObj.ToString() : "nvd";
|
|
bool generateRecommendations = parameters.TryGetValue("generateRecommendations", out var recObj) ? Convert.ToBoolean(recObj) : true;
|
|
|
|
// Validate vulnerability source
|
|
var validSources = new[] { "nvd", "github", "snyk" };
|
|
if (!validSources.Contains(vulnSource.ToLower()))
|
|
{
|
|
return new AIPluginResult(
|
|
new ArgumentException($"Invalid vulnerability source: {vulnSource}. Must be one of: {string.Join(", ", validSources)}"),
|
|
"Invalid vulnerability source"
|
|
);
|
|
}
|
|
|
|
// Find project files to analyze
|
|
var projectFiles = FindProjectFiles(projectPath);
|
|
if (!projectFiles.Any())
|
|
{
|
|
return new AIPluginResult(
|
|
new FileNotFoundException($"No supported project files found in: {projectPath}"),
|
|
"No project files found"
|
|
);
|
|
}
|
|
|
|
var results = new VulnerabilityAnalysisResults
|
|
{
|
|
ProjectPath = projectPath,
|
|
VulnerabilitySource = vulnSource,
|
|
ProjectFilesAnalyzed = projectFiles.Count
|
|
};
|
|
|
|
// Analyze each project file
|
|
foreach (var projectFile in projectFiles)
|
|
{
|
|
await AnalyzeProjectFile(projectFile, results, includeTransitive, checkOutdated);
|
|
}
|
|
|
|
// Check vulnerabilities for discovered packages
|
|
if (results.Dependencies.Any())
|
|
{
|
|
await CheckVulnerabilities(results, vulnSource);
|
|
}
|
|
|
|
// Generate recommendations if requested
|
|
if (generateRecommendations)
|
|
{
|
|
results.Recommendations = GenerateRecommendationsMethod(results);
|
|
}
|
|
|
|
// Calculate overall risk score
|
|
results.RiskScore = CalculateRiskScore(results);
|
|
|
|
return new AIPluginResult(results,
|
|
$"Vulnerability analysis completed. Found {results.VulnerablePackages.Count} vulnerable packages and {results.OutdatedPackages.Count} outdated packages.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AIPluginResult(ex, $"Vulnerability analysis failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds supported project files in the given path
|
|
/// </summary>
|
|
private List<string> FindProjectFiles(string path)
|
|
{
|
|
var projectFiles = new List<string>();
|
|
|
|
if (File.Exists(path))
|
|
{
|
|
if (IsSupportedProjectFile(path))
|
|
{
|
|
projectFiles.Add(path);
|
|
}
|
|
}
|
|
else if (Directory.Exists(path))
|
|
{
|
|
// Look for various project file types
|
|
var patterns = new[]
|
|
{
|
|
"*.csproj", "*.vbproj", "*.fsproj", // .NET
|
|
"package.json", // Node.js
|
|
"requirements.txt", "Pipfile", "pyproject.toml", // Python
|
|
"pom.xml", "build.gradle", // Java
|
|
"Gemfile", // Ruby
|
|
"composer.json", // PHP
|
|
"Cargo.toml", // Rust
|
|
"go.mod" // Go
|
|
};
|
|
|
|
foreach (var pattern in patterns)
|
|
{
|
|
projectFiles.AddRange(Directory.GetFiles(path, pattern, SearchOption.AllDirectories));
|
|
}
|
|
}
|
|
|
|
return projectFiles;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if a file is a supported project file
|
|
/// </summary>
|
|
private bool IsSupportedProjectFile(string filePath)
|
|
{
|
|
var fileName = Path.GetFileName(filePath).ToLower();
|
|
var supportedFiles = new[]
|
|
{
|
|
"package.json", "requirements.txt", "pipfile", "pyproject.toml",
|
|
"pom.xml", "build.gradle", "gemfile", "composer.json", "cargo.toml", "go.mod"
|
|
};
|
|
|
|
return fileName.EndsWith(".csproj") || fileName.EndsWith(".vbproj") || fileName.EndsWith(".fsproj") ||
|
|
supportedFiles.Contains(fileName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes a single project file for dependencies
|
|
/// </summary>
|
|
private async Task AnalyzeProjectFile(string projectFile, VulnerabilityAnalysisResults results, bool includeTransitive, bool checkOutdated)
|
|
{
|
|
var fileName = Path.GetFileName(projectFile).ToLower();
|
|
|
|
try
|
|
{
|
|
if (fileName.EndsWith(".csproj") || fileName.EndsWith(".vbproj") || fileName.EndsWith(".fsproj"))
|
|
{
|
|
await AnalyzeDotNetProject(projectFile, results, includeTransitive, checkOutdated);
|
|
}
|
|
else if (fileName == "package.json")
|
|
{
|
|
await AnalyzeNodeProject(projectFile, results, includeTransitive, checkOutdated);
|
|
}
|
|
else if (fileName == "requirements.txt")
|
|
{
|
|
await AnalyzePythonRequirements(projectFile, results, checkOutdated);
|
|
}
|
|
else if (fileName == "pipfile")
|
|
{
|
|
await AnalyzePipfile(projectFile, results, checkOutdated);
|
|
}
|
|
else if (fileName == "pom.xml")
|
|
{
|
|
await AnalyzeJavaMaven(projectFile, results, checkOutdated);
|
|
}
|
|
// Add more project type handlers as needed
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
results.AnalysisErrors.Add($"Error analyzing {projectFile}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes .NET project files (csproj, vbproj, fsproj)
|
|
/// </summary>
|
|
private async Task AnalyzeDotNetProject(string projectFile, VulnerabilityAnalysisResults results, bool includeTransitive, bool checkOutdated)
|
|
{
|
|
var content = await File.ReadAllTextAsync(projectFile);
|
|
var doc = XDocument.Parse(content);
|
|
|
|
// Extract PackageReference elements
|
|
var packageRefs = doc.Descendants("PackageReference");
|
|
foreach (var packageRef in packageRefs)
|
|
{
|
|
var name = packageRef.Attribute("Include")?.Value;
|
|
var version = packageRef.Attribute("Version")?.Value;
|
|
|
|
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(version))
|
|
{
|
|
var dependency = new PackageDependency
|
|
{
|
|
Name = name,
|
|
CurrentVersion = version,
|
|
Ecosystem = "NuGet",
|
|
ProjectFile = projectFile,
|
|
IsTransitive = false
|
|
};
|
|
|
|
results.Dependencies.Add(dependency);
|
|
}
|
|
}
|
|
|
|
// TODO: For transitive dependencies, would need to parse packages.lock.json or use NuGet APIs
|
|
if (includeTransitive)
|
|
{
|
|
await AnalyzeDotNetTransitiveDependencies(projectFile, results);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes Node.js package.json files
|
|
/// </summary>
|
|
private async Task AnalyzeNodeProject(string projectFile, VulnerabilityAnalysisResults results, bool includeTransitive, bool checkOutdated)
|
|
{
|
|
var content = await File.ReadAllTextAsync(projectFile);
|
|
var packageJson = JsonSerializer.Deserialize<JsonElement>(content);
|
|
|
|
// Analyze dependencies and devDependencies
|
|
var depSections = new[] { "dependencies", "devDependencies" };
|
|
|
|
foreach (var section in depSections)
|
|
{
|
|
if (packageJson.TryGetProperty(section, out var deps))
|
|
{
|
|
foreach (var dep in deps.EnumerateObject())
|
|
{
|
|
var dependency = new PackageDependency
|
|
{
|
|
Name = dep.Name,
|
|
CurrentVersion = dep.Value.GetString(),
|
|
Ecosystem = "npm",
|
|
ProjectFile = projectFile,
|
|
IsTransitive = false,
|
|
IsDevelopmentDependency = section == "devDependencies"
|
|
};
|
|
|
|
results.Dependencies.Add(dependency);
|
|
}
|
|
}
|
|
}
|
|
|
|
// For transitive dependencies, would need to parse package-lock.json or node_modules
|
|
if (includeTransitive)
|
|
{
|
|
await AnalyzeNodeTransitiveDependencies(projectFile, results);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes Python requirements.txt files
|
|
/// </summary>
|
|
private async Task AnalyzePythonRequirements(string projectFile, VulnerabilityAnalysisResults results, bool checkOutdated)
|
|
{
|
|
var lines = await File.ReadAllLinesAsync(projectFile);
|
|
var versionRegex = new Regex(@"^([a-zA-Z0-9\-_\.]+)([>=<~!]+)(.+)$");
|
|
|
|
foreach (var line in lines)
|
|
{
|
|
var trimmedLine = line.Trim();
|
|
if (string.IsNullOrEmpty(trimmedLine) || trimmedLine.StartsWith("#"))
|
|
continue;
|
|
|
|
var match = versionRegex.Match(trimmedLine);
|
|
if (match.Success)
|
|
{
|
|
var dependency = new PackageDependency
|
|
{
|
|
Name = match.Groups[1].Value,
|
|
CurrentVersion = match.Groups[3].Value,
|
|
Ecosystem = "PyPI",
|
|
ProjectFile = projectFile,
|
|
IsTransitive = false,
|
|
VersionConstraint = match.Groups[2].Value
|
|
};
|
|
|
|
results.Dependencies.Add(dependency);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes Python Pipfile
|
|
/// </summary>
|
|
private async Task AnalyzePipfile(string projectFile, VulnerabilityAnalysisResults results, bool checkOutdated)
|
|
{
|
|
// Simple TOML-like parsing for Pipfile
|
|
var content = await File.ReadAllTextAsync(projectFile);
|
|
var lines = content.Split('\n');
|
|
|
|
bool inPackagesSection = false;
|
|
bool inDevPackagesSection = false;
|
|
|
|
foreach (var line in lines)
|
|
{
|
|
var trimmedLine = line.Trim();
|
|
|
|
if (trimmedLine == "[packages]")
|
|
{
|
|
inPackagesSection = true;
|
|
inDevPackagesSection = false;
|
|
continue;
|
|
}
|
|
else if (trimmedLine == "[dev-packages]")
|
|
{
|
|
inDevPackagesSection = true;
|
|
inPackagesSection = false;
|
|
continue;
|
|
}
|
|
else if (trimmedLine.StartsWith("["))
|
|
{
|
|
inPackagesSection = false;
|
|
inDevPackagesSection = false;
|
|
continue;
|
|
}
|
|
|
|
if ((inPackagesSection || inDevPackagesSection) && trimmedLine.Contains("="))
|
|
{
|
|
var parts = trimmedLine.Split('=', 2);
|
|
if (parts.Length == 2)
|
|
{
|
|
var name = parts[0].Trim();
|
|
var version = parts[1].Trim().Trim('"', '\'');
|
|
|
|
var dependency = new PackageDependency
|
|
{
|
|
Name = name,
|
|
CurrentVersion = version,
|
|
Ecosystem = "PyPI",
|
|
ProjectFile = projectFile,
|
|
IsTransitive = false,
|
|
IsDevelopmentDependency = inDevPackagesSection
|
|
};
|
|
|
|
results.Dependencies.Add(dependency);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyzes Java Maven pom.xml files
|
|
/// </summary>
|
|
private async Task AnalyzeJavaMaven(string projectFile, VulnerabilityAnalysisResults results, bool checkOutdated)
|
|
{
|
|
var content = await File.ReadAllTextAsync(projectFile);
|
|
var doc = XDocument.Parse(content);
|
|
var ns = doc.Root?.GetDefaultNamespace();
|
|
|
|
// Extract dependency elements
|
|
var dependencies = doc.Descendants(ns + "dependency");
|
|
foreach (var dep in dependencies)
|
|
{
|
|
var groupId = dep.Element(ns + "groupId")?.Value;
|
|
var artifactId = dep.Element(ns + "artifactId")?.Value;
|
|
var version = dep.Element(ns + "version")?.Value;
|
|
var scope = dep.Element(ns + "scope")?.Value ?? "compile";
|
|
|
|
if (!string.IsNullOrEmpty(groupId) && !string.IsNullOrEmpty(artifactId) && !string.IsNullOrEmpty(version))
|
|
{
|
|
var dependency = new PackageDependency
|
|
{
|
|
Name = $"{groupId}:{artifactId}",
|
|
CurrentVersion = version,
|
|
Ecosystem = "Maven",
|
|
ProjectFile = projectFile,
|
|
IsTransitive = false,
|
|
IsDevelopmentDependency = scope == "test"
|
|
};
|
|
|
|
results.Dependencies.Add(dependency);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Placeholder for analyzing .NET transitive dependencies
|
|
/// </summary>
|
|
private async Task AnalyzeDotNetTransitiveDependencies(string projectFile, VulnerabilityAnalysisResults results)
|
|
{
|
|
// In a real implementation, this would parse packages.lock.json or use NuGet APIs
|
|
// For now, we'll skip transitive dependency analysis
|
|
}
|
|
|
|
/// <summary>
|
|
/// Placeholder for analyzing Node.js transitive dependencies
|
|
/// </summary>
|
|
private async Task AnalyzeNodeTransitiveDependencies(string projectFile, VulnerabilityAnalysisResults results)
|
|
{
|
|
// In a real implementation, this would parse package-lock.json or node_modules
|
|
// For now, we'll skip transitive dependency analysis
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks discovered packages against vulnerability databases
|
|
/// </summary>
|
|
private async Task CheckVulnerabilities(VulnerabilityAnalysisResults results, string source)
|
|
{
|
|
foreach (var dependency in results.Dependencies)
|
|
{
|
|
try
|
|
{
|
|
var vulnerabilities = await QueryVulnerabilityDatabase(dependency, source);
|
|
if (vulnerabilities.Any())
|
|
{
|
|
var vulnerablePackage = new VulnerablePackage
|
|
{
|
|
Name = dependency.Name,
|
|
CurrentVersion = dependency.CurrentVersion,
|
|
Ecosystem = dependency.Ecosystem,
|
|
ProjectFile = dependency.ProjectFile,
|
|
Vulnerabilities = vulnerabilities
|
|
};
|
|
|
|
results.VulnerablePackages.Add(vulnerablePackage);
|
|
}
|
|
|
|
// Check if package is outdated (simplified check)
|
|
if (await IsPackageOutdated(dependency))
|
|
{
|
|
var outdatedPackage = new OutdatedPackage
|
|
{
|
|
Name = dependency.Name,
|
|
CurrentVersion = dependency.CurrentVersion,
|
|
LatestVersion = await GetLatestVersion(dependency),
|
|
Ecosystem = dependency.Ecosystem,
|
|
ProjectFile = dependency.ProjectFile
|
|
};
|
|
|
|
results.OutdatedPackages.Add(outdatedPackage);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
results.AnalysisErrors.Add($"Error checking {dependency.Name}: {ex.Message}");
|
|
}
|
|
|
|
// Add delay to avoid rate limiting
|
|
await Task.Delay(100);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Queries vulnerability database for a specific package
|
|
/// </summary>
|
|
private async Task<List<Vulnerability>> QueryVulnerabilityDatabase(PackageDependency dependency, string source)
|
|
{
|
|
var vulnerabilities = new List<Vulnerability>();
|
|
|
|
try
|
|
{
|
|
// This is a simplified mock implementation
|
|
// In a real implementation, you would call actual vulnerability APIs
|
|
|
|
switch (source.ToLower())
|
|
{
|
|
case "nvd":
|
|
vulnerabilities.AddRange(await QueryNVD(dependency));
|
|
break;
|
|
case "github":
|
|
vulnerabilities.AddRange(await QueryGitHubAdvisory(dependency));
|
|
break;
|
|
case "snyk":
|
|
vulnerabilities.AddRange(await QuerySnyk(dependency));
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Log error but don't fail the entire analysis
|
|
Console.WriteLine($"Error querying vulnerability database for {dependency.Name}: {ex.Message}");
|
|
}
|
|
|
|
return vulnerabilities;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mock NVD vulnerability query
|
|
/// </summary>
|
|
private async Task<List<Vulnerability>> QueryNVD(PackageDependency dependency)
|
|
{
|
|
// In a real implementation, this would call the NVD API
|
|
// For demo purposes, we'll return some mock vulnerabilities for common packages
|
|
|
|
var mockVulnerabilities = new Dictionary<string, List<Vulnerability>>
|
|
{
|
|
["lodash"] = new List<Vulnerability>
|
|
{
|
|
new Vulnerability
|
|
{
|
|
Id = "CVE-2021-23337",
|
|
Severity = "High",
|
|
Description = "Lodash versions prior to 4.17.21 are vulnerable to Command Injection via template.",
|
|
AffectedVersions = "< 4.17.21",
|
|
FixedVersion = "4.17.21",
|
|
PublishedDate = DateTime.Parse("2021-02-15"),
|
|
CvssScore = 7.2
|
|
}
|
|
},
|
|
["jquery"] = new List<Vulnerability>
|
|
{
|
|
new Vulnerability
|
|
{
|
|
Id = "CVE-2020-11022",
|
|
Severity = "Medium",
|
|
Description = "In jQuery versions greater than or equal to 1.2 and before 3.5.0, passing HTML from untrusted sources could result in XSS.",
|
|
AffectedVersions = ">= 1.2, < 3.5.0",
|
|
FixedVersion = "3.5.0",
|
|
PublishedDate = DateTime.Parse("2020-04-29"),
|
|
CvssScore = 6.1
|
|
}
|
|
}
|
|
};
|
|
|
|
return mockVulnerabilities.GetValueOrDefault(dependency.Name.ToLower(), new List<Vulnerability>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mock GitHub Advisory query
|
|
/// </summary>
|
|
private async Task<List<Vulnerability>> QueryGitHubAdvisory(PackageDependency dependency)
|
|
{
|
|
// Mock implementation - in reality would call GitHub Security Advisory API
|
|
return new List<Vulnerability>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mock Snyk query
|
|
/// </summary>
|
|
private async Task<List<Vulnerability>> QuerySnyk(PackageDependency dependency)
|
|
{
|
|
// Mock implementation - in reality would call Snyk API
|
|
return new List<Vulnerability>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if a package is outdated (simplified)
|
|
/// </summary>
|
|
private async Task<bool> IsPackageOutdated(PackageDependency dependency)
|
|
{
|
|
// Simplified mock implementation
|
|
// In reality, would check against package registries
|
|
var outdatedPackages = new[] { "lodash", "jquery", "express", "react" };
|
|
return outdatedPackages.Contains(dependency.Name.ToLower());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the latest version of a package (simplified)
|
|
/// </summary>
|
|
private async Task<string> GetLatestVersion(PackageDependency dependency)
|
|
{
|
|
// Mock implementation
|
|
var mockLatestVersions = new Dictionary<string, string>
|
|
{
|
|
["lodash"] = "4.17.21",
|
|
["jquery"] = "3.6.0",
|
|
["express"] = "4.18.2",
|
|
["react"] = "18.2.0"
|
|
};
|
|
|
|
return mockLatestVersions.GetValueOrDefault(dependency.Name.ToLower(), "unknown");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates overall risk score based on vulnerabilities
|
|
/// </summary>
|
|
private int CalculateRiskScore(VulnerabilityAnalysisResults results)
|
|
{
|
|
var score = 0;
|
|
|
|
foreach (var vulnPackage in results.VulnerablePackages)
|
|
{
|
|
foreach (var vuln in vulnPackage.Vulnerabilities)
|
|
{
|
|
score += vuln.Severity.ToLower() switch
|
|
{
|
|
"critical" => 10,
|
|
"high" => 7,
|
|
"medium" => 4,
|
|
"low" => 1,
|
|
_ => 2
|
|
};
|
|
}
|
|
}
|
|
|
|
// Add points for outdated packages
|
|
score += results.OutdatedPackages.Count * 2;
|
|
|
|
return score;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates upgrade and security recommendations
|
|
/// </summary>
|
|
private List<string> GenerateRecommendationsMethod(VulnerabilityAnalysisResults results)
|
|
{
|
|
var recommendations = new List<string>();
|
|
|
|
if (results.VulnerablePackages.Any())
|
|
{
|
|
recommendations.Add("🚨 **Immediate Action Required**: Update vulnerable packages to secure versions.");
|
|
|
|
var criticalVulns = results.VulnerablePackages
|
|
.SelectMany(p => p.Vulnerabilities)
|
|
.Where(v => v.Severity.ToLower() == "critical")
|
|
.Count();
|
|
|
|
if (criticalVulns > 0)
|
|
{
|
|
recommendations.Add($"⚠️ **Critical**: {criticalVulns} critical vulnerabilities found. Update immediately.");
|
|
}
|
|
|
|
// Specific package recommendations
|
|
foreach (var vulnPackage in results.VulnerablePackages.Take(5))
|
|
{
|
|
var highestSeverityVuln = vulnPackage.Vulnerabilities
|
|
.OrderByDescending(v => v.CvssScore)
|
|
.First();
|
|
|
|
if (!string.IsNullOrEmpty(highestSeverityVuln.FixedVersion))
|
|
{
|
|
recommendations.Add($"📦 Update {vulnPackage.Name} from {vulnPackage.CurrentVersion} to {highestSeverityVuln.FixedVersion} or later");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (results.OutdatedPackages.Any())
|
|
{
|
|
recommendations.Add($"📅 {results.OutdatedPackages.Count} packages are outdated. Consider updating to latest versions.");
|
|
recommendations.Add("🔄 Set up automated dependency updates (Dependabot, Renovate, etc.)");
|
|
}
|
|
|
|
// General security recommendations
|
|
recommendations.Add("🛡️ Enable security alerts in your repository settings");
|
|
recommendations.Add("🔍 Run dependency scans regularly in your CI/CD pipeline");
|
|
recommendations.Add("📋 Consider using a Software Bill of Materials (SBOM) for tracking");
|
|
|
|
if (results.VulnerablePackages.Count == 0 && results.OutdatedPackages.Count == 0)
|
|
{
|
|
recommendations.Add("✅ Excellent! No known vulnerabilities or severely outdated packages found.");
|
|
recommendations.Add("🔒 Continue monitoring dependencies regularly for new vulnerabilities.");
|
|
}
|
|
|
|
return recommendations;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Results of vulnerability analysis
|
|
/// </summary>
|
|
public class VulnerabilityAnalysisResults
|
|
{
|
|
public string ProjectPath { get; set; }
|
|
public int ProjectFilesAnalyzed { get; set; }
|
|
public string VulnerabilitySource { get; set; }
|
|
public List<PackageDependency> Dependencies { get; set; } = new List<PackageDependency>();
|
|
public List<VulnerablePackage> VulnerablePackages { get; set; } = new List<VulnerablePackage>();
|
|
public List<OutdatedPackage> OutdatedPackages { get; set; } = new List<OutdatedPackage>();
|
|
public List<string> Recommendations { get; set; } = new List<string>();
|
|
public List<string> AnalysisErrors { get; set; } = new List<string>();
|
|
public int RiskScore { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a package dependency
|
|
/// </summary>
|
|
public class PackageDependency
|
|
{
|
|
public string Name { get; set; }
|
|
public string CurrentVersion { get; set; }
|
|
public string Ecosystem { get; set; }
|
|
public string ProjectFile { get; set; }
|
|
public bool IsTransitive { get; set; }
|
|
public bool IsDevelopmentDependency { get; set; }
|
|
public string VersionConstraint { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a vulnerable package
|
|
/// </summary>
|
|
public class VulnerablePackage
|
|
{
|
|
public string Name { get; set; }
|
|
public string CurrentVersion { get; set; }
|
|
public string Ecosystem { get; set; }
|
|
public string ProjectFile { get; set; }
|
|
public List<Vulnerability> Vulnerabilities { get; set; } = new List<Vulnerability>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents an outdated package
|
|
/// </summary>
|
|
public class OutdatedPackage
|
|
{
|
|
public string Name { get; set; }
|
|
public string CurrentVersion { get; set; }
|
|
public string LatestVersion { get; set; }
|
|
public string Ecosystem { get; set; }
|
|
public string ProjectFile { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a security vulnerability
|
|
/// </summary>
|
|
public class Vulnerability
|
|
{
|
|
public string Id { get; set; }
|
|
public string Severity { get; set; }
|
|
public string Description { get; set; }
|
|
public string AffectedVersions { get; set; }
|
|
public string FixedVersion { get; set; }
|
|
public DateTime PublishedDate { get; set; }
|
|
public double CvssScore { get; set; }
|
|
}
|
|
} |