app/models/wp_item.rb in wpscan-3.4.5 vs app/models/wp_item.rb in wpscan-3.5.0
- old
+ new
@@ -1,158 +1,170 @@
+# frozen_string_literal: true
+
module WPScan
- # WpItem (superclass of Plugin & Theme)
- class WpItem
- include Vulnerable
- include Finders::Finding
- include CMSScanner::Target::Platform::PHP
- include CMSScanner::Target::Server::Generic
+ module Model
+ # WpItem (superclass of Plugin & Theme)
+ 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
- CHANGELOGS = %w[changelog.txt CHANGELOG.md changelog.md].freeze
+ READMES = %w[readme.txt README.txt README.md readme.md Readme.txt].freeze
- attr_reader :uri, :slug, :detection_opts, :version_detection_opts, :blog, :db_data
+ attr_reader :uri, :slug, :detection_opts, :version_detection_opts, :blog, :path_from_blog, :db_data
- delegate :homepage_res, :xpath_pattern_from_page, :in_scope_urls, to: :blog
+ delegate :homepage_res, :xpath_pattern_from_page, :in_scope_urls, :head_or_get_params, to: :blog
- # @param [ String ] slug The plugin/theme slug
- # @param [ Target ] blog The targeted blog
- # @param [ Hash ] opts
- # @option opts [ Symbol ] :mode The detection mode to use
- # @option opts [ Hash ] :version_detection The options to use when looking for the version
- # @option opts [ String ] :url The URL of the item
- def initialize(slug, blog, opts = {})
- @slug = URI.decode(slug)
- @blog = blog
- @uri = Addressable::URI.parse(opts[:url]) if opts[:url]
+ # @param [ String ] slug The plugin/theme slug
+ # @param [ Target ] blog The targeted blog
+ # @param [ Hash ] opts
+ # @option opts [ Symbol ] :mode The detection mode to use
+ # @option opts [ Hash ] :version_detection The options to use when looking for the version
+ # @option opts [ String ] :url The URL of the item
+ def initialize(slug, blog, opts = {})
+ @slug = URI.decode(slug)
+ @blog = blog
+ @uri = Addressable::URI.parse(opts[:url]) if opts[:url]
- @detection_opts = { mode: opts[:mode] }
- @version_detection_opts = opts[:version_detection] || {}
+ @detection_opts = { mode: opts[:mode] }
+ @version_detection_opts = opts[:version_detection] || {}
- parse_finding_options(opts)
- end
+ parse_finding_options(opts)
+ end
- # @return [ Array<Vulnerabily> ]
- def vulnerabilities
- return @vulnerabilities if @vulnerabilities
+ # @return [ Array<Vulnerabily> ]
+ def vulnerabilities
+ return @vulnerabilities if @vulnerabilities
- @vulnerabilities = []
+ @vulnerabilities = []
- [*db_data['vulnerabilities']].each do |json_vuln|
- vulnerability = Vulnerability.load_from_json(json_vuln)
- @vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
+ [*db_data['vulnerabilities']].each do |json_vuln|
+ vulnerability = Vulnerability.load_from_json(json_vuln)
+ @vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
+ end
+
+ @vulnerabilities
end
- @vulnerabilities
- end
+ # Checks if the wp_item is vulnerable to a specific vulnerability
+ #
+ # @param [ Vulnerability ] vuln Vulnerability to check the item against
+ #
+ # @return [ Boolean ]
+ def vulnerable_to?(vuln)
+ return true unless version && vuln && vuln.fixed_in && !vuln.fixed_in.empty?
- # Checks if the wp_item is vulnerable to a specific vulnerability
- #
- # @param [ Vulnerability ] vuln Vulnerability to check the item against
- #
- # @return [ Boolean ]
- def vulnerable_to?(vuln)
- return true unless version && vuln && vuln.fixed_in && !vuln.fixed_in.empty?
+ version < vuln.fixed_in
+ end
- version < vuln.fixed_in
- end
+ # @return [ String ]
+ def latest_version
+ @latest_version ||= db_data['latest_version'] ? Model::Version.new(db_data['latest_version']) : nil
+ end
- # @return [ String ]
- def latest_version
- @latest_version ||= db_data['latest_version'] ? WPScan::Version.new(db_data['latest_version']) : nil
- end
+ # Not used anywhere ATM
+ # @return [ Boolean ]
+ def popular?
+ @popular ||= db_data['popular']
+ end
- # Not used anywhere ATM
- # @return [ Boolean ]
- def popular?
- @popular ||= db_data['popular']
- end
+ # @return [ String ]
+ def last_updated
+ @last_updated ||= db_data['last_updated']
+ end
- # @return [ String ]
- def last_updated
- @last_updated ||= db_data['last_updated']
- end
+ # @return [ Boolean ]
+ def outdated?
+ @outdated ||= if version && latest_version
+ version < latest_version
+ else
+ false
+ end
+ end
- # @return [ Boolean ]
- def outdated?
- @outdated ||= if version && latest_version
- version < latest_version
- else
- false
- end
- end
+ # URI.encode is preferered over Addressable::URI.encode as it will encode
+ # leading # character:
+ # URI.encode('#t#') => %23t%23
+ # Addressable::URI.encode('#t#') => #t%23
+ #
+ # @param [ String ] path Optional path to merge with the uri
+ #
+ # @return [ String ]
+ def url(path = nil)
+ return unless @uri
+ return @uri.to_s unless path
- # URI.encode is preferered over Addressable::URI.encode as it will encode
- # leading # character:
- # URI.encode('#t#') => %23t%23
- # Addressable::URI.encode('#t#') => #t%23
- #
- # @param [ String ] path Optional path to merge with the uri
- #
- # @return [ String ]
- def url(path = nil)
- return unless @uri
- return @uri.to_s unless path
+ @uri.join(URI.encode(path)).to_s
+ end
- @uri.join(URI.encode(path)).to_s
- end
+ # @return [ Boolean ]
+ def ==(other)
+ self.class == other.class && slug == other.slug
+ end
- # @return [ Boolean ]
- def ==(other)
- self.class == other.class && slug == other.slug
- end
+ def to_s
+ slug
+ end
- def to_s
- slug
- end
+ # @return [ Symbol ] The Class symbol associated to the item
+ def classify
+ @classify ||= classify_slug(slug)
+ end
- # @return [ Symbol ] The Class symbol associated to the item
- def classify
- @classify ||= classify_slug(slug)
- end
+ # @return [ String, False ] The readme url if found, false otherwise
+ def readme_url
+ return if detection_opts[:mode] == :passive
- # @return [ String ] The readme url if found
- def readme_url
- return if detection_opts[:mode] == :passive
+ return @readme_url unless @readme_url.nil?
- if @readme_url.nil?
READMES.each do |path|
- return @readme_url = url(path) if Browser.get(url(path)).code == 200
+ 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
- @readme_url
- end
+ # @param [ String ] path
+ # @param [ Hash ] params The request params
+ #
+ # @return [ Boolean ]
+ def directory_listing?(path = nil, params = {})
+ return if detection_opts[:mode] == :passive
- # @return [ String, false ] The changelog urr if found
- def changelog_url
- return if detection_opts[:mode] == :passive
-
- if @changelog_url.nil?
- CHANGELOGS.each do |path|
- return @changelog_url = url(path) if Browser.get(url(path)).code == 200
- end
+ super(path, params)
end
- @changelog_url
- end
+ # @param [ String ] path
+ # @param [ Hash ] params The request params
+ #
+ # @return [ Boolean ]
+ def error_log?(path = 'error_log', params = {})
+ return if detection_opts[:mode] == :passive
- # @param [ String ] path
- # @param [ Hash ] params The request params
- #
- # @return [ Boolean ]
- def directory_listing?(path = nil, params = {})
- return if detection_opts[:mode] == :passive
+ super(path, params)
+ end
- super(path, params)
- end
+ # See CMSScanner::Target#head_and_get
+ #
+ # This is used by the error_log? above in the super()
+ # to have the correct path (ie readme.txt checked from the plugin/theme location
+ # and not from the blog root). Could also be used in finders
+ #
+ # @param [ String ] path
+ # @param [ Array<String> ] codes
+ # @param [ Hash ] params The requests params
+ # @option params [ Hash ] :head Request params for the HEAD
+ # @option params [ hash ] :get Request params for the GET
+ #
+ # @return [ Typhoeus::Response ]
+ def head_and_get(path, codes = [200], params = {})
+ final_path = +@path_from_blog
+ final_path << URI.encode(path) unless path.nil?
- # @param [ String ] path
- # @param [ Hash ] params The request params
- #
- # @return [ Boolean ]
- def error_log?(path = 'error_log', params = {})
- return if detection_opts[:mode] == :passive
-
- super(path, params)
+ blog.head_and_get(final_path, codes, params)
+ end
end
end
end