# Copyright (c) 2010-2025 Jacob Hammack.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


module Risu
	module Base

		#
		class PostProcessBase
			@possible_postprocesses = Array.new

			class << self
				attr_reader :possible_postprocesses
			end

			#
			attr_accessor :info

			#
			def self.inherited child
				possible_postprocesses << child
			end

			def == other

				if self.info == nil || self.info[:plugin_id] == nil
					false
				elsif other == nil || other.info == nil || other.info[:plugin_id] == nil
					false
				else
					self.info[:plugin_id] == other.info[:plugin_id]
				end
			end

			def <=> other
				if self.info[:plugin_id] < other.info[:plugin_id]
					-1
				elsif self.info[:plugin_id] > other.info[:plugin_id]
					1
				else
					0
				end
			end

			# Create a plugin based on a combination of all plugins
			# to be rolled up.
			def create_plugin

				plugin = Plugin.find_by_id(@info[:plugin_id])

				if plugin == nil
					plugin = Plugin.new
				end

				# Populate items from post process module
				plugin.id = @info[:plugin_id]
				plugin.plugin_name = @info[:plugin_name]
				plugin.description = @info[:description]
				plugin.plugin_version = @info[:version]
				plugin.plugin_publication_date = @info[:publication_date]
				plugin.plugin_modification_date = @info[:modification_date]

				# Boiler plate for all roll up plugins
				plugin.family_name = "Risu Rollup Plugins"
				plugin.synopsis = "Software often has vulnerabilities that are corrected in newer versions. It was determined that an older version of the software is installed on this system."
				plugin.solution = "If possible, update to the latest version of the software."
				plugin.plugin_type = "Rollup"
				plugin.rollup = true
				plugin.compliance = false

				# Find oldest vuln date.
				begin
					p = Plugin.where(:id => @info[:plugin_ids]).where.not(:vuln_publication_date => nil).order(:vuln_publication_date).first
					unless p.nil?
						plugin.vuln_publication_date = p.vuln_publication_date
					end
				end

				begin
					p = Plugin.where(:id => @info[:plugin_ids]).where.not(:cvss_base_score => nil).order(:cvss_base_score).last
					unless p.nil?
						plugin.cvss_base_score = p.cvss_base_score
						plugin.cvss_vector = p.cvss_vector
					end
				end

				begin
					p = Plugin.where(:id => @info[:plugin_ids]).where.not(:cvss_temporal_score => nil).order(:cvss_temporal_score).last
					unless p.nil?
						plugin.cvss_temporal_score = p.cvss_temporal_score
						plugin.cvss_temporal_vector = p.cvss_temporal_vector
					end
				end

				if Plugin.where(:id => @info[:plugin_ids], :exploit_available => true).count > 0
					plugin.exploit_available = true
				end

				if Plugin.where(:id => @info[:plugin_ids], :exploit_framework_core => "true").count > 0
					plugin.exploit_framework_core = true
				end

				if Plugin.where(:id => @info[:plugin_ids], :exploit_framework_metasploit => "true").count > 0
					plugin.exploit_framework_metasploit = true
				end

				if Plugin.where(:id => @info[:plugin_ids], :exploit_framework_canvas => "true").count > 0
					plugin.exploit_framework_canvas = true
				end

				if Plugin.where(:id => @info[:plugin_ids], :exploit_framework_exploithub => "true").count > 0
					plugin.exploit_framework_exploithub = true
				end

				if Plugin.where(:id => @info[:plugin_ids], :exploit_framework_d2_elliot => "true").count > 0
					plugin.exploit_framework_d2_elliot = true
				end

				if Plugin.where(:id => @info[:plugin_ids], :in_the_news => true).count > 0
					plugin.in_the_news = true
				end

				if Plugin.where(:id => @info[:plugin_ids], :exploited_by_malware => "true").count > 0
					plugin.exploited_by_malware = true
				end

				["Critical", "High", "Medium", "Low", "Info"].each do |risk|
					if Plugin.where(:id => @info[:plugin_ids], :risk_factor => risk).size > 0
						plugin.risk_factor = risk
						break
					end
				end

				begin
					p = Plugin.where(:id => @info[:plugin_ids]).where.not(:stig_severity => nil).order(:stig_severity).first
					unless p.nil?
						plugin.stig_severity = p.stig_severity
					end
				end

				# Broken
				#plugin.references << References.where(:plugin_id => @info[:plugin_ids], :reference_name => "cve")

				plugin.save
			end

			#
			def create_item host_id, severity
				item = Item.new

					item.host_id = host_id
					item.plugin_id = @info[:plugin_id]
					item.plugin_output = nil
					item.port = 0
					item.severity = severity
					item.plugin_name = @info[:item_name]
					item.rollup_finding = true

				item.save
			end

			#
			def has_findings
				Item.where(:plugin_id => @info[:plugin_ids]).count > 0
			end

			def has_host_findings(host_id)
				Item.where(:plugin_id => @info[:plugin_ids]).where(:host_id => host_id).count > 0
			end

			#
			def calculate_severity current_severity, severity
				if severity > current_severity
					return severity
				else
					return current_severity
				end
			end

			#
			def run
				if !has_findings()
					return
				end

				# If this is a "roll up" post-process, create a plugin
				if Plugin.where(:id => @info[:plugin_ids]).count > 0
					create_plugin()
				end

				finding_severity = 0

				Host.all.each do |host|
					if !has_host_findings(host.id)
						next
					end

					# Downgrade Nessus findings to -1, to replace with rollup
					@info[:plugin_ids].each do |plugin_id|
						Item.where(:plugin_id => plugin_id).each do |item|

							severity = item.severity
							item.real_severity = severity
							item.severity = -1
							item.save

							finding_severity = calculate_severity(finding_severity, severity)
						end
					end

					create_item(host.id, finding_severity)
				end
			end
		end
	end
end
