Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wpscanteam
GitHub Repository: wpscanteam/wpscan
Path: blob/master/app/models/wp_item.rb
485 views
1
# frozen_string_literal: true
2
3
module WPScan
4
module Model
5
# WpItem (superclass of Plugin & Theme)
6
class WpItem
7
include Vulnerable
8
include Finders::Finding
9
include CMSScanner::Target::Platform::PHP
10
include CMSScanner::Target::Server::Generic
11
12
# Most common readme filenames, based on checking all public plugins and themes.
13
READMES = %w[readme.txt README.txt README.md readme.md Readme.txt].freeze
14
15
attr_reader :uri, :slug, :detection_opts, :version_detection_opts, :blog, :path_from_blog, :db_data
16
17
delegate :homepage_res, :error_404_res, :xpath_pattern_from_page, :in_scope_uris, :head_or_get_params, to: :blog
18
19
# @param [ String ] slug The plugin/theme slug
20
# @param [ Target ] blog The targeted blog
21
# @param [ Hash ] opts
22
# @option opts [ Symbol ] :mode The detection mode to use
23
# @option opts [ Hash ] :version_detection The options to use when looking for the version
24
# @option opts [ String ] :url The URL of the item
25
def initialize(slug, blog, opts = {})
26
@slug = Addressable::URI.unencode(slug)
27
@blog = blog
28
@uri = Addressable::URI.parse(opts[:url]) if opts[:url]
29
30
@detection_opts = { mode: opts[:mode] }
31
@version_detection_opts = opts[:version_detection] || {}
32
33
parse_finding_options(opts)
34
end
35
36
# @return [ Array<Vulnerabily> ]
37
def vulnerabilities
38
return @vulnerabilities if @vulnerabilities
39
40
@vulnerabilities = []
41
42
Array(db_data['vulnerabilities']).each do |json_vuln|
43
vulnerability = Vulnerability.load_from_json(json_vuln)
44
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
45
end
46
47
@vulnerabilities
48
end
49
50
# Checks if the wp_item is vulnerable to a specific vulnerability
51
#
52
# @param [ Vulnerability ] vuln Vulnerability to check the item against
53
#
54
# @return [ Boolean ]
55
def vulnerable_to?(vuln)
56
return false if version && vuln&.introduced_in && version < vuln.introduced_in
57
58
return true unless version && vuln&.fixed_in && !vuln.fixed_in.empty?
59
60
version < vuln.fixed_in
61
end
62
63
# @return [ String ]
64
def latest_version
65
@latest_version ||= metadata['latest_version'] ? Model::Version.new(metadata['latest_version']) : nil
66
end
67
68
# Not used anywhere ATM
69
# @return [ Boolean ]
70
def popular?
71
@popular ||= metadata['popular'] ? true : false
72
end
73
74
# @return [ String ]
75
def last_updated
76
@last_updated ||= metadata['last_updated']
77
end
78
79
# @return [ Boolean ]
80
def outdated?
81
@outdated ||= if version && latest_version
82
version < latest_version
83
else
84
false
85
end
86
end
87
88
# @param [ String ] path Optional path to merge with the uri
89
#
90
# @return [ String ]
91
def url(path = nil)
92
return unless @uri
93
return @uri.to_s unless path
94
95
@uri.join(Addressable::URI.encode(path)).to_s
96
end
97
98
# @return [ Boolean ]
99
def ==(other)
100
self.class == other.class && slug == other.slug
101
end
102
103
def to_s
104
slug
105
end
106
107
# @return [ Symbol ] The Class symbol associated to the item
108
def classify
109
@classify ||= classify_slug(slug)
110
end
111
112
# @return [ String, False ] The readme url if found, false otherwise
113
def readme_url
114
return if detection_opts[:mode] == :passive
115
116
return @readme_url unless @readme_url.nil?
117
118
potential_readme_filenames.each do |path|
119
t_url = url(path)
120
121
return @readme_url = t_url if Browser.forge_request(t_url, blog.head_or_get_params).run.code == 200
122
end
123
124
@readme_url = false
125
end
126
127
def potential_readme_filenames
128
@potential_readme_filenames ||= READMES
129
end
130
131
# @param [ String ] path
132
# @param [ Hash ] params The request params
133
#
134
# @return [ Boolean ]
135
def directory_listing?(path = nil, params = {})
136
return if detection_opts[:mode] == :passive
137
138
super(path, params)
139
end
140
141
# @param [ String ] path
142
# @param [ Hash ] params The request params
143
#
144
# @return [ Boolean ]
145
def error_log?(path = 'error_log', params = {})
146
return if detection_opts[:mode] == :passive
147
148
super(path, params)
149
end
150
151
# See CMSScanner::Target#head_and_get
152
#
153
# This is used by the error_log? above in the super()
154
# to have the correct path (ie readme.txt checked from the plugin/theme location
155
# and not from the blog root). Could also be used in finders
156
#
157
# @param [ String ] path
158
# @param [ Array<String> ] codes
159
# @param [ Hash ] params The requests params
160
# @option params [ Hash ] :head Request params for the HEAD
161
# @option params [ hash ] :get Request params for the GET
162
#
163
# @return [ Typhoeus::Response ]
164
def head_and_get(path, codes = [200], params = {})
165
final_path = @path_from_blog.dup # @path_from_blog is set in the plugin/theme
166
final_path << path unless path.nil?
167
168
blog.head_and_get(final_path, codes, params)
169
end
170
end
171
end
172
end
173
174