class Nexpose::APIRequest
Attributes
error[R]
headers[R]
http[R]
raw_response[R]
raw_response_data[R]
req[R]
res[R]
sid[R]
success[R]
trace[R]
trust_store[R]
uri[R]
Public Class Methods
execute(url, req, api_version = '1.1', options = {}, trust_store = nil)
click to toggle source
# File lib/nexpose/api_request.rb, line 145 def self.execute(url, req, api_version = '1.1', options = {}, trust_store = nil) obj = self.new(req.to_s, url, api_version, trust_store) obj.execute(options) raise APIError.new(obj, "Action failed: #{obj.error}") unless obj.success obj end
new(req, url, api_version = '1.1', trust_store = nil)
click to toggle source
# File lib/nexpose/api_request.rb, line 22 def initialize(req, url, api_version = '1.1', trust_store = nil) @url = url @req = req @api_version = api_version @url = @url.sub('API_VERSION', @api_version) @trust_store = trust_store prepare_http_client end
Public Instance Methods
attributes(*args)
click to toggle source
# File lib/nexpose/api_request.rb, line 140 def attributes(*args) return unless @res.root @res.root.attributes(*args) end
execute(options = {})
click to toggle source
# File lib/nexpose/api_request.rb, line 49 def execute(options = {}) @conn_tries = 0 begin prepare_http_client @http.read_timeout = options.key?(:timeout) ? options[:timeout] : 120 @http.open_timeout = options.key?(:open_timeout) ? options[:open_timeout] : 120 @raw_response = @http.post(@uri.path, @req, @headers) @raw_response_data = @raw_response.read_body # Allow the :raw keyword to bypass XML parsing. if options[:raw] if raw_response_data =~ /success="1"/ @success = true else @success = false @error = 'User requested raw XML response. Not parsing failures.' end else @res = parse_xml(@raw_response_data) unless @res.root @error = 'Nexpose service returned invalid XML.' return @sid end @sid = attributes['session-id'] if (attributes['success'] && attributes['success'].to_i == 1) @success = true elsif @api_version =~ /1.2/ && @res && (@res.get_elements '//Exception').count < 1 @success = true else @success = false if @api_version =~ /1.2/ @res.elements.each('//Exception/Message') do |message| @error = message.text.sub(/.*Exception: */, '') end @res.elements.each('//Exception/Stacktrace') do |stacktrace| @trace = stacktrace.text end else @res.elements.each('//message') do |message| @error = message.text.sub(/.*Exception: */, '') end @res.elements.each('//stacktrace') do |stacktrace| @trace = stacktrace.text end end end end # This is a hack to handle corner cases where a heavily loaded Nexpose instance # drops our HTTP connection before processing. We try 5 times to establish a # connection in these situations. The actual exception occurs in the Ruby # http library, which is why we use such generic error classes. rescue OpenSSL::SSL::SSLError => error if @conn_tries < 5 @conn_tries += 1 $stderr.puts "\n\nRetrying the request due to #{error}. If you see this message please open an Issue on Github with the error.\n\n" retry end rescue ::ArgumentError, ::NoMethodError => error if @conn_tries < 5 @conn_tries += 1 $stderr.puts "\n\nRetrying the request due to #{error}. If you see this message please open an Issue on Github with the error.\n\n" retry end ### HTTP Specific Timeout Errors. rescue ::Net::ReadTimeout, ::Net::OpenTimeout => error timeout_value = error.instance_of?(Net::ReadTimeout) ? @http.read_timeout : @http.open_timeout @error = "Nexpose did not respond within #{timeout_value} seconds #{error}. Reference the Wiki for information on setting the different Timeout values." ### Catch all Timeout Error. rescue ::Timeout::Error => error @error = "Nexpose did not respond within #{@http.read_timeout} seconds #{error}. Reference the Wiki for information on setting the different Timeout values." rescue ::Errno::EHOSTUNREACH, ::Errno::ENETDOWN, ::Errno::ENETUNREACH, ::Errno::ENETRESET, ::Errno::EHOSTDOWN, ::Errno::EACCES, ::Errno::EINVAL, ::Errno::EADDRNOTAVAIL @error = 'Nexpose host is unreachable.' # Handle console-level interrupts rescue ::Interrupt @error = 'Received a user interrupt.' rescue ::Errno::ECONNRESET, ::Errno::ECONNREFUSED, ::Errno::ENOTCONN, ::Errno::ECONNABORTED @error = 'Nexpose service is not available.' rescue ::REXML::ParseException => exc @error = "Error parsing response: #{exc.message}" end if !(@success || @error) @error = "Nexpose service returned an unrecognized response: #{@raw_response_data.inspect}" end @sid end
prepare_http_client()
click to toggle source
# File lib/nexpose/api_request.rb, line 31 def prepare_http_client @uri = URI.parse(@url) @http = Net::HTTP.new(@uri.host, @uri.port) @http.use_ssl = true # # XXX: This is obviously a security issue, however, we handle this at the client level by forcing # a confirmation when the nexpose host is not localhost. In a perfect world, we would present # the server signature before accepting it, but this requires either a direct callback inside # of this module back to whatever UI, or opens a race condition between accept and attempt. if @trust_store.nil? @http.verify_mode = OpenSSL::SSL::VERIFY_NONE else @http.cert_store = @trust_store end @headers = { 'Content-Type' => 'text/xml' } @success = false end