Skip to content

Instantly share code, notes, and snippets.

@dstnat
Last active July 21, 2025 22:51
Show Gist options
  • Select an option

  • Save dstnat/951f258861ea3fa534df8948db3e8c5f to your computer and use it in GitHub Desktop.

Select an option

Save dstnat/951f258861ea3fa534df8948db3e8c5f to your computer and use it in GitHub Desktop.

Revisions

  1. dstnat revised this gist Jul 21, 2025. 1 changed file with 2 additions and 183 deletions.
    185 changes: 2 additions & 183 deletions agent.py
    Original file line number Diff line number Diff line change
    @@ -12,9 +12,7 @@
    from langchain_core.agents import AgentAction, AgentFinish

    # --- Configuración SSH para Kali ---
    # Credenciales proporcionadas por el usuario, integradas directamente para simplificar el despliegue.
    # Para mayor seguridad en producción, estas deberían ser variables de entorno o gestionadas de forma más robusta.
    KALI_SSH_HOST = "192.166.56.101" # IP de tu máquina Kali (ajustar si es diferente)
    KALI_SSH_HOST = "192.168.56.181" # ¡IP de tu máquina Kali actualizada y verificada!
    KALI_SSH_USER = "dstnat"
    KALI_SSH_PASS = "semeter*410"

    @@ -120,183 +118,4 @@ def searchsploit_lookup(query: str) -> str:
    """
    Busca exploits y shellcodes en la base de datos de Exploit-DB usando searchsploit.
    La 'query' puede ser el nombre de un software, un servicio, o una versión específica (ej. "vsftpd 2.3.4", "openssh 7.9p1", "apache 2.4").
    Ejemplo de uso: searchsploit_lookup("Apache httpd 2.4.38")
    """
    try:
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname=KALI_SSH_HOST, username=KALI_SSH_USER, password=KALI_SSH_PASS)

    command = f"searchsploit {query}"

    stdin, stdout, stderr = client.exec_command(command)

    output = stdout.read().decode().strip()
    error = stderr.read().decode().strip()

    client.close()

    if error and "No results" not in error:
    return f"Error al ejecutar searchsploit_lookup remoto con query '{query}': {error}"
    if "No results" in output or not output:
    return f"No se encontraron exploits para la query '{query}' en Searchsploit."
    return output
    except Exception as e:
    return f"Error al intentar conectar a Kali o ejecutar searchsploit_lookup: {e}. Asegúrate de que Kali esté encendida, sea accesible por SSH, y las credenciales sean correctas."

    @tool
    def generate_security_report(nmap_output: str = "", nuclei_output: str = "", searchsploit_output: str = "") -> str:
    """
    Analiza la salida de los escaneos de Nmap, Nuclei y Searchsploit y genera un reporte de seguridad unificado en formato JSON.
    Toma como entrada las cadenas de texto completas de la salida de Nmap, Nuclei y Searchsploit.
    Identifica puertos abiertos, servicios, hallazgos de vulnerabilidades generales, vulnerabilidades web y exploits conocidos.
    La salida es un objeto JSON con la estructura:
    {
    "target": "IP_del_objetivo",
    "scan_summary": "Resumen general del escaneo",
    "open_ports": [
    {"port": "22", "service": "ssh", "version": "OpenSSH 7.9p1", "state": "open"},
    ...
    ],
    "nmap_vulnerabilities": [
    {"script": "ssh-hostkey", "finding": "2048 SHA256:abcd... (RSA)"},
    ...
    ],
    "nuclei_vulnerabilities": [
    {"template": "cves/2017/CVE-2017-xxxx", "severity": "critical", "url": "http://...", "info": "Apache Struts RCE"},
    ...
    ],
    "searchsploit_findings": [
    {"query": "vsftpd 2.3.4", "results": "vsftpd 2.3.4 - Backdoor Command Execution (Metasploit)\n..."},
    ...
    ],
    "full_nmap_output": "Texto completo de la salida de Nmap (si aplica)",
    "full_nuclei_output": "Texto completo de la salida de Nuclei (si aplica)",
    "full_searchsploit_output": "Texto completo de la salida de Searchsploit (si aplica)",
    "report_generated_by": "Agente de Ciberseguridad"
    }
    Ejemplo de uso: generate_security_report(nmap_output="...", nuclei_output="...", searchsploit_output="...")
    """
    report_data = {
    "target": "",
    "scan_summary": "No se encontraron hallazgos significativos.",
    "open_ports": [],
    "nmap_vulnerabilities": [],
    "nuclei_vulnerabilities": [],
    "searchsploit_findings": [],
    "full_nmap_output": nmap_output,
    "full_nuclei_output": nuclei_output,
    "full_searchsploit_output": searchsploit_output,
    "report_generated_by": "Agente de Ciberseguridad"
    }

    # --- Procesar Nmap Output ---
    if nmap_output:
    lines = nmap_output.split('\n')
    port_service_version_pattern = re.compile(r"^(\d+)/tcp\s+(\S+)\s+([^\s]+)\s*(.*)$")
    script_output_pattern = re.compile(r"^\S+\-([^:]+):\s*(.*)")

    target_match = re.search(r"Nmap scan report for (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", nmap_output)
    if target_match:
    report_data["target"] = target_match.group(1)

    for line in lines:
    line = line.strip()

    match_port_service_version = port_service_version_pattern.match(line)
    if match_port_service_version:
    port = match_port_service_version.group(1)
    state = match_port_service_version.group(2)
    service = match_port_service_version.group(3)
    version_info = match_port_service_version.group(4).strip()

    if state == "open":
    port_entry = {"port": port, "service": service, "state": state}
    if version_info:
    port_entry["version"] = version_info
    report_data["open_ports"].append(port_entry)

    match_script = script_output_pattern.match(line)
    if match_script:
    script_name = match_script.group(1).strip()
    script_output = match_script.group(2).strip()
    if len(script_output) > 10 and not any(kw in script_output.lower() for kw in ["no results", "not found", "nothing found", "no issues"]):
    report_data["nmap_vulnerabilities"].append({"script": script_name, "finding": script_output})
    elif "VULNERABLE:" in script_output or "VULN:" in script_output or "vulnerable:" in script_output:
    report_data["nmap_vulnerabilities"].append({"script": script_name, "finding": script_output})

    if report_data["open_ports"] or report_data["nmap_vulnerabilities"]:
    report_data["nmap_scan_summary"] = "Escaneo Nmap completado con hallazgos de puertos y/o vulnerabilidades NSE."
    else:
    report_data["nmap_scan_summary"] = "Escaneo Nmap completado, no se encontraron puertos abiertos o vulnerabilidades NSE."


    # --- Procesar Nuclei Output ---
    if nuclei_output:
    lines = nuclei_output.split('\n')
    for line in lines:
    line = line.strip()
    nuclei_match = re.match(r"^\[([^\]]+)\]\s*\[([^\]]+)\]\s*([^\s]+)\s*(.*)", line)
    if nuclei_match:
    template_id = nuclei_match.group(1)
    severity = nuclei_match.group(2)
    url = nuclei_match.group(3)
    info = nuclei_match.group(4).strip()
    report_data["nuclei_vulnerabilities"].append({
    "template": template_id,
    "severity": severity,
    "url": url,
    "info": info
    })
    elif line:
    if not any(kw in line.lower() for kw in ["no results", "found 0", "all templates loaded"]):
    report_data["nuclei_vulnerabilities"].append({"raw_output": line})


    # --- Procesar Searchsploit Output ---
    if searchsploit_output:
    try:
    # Searchsploit JSON output can be tricky. Try to load as JSON first.
    # If searchsploit returns multiple JSON objects (e.g., --json output), it's a list.
    searchsploit_json_array = json.loads(searchsploit_output)
    if isinstance(searchsploit_json_array, list):
    report_data["searchsploit_findings"].extend(searchsploit_json_array)
    else: # If it's a single JSON object or other unexpected JSON
    report_data["searchsploit_findings"].append({"query": "multiple_queries_or_unknown", "results": searchsploit_output})
    except json.JSONDecodeError:
    # If not a JSON, treat as raw text output
    report_data["searchsploit_findings"].append({"query": "unknown_or_single_query", "results": searchsploit_output})


    # Update general scan summary
    if report_data["open_ports"] or report_data["nmap_vulnerabilities"] or report_data["nuclei_vulnerabilities"] or report_data["searchsploit_findings"]:
    report_data["scan_summary"] = "Escaneo de seguridad completado con hallazgos importantes."
    else:
    report_data["scan_summary"] = "No se encontraron hallazgos significativos en los escaneos."

    # Si no se encontró absolutamente nada, devolver un JSON más simple.
    if not report_data["open_ports"] and not report_data["nmap_vulnerabilities"] and not report_data["nuclei_vulnerabilities"] and not report_data["searchsploit_findings"]:
    return json.dumps({"status": "No se encontraron hallazgos significativos en los escaneos.", "target": report_data["target"] if report_data["target"] else "Desconocido", "report_generated_by": report_data["report_generated_by"]}, indent=2)

    return json.dumps(report_data, indent=2)


    # --- Configuración del Agente de LangChain ---

    class CustomOutputParser(AgentOutputParser):
    def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
    if "Final Answer:" in llm_output:
    return AgentFinish(
    return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
    log=llm_output,
    )
    regex = r"Action:\s*(.*)\nAction Input:\s*(.*)"
    match = re.search(regex, llm_output)
    if not match:
    raise ValueError(f"Could not parse LLM output: `{llm_output}`")
    action = match.group(1).strip()
    action_input = match.group(2).strip()
    return AgentAction(tool=action, tool_input=action_input, log=llm_output)

    # Inicializar el modelo Ollama (por ejemplo, con 'llama3')
    # Asegúrate de tener el modelo 'llama3' descargado en tu Ollama: ollama pull llama3
    Ejemplo de uso: searchsploit_
  2. dstnat revised this gist Jul 21, 2025. 1 changed file with 119 additions and 1 deletion.
    120 changes: 119 additions & 1 deletion agent.py
    Original file line number Diff line number Diff line change
    @@ -181,4 +181,122 @@ def generate_security_report(nmap_output: str = "", nuclei_output: str = "", sea
    "target": "",
    "scan_summary": "No se encontraron hallazgos significativos.",
    "open_ports": [],
    "nmap
    "nmap_vulnerabilities": [],
    "nuclei_vulnerabilities": [],
    "searchsploit_findings": [],
    "full_nmap_output": nmap_output,
    "full_nuclei_output": nuclei_output,
    "full_searchsploit_output": searchsploit_output,
    "report_generated_by": "Agente de Ciberseguridad"
    }

    # --- Procesar Nmap Output ---
    if nmap_output:
    lines = nmap_output.split('\n')
    port_service_version_pattern = re.compile(r"^(\d+)/tcp\s+(\S+)\s+([^\s]+)\s*(.*)$")
    script_output_pattern = re.compile(r"^\S+\-([^:]+):\s*(.*)")

    target_match = re.search(r"Nmap scan report for (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", nmap_output)
    if target_match:
    report_data["target"] = target_match.group(1)

    for line in lines:
    line = line.strip()

    match_port_service_version = port_service_version_pattern.match(line)
    if match_port_service_version:
    port = match_port_service_version.group(1)
    state = match_port_service_version.group(2)
    service = match_port_service_version.group(3)
    version_info = match_port_service_version.group(4).strip()

    if state == "open":
    port_entry = {"port": port, "service": service, "state": state}
    if version_info:
    port_entry["version"] = version_info
    report_data["open_ports"].append(port_entry)

    match_script = script_output_pattern.match(line)
    if match_script:
    script_name = match_script.group(1).strip()
    script_output = match_script.group(2).strip()
    if len(script_output) > 10 and not any(kw in script_output.lower() for kw in ["no results", "not found", "nothing found", "no issues"]):
    report_data["nmap_vulnerabilities"].append({"script": script_name, "finding": script_output})
    elif "VULNERABLE:" in script_output or "VULN:" in script_output or "vulnerable:" in script_output:
    report_data["nmap_vulnerabilities"].append({"script": script_name, "finding": script_output})

    if report_data["open_ports"] or report_data["nmap_vulnerabilities"]:
    report_data["nmap_scan_summary"] = "Escaneo Nmap completado con hallazgos de puertos y/o vulnerabilidades NSE."
    else:
    report_data["nmap_scan_summary"] = "Escaneo Nmap completado, no se encontraron puertos abiertos o vulnerabilidades NSE."


    # --- Procesar Nuclei Output ---
    if nuclei_output:
    lines = nuclei_output.split('\n')
    for line in lines:
    line = line.strip()
    nuclei_match = re.match(r"^\[([^\]]+)\]\s*\[([^\]]+)\]\s*([^\s]+)\s*(.*)", line)
    if nuclei_match:
    template_id = nuclei_match.group(1)
    severity = nuclei_match.group(2)
    url = nuclei_match.group(3)
    info = nuclei_match.group(4).strip()
    report_data["nuclei_vulnerabilities"].append({
    "template": template_id,
    "severity": severity,
    "url": url,
    "info": info
    })
    elif line:
    if not any(kw in line.lower() for kw in ["no results", "found 0", "all templates loaded"]):
    report_data["nuclei_vulnerabilities"].append({"raw_output": line})


    # --- Procesar Searchsploit Output ---
    if searchsploit_output:
    try:
    # Searchsploit JSON output can be tricky. Try to load as JSON first.
    # If searchsploit returns multiple JSON objects (e.g., --json output), it's a list.
    searchsploit_json_array = json.loads(searchsploit_output)
    if isinstance(searchsploit_json_array, list):
    report_data["searchsploit_findings"].extend(searchsploit_json_array)
    else: # If it's a single JSON object or other unexpected JSON
    report_data["searchsploit_findings"].append({"query": "multiple_queries_or_unknown", "results": searchsploit_output})
    except json.JSONDecodeError:
    # If not a JSON, treat as raw text output
    report_data["searchsploit_findings"].append({"query": "unknown_or_single_query", "results": searchsploit_output})


    # Update general scan summary
    if report_data["open_ports"] or report_data["nmap_vulnerabilities"] or report_data["nuclei_vulnerabilities"] or report_data["searchsploit_findings"]:
    report_data["scan_summary"] = "Escaneo de seguridad completado con hallazgos importantes."
    else:
    report_data["scan_summary"] = "No se encontraron hallazgos significativos en los escaneos."

    # Si no se encontró absolutamente nada, devolver un JSON más simple.
    if not report_data["open_ports"] and not report_data["nmap_vulnerabilities"] and not report_data["nuclei_vulnerabilities"] and not report_data["searchsploit_findings"]:
    return json.dumps({"status": "No se encontraron hallazgos significativos en los escaneos.", "target": report_data["target"] if report_data["target"] else "Desconocido", "report_generated_by": report_data["report_generated_by"]}, indent=2)

    return json.dumps(report_data, indent=2)


    # --- Configuración del Agente de LangChain ---

    class CustomOutputParser(AgentOutputParser):
    def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
    if "Final Answer:" in llm_output:
    return AgentFinish(
    return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
    log=llm_output,
    )
    regex = r"Action:\s*(.*)\nAction Input:\s*(.*)"
    match = re.search(regex, llm_output)
    if not match:
    raise ValueError(f"Could not parse LLM output: `{llm_output}`")
    action = match.group(1).strip()
    action_input = match.group(2).strip()
    return AgentAction(tool=action, tool_input=action_input, log=llm_output)

    # Inicializar el modelo Ollama (por ejemplo, con 'llama3')
    # Asegúrate de tener el modelo 'llama3' descargado en tu Ollama: ollama pull llama3
  3. dstnat revised this gist Jul 21, 2025. 1 changed file with 94 additions and 1 deletion.
    95 changes: 94 additions & 1 deletion agent.py
    Original file line number Diff line number Diff line change
    @@ -88,4 +88,97 @@ def nmap_vulnerability_scan(target: str, ports: str, script_category_or_names: s
    @tool
    def nuclei_scan(target_url: str) -> str:
    """
    Realiza un escaneo de
    Realiza un escaneo de vulnerabilidades web utilizando Nuclei en la URL especificada.
    La URL debe incluir el esquema (http:// o https://) y el puerto si no es el predeterminado (ej. http://192.168.56.180:80).
    Utiliza plantillas de vulnerabilidades comunes de Nuclei.
    Ejemplo de uso: nuclei_scan("http://192.168.56.180:80")
    """
    try:
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname=KALI_SSH_HOST, username=KALI_SSH_USER, password=KALI_SSH_PASS)

    command = f"nuclei -u {target_url} -t cves/ -t exposures/ -t http/ -t vulnerabilities/ -silent -no-color"

    stdin, stdout, stderr = client.exec_command(command)

    output = stdout.read().decode().strip()
    error = stderr.read().decode().strip()

    client.close()

    if error and "No results found" not in error and "No targets specified" not in error:
    return f"Error al ejecutar nuclei_scan remoto en {target_url}: {error}"
    if not output:
    return f"No se encontraron hallazgos de Nuclei para {target_url}."
    return output
    except Exception as e:
    return f"Error al intentar conectar a Kali o ejecutar nuclei_scan: {e}. Asegúrate de que Kali esté encendida, sea accesible por SSH, y las credenciales sean correctas."

    @tool
    def searchsploit_lookup(query: str) -> str:
    """
    Busca exploits y shellcodes en la base de datos de Exploit-DB usando searchsploit.
    La 'query' puede ser el nombre de un software, un servicio, o una versión específica (ej. "vsftpd 2.3.4", "openssh 7.9p1", "apache 2.4").
    Ejemplo de uso: searchsploit_lookup("Apache httpd 2.4.38")
    """
    try:
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname=KALI_SSH_HOST, username=KALI_SSH_USER, password=KALI_SSH_PASS)

    command = f"searchsploit {query}"

    stdin, stdout, stderr = client.exec_command(command)

    output = stdout.read().decode().strip()
    error = stderr.read().decode().strip()

    client.close()

    if error and "No results" not in error:
    return f"Error al ejecutar searchsploit_lookup remoto con query '{query}': {error}"
    if "No results" in output or not output:
    return f"No se encontraron exploits para la query '{query}' en Searchsploit."
    return output
    except Exception as e:
    return f"Error al intentar conectar a Kali o ejecutar searchsploit_lookup: {e}. Asegúrate de que Kali esté encendida, sea accesible por SSH, y las credenciales sean correctas."

    @tool
    def generate_security_report(nmap_output: str = "", nuclei_output: str = "", searchsploit_output: str = "") -> str:
    """
    Analiza la salida de los escaneos de Nmap, Nuclei y Searchsploit y genera un reporte de seguridad unificado en formato JSON.
    Toma como entrada las cadenas de texto completas de la salida de Nmap, Nuclei y Searchsploit.
    Identifica puertos abiertos, servicios, hallazgos de vulnerabilidades generales, vulnerabilidades web y exploits conocidos.
    La salida es un objeto JSON con la estructura:
    {
    "target": "IP_del_objetivo",
    "scan_summary": "Resumen general del escaneo",
    "open_ports": [
    {"port": "22", "service": "ssh", "version": "OpenSSH 7.9p1", "state": "open"},
    ...
    ],
    "nmap_vulnerabilities": [
    {"script": "ssh-hostkey", "finding": "2048 SHA256:abcd... (RSA)"},
    ...
    ],
    "nuclei_vulnerabilities": [
    {"template": "cves/2017/CVE-2017-xxxx", "severity": "critical", "url": "http://...", "info": "Apache Struts RCE"},
    ...
    ],
    "searchsploit_findings": [
    {"query": "vsftpd 2.3.4", "results": "vsftpd 2.3.4 - Backdoor Command Execution (Metasploit)\n..."},
    ...
    ],
    "full_nmap_output": "Texto completo de la salida de Nmap (si aplica)",
    "full_nuclei_output": "Texto completo de la salida de Nuclei (si aplica)",
    "full_searchsploit_output": "Texto completo de la salida de Searchsploit (si aplica)",
    "report_generated_by": "Agente de Ciberseguridad"
    }
    Ejemplo de uso: generate_security_report(nmap_output="...", nuclei_output="...", searchsploit_output="...")
    """
    report_data = {
    "target": "",
    "scan_summary": "No se encontraron hallazgos significativos.",
    "open_ports": [],
    "nmap
  4. dstnat revised this gist Jul 21, 2025. 1 changed file with 2 additions and 151 deletions.
    153 changes: 2 additions & 151 deletions agent.py
    Original file line number Diff line number Diff line change
    @@ -14,7 +14,7 @@
    # --- Configuración SSH para Kali ---
    # Credenciales proporcionadas por el usuario, integradas directamente para simplificar el despliegue.
    # Para mayor seguridad en producción, estas deberían ser variables de entorno o gestionadas de forma más robusta.
    KALI_SSH_HOST = "192.168.56.101" # IP de tu máquina Kali (ajustar si es diferente)
    KALI_SSH_HOST = "192.166.56.101" # IP de tu máquina Kali (ajustar si es diferente)
    KALI_SSH_USER = "dstnat"
    KALI_SSH_PASS = "semeter*410"

    @@ -88,153 +88,4 @@ def nmap_vulnerability_scan(target: str, ports: str, script_category_or_names: s
    @tool
    def nuclei_scan(target_url: str) -> str:
    """
    Realiza un escaneo de vulnerabilidades web utilizando Nuclei en la URL especificada.
    La URL debe incluir el esquema (http:// o https://) y el puerto si no es el predeterminado (ej. http://192.168.56.180:80).
    Utiliza plantillas de vulnerabilidades comunes de Nuclei.
    Ejemplo de uso: nuclei_scan("http://192.168.56.180:80")
    """
    try:
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname=KALI_SSH_HOST, username=KALI_SSH_USER, password=KALI_SSH_PASS)

    command = f"nuclei -u {target_url} -t cves/ -t exposures/ -t http/ -t vulnerabilities/ -silent -no-color"

    stdin, stdout, stderr = client.exec_command(command)

    output = stdout.read().decode().strip()
    error = stderr.read().decode().strip()

    client.close()

    if error and "No results found" not in error and "No targets specified" not in error:
    return f"Error al ejecutar nuclei_scan remoto en {target_url}: {error}"
    if not output:
    return f"No se encontraron hallazgos de Nuclei para {target_url}."
    return output
    except Exception as e:
    return f"Error al intentar conectar a Kali o ejecutar nuclei_scan: {e}. Asegúrate de que Kali esté encendida, sea accesible por SSH, y las credenciales sean correctas."

    @tool
    def searchsploit_lookup(query: str) -> str:
    """
    Busca exploits y shellcodes en la base de datos de Exploit-DB usando searchsploit.
    La 'query' puede ser el nombre de un software, un servicio, o una versión específica (ej. "vsftpd 2.3.4", "openssh 7.9p1", "apache 2.4").
    Ejemplo de uso: searchsploit_lookup("Apache httpd 2.4.38")
    """
    try:
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname=KALI_SSH_HOST, username=KALI_SSH_USER, password=KALI_SSH_PASS)

    command = f"searchsploit {query}"

    stdin, stdout, stderr = client.exec_command(command)

    output = stdout.read().decode().strip()
    error = stderr.read().decode().strip()

    client.close()

    if error and "No results" not in error:
    return f"Error al ejecutar searchsploit_lookup remoto con query '{query}': {error}"
    if "No results" in output or not output:
    return f"No se encontraron exploits para la query '{query}' en Searchsploit."
    return output
    except Exception as e:
    return f"Error al intentar conectar a Kali o ejecutar searchsploit_lookup: {e}. Asegúrate de que Kali esté encendida, sea accesible por SSH, y las credenciales sean correctas."

    @tool
    def generate_security_report(nmap_output: str = "", nuclei_output: str = "", searchsploit_output: str = "") -> str:
    """
    Analiza la salida de los escaneos de Nmap, Nuclei y Searchsploit y genera un reporte de seguridad unificado en formato JSON.
    Toma como entrada las cadenas de texto completas de la salida de Nmap, Nuclei y Searchsploit.
    Identifica puertos abiertos, servicios, hallazgos de vulnerabilidades generales, vulnerabilidades web y exploits conocidos.
    La salida es un objeto JSON con la estructura:
    {
    "target": "IP_del_objetivo",
    "scan_summary": "Resumen general del escaneo",
    "open_ports": [
    {"port": "22", "service": "ssh", "version": "OpenSSH 7.9p1", "state": "open"},
    ...
    ],
    "nmap_vulnerabilities": [
    {"script": "ssh-hostkey", "finding": "2048 SHA256:abcd... (RSA)"},
    ...
    ],
    "nuclei_vulnerabilities": [
    {"template": "cves/2017/CVE-2017-xxxx", "severity": "critical", "url": "http://...", "info": "Apache Struts RCE"},
    ...
    ],
    "searchsploit_findings": [
    {"query": "vsftpd 2.3.4", "results": "vsftpd 2.3.4 - Backdoor Command Execution (Metasploit)\n..."},
    ...
    ],
    "full_nmap_output": "Texto completo de la salida de Nmap (si aplica)",
    "full_nuclei_output": "Texto completo de la salida de Nuclei (si aplica)",
    "full_searchsploit_output": "Texto completo de la salida de Searchsploit (si aplica)",
    "report_generated_by": "Agente de Ciberseguridad"
    }
    Ejemplo de uso: generate_security_report(nmap_output="...", nuclei_output="...", searchsploit_output="...")
    """
    report_data = {
    "target": "",
    "scan_summary": "No se encontraron hallazgos significativos.",
    "open_ports": [],
    "nmap_vulnerabilities": [],
    "nuclei_vulnerabilities": [],
    "searchsploit_findings": [],
    "full_nmap_output": nmap_output,
    "full_nuclei_output": nuclei_output,
    "full_searchsploit_output": searchsploit_output,
    "report_generated_by": "Agente de Ciberseguridad"
    }

    # --- Procesar Nmap Output ---
    if nmap_output:
    lines = nmap_output.split('\n')
    port_service_version_pattern = re.compile(r"^(\d+)/tcp\s+(\S+)\s+([^\s]+)\s*(.*)$")
    script_output_pattern = re.compile(r"^\S+\-([^:]+):\s*(.*)")

    target_match = re.search(r"Nmap scan report for (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", nmap_output)
    if target_match:
    report_data["target"] = target_match.group(1)

    for line in lines:
    line = line.strip()

    match_port_service_version = port_service_version_pattern.match(line)
    if match_port_service_version:
    port = match_port_service_version.group(1)
    state = match_port_service_version.group(2)
    service = match_port_service_version.group(3)
    version_info = match_port_service_version.group(4).strip()

    if state == "open":
    port_entry = {"port": port, "service": service, "state": state}
    if version_info:
    port_entry["version"] = version_info
    report_data["open_ports"].append(port_entry)

    match_script = script_output_pattern.match(line)
    if match_script:
    script_name = match_script.group(1).strip()
    script_output = match_script.group(2).strip()
    if len(script_output) > 10 and not any(kw in script_output.lower() for kw in ["no results", "not found", "nothing found", "no issues"]):
    report_data["nmap_vulnerabilities"].append({"script": script_name, "finding": script_output})
    elif "VULNERABLE:" in script_output or "VULN:" in script_output or "vulnerable:" in script_output:
    report_data["nmap_vulnerabilities"].append({"script": script_name, "finding": script_output})

    if report_data["open_ports"] or report_data["nmap_vulnerabilities"]:
    report_data["nmap_scan_summary"] = "Escaneo Nmap completado con hallazgos de puertos y/o vulnerabilidades NSE."
    else:
    report_data["nmap_scan_summary"] = "Escaneo Nmap completado, no se encontraron puertos abiertos o vulnerabilidades NSE."


    # --- Procesar Nuclei Output ---
    if nuclei_output:
    lines = nuclei_output.split('\n')
    for line in lines:
    line = line.strip()
    nuclei_match = re.match(r"^\[([^\]]+)\]\s*\[([^\]]+)\]\s*([^\s]+)\s*(.*)", line)
    if nuclei_match:
    Realiza un escaneo de
  5. dstnat revised this gist Jul 21, 2025. 1 changed file with 1 addition and 141 deletions.
    142 changes: 1 addition & 141 deletions agent.py
    Original file line number Diff line number Diff line change
    @@ -237,144 +237,4 @@ def generate_security_report(nmap_output: str = "", nuclei_output: str = "", sea
    for line in lines:
    line = line.strip()
    nuclei_match = re.match(r"^\[([^\]]+)\]\s*\[([^\]]+)\]\s*([^\s]+)\s*(.*)", line)
    if nuclei_match:
    template_id = nuclei_match.group(1)
    severity = nuclei_match.group(2)
    url = nuclei_match.group(3)
    info = nuclei_match.group(4).strip()
    report_data["nuclei_vulnerabilities"].append({
    "template": template_id,
    "severity": severity,
    "url": url,
    "info": info
    })
    elif line:
    if not any(kw in line.lower() for kw in ["no results", "found 0", "all templates loaded"]):
    report_data["nuclei_vulnerabilities"].append({"raw_output": line})

    # --- Procesar Searchsploit Output ---
    if searchsploit_output:
    try:
    # Searchsploit JSON output can be tricky. Try to load as JSON first.
    # If searchsploit returns multiple JSON objects (e.g., --json output), it's a list.
    searchsploit_json_array = json.loads(searchsploit_output)
    if isinstance(searchsploit_json_array, list):
    report_data["searchsploit_findings"].extend(searchsploit_json_array)
    else: # If it's a single JSON object or other unexpected JSON
    report_data["searchsploit_findings"].append({"query": "multiple_queries_or_unknown", "results": searchsploit_output})
    except json.JSONDecodeError:
    # If not a JSON, treat as raw text output
    report_data["searchsploit_findings"].append({"query": "unknown_or_single_query", "results": searchsploit_output})


    # Update general scan summary
    if report_data["open_ports"] or report_data["nmap_vulnerabilities"] or report_data["nuclei_vulnerabilities"] or report_data["searchsploit_findings"]:
    report_data["scan_summary"] = "Escaneo de seguridad completado con hallazgos importantes."
    else:
    report_data["scan_summary"] = "No se encontraron hallazgos significativos en los escaneos."

    # Si no se encontró absolutamente nada, devolver un JSON más simple.
    if not report_data["open_ports"] and not report_data["nmap_vulnerabilities"] and not report_data["nuclei_vulnerabilities"] and not report_data["searchsploit_findings"]:
    return json.dumps({"status": "No se encontraron hallazgos significativos en los escaneos.", "target": report_data["target"] if report_data["target"] else "Desconocido", "report_generated_by": report_data["report_generated_by"]}, indent=2)

    return json.dumps(report_data, indent=2)


    # --- Configuración del Agente de LangChain ---

    class CustomOutputParser(AgentOutputParser):
    def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
    if "Final Answer:" in llm_output:
    return AgentFinish(
    return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
    log=llm_output,
    )
    regex = r"Action:\s*(.*)\nAction Input:\s*(.*)"
    match = re.search(regex, llm_output)
    if not match:
    raise ValueError(f"Could not parse LLM output: `{llm_output}`")
    action = match.group(1).strip()
    action_input = match.group(2).strip()
    return AgentAction(tool=action, tool_input=action_input, log=llm_output)

    # Inicializar el modelo Ollama (por ejemplo, con 'llama3')
    # Asegúrate de tener el modelo 'llama3' descargado en tu Ollama: ollama pull llama3
    llm = Ollama(model="llama3", temperature=0.0)

    # Lista de herramientas disponibles para el agente
    tools = [
    nmap_recon_scan,
    nmap_vulnerability_scan,
    nuclei_scan,
    searchsploit_lookup,
    generate_security_report
    ]

    # Plantilla de Prompt para el agente
    # Aquí puedes ajustar las instrucciones para el agente.
    # Es crucial que las instrucciones sean claras y específicas sobre cuándo usar cada herramienta.
    # El prompt debe guiar al LLM a seguir el patrón ReAct (Thought, Action, Action Input, Observation, ...)
    prompt = PromptTemplate.from_template("""
    Eres un asistente de ciberseguridad experto. Tu objetivo es ayudar a los usuarios a realizar análisis de seguridad.
    Debes utilizar las herramientas disponibles para recopilar información, identificar vulnerabilidades y generar un reporte.
    Responde en español y sigue el siguiente formato:
    Thought: Siempre debes pensar en qué hacer a continuación.
    Action: La acción a realizar, debe ser una de las herramientas [{tool_names}]
    Action Input: Los parámetros de entrada para la acción.
    Observation: El resultado de la acción.
    ... (Este Thought/Action/Action Input/Observation se puede repetir varias veces)
    Thought: He terminado mi tarea y tengo la respuesta final.
    Final Answer: La respuesta final a la pregunta original del usuario, incluyendo el reporte de seguridad si se solicitó.
    Aquí hay un ejemplo:
    Question: Escanea 192.168.1.1 en busca de puertos abiertos y luego busca exploits para los servicios encontrados.
    Thought: El usuario quiere escanear un objetivo para puertos y servicios. Usaré nmap_recon_scan para el reconocimiento inicial.
    Action: nmap_recon_scan
    Action Input: 192.168.1.1
    Observation: Nmap scan report for 192.168.1.1... (Aquí iría la salida real de Nmap)
    Thought: He obtenido puertos y servicios. Ahora necesito buscar exploits para estos servicios. Por ejemplo, si encontré un servicio FTP vsftpd 2.3.4, buscaré exploits para eso.
    Action: searchsploit_lookup
    Action Input: vsftpd 2.3.4
    Observation: (Aquí iría la salida de Searchsploit)
    Thought: He recolectado la información de reconocimiento y exploits. Ahora debo generar un reporte de seguridad combinando toda la información.
    Action: generate_security_report
    Action Input: nmap_output="...", searchsploit_output="..."
    Observation: (Aquí iría el reporte JSON)
    Thought: El reporte de seguridad ha sido generado exitosamente. Estoy listo para dar la respuesta final al usuario.
    Final Answer: Aquí tienes el reporte de seguridad completo en formato JSON: {...}
    Comienza!
    Question: {input}
    {agent_scratchpad}
    """)

    # Crear el agente
    # Necesitamos pasar tools y tool_names al prompt
    agent = create_react_agent(llm, tools, prompt, tools_arg=tools, tool_names_arg=", ".join([t.name for t in tools]))

    # Crear el ejecutor del agente
    agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True, # Establecer en True para ver los pensamientos y acciones del agente
    handle_parsing_errors=True # Maneja errores de parsing si el LLM no sigue el formato
    )

    if __name__ == "__main__":
    print("¡Agente de Ciberseguridad iniciado! Escribe 'exit' para salir.")
    while True:
    user_input = input("¿En qué puedo ayudarte? ")
    if user_input.lower() == 'exit':
    break
    try:
    # Usa .invoke para ejecutar el agente con la entrada del usuario
    result = agent_executor.invoke({"input": user_input})
    print("\nResultado Final:")
    print(result["output"])
    except Exception as e:
    print(f"\n¡Se produjo un error al ejecutar el agente!: {e}")
    print("Por favor, intenta reformular tu pregunta o revisa la configuración.")
    if nuclei_match:
  6. dstnat created this gist Jul 21, 2025.
    380 changes: 380 additions & 0 deletions agent.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,380 @@
    import os
    import re
    import paramiko
    import json
    from typing import Union, List, Dict

    from langchain_community.llms import Ollama
    from langchain_community.tools import tool
    from langchain.agents import AgentExecutor, create_react_agent
    from langchain_core.prompts import PromptTemplate
    from langchain.agents.agent import AgentOutputParser
    from langchain_core.agents import AgentAction, AgentFinish

    # --- Configuración SSH para Kali ---
    # Credenciales proporcionadas por el usuario, integradas directamente para simplificar el despliegue.
    # Para mayor seguridad en producción, estas deberían ser variables de entorno o gestionadas de forma más robusta.
    KALI_SSH_HOST = "192.168.56.101" # IP de tu máquina Kali (ajustar si es diferente)
    KALI_SSH_USER = "dstnat"
    KALI_SSH_PASS = "semeter*410"

    # --- Definir Herramientas ---

    @tool
    def nmap_recon_scan(target: str) -> str:
    """
    Realiza un escaneo de reconocimiento exhaustivo usando nmap en el objetivo especificado.
    Escanea los puertos del 1 al 1000, detecta versiones de servicios (-sV), ejecuta scripts por defecto (-sC),
    y es muy verboso (-vvv).
    El objetivo puede ser una IP, un rango de IPs, o un hostname.
    Ejemplo de uso: nmap_recon_scan("192.168.1.1")
    """
    try:
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname=KALI_SSH_HOST, username=KALI_SSH_USER, password=KALI_SSH_PASS)

    command = f"nmap -p 1-1000 --open -sS -sC -sV -n -vvv -Pn {target}"

    stdin, stdout, stderr = client.exec_command(command)

    output = stdout.read().decode().strip()
    error = stderr.read().decode().strip()

    client.close()

    if error:
    return f"Error al ejecutar nmap_recon_scan remoto en {target}: {error}"
    if not output:
    return f"No se encontraron resultados para {target}. Asegúrate de que el objetivo esté activo y sea accesible desde Kali."
    return output
    except Exception as e:
    return f"Error al intentar conectar a Kali o ejecutar nmap_recon_scan: {e}. Asegúrate de que Kali esté encendida, sea accesible por SSH, y las credenciales sean correctas."

    @tool
    def nmap_vulnerability_scan(target: str, ports: str, script_category_or_names: str = "vuln") -> str:
    """
    Realiza un escaneo de vulnerabilidades utilizando scripts de Nmap NSE
    en los puertos específicos de un objetivo.
    Los 'ports' deben ser una lista de puertos separados por comas (ej. "22,80,443").
    'script_category_or_names' puede ser una categoría de scripts Nmap (ej. "vuln", "discovery")
    o una lista de nombres de scripts específicos separados por comas (ej. "smb-enum-shares,smb-vuln-ms17-010").
    Ejemplo de uso:
    - Escaneo general: nmap_vulnerability_scan("192.168.1.1", "22,80", "vuln")
    - Escaneo SMB específico: nmap_vulnerability_scan("192.168.1.1", "445", "smb-enum-shares,smb-vuln-ms17-010")
    """
    try:
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname=KALI_SSH_HOST, username=KALI_SSH_USER, password=KALI_SSH_PASS)

    command = f"nmap -p {ports} --script={script_category_or_names} {target}"

    stdin, stdout, stderr = client.exec_command(command)

    output = stdout.read().decode().strip()
    error = stderr.read().decode().strip()

    client.close()

    if error:
    return f"Error al ejecutar nmap_vulnerability_scan remoto en {target} en puertos {ports} con scripts {script_category_or_names}: {error}"
    if not output or "Host is up" not in output:
    return f"No se encontraron resultados de vulnerabilidades para {target}. Asegúrate de que el objetivo esté activo y sea accesible desde Kali."
    return output
    except Exception as e:
    return f"Error al intentar conectar a Kali o ejecutar nmap_vulnerability_scan: {e}. Asegúrate de que Kali esté encendida, sea accesible por SSH, y las credenciales sean correctas."

    @tool
    def nuclei_scan(target_url: str) -> str:
    """
    Realiza un escaneo de vulnerabilidades web utilizando Nuclei en la URL especificada.
    La URL debe incluir el esquema (http:// o https://) y el puerto si no es el predeterminado (ej. http://192.168.56.180:80).
    Utiliza plantillas de vulnerabilidades comunes de Nuclei.
    Ejemplo de uso: nuclei_scan("http://192.168.56.180:80")
    """
    try:
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname=KALI_SSH_HOST, username=KALI_SSH_USER, password=KALI_SSH_PASS)

    command = f"nuclei -u {target_url} -t cves/ -t exposures/ -t http/ -t vulnerabilities/ -silent -no-color"

    stdin, stdout, stderr = client.exec_command(command)

    output = stdout.read().decode().strip()
    error = stderr.read().decode().strip()

    client.close()

    if error and "No results found" not in error and "No targets specified" not in error:
    return f"Error al ejecutar nuclei_scan remoto en {target_url}: {error}"
    if not output:
    return f"No se encontraron hallazgos de Nuclei para {target_url}."
    return output
    except Exception as e:
    return f"Error al intentar conectar a Kali o ejecutar nuclei_scan: {e}. Asegúrate de que Kali esté encendida, sea accesible por SSH, y las credenciales sean correctas."

    @tool
    def searchsploit_lookup(query: str) -> str:
    """
    Busca exploits y shellcodes en la base de datos de Exploit-DB usando searchsploit.
    La 'query' puede ser el nombre de un software, un servicio, o una versión específica (ej. "vsftpd 2.3.4", "openssh 7.9p1", "apache 2.4").
    Ejemplo de uso: searchsploit_lookup("Apache httpd 2.4.38")
    """
    try:
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname=KALI_SSH_HOST, username=KALI_SSH_USER, password=KALI_SSH_PASS)

    command = f"searchsploit {query}"

    stdin, stdout, stderr = client.exec_command(command)

    output = stdout.read().decode().strip()
    error = stderr.read().decode().strip()

    client.close()

    if error and "No results" not in error:
    return f"Error al ejecutar searchsploit_lookup remoto con query '{query}': {error}"
    if "No results" in output or not output:
    return f"No se encontraron exploits para la query '{query}' en Searchsploit."
    return output
    except Exception as e:
    return f"Error al intentar conectar a Kali o ejecutar searchsploit_lookup: {e}. Asegúrate de que Kali esté encendida, sea accesible por SSH, y las credenciales sean correctas."

    @tool
    def generate_security_report(nmap_output: str = "", nuclei_output: str = "", searchsploit_output: str = "") -> str:
    """
    Analiza la salida de los escaneos de Nmap, Nuclei y Searchsploit y genera un reporte de seguridad unificado en formato JSON.
    Toma como entrada las cadenas de texto completas de la salida de Nmap, Nuclei y Searchsploit.
    Identifica puertos abiertos, servicios, hallazgos de vulnerabilidades generales, vulnerabilidades web y exploits conocidos.
    La salida es un objeto JSON con la estructura:
    {
    "target": "IP_del_objetivo",
    "scan_summary": "Resumen general del escaneo",
    "open_ports": [
    {"port": "22", "service": "ssh", "version": "OpenSSH 7.9p1", "state": "open"},
    ...
    ],
    "nmap_vulnerabilities": [
    {"script": "ssh-hostkey", "finding": "2048 SHA256:abcd... (RSA)"},
    ...
    ],
    "nuclei_vulnerabilities": [
    {"template": "cves/2017/CVE-2017-xxxx", "severity": "critical", "url": "http://...", "info": "Apache Struts RCE"},
    ...
    ],
    "searchsploit_findings": [
    {"query": "vsftpd 2.3.4", "results": "vsftpd 2.3.4 - Backdoor Command Execution (Metasploit)\n..."},
    ...
    ],
    "full_nmap_output": "Texto completo de la salida de Nmap (si aplica)",
    "full_nuclei_output": "Texto completo de la salida de Nuclei (si aplica)",
    "full_searchsploit_output": "Texto completo de la salida de Searchsploit (si aplica)",
    "report_generated_by": "Agente de Ciberseguridad"
    }
    Ejemplo de uso: generate_security_report(nmap_output="...", nuclei_output="...", searchsploit_output="...")
    """
    report_data = {
    "target": "",
    "scan_summary": "No se encontraron hallazgos significativos.",
    "open_ports": [],
    "nmap_vulnerabilities": [],
    "nuclei_vulnerabilities": [],
    "searchsploit_findings": [],
    "full_nmap_output": nmap_output,
    "full_nuclei_output": nuclei_output,
    "full_searchsploit_output": searchsploit_output,
    "report_generated_by": "Agente de Ciberseguridad"
    }

    # --- Procesar Nmap Output ---
    if nmap_output:
    lines = nmap_output.split('\n')
    port_service_version_pattern = re.compile(r"^(\d+)/tcp\s+(\S+)\s+([^\s]+)\s*(.*)$")
    script_output_pattern = re.compile(r"^\S+\-([^:]+):\s*(.*)")

    target_match = re.search(r"Nmap scan report for (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", nmap_output)
    if target_match:
    report_data["target"] = target_match.group(1)

    for line in lines:
    line = line.strip()

    match_port_service_version = port_service_version_pattern.match(line)
    if match_port_service_version:
    port = match_port_service_version.group(1)
    state = match_port_service_version.group(2)
    service = match_port_service_version.group(3)
    version_info = match_port_service_version.group(4).strip()

    if state == "open":
    port_entry = {"port": port, "service": service, "state": state}
    if version_info:
    port_entry["version"] = version_info
    report_data["open_ports"].append(port_entry)

    match_script = script_output_pattern.match(line)
    if match_script:
    script_name = match_script.group(1).strip()
    script_output = match_script.group(2).strip()
    if len(script_output) > 10 and not any(kw in script_output.lower() for kw in ["no results", "not found", "nothing found", "no issues"]):
    report_data["nmap_vulnerabilities"].append({"script": script_name, "finding": script_output})
    elif "VULNERABLE:" in script_output or "VULN:" in script_output or "vulnerable:" in script_output:
    report_data["nmap_vulnerabilities"].append({"script": script_name, "finding": script_output})

    if report_data["open_ports"] or report_data["nmap_vulnerabilities"]:
    report_data["nmap_scan_summary"] = "Escaneo Nmap completado con hallazgos de puertos y/o vulnerabilidades NSE."
    else:
    report_data["nmap_scan_summary"] = "Escaneo Nmap completado, no se encontraron puertos abiertos o vulnerabilidades NSE."


    # --- Procesar Nuclei Output ---
    if nuclei_output:
    lines = nuclei_output.split('\n')
    for line in lines:
    line = line.strip()
    nuclei_match = re.match(r"^\[([^\]]+)\]\s*\[([^\]]+)\]\s*([^\s]+)\s*(.*)", line)
    if nuclei_match:
    template_id = nuclei_match.group(1)
    severity = nuclei_match.group(2)
    url = nuclei_match.group(3)
    info = nuclei_match.group(4).strip()
    report_data["nuclei_vulnerabilities"].append({
    "template": template_id,
    "severity": severity,
    "url": url,
    "info": info
    })
    elif line:
    if not any(kw in line.lower() for kw in ["no results", "found 0", "all templates loaded"]):
    report_data["nuclei_vulnerabilities"].append({"raw_output": line})

    # --- Procesar Searchsploit Output ---
    if searchsploit_output:
    try:
    # Searchsploit JSON output can be tricky. Try to load as JSON first.
    # If searchsploit returns multiple JSON objects (e.g., --json output), it's a list.
    searchsploit_json_array = json.loads(searchsploit_output)
    if isinstance(searchsploit_json_array, list):
    report_data["searchsploit_findings"].extend(searchsploit_json_array)
    else: # If it's a single JSON object or other unexpected JSON
    report_data["searchsploit_findings"].append({"query": "multiple_queries_or_unknown", "results": searchsploit_output})
    except json.JSONDecodeError:
    # If not a JSON, treat as raw text output
    report_data["searchsploit_findings"].append({"query": "unknown_or_single_query", "results": searchsploit_output})


    # Update general scan summary
    if report_data["open_ports"] or report_data["nmap_vulnerabilities"] or report_data["nuclei_vulnerabilities"] or report_data["searchsploit_findings"]:
    report_data["scan_summary"] = "Escaneo de seguridad completado con hallazgos importantes."
    else:
    report_data["scan_summary"] = "No se encontraron hallazgos significativos en los escaneos."

    # Si no se encontró absolutamente nada, devolver un JSON más simple.
    if not report_data["open_ports"] and not report_data["nmap_vulnerabilities"] and not report_data["nuclei_vulnerabilities"] and not report_data["searchsploit_findings"]:
    return json.dumps({"status": "No se encontraron hallazgos significativos en los escaneos.", "target": report_data["target"] if report_data["target"] else "Desconocido", "report_generated_by": report_data["report_generated_by"]}, indent=2)

    return json.dumps(report_data, indent=2)


    # --- Configuración del Agente de LangChain ---

    class CustomOutputParser(AgentOutputParser):
    def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
    if "Final Answer:" in llm_output:
    return AgentFinish(
    return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
    log=llm_output,
    )
    regex = r"Action:\s*(.*)\nAction Input:\s*(.*)"
    match = re.search(regex, llm_output)
    if not match:
    raise ValueError(f"Could not parse LLM output: `{llm_output}`")
    action = match.group(1).strip()
    action_input = match.group(2).strip()
    return AgentAction(tool=action, tool_input=action_input, log=llm_output)

    # Inicializar el modelo Ollama (por ejemplo, con 'llama3')
    # Asegúrate de tener el modelo 'llama3' descargado en tu Ollama: ollama pull llama3
    llm = Ollama(model="llama3", temperature=0.0)

    # Lista de herramientas disponibles para el agente
    tools = [
    nmap_recon_scan,
    nmap_vulnerability_scan,
    nuclei_scan,
    searchsploit_lookup,
    generate_security_report
    ]

    # Plantilla de Prompt para el agente
    # Aquí puedes ajustar las instrucciones para el agente.
    # Es crucial que las instrucciones sean claras y específicas sobre cuándo usar cada herramienta.
    # El prompt debe guiar al LLM a seguir el patrón ReAct (Thought, Action, Action Input, Observation, ...)
    prompt = PromptTemplate.from_template("""
    Eres un asistente de ciberseguridad experto. Tu objetivo es ayudar a los usuarios a realizar análisis de seguridad.
    Debes utilizar las herramientas disponibles para recopilar información, identificar vulnerabilidades y generar un reporte.
    Responde en español y sigue el siguiente formato:
    Thought: Siempre debes pensar en qué hacer a continuación.
    Action: La acción a realizar, debe ser una de las herramientas [{tool_names}]
    Action Input: Los parámetros de entrada para la acción.
    Observation: El resultado de la acción.
    ... (Este Thought/Action/Action Input/Observation se puede repetir varias veces)
    Thought: He terminado mi tarea y tengo la respuesta final.
    Final Answer: La respuesta final a la pregunta original del usuario, incluyendo el reporte de seguridad si se solicitó.
    Aquí hay un ejemplo:
    Question: Escanea 192.168.1.1 en busca de puertos abiertos y luego busca exploits para los servicios encontrados.
    Thought: El usuario quiere escanear un objetivo para puertos y servicios. Usaré nmap_recon_scan para el reconocimiento inicial.
    Action: nmap_recon_scan
    Action Input: 192.168.1.1
    Observation: Nmap scan report for 192.168.1.1... (Aquí iría la salida real de Nmap)
    Thought: He obtenido puertos y servicios. Ahora necesito buscar exploits para estos servicios. Por ejemplo, si encontré un servicio FTP vsftpd 2.3.4, buscaré exploits para eso.
    Action: searchsploit_lookup
    Action Input: vsftpd 2.3.4
    Observation: (Aquí iría la salida de Searchsploit)
    Thought: He recolectado la información de reconocimiento y exploits. Ahora debo generar un reporte de seguridad combinando toda la información.
    Action: generate_security_report
    Action Input: nmap_output="...", searchsploit_output="..."
    Observation: (Aquí iría el reporte JSON)
    Thought: El reporte de seguridad ha sido generado exitosamente. Estoy listo para dar la respuesta final al usuario.
    Final Answer: Aquí tienes el reporte de seguridad completo en formato JSON: {...}
    Comienza!
    Question: {input}
    {agent_scratchpad}
    """)

    # Crear el agente
    # Necesitamos pasar tools y tool_names al prompt
    agent = create_react_agent(llm, tools, prompt, tools_arg=tools, tool_names_arg=", ".join([t.name for t in tools]))

    # Crear el ejecutor del agente
    agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True, # Establecer en True para ver los pensamientos y acciones del agente
    handle_parsing_errors=True # Maneja errores de parsing si el LLM no sigue el formato
    )

    if __name__ == "__main__":
    print("¡Agente de Ciberseguridad iniciado! Escribe 'exit' para salir.")
    while True:
    user_input = input("¿En qué puedo ayudarte? ")
    if user_input.lower() == 'exit':
    break
    try:
    # Usa .invoke para ejecutar el agente con la entrada del usuario
    result = agent_executor.invoke({"input": user_input})
    print("\nResultado Final:")
    print(result["output"])
    except Exception as e:
    print(f"\n¡Se produjo un error al ejecutar el agente!: {e}")
    print("Por favor, intenta reformular tu pregunta o revisa la configuración.")