#!/usr/bin/env python3

'''
This script reads the URL https://wordpress.org/plugins/browse/popular/ until page 10, extract each plugin name and namespace,
then in http://plugins.svn.wordpress.org/ website, looks for the "Stable tag" inside the readme.txt and extract the last version
number from trunk branch. Finally generates a template and a payload file with last version number to be used during scan that
compares the detect version with the payload version.

The generated template also includes the tags top-100 and top-200 allowing filtering.

e.g.
nuclei -t http/technologies/wordpress/plugins -tags top-100 -u https://www.example.com
'''

__author__ = "ricardomaia"

from time import sleep
from bs4 import BeautifulSoup
import requests
import re
from markdown import markdown
import os
from termcolor import colored, cprint

# Regex to extract the name of th plugin from the URL
regex = r"https://wordpress.org/plugins/(\w.+)/"

ranking = 1

# Top 200 Wordpress Plugins
for page_number in range(1, 11):

    html = requests.get(url=f"https://wordpress.org/plugins/browse/popular/page/{page_number}", headers={
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
        "Accept-Language": "en-US,en;q=0.9",
        "Accept-Encoding": "gzip, deflate",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "Connection": "keep-alive",
        "Upgrade-Insecure-Requests": "1",
        "Cache-Control": "max-age=0",
        "Pragma": "no-cache",
    }).content

    # Parse HTML
    soup = BeautifulSoup(html, 'html.parser')
    results = soup.find(class_="plugin-cards")
    articles = results.find_all("div", class_="plugin-card")

    # Setting the top tag
    top_tag = "top-100,top-200" if page_number <= 5 else "top-200"

    # Get each plugin in the page
    for article in articles:

        full_title = article.find("h3", class_="entry-title").get_text()
        regex_remove_quotes = r"[\"`:]"
        subst_remove_quotes = "'"
        title = re.sub(regex_remove_quotes, subst_remove_quotes, full_title)

        link = article.find("a").get("href")
        name = re.search(regex, link).group(1)

        cprint(f"Title: {title}", "cyan")
        cprint(f"Link: {link}", "yellow")
        cprint(f"Name: {name} - Ranking: {ranking}", "green")
        print(f"Page Number: {page_number}")
        print(f"Top Tag: {top_tag}")
        print(f"http://plugins.svn.wordpress.org/{name}/trunk/readme.txt")
        ranking += 1

        sleep(0.2)

        # Get the readme.txt file from SVN
        readme = requests.get(
            url=f"http://plugins.svn.wordpress.org/{name}/trunk/readme.txt",
            headers={
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
                "Accept-Encoding": "gzip, deflate",
                "Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6",
                "Cache-Control": "no-cache",
                "Connection": "keep-alive",
                "Host": "plugins.svn.wordpress.org",
                "Pragma": "no-cache",
                "Upgrade-Insecure-Requests": "1",
                "Referer": "http://plugins.svn.wordpress.org/{name}/trunk/"}).content

        # Extract the plugin version
        try:
            version = re.search(r"(?i)Stable.tag:\s+([\w.]+)",
                                readme.decode("utf-8")).group(1)
        except:
            version = "N/A"

        # Extract the plugin description
        try:
            description_markdown = re.search(
                r"(?i)==.Description.==\W+\n?(.*)", readme.decode("utf-8")).group(1)
            html = markdown(description_markdown)
            full_description = BeautifulSoup(html, 'html.parser').get_text()
            regex_max_length = r"(\b.{80}\b)"
            subst_max_lenght = "\\g<1>\\n    "
            description = re.sub(
                regex_max_length, subst_max_lenght, full_description, 0, re.MULTILINE)
        except:
            description = "N/A"

        print(f"Version: {version}")
        print(f"Description: {description}")

        # Write the plugin template to file
        template = f'''id: wordpress-{name}

info:
  name: {title} Detection
  author: ricardomaia
  severity: info
  reference:
    - https://wordpress.org/plugins/{name}/
  metadata:
    plugin_namespace: {name}
    wpscan: https://wpscan.com/plugin/{name}
  tags: tech,wordpress,wp-plugin,{top_tag}

http:
  - method: GET
    path:
      - "{{{{BaseURL}}}}/wp-content/plugins/{name}/readme.txt"

    payloads:
      last_version: helpers/wordpress/plugins/{name}.txt

    extractors:
      - type: regex
        part: body
        internal: true
        name: internal_detected_version
        group: 1
        regex:
          - '(?i)Stable.tag:\s?([\w.]+)'

      - type: regex
        part: body
        name: detected_version
        group: 1
        regex:
          - '(?i)Stable.tag:\s?([\w.]+)'

    matchers-condition: or
    matchers:
      - type: dsl
        name: "outdated_version"
        dsl:
          - compare_versions(internal_detected_version, concat("< ", last_version))

      - type: regex
        part: body
        regex:
          - '(?i)Stable.tag:\s?([\w.]+)'
'''

        work_dir = os.getcwd()
        print(f"Current working directory: {work_dir}")
        helper_dir = f"{work_dir}/helpers/wordpress/plugins"
        template_dir = f"{work_dir}/http/technologies/wordpress/plugins"

        if not os.path.exists(helper_dir):
            os.makedirs(helper_dir)

        if not os.path.exists(template_dir):
            os.makedirs(template_dir)

        helper_path = f"{work_dir}/helpers/wordpress/plugins/{name}.txt"
        version_file = open(helper_path, "w")
        version_file.write(version)
        version_file.close()

        template_path = f"http/technologies/wordpress/plugins/{name}.yaml"
        template_file = open(template_path, "w")  # Dev environment
        template_file.write(template)
        template_file.close()

        print("--------------------------------------------")
        print("\n")
