nexpose_servicenow 0.4.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +16 -0
- data/Rakefile +2 -0
- data/bin/nexpose_servicenow +4 -0
- data/bin/setup +8 -0
- data/lib/nexpose_servicenow.rb +173 -0
- data/lib/nexpose_servicenow/arg_parser.rb +173 -0
- data/lib/nexpose_servicenow/chunker.rb +106 -0
- data/lib/nexpose_servicenow/historical_data.rb +234 -0
- data/lib/nexpose_servicenow/nexpose_helper.rb +162 -0
- data/lib/nexpose_servicenow/nx_logger.rb +166 -0
- data/lib/nexpose_servicenow/queries.rb +245 -0
- data/lib/nexpose_servicenow/queries_original.rb +162 -0
- data/lib/nexpose_servicenow/version.rb +5 -0
- data/nexpose_servicenow.gemspec +25 -0
- metadata +106 -0
@@ -0,0 +1,234 @@
|
|
1
|
+
require_relative './nexpose_helper'
|
2
|
+
require_relative './nx_logger'
|
3
|
+
|
4
|
+
module NexposeServiceNow
|
5
|
+
class HistoricalData
|
6
|
+
REPORT_FILE_NAME = "Nexpose-ServiceNow-latest_scans.csv"
|
7
|
+
STORED_FILE_NAME = "last_scan_data.csv"
|
8
|
+
TIMESTAMP_FILE_NAME = "last_vuln_run.csv"
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
@local_dir = File.expand_path(options[:output_dir])
|
12
|
+
@ids = options[:nexpose_ids]
|
13
|
+
|
14
|
+
@local_file = File.join(@local_dir, STORED_FILE_NAME)
|
15
|
+
@remote_file = File.join(@local_dir, REPORT_FILE_NAME)
|
16
|
+
@timestamp_file = File.join(@local_dir, TIMESTAMP_FILE_NAME)
|
17
|
+
|
18
|
+
@log = NexposeServiceNow::NxLogger.instance
|
19
|
+
@log.log_message "Retrieving environment variables."
|
20
|
+
end
|
21
|
+
|
22
|
+
#Filters the saved report down to the sites being queried
|
23
|
+
#This can then be used as a basis to update last_scan_data
|
24
|
+
def filter_report
|
25
|
+
#Create a full last_scan_data if it doesn't already exist
|
26
|
+
create_last_scan_data unless File.exist? @local_file
|
27
|
+
|
28
|
+
@log.log_message 'Filtering report down sites which will be queried'
|
29
|
+
|
30
|
+
remote_csv = load_scan_id_report
|
31
|
+
site_ids = @ids.map(&:to_s)
|
32
|
+
filtered_csv = remote_csv.delete_if { |r| !site_ids.include?(r['site_id']) }
|
33
|
+
File.open(@remote_file, 'w') do |f|
|
34
|
+
f.write(remote_csv.to_csv)
|
35
|
+
end
|
36
|
+
|
37
|
+
puts filtered_csv
|
38
|
+
end
|
39
|
+
|
40
|
+
#Reads the downloaded report containing LATEST scan IDs
|
41
|
+
def load_scan_id_report
|
42
|
+
@log.log_message "Loading scan data report"
|
43
|
+
unless File.exists? @remote_file
|
44
|
+
@log.log_message "No existing report file found."
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
CSV.read(@remote_file, headers: true)
|
48
|
+
end
|
49
|
+
|
50
|
+
#Loads the last scan data file as CSV.
|
51
|
+
#It may be necessary to create one first.
|
52
|
+
def load_last_scan_data
|
53
|
+
@log.log_message "Loading last scan data"
|
54
|
+
|
55
|
+
create_last_scan_data unless File.exist? @local_file
|
56
|
+
CSV.read(@local_file, headers: true)
|
57
|
+
end
|
58
|
+
|
59
|
+
def last_scan_ids(sites)
|
60
|
+
return [] if !File.exist? @local_file
|
61
|
+
|
62
|
+
csv = load_last_scan_data
|
63
|
+
last_scan_ids = {}
|
64
|
+
sites.each do |id|
|
65
|
+
row = csv.find { |r| r['site_id'] == id.to_s } || { 'last_scan_id' => '0' }
|
66
|
+
last_scan_ids[id.to_s] = row['last_scan_id']
|
67
|
+
end
|
68
|
+
|
69
|
+
last_scan_ids
|
70
|
+
end
|
71
|
+
|
72
|
+
#Compares stored scan IDs versus remote scan IDs.
|
73
|
+
#This determines which scans are included as filters.
|
74
|
+
def sites_to_scan
|
75
|
+
return @ids unless File.exist? @remote_file
|
76
|
+
|
77
|
+
@log.log_message 'Filtering for sites with new scans'
|
78
|
+
|
79
|
+
remote_csv = CSV.read(@remote_file, headers: true)
|
80
|
+
local_csv = load_last_scan_data
|
81
|
+
|
82
|
+
filtered_sites = []
|
83
|
+
|
84
|
+
@ids.each do |id|
|
85
|
+
remote_scan_id = remote_csv.find { |r| r['site_id'] == id.to_s } || {}
|
86
|
+
remote_scan_id = remote_scan_id['last_scan_id'] || 1
|
87
|
+
|
88
|
+
local_scan_id = local_csv.find { |r| r['site_id'] == id.to_s }
|
89
|
+
local_scan_id = local_scan_id['last_scan_id']
|
90
|
+
|
91
|
+
filtered_sites << id if local_scan_id.to_i < remote_scan_id.to_i
|
92
|
+
end
|
93
|
+
|
94
|
+
@ids = filtered_sites
|
95
|
+
end
|
96
|
+
|
97
|
+
#Creates a base last scan data file from a downloaded report
|
98
|
+
def create_last_scan_data
|
99
|
+
@log.log_message 'Creating base last scan data file'
|
100
|
+
|
101
|
+
csv = load_scan_id_report
|
102
|
+
csv.delete('finished')
|
103
|
+
csv.each { |l| l['last_scan_id'] = 0 }
|
104
|
+
|
105
|
+
save_last_scan_data(csv)
|
106
|
+
end
|
107
|
+
|
108
|
+
#Updates only the rows that were affected by this scan
|
109
|
+
def update_last_scan_data
|
110
|
+
@log.log_message "Updating last scan data"
|
111
|
+
|
112
|
+
if !(File.exist? @local_file) && !(File.exist? @remote_file)
|
113
|
+
@log.log_message 'Last scan data does not exist yet.'
|
114
|
+
return
|
115
|
+
end
|
116
|
+
|
117
|
+
updated_csv = load_last_scan_data
|
118
|
+
remote_csv = load_scan_id_report
|
119
|
+
|
120
|
+
#merge changes in from remote_csv
|
121
|
+
remote_csv.each do |row|
|
122
|
+
updated_row = updated_csv.find { |r| r['site_id'] == row['site_id'] }
|
123
|
+
updated_row['last_scan_id'] = row['last_scan_id']
|
124
|
+
end
|
125
|
+
|
126
|
+
save_last_scan_data(updated_csv)
|
127
|
+
|
128
|
+
#puts updated_csv
|
129
|
+
end
|
130
|
+
|
131
|
+
#Overwrite the last scan data file with new csv
|
132
|
+
def save_last_scan_data(csv)
|
133
|
+
@log.log_message 'Saving last scan data'
|
134
|
+
File.open(@local_file, 'w') do |f|
|
135
|
+
f.write(csv.to_csv)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def create_last_vuln_data(time=nil, sites=[])
|
140
|
+
@log.log_message 'Creating last vulnerability scan time file.'
|
141
|
+
|
142
|
+
time ||= Time.new(1985)
|
143
|
+
time = time.strftime("%Y-%m-%d") if time.class.to_s == 'Time'
|
144
|
+
|
145
|
+
file = File.expand_path(@timestamp_file)
|
146
|
+
|
147
|
+
CSV.open(file, 'w') do |csv|
|
148
|
+
csv << ['Last Scan Time', 'Sites']
|
149
|
+
csv << [time, sites.join(',')]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
#Current IDs are inserted into the updated CSV file.
|
154
|
+
def last_vuln_run
|
155
|
+
@log.log_message 'Retrieving the last vulnerability timestamp'
|
156
|
+
|
157
|
+
create_last_vuln_data if !File.exist? @timestamp_file
|
158
|
+
|
159
|
+
file = File.expand_path(@timestamp_file)
|
160
|
+
csv = CSV.open(file, headers:true)
|
161
|
+
last_run = csv.readline['Last Scan Time']
|
162
|
+
csv.close
|
163
|
+
|
164
|
+
last_run
|
165
|
+
end
|
166
|
+
|
167
|
+
#Experimental
|
168
|
+
#These should probably return strings that can be mlog'd
|
169
|
+
def log_and_print(message)
|
170
|
+
puts message
|
171
|
+
@log.log_message message unless @log.nil?
|
172
|
+
end
|
173
|
+
|
174
|
+
def log_and_error(message)
|
175
|
+
$stderr.puts "ERROR: #{message}"
|
176
|
+
@log.log_message unless @log.nil?
|
177
|
+
end
|
178
|
+
|
179
|
+
def set_last_scan(nexpose_id, scan_id)
|
180
|
+
unless File.exist? @local_file
|
181
|
+
log_and_error 'Last scan data does not exist.'
|
182
|
+
log_and_error 'Can\'t set last scan data without existing file.'
|
183
|
+
exit -1
|
184
|
+
end
|
185
|
+
|
186
|
+
csv = load_last_scan_data
|
187
|
+
row = csv.find { |r| r['site_id'] == nexpose_id }
|
188
|
+
|
189
|
+
if row.nil?
|
190
|
+
csv << [nexpose_id, scan_id]
|
191
|
+
else
|
192
|
+
row['last_scan_id'] = scan_id
|
193
|
+
end
|
194
|
+
|
195
|
+
save_last_scan_data csv
|
196
|
+
|
197
|
+
log_and_print 'Last scan data updated.'
|
198
|
+
end
|
199
|
+
|
200
|
+
def set_last_vuln(date, sites=nil)
|
201
|
+
create_last_vuln_data(date, sites)
|
202
|
+
log_and_print 'Last vuln data updated.'
|
203
|
+
end
|
204
|
+
|
205
|
+
def remove_local_file(filename)
|
206
|
+
unless File.exist? filename
|
207
|
+
log_and_error 'Can\'t remove file.'
|
208
|
+
log_and_error "File #{filename} cannot be located."
|
209
|
+
exit -1
|
210
|
+
end
|
211
|
+
|
212
|
+
new_name = "#{filename}.#{Time.new.strftime('%Y-%m-%d.%H:%M:%S')}"
|
213
|
+
begin
|
214
|
+
#Delete existing file with same name
|
215
|
+
File.delete new_name if File.exist? new_name
|
216
|
+
File.rename(filename, new_name)
|
217
|
+
rescue Exception => e
|
218
|
+
log_and_error "Error removing file:\n#{e}"
|
219
|
+
exit -1
|
220
|
+
end
|
221
|
+
|
222
|
+
log_and_print "File #{filename} removed"
|
223
|
+
end
|
224
|
+
|
225
|
+
def remove_last_scan_data
|
226
|
+
remove_local_file @local_file
|
227
|
+
end
|
228
|
+
|
229
|
+
def remove_last_vuln_data
|
230
|
+
remove_local_file @timestamp_file
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'nexpose'
|
2
|
+
require 'fileutils'
|
3
|
+
require_relative './queries'
|
4
|
+
require_relative './nx_logger'
|
5
|
+
|
6
|
+
module NexposeServiceNow
|
7
|
+
class NexposeHelper
|
8
|
+
def initialize(url, port, username, password)
|
9
|
+
@log = NexposeServiceNow::NxLogger.instance
|
10
|
+
@url = url
|
11
|
+
@port = port
|
12
|
+
@nsc = connect(username, password)
|
13
|
+
|
14
|
+
@timeout = 7200
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.get_report_names(query_name, ids)
|
18
|
+
unless Queries.multiple_reports?(query_name)
|
19
|
+
return [ id: -1, report_name: get_report_name(query_name) ]
|
20
|
+
end
|
21
|
+
|
22
|
+
ids.map { |id| { id: id, report_name: get_report_name(query_name, id) }}
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.get_report_name(query_name, id=nil)
|
26
|
+
name = "Nexpose-ServiceNow-#{query_name}"
|
27
|
+
name += "-#{id}" if Queries.multiple_reports?(query_name) && !id.nil?
|
28
|
+
name
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.get_filepath(report_name, output_dir)
|
32
|
+
path = File.join output_dir, "#{report_name}.csv"
|
33
|
+
File.expand_path path
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_report(query_name, ids, id_type, output_dir, query_options={})
|
37
|
+
output_dir = File.expand_path(output_dir.to_s)
|
38
|
+
|
39
|
+
#A single report doesn't use site filters
|
40
|
+
ids = [-1] unless Queries.multiple_reports?(query_name)
|
41
|
+
|
42
|
+
reports = []
|
43
|
+
ids.each do |id|
|
44
|
+
report_name = self.class.get_report_name(query_name, id)
|
45
|
+
clean_up_reports(report_name)
|
46
|
+
|
47
|
+
delta_options = create_query_options(query_options, id)
|
48
|
+
|
49
|
+
query = Queries.send(query_name, delta_options)
|
50
|
+
report_id = generate_config(query, report_name, [id], id_type)
|
51
|
+
run_report(report_id)
|
52
|
+
reports << save_report(report_name, report_id, output_dir)
|
53
|
+
end
|
54
|
+
|
55
|
+
return reports
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_query_options(query_options, nexpose_id=nil)
|
59
|
+
options = {}
|
60
|
+
options[:vuln_query_date] = query_options[:vuln_query_date]
|
61
|
+
|
62
|
+
return options if nexpose_id == nil || nexpose_id == -1
|
63
|
+
return 0 if query_options[:last_scans].empty?
|
64
|
+
options[:last_scan_id] = "#{query_options[:last_scans][nexpose_id] || 0}"
|
65
|
+
|
66
|
+
@log.log_message("Query options: #{options}")
|
67
|
+
|
68
|
+
options
|
69
|
+
end
|
70
|
+
|
71
|
+
def clean_up_reports(report_name)
|
72
|
+
#log 'Deleting existing report...'
|
73
|
+
reports = @nsc.list_reports
|
74
|
+
reports.select! { |r| r.name.start_with? report_name }
|
75
|
+
reports.each do |r|
|
76
|
+
@nsc.delete_report_config(r.config_id)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def generate_config(query, report_name, ids, id_type)
|
81
|
+
@log.log_message "Generating query config with name #{report_name}..."
|
82
|
+
report_config = Nexpose::ReportConfig.new(report_name, nil, 'sql')
|
83
|
+
report_config.add_filter('version', '2.0.1')
|
84
|
+
report_config.add_filter('query', query)
|
85
|
+
|
86
|
+
ids.each { |id| report_config.add_filter(id_type, id) if id != -1 }
|
87
|
+
report_id = report_config.save(@nsc, false)
|
88
|
+
end
|
89
|
+
|
90
|
+
def run_report(report_id)
|
91
|
+
#log 'Running report...'
|
92
|
+
@nsc.generate_report(report_id, false)
|
93
|
+
wait_for_report(report_id)
|
94
|
+
end
|
95
|
+
|
96
|
+
def wait_for_report(id)
|
97
|
+
wait_until(:fail_on_exceptions => TRUE, :on_timeout => "Report generation timed out. Status: #{r = @nsc.last_report(id); r ? r.status : 'unknown'}") {
|
98
|
+
if %w(Failed Aborted Unknown).include?(@nsc.last_report(id).status)
|
99
|
+
raise "Report failed to generate! Status <#{@nsc.last_report(id).status}>"
|
100
|
+
end
|
101
|
+
@nsc.last_report(id).status == 'Generated'
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
def wait_until(options = {})
|
106
|
+
polling_interval = 15
|
107
|
+
time_limit = Time.now + @timeout
|
108
|
+
loop do
|
109
|
+
begin
|
110
|
+
val = yield
|
111
|
+
return val if val
|
112
|
+
rescue Exception => error
|
113
|
+
raise error if options[:fail_on_exceptions]
|
114
|
+
end
|
115
|
+
if Time.now >= time_limit
|
116
|
+
raise options[:on_timeout] if options[:on_timeout]
|
117
|
+
error ||= 'Timed out waiting for condition.'
|
118
|
+
raise error
|
119
|
+
end
|
120
|
+
sleep polling_interval
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def save_report(report_name, report_id, output_dir)
|
125
|
+
@log.log_message 'Saving report...'
|
126
|
+
local_file_name = self.class.get_filepath(report_name, output_dir)
|
127
|
+
File.delete(local_file_name) if File.exists? local_file_name
|
128
|
+
|
129
|
+
#log 'Downloading report...'
|
130
|
+
report_details = @nsc.last_report(report_id)
|
131
|
+
File.open(local_file_name, 'wb') do |f|
|
132
|
+
f.write(@nsc.download(report_details.uri))
|
133
|
+
end
|
134
|
+
|
135
|
+
#Got the report, cleanup server-side
|
136
|
+
@nsc.delete_report_config(report_id)
|
137
|
+
local_file_name
|
138
|
+
end
|
139
|
+
|
140
|
+
def connect(username, password)
|
141
|
+
begin
|
142
|
+
console = Nexpose::Connection.new(@url, username, password)
|
143
|
+
console.login
|
144
|
+
@log.log_message 'Logged in.'
|
145
|
+
rescue Exception => e
|
146
|
+
@log.log_error_message 'Error logging in...'
|
147
|
+
@log.log_error_message e
|
148
|
+
|
149
|
+
$stderr.puts "ERROR: Could not log in. Check log and Nexpose settings.\n#{e}"
|
150
|
+
exit -1
|
151
|
+
end
|
152
|
+
|
153
|
+
#@log.on_connect(@url, @port || 3780, console.session_id, "{}")
|
154
|
+
console
|
155
|
+
end
|
156
|
+
|
157
|
+
def all_sites
|
158
|
+
@nsc.sites.map { |s| s.id }
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'json'
|
3
|
+
require 'net/http'
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module NexposeServiceNow
|
7
|
+
class NxLogger
|
8
|
+
include Singleton
|
9
|
+
LOG_PATH = "./logs/rapid7_%s.log"
|
10
|
+
KEY_FORMAT = "external.integration.%s"
|
11
|
+
PRODUCT_FORMAT = "%s_%s"
|
12
|
+
|
13
|
+
DEFAULT_LOG = 'integration'
|
14
|
+
PRODUCT_RANGE = 4..30
|
15
|
+
KEY_RANGE = 3..15
|
16
|
+
|
17
|
+
ENDPOINT = '/data/external/statistic/'
|
18
|
+
|
19
|
+
def initialize()
|
20
|
+
create_calls
|
21
|
+
@logger_file = get_log_path @product
|
22
|
+
setup_logging(true, 'info')
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup_statistics_collection(vendor, product_name, gem_version)
|
26
|
+
begin
|
27
|
+
@statistic_key = get_statistic_key vendor
|
28
|
+
@product = get_product product_name, gem_version
|
29
|
+
rescue => e
|
30
|
+
#Continue
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup_logging(enabled, log_level = 'info', stdout=false)
|
35
|
+
@stdout = stdout
|
36
|
+
|
37
|
+
log_message('Logging disabled.') unless enabled || @log.nil?
|
38
|
+
@enabled = enabled
|
39
|
+
return unless @enabled
|
40
|
+
|
41
|
+
@logger_file = get_log_path @product
|
42
|
+
|
43
|
+
require 'logger'
|
44
|
+
directory = File.dirname(@logger_file)
|
45
|
+
FileUtils.mkdir_p(directory) unless File.directory?(directory)
|
46
|
+
io = IO.for_fd(IO.sysopen(@logger_file, 'a'), 'a')
|
47
|
+
io.autoclose = false
|
48
|
+
io.sync = true
|
49
|
+
@log = Logger.new(io, 'weekly')
|
50
|
+
@log.level = if log_level.to_s.casecmp('info') == 0
|
51
|
+
Logger::INFO
|
52
|
+
else
|
53
|
+
Logger::DEBUG
|
54
|
+
end
|
55
|
+
log_message("Logging enabled at level <#{log_level}>")
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_calls
|
59
|
+
levels = [:info, :debug, :error, :warn]
|
60
|
+
levels.each do |level|
|
61
|
+
method_name =
|
62
|
+
define_singleton_method("log_#{level.to_s}_message") do |message|
|
63
|
+
puts message if @stdout
|
64
|
+
@log.send(level, message) unless !@enabled || @log.nil?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def log_message(message)
|
70
|
+
log_info_message message
|
71
|
+
end
|
72
|
+
|
73
|
+
def log_stat_message(message)
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_log_path(product)
|
77
|
+
product.downcase! unless product.nil?
|
78
|
+
File.join(File.dirname(__FILE__), LOG_PATH % (product || DEFAULT_LOG))
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_statistic_key(vendor)
|
82
|
+
if vendor.nil? || vendor.length < KEY_RANGE.min
|
83
|
+
log_stat_message("Vendor length is below minimum of <#{KEY_RANGE}>")
|
84
|
+
return nil
|
85
|
+
end
|
86
|
+
|
87
|
+
vendor.gsub!('-', '_')
|
88
|
+
vendor.slice! vendor.rindex('_') until vendor.count('_') <= 1
|
89
|
+
|
90
|
+
vendor.delete! "^A-Za-z0-9\_"
|
91
|
+
|
92
|
+
KEY_FORMAT % vendor[0...KEY_RANGE.max].downcase
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_product(product, version)
|
96
|
+
return nil if ((product.nil? || product.empty?) ||
|
97
|
+
(version.nil? || version.empty?))
|
98
|
+
|
99
|
+
product.gsub!('-', '_')
|
100
|
+
product.slice! product.rindex('_') until product.count('_') <= 1
|
101
|
+
|
102
|
+
product.delete! "^A-Za-z0-9\_"
|
103
|
+
version.delete! "^A-Za-z0-9\.\-"
|
104
|
+
|
105
|
+
product = (PRODUCT_FORMAT % [product, version])[0...PRODUCT_RANGE.max]
|
106
|
+
|
107
|
+
product.slice! product.rindex(/[A-Z0-9]/i)+1..-1
|
108
|
+
|
109
|
+
if product.length < PRODUCT_RANGE.min
|
110
|
+
log_stat_message("Product length below minimum <#{PRODUCT_RANGE.min}>.")
|
111
|
+
return nil
|
112
|
+
end
|
113
|
+
product.downcase
|
114
|
+
end
|
115
|
+
|
116
|
+
def generate_payload(statistic_value='')
|
117
|
+
product_name, separator, version = @product.to_s.rpartition('_')
|
118
|
+
payload_value = {'version' => version}.to_json
|
119
|
+
|
120
|
+
payload = {'statistic-key' => @statistic_key.to_s,
|
121
|
+
'statistic-value' => payload_value,
|
122
|
+
'product' => product_name}
|
123
|
+
JSON.generate(payload)
|
124
|
+
end
|
125
|
+
|
126
|
+
def send(nexpose_address, nexpose_port, session_id, payload)
|
127
|
+
header = {'Content-Type' => 'application/json',
|
128
|
+
'nexposeCCSessionID' => session_id,
|
129
|
+
'Cookie' => "nexposeCCSessionID=#{session_id}"}
|
130
|
+
req = Net::HTTP::Put.new(ENDPOINT, header)
|
131
|
+
req.body = payload
|
132
|
+
http_instance = Net::HTTP.new(nexpose_address, nexpose_port)
|
133
|
+
http_instance.use_ssl = true
|
134
|
+
http_instance.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
135
|
+
response = http_instance.start { |http| http.request(req) }
|
136
|
+
log_stat_message "Received code #{response.code} from Nexpose console."
|
137
|
+
log_stat_message "Received message #{response.msg} from Nexpose console."
|
138
|
+
log_stat_message 'Finished sending statistics data to Nexpose.'
|
139
|
+
|
140
|
+
response.code
|
141
|
+
end
|
142
|
+
|
143
|
+
def on_connect(nexpose_address, nexpose_port, session_id, value)
|
144
|
+
log_stat_message 'Sending statistics data to Nexpose'
|
145
|
+
|
146
|
+
if @product.nil? || @statistic_key.nil?
|
147
|
+
log_stat_message('Invalid product name and/or statistics key.')
|
148
|
+
log_stat_message('Statistics collection not enabled.')
|
149
|
+
return
|
150
|
+
end
|
151
|
+
|
152
|
+
begin
|
153
|
+
payload = generate_payload value
|
154
|
+
send(nexpose_address, nexpose_port, session_id, payload)
|
155
|
+
rescue => e
|
156
|
+
#Let the program continue
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
#Used by net library for debugging
|
161
|
+
def <<(value)
|
162
|
+
log_debug_message(value)
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|