#!/usr/bin/env ruby
# Ruby CVE Exploit Runner
# Runs all CVE exploits for Discourse (2022, 2023, 2024)

require 'json'
require 'optparse'
require 'fileutils'

class CVERunner
  def initialize
    @options = {}
    @results = {
      'scan_info' => {
        'start_time' => Time.now.to_f,
        'target' => nil,
        'total_cves_tested' => 0,
        'vulnerabilities_found' => 0
      },
      'results_by_year' => {
        '2022' => [],
        '2023' => [],
        '2024' => []
      },
      'summary' => {
        'vulnerable_cves' => [],
        'potential_cves' => [],
        'not_vulnerable_cves' => [],
        'error_cves' => []
      }
    }
  end

  def run
    parse_options
    
    unless @options[:target]
      puts "Error: Target URL is required"
      puts "Usage: ruby run_all_cves.rb -t <target_url> [options]"
      exit 1
    end
    
    @results['scan_info']['target'] = @options[:target]
    
    puts "[INFO] Starting comprehensive CVE scan for #{@options[:target]}"
    puts "[INFO] Testing CVEs from 2022, 2023, and 2024"
    puts "=" * 60
    
    # Run CVE exploits by year
    run_year_exploits('2022')
    run_year_exploits('2023')
    run_year_exploits('2024')
    
    # Generate summary
    generate_summary
    
    # Save results
    save_results
    
    # Display final summary
    display_final_summary
  end

  private

  def parse_options
    OptionParser.new do |opts|
      opts.banner = "Usage: ruby run_all_cves.rb [options]"
      
      opts.on('-t', '--target URL', 'Target Discourse URL (required)') do |url|
        @options[:target] = url.chomp('/')
      end
      
      opts.on('-o', '--output FILE', 'Output file for results (default: cve_results_TIMESTAMP.json)') do |file|
        @options[:output] = file
      end
      
      opts.on('-v', '--verbose', 'Verbose output') do
        @options[:verbose] = true
      end
      
      opts.on('-y', '--year YEAR', 'Run only specific year (2022, 2023, or 2024)') do |year|
        if ['2022', '2023', '2024'].include?(year)
          @options[:year] = year
        else
          puts "Error: Invalid year. Must be 2022, 2023, or 2024"
          exit 1
        end
      end
      
      opts.on('-h', '--help', 'Show this help message') do
        puts opts
        exit
      end
    end.parse!
  end

  def run_year_exploits(year)
    return if @options[:year] && @options[:year] != year
    
    exploit_file = "cve_#{year}_exploits.rb"
    
    unless File.exist?(exploit_file)
      puts "[WARNING] #{exploit_file} not found, skipping #{year} CVEs"
      return
    end
    
    puts "\n[INFO] Running #{year} CVE exploits..."
    puts "-" * 40
    
    begin
      # Load and run the exploit file
      load exploit_file
      
      # Get the class name for the year
      class_name = "CVE#{year}Exploits"
      
      if Object.const_defined?(class_name)
        exploit_class = Object.const_get(class_name)
        scanner = exploit_class.new(@options[:target], {
          timeout: 15,
          user_agent: 'Mozilla/5.0 (compatible; DiscourseScanner/CVERunner)'
        })
        
        year_results = scanner.run_all_tests
        @results['results_by_year'][year] = year_results
        
        # Count results
        vulnerable_count = year_results.count { |r| r[:status] == 'VULNERABLE' }
        potential_count = year_results.count { |r| r[:status] == 'POTENTIAL' }
        
        puts "[INFO] #{year} CVE scan completed:"
        puts "  - Total tests: #{year_results.length}"
        puts "  - Vulnerable: #{vulnerable_count}"
        puts "  - Potential: #{potential_count}"
        
        @results['scan_info']['total_cves_tested'] += year_results.length
        @results['scan_info']['vulnerabilities_found'] += vulnerable_count
        
      else
        puts "[ERROR] Class #{class_name} not found in #{exploit_file}"
      end
      
    rescue => e
      puts "[ERROR] Failed to run #{year} exploits: #{e.message}"
      puts e.backtrace.first(3) if @options[:verbose]
    end
  end

  def generate_summary
    puts "\n[INFO] Generating summary..."
    
    @results['results_by_year'].each do |year, results|
      results.each do |result|
        case result[:status]
        when 'VULNERABLE'
          @results['summary']['vulnerable_cves'] << {
            'year' => year,
            'cve_id' => result[:cve_id],
            'description' => result[:description],
            'target' => result[:target]
          }
        when 'POTENTIAL'
          @results['summary']['potential_cves'] << {
            'year' => year,
            'cve_id' => result[:cve_id],
            'description' => result[:description],
            'target' => result[:target]
          }
        when 'NOT_VULNERABLE'
          @results['summary']['not_vulnerable_cves'] << {
            'year' => year,
            'cve_id' => result[:cve_id],
            'description' => result[:description]
          }
        when 'ERROR'
          @results['summary']['error_cves'] << {
            'year' => year,
            'cve_id' => result[:cve_id],
            'description' => result[:description],
            'error' => result[:description]
          }
        end
      end
    end
    
    @results['scan_info']['end_time'] = Time.now.to_f
    @results['scan_info']['duration'] = @results['scan_info']['end_time'] - @results['scan_info']['start_time']
  end

  def save_results
    output_file = @options[:output] || "cve_results_#{Time.now.strftime('%Y%m%d_%H%M%S')}.json"
    
    begin
      File.write(output_file, JSON.pretty_generate(@results))
      puts "\n[INFO] Results saved to #{output_file}"
    rescue => e
      puts "\n[ERROR] Failed to save results: #{e.message}"
    end
  end

  def display_final_summary
    puts "\n" + "=" * 60
    puts "CVE SCAN SUMMARY"
    puts "=" * 60
    
    puts "Target: #{@results['scan_info']['target']}"
    puts "Duration: #{@results['scan_info']['duration'].round(2)} seconds"
    puts "Total CVEs tested: #{@results['scan_info']['total_cves_tested']}"
    puts "\nResults breakdown:"
    puts "  🔴 VULNERABLE: #{@results['summary']['vulnerable_cves'].length}"
    puts "  🟡 POTENTIAL: #{@results['summary']['potential_cves'].length}"
    puts "  🟢 NOT VULNERABLE: #{@results['summary']['not_vulnerable_cves'].length}"
    puts "  ❌ ERRORS: #{@results['summary']['error_cves'].length}"
    
    if @results['summary']['vulnerable_cves'].any?
      puts "\n🔴 VULNERABLE CVEs FOUND:"
      @results['summary']['vulnerable_cves'].each do |vuln|
        puts "  - #{vuln['cve_id']} (#{vuln['year']}): #{vuln['description']}"
      end
    end
    
    if @results['summary']['potential_cves'].any?
      puts "\n🟡 POTENTIAL VULNERABILITIES:"
      @results['summary']['potential_cves'].each do |pot|
        puts "  - #{pot['cve_id']} (#{pot['year']}): #{pot['description']}"
      end
    end
    
    if @results['summary']['error_cves'].any?
      puts "\n❌ ERRORS ENCOUNTERED:"
      @results['summary']['error_cves'].each do |err|
        puts "  - #{err['cve_id']} (#{err['year']}): #{err['error']}"
      end
    end
    
    puts "\n" + "=" * 60
    
    if @results['summary']['vulnerable_cves'].any?
      puts "⚠️  SECURITY ALERT: #{@results['summary']['vulnerable_cves'].length} vulnerable CVE(s) found!"
      puts "   Please review and patch your Discourse installation."
    else
      puts "✅ No confirmed vulnerabilities found."
    end
    
    puts "\nFor detailed results, check the JSON output file."
  end
end

# Main execution
if __FILE__ == $0
  runner = CVERunner.new
  runner.run
end