module WPScan
module Model
class WpItem
include Vulnerable
include Finders::Finding
include CMSScanner::Target::Platform::PHP
include CMSScanner::Target::Server::Generic
READMES = %w[readme.txt README.txt README.md readme.md Readme.txt].freeze
attr_reader :uri, :slug, :detection_opts, :version_detection_opts, :blog, :path_from_blog, :db_data
delegate :homepage_res, :error_404_res, :xpath_pattern_from_page, :in_scope_uris, :head_or_get_params, to: :blog
def initialize(slug, blog, opts = {})
@slug = Addressable::URI.unencode(slug)
@blog = blog
@uri = Addressable::URI.parse(opts[:url]) if opts[:url]
@detection_opts = { mode: opts[:mode] }
@version_detection_opts = opts[:version_detection] || {}
parse_finding_options(opts)
end
def vulnerabilities
return @vulnerabilities if @vulnerabilities
@vulnerabilities = []
Array(db_data['vulnerabilities']).each do |json_vuln|
vulnerability = Vulnerability.load_from_json(json_vuln)
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
end
@vulnerabilities
end
def vulnerable_to?(vuln)
return false if version && vuln&.introduced_in && version < vuln.introduced_in
return true unless version && vuln&.fixed_in && !vuln.fixed_in.empty?
version < vuln.fixed_in
end
def latest_version
@latest_version ||= metadata['latest_version'] ? Model::Version.new(metadata['latest_version']) : nil
end
def popular?
@popular ||= metadata['popular'] ? true : false
end
def last_updated
@last_updated ||= metadata['last_updated']
end
def outdated?
@outdated ||= if version && latest_version
version < latest_version
else
false
end
end
def url(path = nil)
return unless @uri
return @uri.to_s unless path
@uri.join(Addressable::URI.encode(path)).to_s
end
def ==(other)
self.class == other.class && slug == other.slug
end
def to_s
slug
end
def classify
@classify ||= classify_slug(slug)
end
def readme_url
return if detection_opts[:mode] == :passive
return @readme_url unless @readme_url.nil?
potential_readme_filenames.each do |path|
t_url = url(path)
return @readme_url = t_url if Browser.forge_request(t_url, blog.head_or_get_params).run.code == 200
end
@readme_url = false
end
def potential_readme_filenames
@potential_readme_filenames ||= READMES
end
def directory_listing?(path = nil, params = {})
return if detection_opts[:mode] == :passive
super(path, params)
end
def error_log?(path = 'error_log', params = {})
return if detection_opts[:mode] == :passive
super(path, params)
end
def head_and_get(path, codes = [200], params = {})
final_path = @path_from_blog.dup
final_path << path unless path.nil?
blog.head_and_get(final_path, codes, params)
end
end
end
end