CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/parser/nokogiri_doc_mixin.rb
Views: 11623
1
# -*- coding: binary -*-
2
module Rex
3
module Parser
4
5
# Determines if Nokogiri is available and if it's a minimum
6
# acceptable version.
7
def self.load_nokogiri
8
@nokogiri_loaded = false
9
begin
10
require 'nokogiri'
11
major,minor = Nokogiri::VERSION.split(".")[0,2]
12
if major.to_i >= 1
13
if minor.to_i >= 4
14
@nokogiri_loaded = true
15
end
16
end
17
rescue LoadError => e
18
@nokogiri_loaded = false
19
@nokogiri_error = e
20
end
21
@nokogiri_loaded
22
end
23
24
def self.nokogiri_loaded
25
!!@nokogiri_loaded
26
end
27
28
# Useful during development, shouldn't be used in normal operation.
29
def self.reload(fname)
30
$stdout.puts "Reloading #{fname}..."
31
load __FILE__
32
load File.join(File.expand_path(File.dirname(__FILE__)),fname)
33
end
34
35
end
36
end
37
38
module Rex
39
module Parser
40
41
load_nokogiri && module NokogiriDocMixin
42
43
# Set up the getters and instance variables for the document
44
eval("attr_reader :args, :db, :state, :block, :report_data")
45
46
def initialize(args,db,&block)
47
@args = args
48
@db = db
49
@state = {}
50
@state[:current_tag] = {}
51
@block = block if block
52
@report_data = {:workspace => args[:workspace]}
53
@nx_console_id = args[:nx_console_id]
54
super()
55
end
56
57
# Turn XML attribute pairs in to more workable hashes (there
58
# are better Enumerable tricks in Ruby 1.9, but ignoring for now)
59
def attr_hash(attrs)
60
h = {}
61
attrs.each {|k,v| h[k] = v}
62
h
63
end
64
65
def valid_ip(addr)
66
valid = false
67
valid = ::Rex::Socket::RangeWalker.new(addr).valid? rescue false
68
!!valid
69
end
70
71
def normalize_ref(ref_type, ref_value)
72
return if ref_type.nil? || ref_type.empty? || ref_value.nil? || ref_value.empty?
73
ref_value = ref_value.strip
74
ref_type = ref_type.strip.upcase
75
76
ret = case ref_type
77
when "CVE"
78
ref_value.gsub("CAN", "CVE")
79
when "MS"
80
if ref_value =~ /^MS[0-9]/
81
"MSB-#{ref_value}"
82
else
83
"MSB-MS#{ref_value}"
84
end
85
when "URL", "BID"
86
"#{ref_type}-#{ref_value}"
87
when "APPLE"
88
ref_value
89
when "XF"
90
if ref_value =~ /\((\d+)\)$/
91
"#{ref_type}-#{$1}"
92
else
93
"#{ref_type}-#{ref_value}"
94
end
95
when "CB"
96
ref_value
97
when "DFN-CERT"
98
ref_value
99
else # Handle others?
100
"#{ref_type}-#{ref_value}"
101
end
102
return ret
103
end
104
105
def normalize_references(orig_refs)
106
return [] unless orig_refs
107
refs = []
108
orig_refs.each do |ref_hash|
109
110
ref_hash_sym = Hash[ref_hash.map {|k, v| [k.to_sym, v] }]
111
ref_type = ref_hash_sym[:source].to_s.strip.upcase
112
ref_value = ref_hash_sym[:value].to_s.strip
113
refs << normalize_ref(ref_type, ref_value)
114
end
115
return refs.compact.uniq
116
end
117
118
def in_tag(tagname)
119
@state[:current_tag].keys.include? tagname
120
end
121
122
# If there's an address, it's not on the blacklist,
123
# it has ports, and the port list isn't
124
# empty... it's okay.
125
def host_is_okay
126
return false unless @report_data[:host]
127
return false unless valid_ip(@report_data[:host])
128
return false unless @report_data[:state] == Msf::HostState::Alive
129
if @args[:blacklist]
130
return false if @args[:blacklist].include?(@report_data[:host])
131
end
132
return true
133
end
134
135
# XXX: Document classes ought to define this
136
def determine_port_state(v)
137
return v
138
end
139
140
# Circumvent the unknown attribute logging by the various reporters. They
141
# seem to be there just for debugging anyway.
142
def db_report(table, data)
143
raise "Data should be a hash" unless data.kind_of? Hash
144
nonempty_data = data.compact
145
valid_attrs = db_valid_attributes(table)
146
raise "Unknown table `#{table}'" if valid_attrs.empty?
147
case table
148
when :note, :web_site, :web_page, :web_form, :web_vuln
149
just_the_facts = nonempty_data
150
else
151
just_the_facts = nonempty_data.select {|k,v| valid_attrs.include? k.to_s.to_sym}
152
end
153
return nil if just_the_facts.empty?
154
just_the_facts[:task] = @args[:task]
155
just_the_facts[:workspace] = @args[:workspace] # workspace context is a required `fact`
156
db.send("report_#{table}", just_the_facts)
157
end
158
159
# XXX: It would be better to either have a single registry of acceptable
160
# keys if we're going to alert on bad ones, or to be more forgiving if
161
# the caller is this thing. There is basically no way to tell if
162
# report_host()'s tastes are going to change with this scheme.
163
def db_valid_attributes(table)
164
case table.to_s.to_sym
165
when :host
166
::Mdm::Host.new.attribute_names.map {|x| x.to_sym} |
167
[:host, :workspace]
168
when :service
169
::Mdm::Service.new.attribute_names.map {|x| x.to_sym} |
170
[:host, :host_name, :mac, :workspace]
171
when :vuln
172
::Mdm::Vuln.new.attribute_names.map {|x| x.to_sym} |
173
[:host, :refs, :workspace, :port, :proto, :details, :exploited_at]
174
when :vuln_details
175
::Mdm::VulnDetails.new.attribute_names.map {|x| x.to_sym} | [ :key ]
176
when :host_details
177
::Mdm::HostDetails.new.attribute_names.map {|x| x.to_sym} | [ :key ]
178
when :note, :web_site, :web_page, :web_form, :web_vuln
179
# These guys don't complain
180
[:anything]
181
else
182
[]
183
end
184
end
185
186
# Nokogiri 1.4.4 (and presumably beyond) generates attrs as pairs,
187
# like [["value1","foo"],["value2","bar"]] (but not hashes for some
188
# reason). 1.4.3.1 (and presumably 1.4.3.x and prior) generates attrs
189
# as a flat array of strings. We want array_pairs.
190
def normalize_attrs(attrs)
191
attr_pairs = []
192
case attrs.first
193
when Array, NilClass
194
attr_pairs = attrs
195
when String
196
attrs.each_index {|i|
197
next if i % 2 == 0
198
attr_pairs << [attrs[i-1],attrs[i]]
199
}
200
else # Wow, yet another format! It's either from the distant past or distant future.
201
raise ::Msf::DBImportError.new("Unknown format for XML attributes. Please check your Nokogiri version.")
202
end
203
return attr_pairs
204
end
205
206
# Removes HTML from a string
207
def strip_html_tags(text)
208
return text.gsub!(/(<[^>]*>)|\n|\t/s) {" "}
209
end
210
211
# This breaks xml-encoded characters, so need to append.
212
# It's on the end_element tag name to turn the appending
213
# off and clear out the data.
214
def characters(text)
215
return unless @state[:has_text]
216
@text ||= ""
217
@text << text
218
end
219
220
# Effectively the same as characters()
221
def cdata_block(text)
222
return unless @state[:has_text]
223
@text ||= ""
224
@text << text
225
end
226
227
def end_document
228
block = @block
229
return unless @report_type_ok
230
unless @state[:current_tag].empty?
231
missing_ends = @state[:current_tag].keys.map {|x| "'#{x}'"}.join(", ")
232
msg = "Warning, the provided file is incomplete, and there may be missing\n"
233
msg << "data. The following tags were not closed: #{missing_ends}."
234
db.emit(:warning,msg,&block) if block
235
end
236
end
237
238
end
239
240
end
241
end
242
243