ssl_check.pyβ’6.75 kB
"""
SSL/TLS Check Tool - Windows/WSL Compatible
Comprehensive SSL/TLS certificate and security analysis
"""
from typing import Dict, Any, List
from .base_tool import BaseTool
import subprocess
import platform
class SSLCheckTool(BaseTool):
"""SSL/TLS security analysis tool"""
def __init__(self):
super().__init__()
self.name = "ssl_check"
self.description = "Comprehensive SSL/TLS certificate and security analysis. Checks certificate validity, expiration, cipher suites, and security configurations."
def get_tool_definition(self) -> Dict[str, Any]:
"""Return MCP-compatible tool definition"""
return {
"name": self.name,
"description": self.description,
"inputSchema": {
"type": "object",
"properties": {
"target": {
"type": "string",
"description": "Target domain to check SSL/TLS (e.g., google.com or github.com)"
},
"port": {
"type": "integer",
"description": "Port number (default: 443)",
"default": 443
}
},
"required": ["target"]
}
}
async def execute(self, arguments: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Execute SSL/TLS check"""
try:
target = arguments.get("target", "")
port = arguments.get("port", 443)
if not target:
return self.format_error("Target domain is required")
# Clean up target (remove protocol and path)
target = target.replace("https://", "").replace("http://", "").split("/")[0]
# Detect platform
is_windows = platform.system() == 'Windows'
# Build OpenSSL command for certificate check
openssl_cmd = f"echo | timeout 10 openssl s_client -connect {target}:{port} -servername {target} 2>/dev/null | openssl x509 -noout -text"
if is_windows:
# Use WSL on Windows
cmd = ["wsl", "bash", "-c", openssl_cmd]
else:
# Direct execution on Linux/Mac
cmd = ["bash", "-c", openssl_cmd]
# Execute SSL check
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=30
)
if result.returncode == 0 and result.stdout:
output = result.stdout
# Parse and format key information
formatted_output = self._parse_certificate_info(output, target, port)
return self.format_success(formatted_output)
else:
# Try simple connection test first
test_cmd = f"timeout 5 openssl s_client -connect {target}:{port} </dev/null 2>&1 | grep -i 'verify return'"
if is_windows:
test_result = subprocess.run(
["wsl", "bash", "-c", test_cmd],
capture_output=True,
text=True,
timeout=10
)
else:
test_result = subprocess.run(
["bash", "-c", test_cmd],
capture_output=True,
text=True,
timeout=10
)
if test_result.returncode == 0 and test_result.stdout:
return self.format_error(f"SSL connection established but certificate parsing failed. {test_result.stdout}")
else:
return self.format_error(f"Unable to connect to {target}:{port}. The server may not have SSL/TLS configured, or the port may be blocked.")
except FileNotFoundError:
if is_windows:
return self.format_error("WSL not found. Please install WSL: wsl --install")
else:
return self.format_error("OpenSSL not found. Please install: sudo apt install openssl")
except subprocess.TimeoutExpired:
return self.format_error(f"SSL check timeout for {target}:{port}. The server may be slow or unresponsive.")
except Exception as e:
return self.format_error(f"Execution failed: {str(e)}")
def _parse_certificate_info(self, cert_output: str, target: str, port: int) -> str:
"""Parse and format certificate information"""
lines = cert_output.split('\n')
# Extract key information
subject = issuer = not_before = not_after = ""
san_domains = []
for line in lines:
line = line.strip()
if 'Subject:' in line:
subject = line.split('Subject:')[1].strip()
elif 'Issuer:' in line:
issuer = line.split('Issuer:')[1].strip()
elif 'Not Before:' in line:
not_before = line.split('Not Before:')[1].strip()
elif 'Not After :' in line:
not_after = line.split('Not After :')[1].strip()
elif 'DNS:' in line:
# Extract SAN domains
dns_entries = line.split('DNS:')
for entry in dns_entries[1:]:
domain = entry.split(',')[0].strip()
if domain:
san_domains.append(domain)
# Format output
formatted = f"""β
SSL/TLS Certificate Analysis for {target}:{port}
{'='*60}
π Certificate Details:
Subject: {subject or 'Not found'}
Issuer: {issuer or 'Not found'}
π
Validity Period:
Valid From: {not_before or 'Not found'}
Valid Until: {not_after or 'Not found'}
π Subject Alternative Names (SAN):"""
if san_domains:
for domain in san_domains[:10]: # Show first 10
formatted += f"\n β’ {domain}"
if len(san_domains) > 10:
formatted += f"\n ... and {len(san_domains) - 10} more domains"
else:
formatted += "\n (None found or not parsed)"
formatted += f"\n\n{'='*60}"
return formatted
tool_instance = SSLCheckTool()