--- name: vulnerability-management description: Vulnerability lifecycle management including CVE tracking, CVSS scoring, risk prioritization, remediation workflows, and coordinated disclosure practices allowed-tools: Read, Grep, Glob, Write, Edit, Task, mcp__perplexity__*, mcp__context7__* --- # Vulnerability Management End-to-end vulnerability lifecycle from discovery through remediation and verification. ## When to Use This Skill **Keywords:** vulnerability management, CVE, CVSS, remediation, patching, risk prioritization, EPSS, KEV, vulnerability disclosure, bug bounty, patch management, vulnerability scanning, asset inventory **Use this skill when:** - Setting up vulnerability management programs - Interpreting CVSS scores and metrics - Prioritizing vulnerability remediation - Designing patch management processes - Implementing vulnerability disclosure programs - Managing bug bounty programs - Tracking CVE/CWE/NVD data - Creating SLA policies for remediation ## Quick Decision Tree 1. **Understanding vulnerability scores?** → See [CVSS Scoring](#cvss-scoring-overview) 2. **Prioritizing what to fix first?** → See [Risk-Based Prioritization](#risk-based-prioritization) 3. **Designing remediation workflow?** → See [references/remediation-workflow.md](references/remediation-workflow.md) 4. **Setting up disclosure program?** → See [Vulnerability Disclosure](#vulnerability-disclosure) 5. **CVSS calculation details?** → See [references/cvss-scoring.md](references/cvss-scoring.md) ## Vulnerability Lifecycle ```text ┌─────────────────────────────────────────────────────────────────┐ │ VULNERABILITY MANAGEMENT LIFECYCLE │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ DISCOVER ──▶ ASSESS ──▶ PRIORITIZE ──▶ REMEDIATE ──▶ VERIFY │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ │ │ ┌───────┐ ┌───────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │ │Scanner│ │CVSS │ │Risk │ │Patch/ │ │Rescan/ │ │ │ │Pentest│ │Context│ │Matrix │ │Config │ │Validate│ │ │ │Bug │ │Assets │ │EPSS+KEV│ │Mitigate│ │Close │ │ │ │Bounty │ │Impact │ │SLA │ │Accept │ │ │ │ │ └───────┘ └───────┘ └────────┘ └────────┘ └────────┘ │ │ │ │ CONTINUOUS: Monitor ◀──────────────────────────────────────────┤ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ## CVSS Scoring Overview Common Vulnerability Scoring System provides standardized severity ratings. ### CVSS v3.1 Score Ranges | Score | Severity | Typical SLA | |-------|----------|-------------| | 9.0-10.0 | Critical | 24-72 hours | | 7.0-8.9 | High | 7-14 days | | 4.0-6.9 | Medium | 30-60 days | | 0.1-3.9 | Low | 90-180 days | | 0.0 | None | Risk acceptance | ### CVSS v3.1 Metric Groups ```csharp public enum AttackVector { Network, Adjacent, Local, Physical } public enum AttackComplexity { Low, High } public enum PrivilegesRequired { None, Low, High } public enum UserInteraction { None, Required } public enum Scope { Unchanged, Changed } public enum ImpactLevel { None, Low, High } /// /// CVSS v3.1 Base Score vector /// public sealed record CvssV31Vector( AttackVector AttackVector, AttackComplexity AttackComplexity, PrivilegesRequired PrivilegesRequired, UserInteraction UserInteraction, Scope Scope, ImpactLevel Confidentiality, ImpactLevel Integrity, ImpactLevel Availability) { private static readonly FrozenDictionary AvMetrics = new Dictionary { [AttackVector.Network] = ("N", 0.85), [AttackVector.Adjacent] = ("A", 0.62), [AttackVector.Local] = ("L", 0.55), [AttackVector.Physical] = ("P", 0.20) }.ToFrozenDictionary(); private static readonly FrozenDictionary AcMetrics = new Dictionary { [AttackComplexity.Low] = ("L", 0.77), [AttackComplexity.High] = ("H", 0.44) }.ToFrozenDictionary(); private static readonly FrozenDictionary ImpactMetrics = new Dictionary { [ImpactLevel.None] = ("N", 0.00), [ImpactLevel.Low] = ("L", 0.22), [ImpactLevel.High] = ("H", 0.56) }.ToFrozenDictionary(); public string ToVectorString() => $"CVSS:3.1/AV:{AvMetrics[AttackVector].Code}/" + $"AC:{AcMetrics[AttackComplexity].Code}/" + $"PR:{GetPrCode()}/" + $"UI:{(UserInteraction == UserInteraction.None ? "N" : "R")}/" + $"S:{(Scope == Scope.Unchanged ? "U" : "C")}/" + $"C:{ImpactMetrics[Confidentiality].Code}/" + $"I:{ImpactMetrics[Integrity].Code}/" + $"A:{ImpactMetrics[Availability].Code}"; public double CalculateBaseScore() { // Impact sub-score var iscBase = 1 - ( (1 - ImpactMetrics[Confidentiality].Value) * (1 - ImpactMetrics[Integrity].Value) * (1 - ImpactMetrics[Availability].Value)); var impact = Scope == Scope.Unchanged ? 6.42 * iscBase : 7.52 * (iscBase - 0.029) - 3.25 * Math.Pow(iscBase - 0.02, 15); // Exploitability sub-score var exploitability = 8.22 * AvMetrics[AttackVector].Value * AcMetrics[AttackComplexity].Value * GetPrValue() * (UserInteraction == UserInteraction.None ? 0.85 : 0.62); if (impact <= 0) return 0.0; var score = Scope == Scope.Unchanged ? Math.Min(impact + exploitability, 10) : Math.Min(1.08 * (impact + exploitability), 10); return Math.Round(score * 10) / 10; // Round to 1 decimal } private string GetPrCode() => PrivilegesRequired switch { PrivilegesRequired.None => "N", PrivilegesRequired.Low => "L", PrivilegesRequired.High => "H", _ => "N" }; private double GetPrValue() => (PrivilegesRequired, Scope) switch { (PrivilegesRequired.None, _) => 0.85, (PrivilegesRequired.Low, Scope.Unchanged) => 0.62, (PrivilegesRequired.Low, Scope.Changed) => 0.68, (PrivilegesRequired.High, Scope.Unchanged) => 0.27, (PrivilegesRequired.High, Scope.Changed) => 0.50, _ => 0.85 }; } // Example: Log4Shell (CVE-2021-44228) var log4shell = new CvssV31Vector( AttackVector: AttackVector.Network, AttackComplexity: AttackComplexity.Low, PrivilegesRequired: PrivilegesRequired.None, UserInteraction: UserInteraction.None, Scope: Scope.Changed, Confidentiality: ImpactLevel.High, Integrity: ImpactLevel.High, Availability: ImpactLevel.High); Console.WriteLine($"Vector: {log4shell.ToVectorString()}"); Console.WriteLine($"Score: {log4shell.CalculateBaseScore()}"); // 10.0 ``` **For detailed CVSS scoring including Temporal and Environmental metrics**, see [references/cvss-scoring.md](references/cvss-scoring.md). ## Risk-Based Prioritization CVSS alone is insufficient for prioritization. Use multiple signals: ### Prioritization Framework ```csharp using System.Collections.Frozen; public enum AssetCriticality { Low, Medium, High, Critical } public enum AssetExposure { Internal, Dmz, External } public enum DataSensitivity { Public, Internal, Confidential, Restricted } public enum ExploitMaturity { Unproven, Poc, Functional, High } /// Complete context for vulnerability prioritization public sealed record VulnerabilityContext( string CveId, double CvssBase, double? CvssEnvironmental = null, double? CvssTemporal = null, double EpssProbability = 0.0, // 0-1 probability of exploitation double EpssPercentile = 0.0, // Relative ranking bool InKev = false, DateTimeOffset? KevDueDate = null, AssetCriticality AssetCriticality = AssetCriticality.Medium, AssetExposure AssetExposure = AssetExposure.Internal, DataSensitivity DataSensitivity = DataSensitivity.Internal, bool PublicExploit = false, ExploitMaturity ExploitMaturity = ExploitMaturity.Unproven); public sealed record PriorityResult(double Score, string Category, int SlaHours); /// Multi-factor vulnerability prioritization public sealed class RiskPrioritizer { // Weights for different factors (tune based on org priorities) private static readonly FrozenDictionary Weights = new Dictionary { ["cvss"] = 0.25, ["epss"] = 0.20, ["kev"] = 0.20, ["asset"] = 0.20, ["exploit"] = 0.15 }.ToFrozenDictionary(); private static readonly FrozenDictionary AssetScores = new Dictionary { [AssetCriticality.Low] = 25, [AssetCriticality.Medium] = 50, [AssetCriticality.High] = 75, [AssetCriticality.Critical] = 100 }.ToFrozenDictionary(); private static readonly FrozenDictionary ExposureMultipliers = new Dictionary { [AssetExposure.Internal] = 0.7, [AssetExposure.Dmz] = 0.85, [AssetExposure.External] = 1.0 }.ToFrozenDictionary(); private static readonly FrozenDictionary DataMultipliers = new Dictionary { [DataSensitivity.Public] = 0.5, [DataSensitivity.Internal] = 0.7, [DataSensitivity.Confidential] = 0.9, [DataSensitivity.Restricted] = 1.0 }.ToFrozenDictionary(); private static readonly FrozenDictionary ExploitScores = new Dictionary { [ExploitMaturity.Unproven] = 20, [ExploitMaturity.Poc] = 50, [ExploitMaturity.Functional] = 80, [ExploitMaturity.High] = 100 }.ToFrozenDictionary(); /// Calculate composite priority score (0-100) public double CalculatePriorityScore(VulnerabilityContext vuln) { // CVSS component (0-10 normalized to 0-100) var cvssScore = (vuln.CvssEnvironmental ?? vuln.CvssTemporal ?? vuln.CvssBase) * 10; // EPSS component (probability * 100) var epssScore = vuln.EpssProbability * 100; // KEV component (binary with boost) var kevScore = vuln.InKev ? 100.0 : 0.0; // Asset criticality component var assetScore = AssetScores[vuln.AssetCriticality] * ExposureMultipliers[vuln.AssetExposure] * DataMultipliers[vuln.DataSensitivity]; // Exploit maturity component var exploitScore = ExploitScores[vuln.ExploitMaturity]; if (vuln.PublicExploit) exploitScore = Math.Max(exploitScore, 60); // Weighted composite var priority = Weights["cvss"] * cvssScore + Weights["epss"] * epssScore + Weights["kev"] * kevScore + Weights["asset"] * assetScore + Weights["exploit"] * exploitScore; return Math.Round(priority, 1); } /// Categorize priority score public static string GetPriorityCategory(double score) => score switch { >= 80 => "P1 - Critical", >= 60 => "P2 - High", >= 40 => "P3 - Medium", _ => "P4 - Low" }; /// Get remediation SLA in hours public static int GetSlaHours(double score) => score switch { >= 80 => 24, >= 60 => 168, // 7 days >= 40 => 720, // 30 days _ => 2160 // 90 days }; /// Calculate complete priority result public PriorityResult Evaluate(VulnerabilityContext vuln) { var score = CalculatePriorityScore(vuln); return new PriorityResult(score, GetPriorityCategory(score), GetSlaHours(score)); } } // Example usage var vuln = new VulnerabilityContext( CveId: "CVE-2021-44228", CvssBase: 10.0, EpssProbability: 0.975, EpssPercentile: 0.999, InKev: true, AssetCriticality: AssetCriticality.Critical, AssetExposure: AssetExposure.External, DataSensitivity: DataSensitivity.Confidential, PublicExploit: true, ExploitMaturity: ExploitMaturity.High); var prioritizer = new RiskPrioritizer(); var result = prioritizer.Evaluate(vuln); Console.WriteLine($"Priority Score: {result.Score}"); // ~95 Console.WriteLine($"Category: {result.Category}"); // P1 - Critical Console.WriteLine($"SLA: {result.SlaHours} hours"); // 24 ``` ### EPSS Integration ```csharp using System.Net.Http.Json; using System.Text.Json.Serialization; public sealed record EpssScore( string Cve, double Epss, double Percentile, string Date); /// Client for EPSS (Exploit Prediction Scoring System) API public sealed class EpssClient(HttpClient httpClient) { private const string BaseUrl = "https://api.first.org/data/v1/epss"; /// Get EPSS score for a CVE public async Task GetScoreAsync(string cveId, CancellationToken ct = default) { var response = await httpClient.GetFromJsonAsync( $"{BaseUrl}?cve={Uri.EscapeDataString(cveId)}", ct); if (response?.Data is not { Count: > 0 }) return null; var data = response.Data[0]; return new EpssScore(data.Cve, data.Epss, data.Percentile, data.Date); } /// Get EPSS scores for multiple CVEs public async Task> GetBulkScoresAsync( IEnumerable cveIds, CancellationToken ct = default) { var cveParam = string.Join(",", cveIds.Select(Uri.EscapeDataString)); var response = await httpClient.GetFromJsonAsync( $"{BaseUrl}?cve={cveParam}", ct); return response?.Data? .Select(d => new EpssScore(d.Cve, d.Epss, d.Percentile, d.Date)) .ToArray() ?? []; } private sealed record EpssResponse( [property: JsonPropertyName("data")] List? Data); private sealed record EpssData( [property: JsonPropertyName("cve")] string Cve, [property: JsonPropertyName("epss")] double Epss, [property: JsonPropertyName("percentile")] double Percentile, [property: JsonPropertyName("date")] string Date); } // Registration with IHttpClientFactory // services.AddHttpClient(); ``` ### KEV (Known Exploited Vulnerabilities) Integration ```csharp using System.Collections.Concurrent; using System.Net.Http.Json; using System.Text.Json.Serialization; /// CISA Known Exploited Vulnerability entry public sealed record KevEntry( string CveId, string VendorProject, string Product, string VulnerabilityName, DateOnly DateAdded, DateOnly DueDate, string ShortDescription, string RequiredAction, string Notes); /// Client for CISA KEV catalog public sealed class KevClient(HttpClient httpClient) { private const string CatalogUrl = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"; private readonly ConcurrentDictionary _cache = new(); private DateTimeOffset _lastRefresh = DateTimeOffset.MinValue; public DateTimeOffset LastRefresh => _lastRefresh; /// Refresh KEV catalog from CISA public async Task RefreshCatalogAsync(CancellationToken ct = default) { var catalog = await httpClient.GetFromJsonAsync(CatalogUrl, ct) ?? throw new InvalidOperationException("Failed to fetch KEV catalog"); _cache.Clear(); foreach (var vuln in catalog.Vulnerabilities ?? []) { var entry = new KevEntry( CveId: vuln.CveId, VendorProject: vuln.VendorProject, Product: vuln.Product, VulnerabilityName: vuln.VulnerabilityName, DateAdded: DateOnly.Parse(vuln.DateAdded), DueDate: DateOnly.Parse(vuln.DueDate), ShortDescription: vuln.ShortDescription, RequiredAction: vuln.RequiredAction, Notes: vuln.Notes ?? string.Empty); _cache[entry.CveId] = entry; } _lastRefresh = DateTimeOffset.UtcNow; } /// Check if CVE is in KEV catalog public bool IsInKev(string cveId) => _cache.ContainsKey(cveId); /// Get KEV entry for CVE public KevEntry? GetEntry(string cveId) => _cache.TryGetValue(cveId, out var entry) ? entry : null; /// Get all overdue KEV entries public IReadOnlyList GetOverdue() { var today = DateOnly.FromDateTime(DateTime.UtcNow); return _cache.Values.Where(e => e.DueDate < today).ToArray(); } private sealed record KevCatalog( [property: JsonPropertyName("vulnerabilities")] List? Vulnerabilities); private sealed record KevVuln( [property: JsonPropertyName("cveID")] string CveId, [property: JsonPropertyName("vendorProject")] string VendorProject, [property: JsonPropertyName("product")] string Product, [property: JsonPropertyName("vulnerabilityName")] string VulnerabilityName, [property: JsonPropertyName("dateAdded")] string DateAdded, [property: JsonPropertyName("dueDate")] string DueDate, [property: JsonPropertyName("shortDescription")] string ShortDescription, [property: JsonPropertyName("requiredAction")] string RequiredAction, [property: JsonPropertyName("notes")] string? Notes); } // Registration with IHttpClientFactory // services.AddHttpClient(); ``` ## Vulnerability Disclosure ### Security.txt ```text # security.txt - RFC 9116 compliant # Place at /.well-known/security.txt Contact: mailto:security@example.com Contact: https://example.com/security/report Expires: 2025-12-31T23:59:59.000Z Encryption: https://example.com/.well-known/pgp-key.txt Acknowledgments: https://example.com/security/hall-of-fame Preferred-Languages: en Canonical: https://example.com/.well-known/security.txt Policy: https://example.com/security/policy Hiring: https://example.com/careers/security ``` ### Disclosure Policy ```markdown # Vulnerability Disclosure Policy ## Scope The following assets are in scope: - *.example.com - Example Mobile Apps (iOS, Android) - Example API (api.example.com) Out of scope: - Third-party services - Social engineering attacks - Physical security testing - Denial of service attacks ## Rules of Engagement - Do not access or modify data belonging to others - Stop testing and report immediately if you access user data - Do not publicly disclose before we've addressed the issue - Provide sufficient detail to reproduce the issue ## Safe Harbor We will not pursue legal action against researchers who: - Act in good faith - Avoid privacy violations - Do not cause service disruption - Follow responsible disclosure timeline ## Timeline - Acknowledgment: 3 business days - Initial assessment: 7 business days - Status updates: Every 14 days - Resolution target: 90 days (varies by severity) ## Rewards | Severity | Bounty Range | |----------|--------------| | Critical | $5,000 - $25,000 | | High | $2,000 - $5,000 | | Medium | $500 - $2,000 | | Low | $100 - $500 | ``` ### Bug Bounty Integration ```csharp using System.Collections.Frozen; public enum ReportStatus { New, Triaged, Duplicate, Informative, NotApplicable, Resolved, BountyAwarded } public enum BountySeverity { Critical, High, Medium, Low } /// Bug bounty report tracking public sealed record BountyReport( string Id, string Title, string Reporter, BountySeverity Severity, ReportStatus Status, DateTimeOffset SubmittedAt, string Asset, string Description, string ReproductionSteps, string Impact, string? CveId = null, decimal? BountyAmount = null, DateTimeOffset? ResolvedAt = null); public sealed record SlaComplianceResult( int SlaHours, double ElapsedHours, bool IsCompliant, double RemainingHours); public sealed record DisclosureTimeline( DateTimeOffset Submitted, DateTimeOffset TriageDeadline, DateTimeOffset InitialResponse, DateTimeOffset ResolutionTarget, DateTimeOffset PublicDisclosure); /// Manage bug bounty program operations public sealed class BugBountyManager { private static readonly FrozenDictionary SeverityBountyRange = new Dictionary { [BountySeverity.Critical] = (5000, 25000), [BountySeverity.High] = (2000, 5000), [BountySeverity.Medium] = (500, 2000), [BountySeverity.Low] = (100, 500) }.ToFrozenDictionary(); private static readonly FrozenDictionary SlaHours = new Dictionary { [BountySeverity.Critical] = 24, [BountySeverity.High] = 72, [BountySeverity.Medium] = 168, [BountySeverity.Low] = 336 }.ToFrozenDictionary(); /// Calculate bounty amount based on severity and factors public decimal CalculateBounty( BountySeverity severity, double impactFactor = 1.0, double qualityFactor = 1.0) { var (minBounty, maxBounty) = SeverityBountyRange.GetValueOrDefault(severity); // Base bounty is midpoint var baseBounty = (minBounty + maxBounty) / 2; // Apply factors var adjusted = baseBounty * (decimal)impactFactor * (decimal)qualityFactor; // Clamp to range return Math.Clamp(adjusted, minBounty, maxBounty); } /// Check if report is within SLA public SlaComplianceResult CheckSlaCompliance(BountyReport report) { var slaHours = SlaHours.GetValueOrDefault(report.Severity, 168); var elapsed = (DateTimeOffset.UtcNow - report.SubmittedAt).TotalHours; return new SlaComplianceResult( SlaHours: slaHours, ElapsedHours: elapsed, IsCompliant: elapsed <= slaHours, RemainingHours: Math.Max(0, slaHours - elapsed)); } /// Generate coordinated disclosure timeline public static DisclosureTimeline GenerateDisclosureTimeline(DateTimeOffset submitted) => new( Submitted: submitted, TriageDeadline: submitted.AddDays(3), InitialResponse: submitted.AddDays(7), ResolutionTarget: submitted.AddDays(90), PublicDisclosure: submitted.AddDays(104)); // 90 + 14 days } ``` ## SLA Configuration ```yaml # vulnerability-sla.yaml sla_policy: name: "Production Vulnerability SLA" version: "2.0" effective_date: "2024-01-01" # Default SLAs by CVSS score severity_slas: critical: # CVSS 9.0-10.0 detection_to_triage: 4h triage_to_remediation_start: 24h remediation_sla: 72h total_sla: 96h escalation_path: - level: 1 after: 24h notify: [security-team, engineering-lead] - level: 2 after: 48h notify: [ciso, vp-engineering] - level: 3 after: 72h notify: [ceo, board] high: # CVSS 7.0-8.9 detection_to_triage: 8h triage_to_remediation_start: 48h remediation_sla: 168h # 7 days total_sla: 216h escalation_path: - level: 1 after: 72h notify: [security-team] - level: 2 after: 120h notify: [ciso] medium: # CVSS 4.0-6.9 detection_to_triage: 24h triage_to_remediation_start: 168h remediation_sla: 720h # 30 days total_sla: 888h escalation_path: - level: 1 after: 336h notify: [security-team] low: # CVSS 0.1-3.9 detection_to_triage: 72h triage_to_remediation_start: 336h remediation_sla: 2160h # 90 days total_sla: 2496h escalation_path: [] # Overrides for specific conditions overrides: - condition: "in_kev == true" override_sla: "critical" reason: "CISA KEV mandates accelerated remediation" - condition: "epss_percentile >= 0.95" decrease_sla_percent: 50 reason: "High exploitation probability" - condition: "asset_exposure == 'external' and asset_criticality == 'critical'" override_sla: "critical" reason: "Internet-facing critical assets" # Risk acceptance policy risk_acceptance: allowed_severities: [low, medium] max_acceptance_period: 180d required_approvers: low: [security-team-lead] medium: [ciso] documentation_required: true review_frequency: 30d ``` ## Security Checklist Before finalizing vulnerability management setup: - [ ] Asset inventory complete and current - [ ] Scanning coverage for all assets - [ ] CVSS + EPSS + KEV prioritization configured - [ ] SLAs defined and communicated - [ ] Escalation paths established - [ ] Remediation workflow documented - [ ] Disclosure policy published (security.txt) - [ ] Bug bounty program considered - [ ] Metrics and reporting established - [ ] Exception/risk acceptance process defined ## References - [CVSS Scoring Deep Dive](references/cvss-scoring.md) - v3.1 and v4.0 detailed calculation - [Remediation Workflow](references/remediation-workflow.md) - End-to-end remediation processes ## Version History - **v1.0.0** (2025-12-26): Initial release with CVSS, prioritization, disclosure --- **Last Updated:** 2025-12-26