| import requests
|
| from typing import Optional
|
| from smolagents import Tool
|
|
|
| class CVEDBTool(Tool):
|
| """
|
| Tool for searching vulnerabilities using Shodan's CVEDB API (free, no API key required).
|
| """
|
|
|
| name = "cvedb_search"
|
| description = """Tool for searching vulnerabilities using Shodan's CVEDB API (free, no API key required).
|
|
|
| REQUIRED PARAMETERS:
|
| - search_type: Must be 'cve' for specific CVE ID or 'product' for product name search
|
| - identifier: The CVE ID or product name to search for
|
|
|
| This tool allows searching for vulnerabilities in two ways:
|
| 1. For a specific CVE: cvedb_search(search_type="cve", identifier="CVE-2021-44228")
|
| 2. For a product: cvedb_search(search_type="product", identifier="microsoft")
|
|
|
| CRITICAL - Product name format for product search:
|
| - ALWAYS use ONLY the base product name, NEVER include versions
|
| - CORRECT examples: "log4j", "apache", "microsoft", "chrome", "tomcat", "nginx"
|
| - INCORRECT examples: "log4j 2.14.1", "apache http server", "microsoft windows", "chrome 120.0.6099.109"
|
| - When searching, strip version numbers and descriptive words
|
| - Example: "Apache Tomcat 9.0.65" → search for "tomcat"
|
| - Example: "Google Chrome 120.0.6099.109" → search for "chrome"
|
| - Example: "Log4j 2.14.1" → search for "log4j"
|
|
|
| CORRECT Usage examples:
|
| - cvedb_search(search_type="cve", identifier="CVE-2021-44228")
|
| - cvedb_search(search_type="product", identifier="microsoft")
|
| - cvedb_search(search_type="product", identifier="mobaxterm")
|
|
|
| INCORRECT Usage (will cause errors):
|
| - cvedb_search(identifier="microsoft") # Missing search_type
|
| - cvedb_search(search_type="product") # Missing identifier"""
|
|
|
| inputs = {
|
| "search_type": {
|
| "description": "Type of search to perform. Must be 'cve' for specific CVE ID or 'product' for product name search.",
|
| "type": "string",
|
| },
|
| "identifier": {
|
| "description": "The CVE ID (e.g., 'CVE-2021-44228') or product name (e.g., 'microsoft'). For products, use ONLY base product names without versions (e.g., 'microsoft' not 'microsoft windows').",
|
| "type": "string",
|
| },
|
| }
|
|
|
| output_type = "string"
|
|
|
| def __init__(self):
|
| super().__init__()
|
|
|
| def forward(self, search_type: str, identifier: str) -> str:
|
| """Search for vulnerabilities in CVEDB."""
|
| try:
|
| import requests
|
|
|
| if search_type == "cve":
|
| return self._search_by_cve(identifier)
|
| elif search_type == "product":
|
| return self._search_by_product(identifier)
|
| else:
|
| return "Error: search_type must be 'cve' or 'product'"
|
|
|
| except Exception as e:
|
| return f"Error searching CVEDB: {str(e)}"
|
|
|
| def _search_by_cve(self, cve_id: str) -> str:
|
| """Search for a specific CVE."""
|
| import requests
|
|
|
| url = f"https://cvedb.shodan.io/cve/{cve_id}"
|
|
|
| try:
|
| response = requests.get(url)
|
| response.raise_for_status()
|
| data = response.json()
|
|
|
| if not data:
|
| return f"No vulnerabilities found for CVE {cve_id}"
|
|
|
| result = f"Vulnerability found for {cve_id}:\n\n"
|
| result += f"- CVE ID: {data.get('cve_id', 'Not available')}\n"
|
| result += f"- Summary: {data.get('summary', 'Not available')}\n"
|
| result += f"- CVSS Score: {data.get('cvss', 'Not available')}\n"
|
| result += f"- CVSS Version: {data.get('cvss_version', 'Not available')}\n"
|
| result += f"- CVSS V2: {data.get('cvss_v2', 'Not available')}\n"
|
| result += f"- CVSS V3: {data.get('cvss_v3', 'Not available')}\n"
|
| result += f"- EPSS Score: {data.get('epss', 'Not available')}\n"
|
| result += f"- EPSS Ranking: {data.get('ranking_epss', 'Not available')}\n"
|
| result += f"- Known as exploitable (KEV): {'Yes' if data.get('kev') else 'No'}\n"
|
| result += f"- Propose Action: {data.get('propose_action', 'Not available')}\n"
|
| result += f"- Ransomware Campaign: {data.get('ransomware_campaign', 'Not available')}\n"
|
| result += f"- Publication Date: {data.get('published_time', 'Not available')}\n"
|
|
|
|
|
| references = data.get('references', [])
|
| if references:
|
| result += f"- References ({len(references)} total):\n"
|
| for i, ref in enumerate(references[:10], 1):
|
| result += f" {i}. {ref}\n"
|
| if len(references) > 10:
|
| result += f" ... and {len(references) - 10} more references\n"
|
| else:
|
| result += "- References: None\n"
|
|
|
|
|
| cpes = data.get('cpes', [])
|
| if cpes:
|
| result += f"- Affected CPEs: {len(cpes)} CPEs found (list omitted due to length)\n"
|
| else:
|
| result += "- Affected CPEs: None\n"
|
|
|
| return result
|
|
|
| except requests.exceptions.RequestException as e:
|
| return f"Error accessing CVEDB API: {str(e)}"
|
|
|
| def _search_by_product(self, product: str) -> str:
|
| """Search for vulnerabilities by product."""
|
| import requests
|
|
|
|
|
| product = product.lower()
|
|
|
| url = f"https://cvedb.shodan.io/cves"
|
| params = {"product": product}
|
|
|
| try:
|
| response = requests.get(url, params=params)
|
| response.raise_for_status()
|
| data = response.json()
|
|
|
| if not data or not data.get('cves'):
|
| return f"No vulnerabilities found for the product {product}"
|
|
|
| result = f"Vulnerabilities found for {product}:\n\n"
|
|
|
|
|
| for i, vuln in enumerate(data['cves']):
|
| result += f"**Vulnerability {i+1}:**\n"
|
| result += f"- CVE ID: {vuln.get('cve_id', vuln.get('id', 'Not available'))}\n"
|
| result += f"- Summary: {vuln.get('summary', 'Not available')}\n"
|
| result += f"- CVSS Score: {vuln.get('cvss', 'Not available')}\n"
|
| result += f"- CVSS Version: {vuln.get('cvss_version', 'Not available')}\n"
|
| result += f"- CVSS V2: {vuln.get('cvss_v2', 'Not available')}\n"
|
| result += f"- CVSS V3: {vuln.get('cvss_v3', 'Not available')}\n"
|
| result += f"- EPSS Score: {vuln.get('epss', 'Not available')}\n"
|
| result += f"- EPSS Ranking: {vuln.get('ranking_epss', 'Not available')}\n"
|
| result += f"- Known as exploitable (KEV): {'Yes' if vuln.get('kev') else 'No'}\n"
|
| result += f"- Propose Action: {vuln.get('propose_action', 'Not available')}\n"
|
| result += f"- Ransomware Campaign: {vuln.get('ransomware_campaign', 'Not available')}\n"
|
| result += f"- Publication Date: {vuln.get('published_time', 'Not available')}\n"
|
|
|
|
|
| references = vuln.get('references', [])
|
| if references:
|
| result += f"- References: {len(references)} references available\n"
|
| else:
|
| result += "- References: None\n"
|
|
|
|
|
| cpes = vuln.get('cpes', [])
|
| if cpes:
|
| result += f"- Affected CPEs: {len(cpes)} CPEs found (list omitted due to length)\n"
|
| else:
|
| result += "- Affected CPEs: None\n"
|
|
|
| result += "\n"
|
|
|
| return result
|
|
|
| except requests.exceptions.RequestException as e:
|
| return f"Error accessing CVEDB API: {str(e)}"
|
|
|
| def _format_context(self, data: dict, search_type: str, identifier: str) -> str:
|
| """Format the data for context."""
|
| context = []
|
|
|
| if search_type == "cve":
|
| if not data:
|
| return f"No vulnerabilities found for CVE {identifier}"
|
|
|
| context.append(f"CVSS Score: {data['cvss']}")
|
| context.append(f"EPSS Score: {data['epss']}")
|
| context.append(f"Known as exploitable (KEV): {'Yes' if data.get('kev') else 'No'}")
|
|
|
|
|
| if data.get('published_time'):
|
| context.append(f"Publication Date: {data['published_time']}")
|
|
|
| context.append(f"Summary: {data.get('summary', 'Not available')}")
|
|
|
|
|
| if data.get('references'):
|
| context.append("\nImportant references:")
|
| for ref in data['references'][:3]:
|
| context.append(f"- {ref}")
|
|
|
| elif search_type == "product":
|
| if not data or not data.get('cves'):
|
| return "No vulnerabilities found for this product."
|
|
|
| context.append(f"Found {len(data['cves'])} vulnerabilities for this product.")
|
| context.append("\nMost relevant vulnerabilities:")
|
|
|
|
|
| for i, vuln in enumerate(data['cves']):
|
| context.append(f"{i+1}. {vuln.get('id', 'Unknown CVE')} - CVSS: {vuln.get('cvss', 'N/A')}")
|
|
|
| if len(data['cves']) > 5:
|
| context.append(f"\n... and {len(data['cves']) - 5} more vulnerabilities")
|
|
|
| return "\n".join(context) |