"""
This plugin provides a command to search Exploit-DB for security exploits and vulnerabilities.
Uses the searchsploit-style approach with the files.csv database.
"""
import logging
import requests
import csv
import io
import simplematrixbotlib as botlib
from datetime import datetime
# Exploit-DB CSV database URL
EXPLOITDB_CSV_URL = "https://gitlab.com/exploit-database/exploitdb/-/raw/main/files_exploits.csv"
def format_exploit(exploit, index, total):
    """
    Format an exploit entry for display.
    Args:
        exploit (dict): The exploit data.
        index (int): Current result index.
        total (int): Total number of results.
    Returns:
        str: Formatted HTML string.
    """
    edb_id = exploit.get('id', 'N/A')
    title = exploit.get('description', 'No title')
    date = exploit.get('date', 'Unknown')
    author = exploit.get('author', 'Unknown')
    exploit_type = exploit.get('type', 'Unknown')
    platform = exploit.get('platform', 'Unknown')
    # Build the URL
    url = f"https://www.exploit-db.com/exploits/{edb_id}"
    output = f"""💣 Exploit {index}/{total}
Title: {title}
EDB-ID: {edb_id}
Type: {exploit_type} | Platform: {platform}
Author: {author} | Date: {date}
URL: {url}"""
    return output
async def search_exploitdb_csv(query, max_results=5):
    """
    Search Exploit-DB CSV database for exploits matching the query.
    Args:
        query (str): Search term.
        max_results (int): Maximum number of results to return.
    Returns:
        list: List of exploit dictionaries, or None on error.
    """
    try:
        logging.info(f"Downloading Exploit-DB CSV database...")
        headers = {
            'User-Agent': 'FunguyBot/1.0',
        }
        # Download the CSV file
        response = requests.get(EXPLOITDB_CSV_URL, headers=headers, timeout=30)
        response.raise_for_status()
        # Parse CSV
        csv_data = response.text
        csv_file = io.StringIO(csv_data)
        reader = csv.DictReader(csv_file)
        # Search through CSV
        results = []
        query_lower = query.lower()
        logging.info(f"Searching CSV for: {query}")
        for row in reader:
            # Search in description (title) and other fields
            description = row.get('description', '').lower()
            file_path = row.get('file', '').lower()
            if query_lower in description or query_lower in file_path:
                exploit = {
                    'id': row.get('id', 'N/A'),
                    'description': row.get('description', 'No title'),
                    'date': row.get('date_published', row.get('date', 'Unknown')),
                    'author': row.get('author', 'Unknown'),
                    'type': row.get('type', 'Unknown'),
                    'platform': row.get('platform', 'Unknown')
                }
                results.append(exploit)
                if len(results) >= max_results:
                    break
        return results
    except requests.exceptions.Timeout:
        logging.error("Timeout downloading Exploit-DB database")
        return None
    except requests.exceptions.RequestException as e:
        logging.error(f"Error downloading Exploit-DB database: {e}")
        return None
    except Exception as e:
        logging.error(f"Unexpected error searching Exploit-DB: {e}", exc_info=True)
        return None
async def search_exploitdb_google(query, max_results=5):
    """
    Alternative: Search Exploit-DB using site-specific search.
    Returns formatted search URLs instead of parsing.
    Args:
        query (str): Search term.
        max_results (int): Maximum number of results to return.
    Returns:
        str: Formatted search information.
    """
    # Create search URLs
    exploitdb_search_url = f"https://www.exploit-db.com/search?q={query}"
    google_search_url = f"https://www.google.com/search?q=site:exploit-db.com+{query}"
    output = f"""💣 Exploit-DB Search for: {query}
Direct Search:
{exploitdb_search_url}
Google Site Search:
{google_search_url}
💡 Tip: You can also use searchsploit command-line tool for offline searches.
⚠️ Use responsibly and only on systems you have permission to test."""
    return output
async def handle_command(room, message, bot, prefix, config):
    """
    Function to handle the !exploitdb command.
    Args:
        room (Room): The Matrix room where the command was invoked.
        message (RoomMessage): The message object containing the command.
        bot (Bot): The bot object.
        prefix (str): The command prefix.
        config (dict): Configuration parameters.
    Returns:
        None
    """
    match = botlib.MessageMatch(room, message, bot, prefix)
    if match.is_not_from_this_bot() and match.prefix() and match.command("exploitdb"):
        logging.info("Received !exploitdb command")
        args = match.args()
        if len(args) < 1:
            await bot.api.send_text_message(
                room.room_id,
                "Usage: !exploitdb  [max_results]\n"
                "Examples:\n"
                "  !exploitdb wordpress\n"
                "  !exploitdb apache 3\n"
                "  !exploitdb windows privilege escalation\n"
                "Searches Exploit-DB for security vulnerabilities and exploits."
            )
            logging.info("Sent usage message for !exploitdb")
            return
        # Check if last argument is a number (max results)
        max_results = 5
        search_terms = args
        if args[-1].isdigit():
            max_results = int(args[-1])
            if max_results < 1:
                max_results = 1
            elif max_results > 10:
                max_results = 10
            search_terms = args[:-1]
        query = ' '.join(search_terms)
        try:
            # Send "searching" message
            await bot.api.send_text_message(
                room.room_id,
                f"🔍 Searching Exploit-DB for: {query}... (this may take a moment)"
            )
            # Try CSV search first
            exploits = await search_exploitdb_csv(query, max_results)
            if exploits is None:
                # Fallback to providing search links
                logging.warning("CSV search failed, providing search links instead")
                output = await search_exploitdb_google(query, max_results)
                await bot.api.send_markdown_message(room.room_id, output)
                return
            if not exploits:
                # Also provide search links when no results
                output = f"No exploits found in local search for: {query}
"
                output += await search_exploitdb_google(query, max_results)
                await bot.api.send_markdown_message(room.room_id, output)
                logging.info(f"No exploits found for: {query}")
                return
            total = len(exploits)
            logging.info(f"Found {total} exploit(s) for: {query}")
            # Format all results
            output = f"💣 Exploit-DB Search Results for: {query}
"
            for idx, exploit in enumerate(exploits, 1):
                output += format_exploit(exploit, idx, total)
                output += "
"
            output += f"⚠️ Use responsibly and only on systems you have permission to test."
            # Wrap in collapsible details if more than 2 results
            if total > 2:
                summary = f"💣 Exploit-DB: {query} ({total} results)"
                output = f"{summary}
{output} "
            await bot.api.send_markdown_message(room.room_id, output)
            logging.info(f"Sent {total} exploit(s) for: {query}")
        except Exception as e:
            await bot.api.send_text_message(
                room.room_id,
                f"An error occurred while searching Exploit-DB: {str(e)}"
            )
            logging.error(f"Error in exploitdb plugin: {e}", exc_info=True)