Skip to main content
Glama

MCP Vulnerability Checker Server

by firetix
vulnerability_timeline.pyโ€ข12.2 kB
""" Vulnerability Timeline Tool This module provides functionality to track vulnerability timeline including patch availability, vendor responses, and remediation status. """ import re from datetime import datetime from typing import List, Optional import httpx import mcp.types as types def parse_date(date_str: str) -> Optional[datetime]: """Parse various date formats commonly found in vulnerability data.""" if not date_str: return None # Remove timezone info for simplicity date_str = date_str.replace("Z", "").replace("+00:00", "") try: # Try ISO format first if "T" in date_str: return datetime.fromisoformat(date_str.split("T")[0]) else: return datetime.fromisoformat(date_str) except (ValueError, TypeError): # Try other common formats for fmt in ["%Y-%m-%d", "%Y/%m/%d", "%m/%d/%Y", "%d/%m/%Y"]: try: return datetime.strptime(date_str, fmt) except (ValueError, TypeError): continue return None async def get_vulnerability_timeline( cve_id: str, ) -> List[types.TextContent | types.ImageContent | types.EmbeddedResource]: """ Get vulnerability timeline including patch status and remediation information. Args: cve_id: CVE identifier in format CVE-YYYY-NNNN Returns: List of content containing timeline information or error messages """ # Clean up CVE ID format cve_id = cve_id.upper().strip() if not cve_id.startswith("CVE-"): cve_id = f"CVE-{cve_id}" # Validate CVE ID format (CVE-YYYY-NNNN) if not re.match(r"^CVE-\d{4}-\d{4,}$", cve_id): return [ types.TextContent( type="text", text=f"Error: Invalid CVE ID format. Expected format: CVE-YYYY-NNNN (e.g., CVE-2021-44228). Got: {cve_id}", ) ] headers = { "User-Agent": "MCP Vulnerability Timeline Tool v1.0", "Accept": "application/json", } timeline_data = {} try: timeout = httpx.Timeout(20.0, connect=10.0) async with httpx.AsyncClient( follow_redirects=True, headers=headers, timeout=timeout ) as client: # Get comprehensive CVE data from NVD nvd_url = f"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId={cve_id}" try: nvd_response = await client.get(nvd_url) if nvd_response.status_code == 200: nvd_data = nvd_response.json() if nvd_data.get("totalResults", 0) > 0: cve_item = nvd_data["vulnerabilities"][0]["cve"] # Extract key timeline dates published_date = parse_date(cve_item.get("published", "")) modified_date = parse_date(cve_item.get("lastModified", "")) timeline_data["nvd_info"] = { "published": published_date, "last_modified": modified_date, "descriptions": cve_item.get("descriptions", []), "references": cve_item.get("references", []), "cvss_scores": cve_item.get("metrics", {}), } # Analyze references for patch and vendor information patch_refs = [] vendor_refs = [] advisory_refs = [] for ref in cve_item.get("references", []): url = ref.get("url", "").lower() tags = [tag.lower() for tag in ref.get("tags", [])] # Categorize references if any(tag in ["patch", "vendor advisory"] for tag in tags): if "patch" in tags: patch_refs.append(ref) if "vendor advisory" in tags: vendor_refs.append(ref) elif any( keyword in url for keyword in [ "security", "advisory", "bulletin", "patch", "update", ] ): advisory_refs.append(ref) timeline_data["patches"] = patch_refs timeline_data["vendor_advisories"] = vendor_refs timeline_data["security_advisories"] = advisory_refs except Exception as e: timeline_data["nvd_error"] = str(e) # Try to get additional timeline info from MITRE try: mitre_url = f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve_id}" mitre_response = await client.get(mitre_url) if mitre_response.status_code == 200: content = mitre_response.text # Look for timeline indicators in MITRE page timeline_indicators = [] if "reserved" in content.lower(): timeline_indicators.append("CVE ID was reserved") if "disputed" in content.lower(): timeline_indicators.append("Vulnerability is disputed") if "rejected" in content.lower(): timeline_indicators.append("CVE was rejected") timeline_data["mitre_indicators"] = timeline_indicators except Exception as e: timeline_data["mitre_error"] = str(e) except Exception as e: return [ types.TextContent( type="text", text=f"Error: Failed to fetch vulnerability timeline: {str(e)}", ) ] # Generate timeline report if not timeline_data.get("nvd_info"): return [ types.TextContent( type="text", text=f"No timeline information found for {cve_id}. The CVE might not exist or be publicly available yet.", ) ] nvd_info = timeline_data["nvd_info"] published_date = nvd_info["published"] modified_date = nvd_info["last_modified"] # Calculate timeline metrics current_date = datetime.now() age_days = 0 update_days = 0 if published_date: age_days = (current_date - published_date).days if modified_date and published_date: update_days = (modified_date - published_date).days # Format the response result = f"โฐ **Vulnerability Timeline Report: {cve_id}**\n\n" # Timeline Overview result += "๐Ÿ“Š **Timeline Overview:**\n" if published_date: result += f" โ€ข **Published:** {published_date.strftime('%Y-%m-%d')} ({age_days} days ago)\n" if modified_date: result += f" โ€ข **Last Modified:** {modified_date.strftime('%Y-%m-%d')} ({update_days} days after publication)\n" result += f" โ€ข **Age:** {age_days} days\n" # Age-based risk assessment if age_days > 365: age_risk = "๐ŸŸข STABLE" age_desc = "Well-established vulnerability with likely available patches" elif age_days > 90: age_risk = "๐ŸŸก MATURING" age_desc = "Patches likely available, check vendor advisories" elif age_days > 30: age_risk = "๐ŸŸ  RECENT" age_desc = "Recent vulnerability, patches may be in development" else: age_risk = "๐Ÿ”ด NEW" age_desc = "Very recent vulnerability, patches may not be available yet" result += f" โ€ข **Maturity:** {age_risk} - {age_desc}\n\n" # Patch Information patches = timeline_data.get("patches", []) if patches: result += ( f"๐Ÿ”ง **Patch Information:** โœ… {len(patches)} patch reference(s) found\n" ) for i, patch in enumerate(patches[:3], 1): # Show first 3 result += f" {i}. {patch.get('url', 'N/A')}\n" if patch.get("source"): result += f" Source: {patch.get('source')}\n" if len(patches) > 3: result += f" ... and {len(patches) - 3} more patch references\n" else: result += ( "๐Ÿ”ง **Patch Information:** โš ๏ธ No explicit patch references found in NVD\n" ) result += "\n" # Vendor Advisories vendor_advisories = timeline_data.get("vendor_advisories", []) if vendor_advisories: result += f"๐Ÿญ **Vendor Advisories:** โœ… {len(vendor_advisories)} advisory(ies) found\n" for i, advisory in enumerate(vendor_advisories[:3], 1): result += f" {i}. {advisory.get('url', 'N/A')}\n" if advisory.get("source"): result += f" Source: {advisory.get('source')}\n" else: result += ( "๐Ÿญ **Vendor Advisories:** โšช No vendor advisories in NVD references\n" ) result += "\n" # Security Advisories security_advisories = timeline_data.get("security_advisories", []) if security_advisories: result += f"๐Ÿ“ข **Security Advisories:** โœ… {len(security_advisories)} security-related reference(s)\n" for i, advisory in enumerate(security_advisories[:3], 1): result += f" {i}. {advisory.get('url', 'N/A')}\n" else: result += "๐Ÿ“ข **Security Advisories:** โšช No security advisories detected\n" result += "\n" # MITRE Status Indicators mitre_indicators = timeline_data.get("mitre_indicators", []) if mitre_indicators: result += "๐Ÿ›๏ธ **MITRE Status:** โš ๏ธ Special status detected\n" for indicator in mitre_indicators: result += f" โ€ข {indicator}\n" else: result += "๐Ÿ›๏ธ **MITRE Status:** โœ… Standard active CVE\n" result += "\n" # Remediation Timeline Estimate result += "๐Ÿ› ๏ธ **Remediation Timeline Guidance:**\n" if patches: result += " โ€ข **Patch Status:** โœ… Patches appear to be available\n" result += " โ€ข **Action:** Verify and apply vendor patches immediately\n" result += " โ€ข **Timeline:** Patch within 24-72 hours for critical systems\n" elif vendor_advisories: result += " โ€ข **Patch Status:** ๐ŸŸก Vendor acknowledgment found\n" result += " โ€ข **Action:** Monitor vendor channels for patch release\n" result += " โ€ข **Timeline:** Patches typically released within 30-90 days\n" elif age_days > 90: result += " โ€ข **Patch Status:** โš ๏ธ Patches may be available from vendors\n" result += " โ€ข **Action:** Check vendor security pages directly\n" result += " โ€ข **Timeline:** Manual verification required\n" else: result += " โ€ข **Patch Status:** ๐Ÿ”ด Patches likely not available yet\n" result += " โ€ข **Action:** Implement workarounds and monitor for updates\n" result += " โ€ข **Timeline:** Monitor for next 30-60 days\n" result += "\n๐Ÿ“‹ **Next Steps:**\n" result += " 1. ๐Ÿ” **Verify Impact:** Check if your systems are affected\n" result += " 2. ๐Ÿ›ก๏ธ **Implement Workarounds:** If patches unavailable\n" result += " 3. ๐Ÿ“Š **Monitor Updates:** Set up alerts for patch releases\n" result += " 4. ๐ŸŽฏ **Apply Patches:** Test and deploy when available\n" result += " 5. ๐Ÿ”’ **Validate Fix:** Confirm vulnerability is resolved\n\n" result += "๐Ÿ”— **Useful Resources:**\n" result += f" โ€ข NVD CVE Page: https://nvd.nist.gov/vuln/detail/{cve_id}\n" result += f" โ€ข MITRE CVE Page: https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve_id}\n" result += " โ€ข Vendor Security Pages: Check affected product vendors\n\n" result += f"๐Ÿ“Š **Report Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" return [types.TextContent(type="text", text=result)]

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/firetix/vulnerability-intelligence-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server