From b9524499bfe605e84c15d4a60ec07597880db159 Mon Sep 17 00:00:00 2001 From: erwanlr Date: Tue, 9 Apr 2013 21:40:19 +0200 Subject: [PATCH] Some Browser::Options work --- lib/common/browser.rb | 83 +------------- lib/common/browser/options.rb | 128 ++++++++++++++++++++++ lib/common/collections/wp_users/output.rb | 2 +- lib/environment.rb | 2 +- lib/wpscan/wpscan_helper.rb | 1 - spec/lib/common/browser_spec.rb | 57 +--------- spec/shared_examples/browser/actions.rb | 60 ++++++++++ spec/shared_examples/browser/options.rb | 5 + 8 files changed, 203 insertions(+), 135 deletions(-) create mode 100644 spec/shared_examples/browser/actions.rb create mode 100644 spec/shared_examples/browser/options.rb diff --git a/lib/common/browser.rb b/lib/common/browser.rb index c3ac9e35..250f9ed6 100644 --- a/lib/common/browser.rb +++ b/lib/common/browser.rb @@ -2,30 +2,20 @@ require 'common/typhoeus_cache' require 'common/browser/actions' +require 'common/browser/options' class Browser - extend Browser::Actions + extend Browser::Actions + include Browser::Options @@instance = nil - USER_AGENT_MODES = %w{ static semi-static random } - - ACCESSOR_OPTIONS = [ - :user_agent, - :user_agent_mode, - :available_user_agents, - :proxy, - :proxy_auth, - :max_threads, - :cache_ttl, - :request_timeout, - :basic_auth - ] attr_reader :hydra, :config_file - attr_accessor *ACCESSOR_OPTIONS def initialize(options = {}) @config_file = options[:config_file] || CONF_DIR + '/browser.conf.json' + @cache_dir = CACHE_DIR + '/browser' + options.delete(:config_file) load_config() @@ -34,12 +24,9 @@ class Browser override_config_with_options(options) end - @hydra = Typhoeus::Hydra.new(max_concurrency: @max_threads) - + @hydra = Typhoeus::Hydra.new(max_concurrency: self.max_threads) # TODO : add an argument for the cache dir instead of using a constant - @cache_dir = CACHE_DIR + '/browser' @cache = TyphoeusCache.new(@cache_dir) - @cache.clean Typhoeus::Config.cache = @cache @@ -58,55 +45,6 @@ class Browser @@instance = nil end - def user_agent_mode=(ua_mode) - ua_mode ||= 'static' - - if USER_AGENT_MODES.include?(ua_mode) - @user_agent_mode = ua_mode - # For semi-static user agent mode, the user agent has to - # be nil the first time (it will be set with the getter) - @user_agent = nil if ua_mode === 'semi-static' - else - raise "Unknow user agent mode : '#{ua_mode}'" - end - end - - # return the user agent, according to the user_agent_mode - def user_agent - case @user_agent_mode - when 'semi-static' - unless @user_agent - @user_agent = @available_user_agents.sample - end - when 'random' - @user_agent = @available_user_agents.sample - end - @user_agent - end - - def max_threads=(max_threads) - if max_threads.nil? or max_threads <= 0 - max_threads = 1 - end - @max_threads = max_threads - end - - def proxy_auth=(auth) - unless auth.nil? - if auth.is_a?(Hash) && auth.include?(:proxy_username) && auth.include?(:proxy_password) - @proxy_auth = auth[:proxy_username] + ':' + auth[:proxy_password] - elsif auth.is_a?(String) && auth.index(':') != nil - @proxy_auth = auth - else - raise invalid_proxy_auth_format - end - end - end - - def invalid_proxy_auth_format - 'Invalid proxy auth format, expected username:password or {proxy_username: username, proxy_password: password}' - end - # TODO reload hydra (if the .load_config is called on a browser object, # hydra will not have the new @max_threads and @request_timeout) def load_config(config_file = nil) @@ -182,13 +120,4 @@ class Browser params end - # Override with the options if they are set - def override_config_with_options(options) - options.each do |option, value| - if value != nil and ACCESSOR_OPTIONS.include?(option) - self.send(:"#{option}=", value) - end - end - end - end diff --git a/lib/common/browser/options.rb b/lib/common/browser/options.rb index a663802a..a91850db 100644 --- a/lib/common/browser/options.rb +++ b/lib/common/browser/options.rb @@ -3,5 +3,133 @@ class Browser module Options + OPTIONS = [ + :available_user_agents, + :basic_auth, + :cache_ttl, + :max_threads, + :user_agent, + :user_agent_mode, + :proxy, + :proxy_auth, + #:request_timeout, + ] + + USER_AGENT_MODES = %w{ static semi-static random } + + attr_reader :basic_auth, :user_agent_mode, :proxy, :proxy_auth + attr_accessor :available_user_agents, :cache_ttl + attr_writer :max_threads, :user_agent + + # Sets the Basic Authentification credentials + # Accepted format: + # login:password + # Basic base_64_encoded + # + # @param [ String ] auth + # + # @return [ void ] + def basic_auth=(auth) + if auth.index(':') + @basic_auth = "Basic #{Base64.encode64(basic_auth.chomp)}" + elsif auth =~ /\ABasic .*\z/ + @basic_auth = auth.chomp + else + raise 'Invalid basic authentication format, "login:password" or "Basic base_64_encoded" expected' + end + end + + # @return [ Integer ] + def max_threads + @max_threads || 1 + end + + # @return [ String ] The user agent, according to the user_agent_mode + def user_agent + case @user_agent_mode + when 'semi-static' + unless @user_agent + @user_agent = @available_user_agents.sample + end + when 'random' + @user_agent = @available_user_agents.sample + end + @user_agent + end + + # Sets the user_agent_mode, which can be one of the following: + # static: The UA is defined by the user, and will be the same in each requests + # semi-static: The UA is randomly chosen at the first request, and will not change + # random: UA randomly chosen each request + # + # @param [ String ] ua_mode + # + # @return [ void ] + def user_agent_mode=(ua_mode) + ua_mode ||= 'static' + + if USER_AGENT_MODES.include?(ua_mode) + @user_agent_mode = ua_mode + # For semi-static user agent mode, the user agent has to + # be nil the first time (it will be set with the getter) + @user_agent = nil if ua_mode === 'semi-static' + else + raise "Unknow user agent mode : '#{ua_mode}'" + end + end + + # Sets the proxy + # Accepted format: + # host:post + # + # @param [ String ] proxy + # + # @return [ void ] + def proxy=(proxy) + if proxy.index(':') + @proxy = proxy + else + raise 'Invalid proxy format. Should be host:port.' + end + end + + # Sets the proxy credentials + # Accepted format: + # username:password + # { proxy_username: username, :proxy_password: password } + # + # @param [ String ] auth + # + # @return [ void ] + def proxy_auth=(auth) + unless auth.nil? + if auth.is_a?(Hash) && auth.include?(:proxy_username) && auth.include?(:proxy_password) + @proxy_auth = auth[:proxy_username] + ':' + auth[:proxy_password] + elsif auth.is_a?(String) && auth.index(':') != nil + @proxy_auth = auth + else + raise invalid_proxy_auth_format + end + end + end + + protected + + def invalid_proxy_auth_format + 'Invalid proxy auth format, expected username:password or {proxy_username: username, proxy_password: password}' + end + + # Override with the options if they are set + # @param [ Hash ] options + # + # @return [ void ] + def override_config_with_options(options = {}) + options.each do |option, value| + if value != nil and OPTIONS.include?(option) + self.send(:"#{option}=", value) + end + end + end + end end diff --git a/lib/common/collections/wp_users/output.rb b/lib/common/collections/wp_users/output.rb index a664d73e..bff7c13e 100644 --- a/lib/common/collections/wp_users/output.rb +++ b/lib/common/collections/wp_users/output.rb @@ -10,7 +10,7 @@ class WpUsers < WpItems max_display_name_length = self.sort { |a, b| a.display_name.length <=> b.display_name.length }.last.display_name.length inner_space = 2 - id_length = (max_id_length + inner_space * 2) /2 *2 + id_length = (max_id_length + inner_space * 2) /2 * 2 login_length = max_login_length + inner_space * 2 display_name_length = max_display_name_length + inner_space * 2 diff --git a/lib/environment.rb b/lib/environment.rb index 5aa2d592..1ed89e0e 100644 --- a/lib/environment.rb +++ b/lib/environment.rb @@ -33,7 +33,7 @@ begin rescue LoadError => e puts "[ERROR] #{e}" - missing_gem = e.to_s[%r{ -- ([^\z/]+)/?}, 1] + missing_gem = e.to_s[%r{ -- ([^/]+)/?\z}, 1] if missing_gem if missing_gem =~ /nokogiri/i puts diff --git a/lib/wpscan/wpscan_helper.rb b/lib/wpscan/wpscan_helper.rb index 059a8984..d26b6d2e 100644 --- a/lib/wpscan/wpscan_helper.rb +++ b/lib/wpscan/wpscan_helper.rb @@ -2,7 +2,6 @@ require File.expand_path(File.dirname(__FILE__) + '/../common/common_helper') -require_files_from_directory(WPSCAN_LIB_DIR + '/modules') require_files_from_directory(WPSCAN_LIB_DIR, '**/*.rb') # wpscan usage diff --git a/spec/lib/common/browser_spec.rb b/spec/lib/common/browser_spec.rb index 64f7008e..ae62ea60 100644 --- a/spec/lib/common/browser_spec.rb +++ b/spec/lib/common/browser_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe Browser do + it_behaves_like 'Browser::Actions' + CONFIG_FILE_WITHOUT_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf.json' CONFIG_FILE_WITH_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf_proxy.json' CONFIG_FILE_WITH_PROXY_AND_AUTH = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf_proxy_auth.json' @@ -326,61 +328,6 @@ describe Browser do end - describe '#post' do - it 'should return a Typhoeus::Response wth body = "Welcome Master" if login=master&password=itsme!' do - url = 'http://example.com/' - - stub_request(:post, url).with(body: { login: 'master', password: 'itsme!' }). - to_return(status: 200, body: 'Welcome Master') - - response = Browser.post( - url, - body: 'login=master&password=itsme!' - #body: { login: 'master', password: 'hello' } # It's should be this line, but it fails - ) - - response.should be_a Typhoeus::Response - response.body.should == 'Welcome Master' - end - end - - describe '#get' do - it "should return a Typhoeus::Response with body = 'Hello World !'" do - url = 'http://example.com/' - - stub_request(:get, url). - to_return(status: 200, body: 'Hello World !') - - response = Browser.get(url) - - response.should be_a Typhoeus::Response - response.body.should == 'Hello World !' - end - end - - describe '#get_and_follow_location' do - # Typhoeus does not follow the location (maybe it's fixed in > 0.4.2) - # Or, something else is wrong - - #context 'whitout max_redirects params' do - # context 'when multiples redirection' do - # it 'returns the last redirection response' do - # url = 'http://target.com' - # first_redirection = 'www.first-redirection.com' - # last_redirection = 'last-redirection.com' - - # stub_request(:get, url).to_return(status: 301, headers: { location: first_redirection }) - # stub_request(:get, first_redirection).to_return(status: 301, headers: { location: last_redirection }) - # stub_request(:get, last_redirection).to_return(status: 200, body: 'Hello World!') - - # response = @browser.get_and_follow_location(url) - - # response.body.should === 'Hellow World!' - # end - # end - #end - end - describe 'testing caching' do it 'should only do 1 request, and retrieve the other one from the cache' do diff --git a/spec/shared_examples/browser/actions.rb b/spec/shared_examples/browser/actions.rb new file mode 100644 index 00000000..7142797c --- /dev/null +++ b/spec/shared_examples/browser/actions.rb @@ -0,0 +1,60 @@ +# encoding: UTF-8 + +shared_examples 'Browser::Actions' do + + describe '#post' do + it 'returns a Typhoeus::Response wth body = "Welcome Master" if login=master&password=itsme!' do + url = 'http://example.com/' + + stub_request(:post, url).with(body: { login: 'master', password: 'itsme!' }). + to_return(status: 200, body: 'Welcome Master') + + response = Browser.post( + url, + body: 'login=master&password=itsme!' + #body: { login: 'master', password: 'hello' } # It's should be this line, but it fails + ) + + response.should be_a Typhoeus::Response + response.body.should == 'Welcome Master' + end + end + + describe '#get' do + it "returns a Typhoeus::Response with body = 'Hello World !'" do + url = 'http://example.com/' + + stub_request(:get, url). + to_return(status: 200, body: 'Hello World !') + + response = Browser.get(url) + + response.should be_a Typhoeus::Response + response.body.should == 'Hello World !' + end + end + + describe '#get_and_follow_location' do + # Typhoeus does not follow the location with rspec + # See https://github.com/typhoeus/typhoeus/issues/279 + + #context 'whitout max_redirects params' do + # context 'when multiples redirection' do + # it 'returns the last redirection response' do + # url = 'http://target.com' + # first_redirection = 'www.first-redirection.com' + # last_redirection = 'last-redirection.com' + + # stub_request(:get, url).to_return(status: 301, headers: { location: first_redirection }) + # stub_request(:get, first_redirection).to_return(status: 301, headers: { location: last_redirection }) + # stub_request(:get, last_redirection).to_return(status: 200, body: 'Hello World!') + + # response = Browser.get_and_follow_location(url) + + # response.body.should === 'Hellow World!' + # end + # end + #end + end + +end diff --git a/spec/shared_examples/browser/options.rb b/spec/shared_examples/browser/options.rb new file mode 100644 index 00000000..93ba5bab --- /dev/null +++ b/spec/shared_examples/browser/options.rb @@ -0,0 +1,5 @@ +# encoding: UTF-8 + +shared_examples 'Browser::Options' do + +end