Claude Code Plugins

Community-maintained marketplace

Feedback
3
0

Vulnerability lifecycle management including CVE tracking, CVSS scoring, risk prioritization, remediation workflows, and coordinated disclosure practices

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

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
  2. Prioritizing what to fix first? → See Risk-Based Prioritization
  3. Designing remediation workflow? → See references/remediation-workflow.md
  4. Setting up disclosure program? → See Vulnerability Disclosure
  5. CVSS calculation details? → See references/cvss-scoring.md

Vulnerability Lifecycle

┌─────────────────────────────────────────────────────────────────┐
│               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

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 }

/// <summary>
/// CVSS v3.1 Base Score vector
/// </summary>
public sealed record CvssV31Vector(
    AttackVector AttackVector,
    AttackComplexity AttackComplexity,
    PrivilegesRequired PrivilegesRequired,
    UserInteraction UserInteraction,
    Scope Scope,
    ImpactLevel Confidentiality,
    ImpactLevel Integrity,
    ImpactLevel Availability)
{
    private static readonly FrozenDictionary<AttackVector, (string Code, double Value)> AvMetrics =
        new Dictionary<AttackVector, (string, double)>
        {
            [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<AttackComplexity, (string Code, double Value)> AcMetrics =
        new Dictionary<AttackComplexity, (string, double)>
        {
            [AttackComplexity.Low] = ("L", 0.77),
            [AttackComplexity.High] = ("H", 0.44)
        }.ToFrozenDictionary();

    private static readonly FrozenDictionary<ImpactLevel, (string Code, double Value)> ImpactMetrics =
        new Dictionary<ImpactLevel, (string, double)>
        {
            [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.

Risk-Based Prioritization

CVSS alone is insufficient for prioritization. Use multiple signals:

Prioritization Framework

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 }

/// <summary>Complete context for vulnerability prioritization</summary>
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);

/// <summary>Multi-factor vulnerability prioritization</summary>
public sealed class RiskPrioritizer
{
    // Weights for different factors (tune based on org priorities)
    private static readonly FrozenDictionary<string, double> Weights =
        new Dictionary<string, double>
        {
            ["cvss"] = 0.25,
            ["epss"] = 0.20,
            ["kev"] = 0.20,
            ["asset"] = 0.20,
            ["exploit"] = 0.15
        }.ToFrozenDictionary();

    private static readonly FrozenDictionary<AssetCriticality, double> AssetScores =
        new Dictionary<AssetCriticality, double>
        {
            [AssetCriticality.Low] = 25,
            [AssetCriticality.Medium] = 50,
            [AssetCriticality.High] = 75,
            [AssetCriticality.Critical] = 100
        }.ToFrozenDictionary();

    private static readonly FrozenDictionary<AssetExposure, double> ExposureMultipliers =
        new Dictionary<AssetExposure, double>
        {
            [AssetExposure.Internal] = 0.7,
            [AssetExposure.Dmz] = 0.85,
            [AssetExposure.External] = 1.0
        }.ToFrozenDictionary();

    private static readonly FrozenDictionary<DataSensitivity, double> DataMultipliers =
        new Dictionary<DataSensitivity, double>
        {
            [DataSensitivity.Public] = 0.5,
            [DataSensitivity.Internal] = 0.7,
            [DataSensitivity.Confidential] = 0.9,
            [DataSensitivity.Restricted] = 1.0
        }.ToFrozenDictionary();

    private static readonly FrozenDictionary<ExploitMaturity, double> ExploitScores =
        new Dictionary<ExploitMaturity, double>
        {
            [ExploitMaturity.Unproven] = 20,
            [ExploitMaturity.Poc] = 50,
            [ExploitMaturity.Functional] = 80,
            [ExploitMaturity.High] = 100
        }.ToFrozenDictionary();

    /// <summary>Calculate composite priority score (0-100)</summary>
    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);
    }

    /// <summary>Categorize priority score</summary>
    public static string GetPriorityCategory(double score) => score switch
    {
        >= 80 => "P1 - Critical",
        >= 60 => "P2 - High",
        >= 40 => "P3 - Medium",
        _ => "P4 - Low"
    };

    /// <summary>Get remediation SLA in hours</summary>
    public static int GetSlaHours(double score) => score switch
    {
        >= 80 => 24,
        >= 60 => 168,   // 7 days
        >= 40 => 720,   // 30 days
        _ => 2160       // 90 days
    };

    /// <summary>Calculate complete priority result</summary>
    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

using System.Net.Http.Json;
using System.Text.Json.Serialization;

public sealed record EpssScore(
    string Cve,
    double Epss,
    double Percentile,
    string Date);

/// <summary>Client for EPSS (Exploit Prediction Scoring System) API</summary>
public sealed class EpssClient(HttpClient httpClient)
{
    private const string BaseUrl = "https://api.first.org/data/v1/epss";

    /// <summary>Get EPSS score for a CVE</summary>
    public async Task<EpssScore?> GetScoreAsync(string cveId, CancellationToken ct = default)
    {
        var response = await httpClient.GetFromJsonAsync<EpssResponse>(
            $"{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);
    }

    /// <summary>Get EPSS scores for multiple CVEs</summary>
    public async Task<IReadOnlyList<EpssScore>> GetBulkScoresAsync(
        IEnumerable<string> cveIds,
        CancellationToken ct = default)
    {
        var cveParam = string.Join(",", cveIds.Select(Uri.EscapeDataString));
        var response = await httpClient.GetFromJsonAsync<EpssResponse>(
            $"{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<EpssData>? 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<EpssClient>();

KEV (Known Exploited Vulnerabilities) Integration

using System.Collections.Concurrent;
using System.Net.Http.Json;
using System.Text.Json.Serialization;

/// <summary>CISA Known Exploited Vulnerability entry</summary>
public sealed record KevEntry(
    string CveId,
    string VendorProject,
    string Product,
    string VulnerabilityName,
    DateOnly DateAdded,
    DateOnly DueDate,
    string ShortDescription,
    string RequiredAction,
    string Notes);

/// <summary>Client for CISA KEV catalog</summary>
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<string, KevEntry> _cache = new();
    private DateTimeOffset _lastRefresh = DateTimeOffset.MinValue;

    public DateTimeOffset LastRefresh => _lastRefresh;

    /// <summary>Refresh KEV catalog from CISA</summary>
    public async Task RefreshCatalogAsync(CancellationToken ct = default)
    {
        var catalog = await httpClient.GetFromJsonAsync<KevCatalog>(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;
    }

    /// <summary>Check if CVE is in KEV catalog</summary>
    public bool IsInKev(string cveId) => _cache.ContainsKey(cveId);

    /// <summary>Get KEV entry for CVE</summary>
    public KevEntry? GetEntry(string cveId) =>
        _cache.TryGetValue(cveId, out var entry) ? entry : null;

    /// <summary>Get all overdue KEV entries</summary>
    public IReadOnlyList<KevEntry> GetOverdue()
    {
        var today = DateOnly.FromDateTime(DateTime.UtcNow);
        return _cache.Values.Where(e => e.DueDate < today).ToArray();
    }

    private sealed record KevCatalog(
        [property: JsonPropertyName("vulnerabilities")] List<KevVuln>? 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<KevClient>();

Vulnerability Disclosure

Security.txt

# 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

# 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

using System.Collections.Frozen;

public enum ReportStatus
{
    New,
    Triaged,
    Duplicate,
    Informative,
    NotApplicable,
    Resolved,
    BountyAwarded
}

public enum BountySeverity { Critical, High, Medium, Low }

/// <summary>Bug bounty report tracking</summary>
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);

/// <summary>Manage bug bounty program operations</summary>
public sealed class BugBountyManager
{
    private static readonly FrozenDictionary<BountySeverity, (decimal Min, decimal Max)> SeverityBountyRange =
        new Dictionary<BountySeverity, (decimal, decimal)>
        {
            [BountySeverity.Critical] = (5000, 25000),
            [BountySeverity.High] = (2000, 5000),
            [BountySeverity.Medium] = (500, 2000),
            [BountySeverity.Low] = (100, 500)
        }.ToFrozenDictionary();

    private static readonly FrozenDictionary<BountySeverity, int> SlaHours =
        new Dictionary<BountySeverity, int>
        {
            [BountySeverity.Critical] = 24,
            [BountySeverity.High] = 72,
            [BountySeverity.Medium] = 168,
            [BountySeverity.Low] = 336
        }.ToFrozenDictionary();

    /// <summary>Calculate bounty amount based on severity and factors</summary>
    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);
    }

    /// <summary>Check if report is within SLA</summary>
    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));
    }

    /// <summary>Generate coordinated disclosure timeline</summary>
    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

# 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

Version History

  • v1.0.0 (2025-12-26): Initial release with CVSS, prioritization, disclosure

Last Updated: 2025-12-26