Class: ServiceDeskHelper

Inherits:
BaseHelper show all
Defined in:
lib/nexpose_ticketing/helpers/servicedesk_helper.rb

Instance Attribute Summary collapse

Attributes inherited from BaseHelper

#options, #service_data

Instance Method Summary collapse

Methods inherited from BaseHelper

#finish, #load_dependencies

Constructor Details

#initialize(service_data, options, mode) ⇒ ServiceDeskHelper

Returns a new instance of ServiceDeskHelper.



11
12
13
14
15
16
17
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 11

def initialize(service_data, options, mode)
  super(service_data, options, mode)

  @rest_uri = service_data[:rest_uri]
  @api_key = service_data[:api_key]
  @ticket_db_path = service_data[:ticket_db_path]
end

Instance Attribute Details

#logObject

Returns the value of attribute log.



9
10
11
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 9

def log
  @log
end

Instance Method Details

#add_ticket_to_database(workorderid, nxid) ⇒ Object



25
26
27
28
29
30
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 25

def add_ticket_to_database(workorderid, nxid)
  @log.log_message("Adding ticket <#{workorderid}> for NXID <#{nxid}> to local db.")
  db = open_database()
  db[nxid] = workorderid
  db.close()
end

#close_ticket(ticket) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 257

def close_ticket(ticket)
  @log.log_message("Connecting to #{@rest_uri}/#{ticket[:workorderid]}")
  uri = URI( "#{@rest_uri}/#{ticket[:workorderid]}" )
  res = Net::HTTP::post_form(uri,
                             'OPERATION_NAME' => 'CLOSE_REQUEST',
                             'TECHNICIAN_KEY' => @api_key)

  response = Nokogiri::XML.parse(res.read_body)
  begin
    status = Integer(response.xpath('//statuscode').text)
  rescue Exception => e
    @log.log_message("XML request was #{ticket[:description]} response is #{response.to_xml}")
    raise e
  end

  unless status == 200
    @log.log_message("Unable to close ticket #{ticket}, got response #{response.to_xml}")
  end
end

#close_tickets(tickets) ⇒ Object



334
335
336
337
338
339
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 334

def close_tickets( tickets )
  to_close = tickets.select { |t| t[:action] == :close && !t[:workorderid].nil? }
  @metrics.closed to_close.count
  to_close.each { |ticket| close_ticket(ticket) }
  remove_tickets_from_database(tickets)
end

#create_ticket_request(subject, description) ⇒ Object

Uses the configured or default options to set up a ticket creation request



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 128

def create_ticket_request(subject, description)
  request = Nokogiri::XML::Builder.new do |xml|
    xml.Operation {
      xml.Details {
        xml.parameter {
          xml.name {
              xml.text 'requester'
          }
          xml.value {
            xml.text @service_data[:requester]
          }
        }
        xml.parameter {
          xml.name {
            xml.text 'Group'
          }
          xml.value {
            xml.text @service_data[:group]
          }
        }
        xml.parameter {
          xml.name {
            xml.text 'subject'
          }
          xml.value {
            xml.text subject
          }
        }
        xml.parameter {
          xml.name {
            xml.text 'description'
          }
          xml.value {
            xml.cdata description
          }
        }
      }
    }
  end
  request.to_xml
end

#create_tickets(tickets) ⇒ Object



278
279
280
281
282
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 278

def create_tickets(tickets)
  @log.log_message("Creating tickets on server at #{@rest_uri}")

  tickets.each { |ticket| submit_ticket(ticket) }
end

#find_ticket_in_database(nxid) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 33

def find_ticket_in_database(nxid) 
  @log.log_message("Finding workorder id for NXID <#{nxid}> from local db.")
  db = open_database()
  begin
    workorderid = db[nxid]
    @log.log_message("Lookup found incident <#{workorderid}> in the db.")
  rescue Exception => e
    @log.log_message("Threw an exception accessing the dbm <#{e.class} #{e} #{e.message}>.")
    raise e
  end
  db.close()

  workorderid
end

#modify_ticket(ticket) ⇒ Object



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 235

def modify_ticket(ticket)
  @log.log_message("Connecting to #{@rest_uri}/#{ticket[:workorderid]}")
  uri = URI( "#{@rest_uri}/#{ticket[:workorderid]}")
  res = Net::HTTP::post_form(uri,
                             'OPERATION_NAME' => 'EDIT_REQUEST',
                             'TECHNICIAN_KEY' => @api_key,
                             'INPUT_DATA' => ticket[:description])

  response = Nokogiri::XML.parse(res.read_body)
  begin
    status = Integer(response.xpath('//statuscode').text)
  rescue Exception => e
    @log.log_message("XML request was #{ticket[:description]} response is #{response.to_xml}")
    raise e
  end
  
  unless status == 200
    @log.log_message("Unable to modify ticket #{ticket}, got response #{response.to_xml}")
  end
end

#modify_ticket_request(description) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 170

def modify_ticket_request(description)
#         modifyRequest = """
# <Operation>
#     <Details>
#         <parameter>
#             <name>description</name>
#             <value>#{description}</value>
#         </parameter>
#     </Details>
# </Operation>
# """
  doc = Nokogiri::XML::Builder.new() do |xml|
    xml.Operation {
      xml.Details {
        xml.parameter {
          xml.name {
              xml.text 'requester'
          }
          xml.value {
            xml.text @service_data[:requester]
          }
        }
        xml.parameter {
          xml.name {
            xml.text 'description'
          }
          xml.value {
            xml.cdata description
          }
        }
      }
    }
  end
  doc.to_xml
end

#open_databaseObject



20
21
22
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 20

def open_database()
  DBM.open(@ticket_db_path, 0600, DBM::WRCREAT)
end

#prepare_close_tickets(vulnerability_list, nexpose_identifier_id) ⇒ Object

Prepare ticket closures from the CSV of vulnerabilities exported from Nexpose.

  • Args :

    • vulnerability_list - CSV of vulnerabilities within Nexpose.

  • Returns :

    • List of savon-formated (hash) tickets for closing within ServiceDesk.



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 312

def prepare_close_tickets(vulnerability_list, nexpose_identifier_id)
  @log.log_message("Preparing ticket closures for mode #{@options[:ticket_mode]}.")
  @nxid = nil
  tickets = []
  CSV.parse(vulnerability_list.chomp, headers: :first_row)  do |row|
    @nxid = @mode_helper.get_nxid(nexpose_identifier_id, row)

    workorderid = find_ticket_in_database(@nxid)
    # Query ServiceDesk for the incident by unique id (generated NXID)
    if workorderid.nil? || workorderid.empty?
      @log.log_message("No workorderid found for NXID #{@nxid}")
    else
      tickets << { :action => :close, :nxid => @nxid, 
                   :workorderid => workorderid,
                   :description => 'Automatically closing ticket.' }
    end
  end
  tickets
end

#prepare_create_tickets(vulnerability_list, nexpose_identifier_id) ⇒ Object



60
61
62
63
64
65
66
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 60

def prepare_create_tickets(vulnerability_list, nexpose_identifier_id)
  @log.log_message('Preparing ticket creation...')
  tickets = prepare_tickets(vulnerability_list, nexpose_identifier_id)

  tickets.each { |ticket| @log.log_message("Prepared ticket: #{ticket}")}
  tickets
end

#prepare_tickets(vulnerability_list, nexpose_identifier_id) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 69

def prepare_tickets(vulnerability_list, nexpose_identifier_id)
  @metrics.start
  @log.log_message("Preparing ticket for #{@options[:ticket_mode]} mode.")
  matching_fields = @mode_helper.get_matching_fields
  tickets = []
  host_vulns={}
  previous_row = nil
  description = nil
  nxid = nil

  initial_scan = false

  CSV.parse( vulnerability_list.chomp, headers: :first_row )  do |row|
    initial_scan = initial_scan || row['comparison'].nil?
      
    if previous_row.nil?
      nxid = @mode_helper.get_nxid(nexpose_identifier_id, row)
      previous_row = row.dup
      description = @mode_helper.get_description(nexpose_identifier_id, row)  

      host_vulns[nxid] = { :ip => row['ip_address'], 
                           :description => "",
                           :title => @mode_helper.get_title(row) } 
    elsif matching_fields.any? {  |x| previous_row[x].nil? || previous_row[x] != row[x] }
      info = @mode_helper.get_field_info(matching_fields, previous_row)
      @log.log_message("Generated ticket with #{info}")

      host_vulns[nxid][:description] = @mode_helper.print_description(description)
      previous_row = nil
      description = nil
      redo
    else
      description = @mode_helper.update_description(description, row)
    end
  end

  unless host_vulns[nxid].nil? || host_vulns[nxid].empty?
    host_vulns[nxid][:description] = @mode_helper.print_description(description)
  end

  host_vulns.each do |nxid, vuln_info|
    workorderid = initial_scan ? nil : find_ticket_in_database(nxid)
    if workorderid.nil? || workorderid.empty?
      @log.log_message("Creating new incident for assetid #{nxid}")
      @metrics.created
      tickets << { :action => :create, :nxid => nxid,
                   :description => create_ticket_request(vuln_info[:title], vuln_info[:description]) }
    else
      @log.log_message("Updating incident for assetid #{nxid}")
      @metrics.updated
      tickets << { :action => :modify, :nxid => nxid, 
                   :workorderid => workorderid,
                   :description => modify_ticket_request(vuln_info[:description]) }
    end
  end
  tickets
end

#prepare_update_tickets(vulnerability_list, nexpose_identifier_id) ⇒ Object



285
286
287
288
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 285

def prepare_update_tickets(vulnerability_list, nexpose_identifier_id)
  @log.log_message('Preparing ticket updates...')
  prepare_tickets(vulnerability_list, nexpose_identifier_id)
end

#remove_tickets_from_database(tickets) ⇒ Object



49
50
51
52
53
54
55
56
57
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 49

def remove_tickets_from_database(tickets)
  db = open_database()
  tickets.each do |t|
    nxid = t[:nxid]
    @log.log_message("Removing workorder id from database for NXID <#{nxid}>")
    db.delete(nxid) unless db[nxid].nil?
  end
  db.close()
end

#submit_ticket(ticket) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 206

def submit_ticket(ticket)
  @log.log_message("Connecting to #{@rest_uri}.")
  uri = URI( @rest_uri )
  res = Net::HTTP::post_form(uri,
                             'OPERATION_NAME' => 'ADD_REQUEST',
                             'TECHNICIAN_KEY' => @api_key,
                             'INPUT_DATA' => ticket[:description])

  response = Nokogiri::XML.parse(res.read_body)
  begin
    status = response.xpath('//statuscode').text
    status_code = status.empty? ? -1 : Integer(status)
  
    if status_code != 200
      @log.log_message("Unable to create ticket #{ticket}, got response #{response.to_xml}")
      return
    end

    workorderid = Integer(response.xpath('//workorderid').text)
  rescue ArgumentError => ae
    @log.log_message("Failed to parse response from servicedesk #{response}")
    raise ae
  end

  @log.log_message( "created ticket #{workorderid}")
  add_ticket_to_database( workorderid, ticket[:nxid] )
end

#update_tickets(tickets) ⇒ Object



291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/nexpose_ticketing/helpers/servicedesk_helper.rb', line 291

def update_tickets(tickets)
  @log.log_message('Updating tickets')
  tickets.each do |ticket|
    if ticket[:action] == :create
      @log.log_message('Creating ticket')
      submit_ticket(ticket)
    else
      @log.log_message("Updating ticket #{ticket[:workorderid]}")
      modify_ticket(ticket)
    end
  end
end