Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wpscanteam
GitHub Repository: wpscanteam/wpscan
Path: blob/master/app/models/theme.rb
485 views
1
# frozen_string_literal: true
2
3
module WPScan
4
module Model
5
# WordPress Theme
6
class Theme < WpItem
7
attr_reader :style_url, :style_name, :style_uri, :author, :author_uri, :template, :description,
8
:license, :license_uri, :tags, :text_domain
9
10
# See WpItem
11
def initialize(slug, blog, opts = {})
12
super(slug, blog, opts)
13
14
# To be used by #head_and_get
15
# If custom wp-content, it will be replaced by blog#url
16
@path_from_blog = "wp-content/themes/#{slug}/"
17
18
@uri = Addressable::URI.parse(blog.url(path_from_blog))
19
@style_url = opts[:style_url] || url('style.css')
20
21
parse_style
22
end
23
24
# Retrieve the metadata from the vuln API if available (and a valid token is given),
25
# or the local metadata db otherwise
26
# @return [ JSON ]
27
def metadata
28
@metadata ||= db_data.empty? ? DB::Theme.metadata_at(slug) : db_data
29
end
30
31
# @return [ Hash ]
32
def db_data
33
@db_data ||= DB::VulnApi.theme_data(slug)
34
end
35
36
# @param [ Hash ] opts
37
#
38
# @return [ Model::Version, false ]
39
def version(opts = {})
40
@version = Finders::ThemeVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
41
42
@version
43
end
44
45
# @return [ Theme ]
46
def parent_theme
47
return unless template
48
return unless style_body =~ /^@import\surl\(["']?([^"')]+)["']?\);\s*$/i
49
50
opts = detection_opts.merge(
51
style_url: url(Regexp.last_match[1]),
52
found_by: 'Parent Themes (Passive Detection)',
53
confidence: 100
54
).merge(version_detection: version_detection_opts)
55
56
self.class.new(template, blog, opts)
57
end
58
59
# @param [ Integer ] depth
60
#
61
# @retun [ Array<Theme> ]
62
def parent_themes(depth = 3)
63
theme = self
64
found = []
65
66
(1..depth).each do |_|
67
parent = theme.parent_theme
68
69
break unless parent
70
71
found << parent
72
theme = parent
73
end
74
75
found
76
end
77
78
def style_body
79
@style_body ||= Browser.get(style_url).body
80
end
81
82
def parse_style
83
{
84
style_name: 'Theme Name',
85
style_uri: 'Theme URI',
86
author: 'Author',
87
author_uri: 'Author URI',
88
template: 'Template',
89
description: 'Description',
90
license: 'License',
91
license_uri: 'License URI',
92
tags: 'Tags',
93
text_domain: 'Text Domain'
94
}.each do |attribute, tag|
95
instance_variable_set(:"@#{attribute}", parse_style_tag(style_body, tag)&.force_encoding('UTF-8'))
96
end
97
end
98
99
# @param [ String ] bofy
100
# @param [ String ] tag
101
#
102
# @return [ String ]
103
def parse_style_tag(body, tag)
104
value = body[/\b#{Regexp.escape(tag)}:[\t ]*([^\r\n*]+)/, 1]
105
106
value && !value.strip.empty? ? value.strip : nil
107
end
108
109
def ==(other)
110
super(other) && style_url == other.style_url
111
end
112
end
113
end
114
end
115
116