spec/arachni/session_spec.rb in arachni-0.4.7 vs spec/arachni/session_spec.rb in arachni-1.0
- old
+ new
@@ -1,375 +1,403 @@
require 'spec_helper'
describe Arachni::Session do
before( :all ) do
- @url = web_server_url_for( :session )
+ @url = web_server_url_for( :session )
@opts = Arachni::Options.instance
end
+ before(:each) do
+ @opts.url = @url
+ end
after( :each ) do
- Arachni::Options.reset
- Arachni::HTTP.reset
+ @session.clean_up if @session
+ @opts.reset
+ Arachni::HTTP::Client.reset
+ Arachni::Data.session.clear
end
- def new_session
- Arachni::Session.new
+ subject { @session = Arachni::Session.new }
+ let(:configured) do
+ subject.configure(
+ url: "#{@url}/login",
+ inputs: {
+ username: 'john',
+ password: 'doe'
+ }
+ )
+
+ @opts.session.check_url = @url
+ @opts.session.check_pattern = 'logged-in user'
+
+ subject
end
- describe '#opts' do
- describe '#login_check_url and #login_check_pattern' do
- it 'sets a login check' do
- s = new_session
- s.opts.url = @url
+ describe "#{Arachni::OptionGroups::Session}" do
+ describe '#has_login_check?' do
+ context 'when #check_url and #check_pattern have not been configured' do
+ it 'returns false' do
+ subject.has_login_check?.should be_false
+ end
+ end
- s.has_login_sequence?.should be_false
- s.login_sequence = proc do
- res = s.http.get( @url, async: false, follow_location: true ).response
- return false if !res
+ context 'when #check_url and #check_pattern have been configured' do
+ it 'returns true' do
+ @opts.session.check_url = @url
+ @opts.session.check_pattern = 'logged-in user'
- login_form = s.forms_from_response( res ).first
- next false if !login_form
+ subject.has_login_check?.should be_true
+ end
+ end
+ end
+ end
- login_form['username'] = 'john'
- login_form['password'] = 'doe'
- res = login_form.submit( async: false, update_cookies: true, follow_location: false ).response
- return false if !res
+ describe '#configuration' do
+ it "returns #{Arachni::Data::Session}#configuration" do
+ subject.configuration.object_id.should ==
+ Arachni::Data.session.configuration.object_id
+ end
+ end
- true
+ describe '#clean_up' do
+ it 'shuts down the #browser' do
+ configured.login
+ configured.should be_logged_in
+
+ browser = configured.browser
+ configured.clean_up
+ browser.pid.should be_nil
+ end
+
+ it 'clears the #configuration' do
+ configured.should be_configured
+ configured.clean_up
+ configured.should_not be_configured
+ end
+ end
+
+ describe '#browser' do
+ context 'before calling #login' do
+ it 'returns nil' do
+ configured.browser.should be_nil
+ end
+ end
+
+ context 'after #login' do
+ it "returns an #{Arachni::Browser}" do
+ configured.login
+ configured.browser.should be_kind_of Arachni::Browser
+ end
+ end
+ end
+
+ describe '#login' do
+ it 'finds and submits the login form with the given credentials' do
+ configured.login
+ configured.should be_logged_in
+ end
+
+ it 'returns the resulting page' do
+ configured.login.should be_kind_of Arachni::Page
+
+ transition = configured.login.dom.transitions.first
+ transition.event.should == :load
+ transition.element.should == :page
+ transition.options[:url].should == configured.configuration[:url]
+
+ transition = configured.login.dom.transitions.last
+ transition.event.should == :submit
+ transition.element.tag_name.should == :form
+
+ transition.options[:inputs]['username'].should ==
+ configured.configuration[:inputs][:username]
+
+ transition.options[:inputs]['password'].should ==
+ configured.configuration[:inputs][:password]
+ end
+
+ it 'can handle Javascript forms' do
+ subject.configure(
+ url: "#{@url}/javascript_login",
+ inputs: {
+ username: 'john',
+ password: 'doe'
+ }
+ )
+
+ @opts.session.check_url = @url
+ @opts.session.check_pattern = 'logged-in user'
+
+ subject.login
+
+ subject.should be_logged_in
+ end
+
+ context 'when no configuration has been provided' do
+ it "raises #{described_class::Error::NotConfigured}" do
+ expect { subject.login }.to raise_error described_class::Error::NotConfigured
+ end
+ end
+
+ context 'each time' do
+ it 'uses a fresh #browser' do
+ configured.login
+ browser = configured.browser
+
+ configured.login
+ configured.browser.object_id.should_not == browser.object_id
+ configured.browser.should be_kind_of Arachni::Browser
+ end
+ end
+ end
+
+ describe '#logged_in?' do
+ context 'when no login check is available' do
+ it "raises #{described_class::Error::NoLoginCheck}" do
+ expect { subject.logged_in? }.to raise_error described_class::Error::NoLoginCheck
+ end
+ end
+
+ context 'when a login check is available' do
+ context 'and a valid session is available' do
+ it 'returns true' do
+ configured.login
+ configured.should be_logged_in
end
- s.has_login_sequence?.should be_true
+ end
- s.has_login_check?.should be_false
- s.opts.login_check_url = @url
- s.opts.login_check_pattern = 'logged-in user'
- s.has_login_check?.should be_true
+ context 'and a valid session is not available' do
+ it 'returns true' do
+ @opts.session.check_url = @url
+ @opts.session.check_pattern = 'logged-in user'
- s.logged_in?.should be_false
- s.login.should be_true
- s.logged_in?.should be_true
+ subject.should_not be_logged_in
+ end
+ end
- bool = false
- s.logged_in? { |b| bool = b }
- s.http.run
- bool.should be_true
+ context 'when a block is given' do
+ it 'performs the check asynchronously' do
+ configured.login
- not_bool = true
- s.logged_in?( no_cookiejar: true ) { |b| not_bool = b }
- s.http.run
- not_bool.should be_false
+ bool = false
+ configured.logged_in? { |b| bool = b }
+ configured.http.run
+ bool.should be_true
+
+ not_bool = true
+ configured.logged_in?( no_cookie_jar: true ) { |b| not_bool = b }
+ configured.http.run
+ not_bool.should be_false
+ end
end
end
end
+ describe '#configured?' do
+ context 'when login instructions have been provided' do
+ it 'returns true' do
+ configured.configured?.should be_true
+ end
+ end
+
+ context 'when login instructions have not been provided' do
+ it 'returns false' do
+ subject.configured?.should be_false
+ end
+ end
+ end
+
describe '#cookies' do
it 'returns session cookies' do
- s = new_session
- s.http.get @url + '/cookies', async: false, update_cookies: true
+ subject.http.get @url + '/with_nonce', mode: :sync, update_cookies: true
- s.cookies.select { |c| c.name == 'rack.session' }.size == 1
- s.cookies.select { |c| c.name == 'session_cookie' }.size == 1
+ subject.cookies.map(&:name).sort.should == %w(rack.session session_cookie).sort
+ end
+ end
- s.can_login?.should be_false
- s.has_login_sequence?.should be_false
+ describe '#cookie' do
+ it 'returns the cookie that determines the login status' do
+ subject.configure(
+ url: "#{@url}/nonce_login",
+ inputs: {
+ username: 'nonce_john',
+ password: 'nonce_doe'
+ }
+ )
- s.login_form = s.find_login_form( url: @url + '/nonce_login' ).
- update( username: 'nonce_john', password: 'nonce_doe' )
-
# lets invalidate the form nonce now
# (to make sure that it will be refreshed before logging in)
- s.http.get @url + '/nonce_login', async: false
+ subject.http.get @url + '/nonce_login', mode: :sync
- s.has_login_sequence?.should be_true
+ subject.configured?.should be_true
- s.set_login_check @url + '/with_nonce', 'logged-in user'
+ @opts.session.check_url = @url + '/with_nonce'
+ @opts.session.check_pattern = 'logged-in user'
+ subject.login
+
cookie = nil
- s.cookie { |c| cookie = c }
- s.http.run
+ subject.cookie { |c| cookie = c }
+ subject.http.run
cookie.name.should == 'rack.session'
- s.can_login?.should be_true
- s.logged_in?.should be_false
+ subject.can_login?.should be_true
end
+
context 'when called without having configured a login check' do
it 'should raise an exception' do
- trigger = proc { new_session.cookie }
-
- raised = false
- begin
- trigger.call
- rescue Arachni::Error
- raised = true
- end
- raised.should be_true
-
- raised = false
- begin
- trigger.call
- rescue Arachni::Session::Error
- raised = true
- end
- raised.should be_true
-
- raised = false
- begin
- trigger.call
- rescue Arachni::Session::Error::NoLoginCheck
- raised = true
- end
- raised.should be_true
+ expect { subject.cookie }.to raise_error described_class::Error::NoLoginCheck
end
end
end
describe '#find_login_form' do
- before { @id = "#{@url}/login::post::[\"password\", \"token\", \"username\"]" }
+ before { @id = "#{@url}/login:form:[\"password\", \"token\", \"username\"]" }
context 'when passed an array of :pages' do
it 'should go through its forms and locate the login one' do
p = Arachni::Page.from_url( @url + '/login' )
- s = new_session
-
- s.find_login_form( pages: [ p, p ] ).id.should == @id
+ subject.find_login_form( pages: [ p, p ] ).coverage_id.should == @id
end
end
context 'when passed an array of :forms' do
it 'should go through its forms and locate the login one' do
p = Arachni::Page.from_url( @url + '/login' )
- s = new_session
-
- s.find_login_form( forms: p.forms ).id.should == @id
+ subject.find_login_form( forms: p.forms ).coverage_id.should == @id
end
end
context 'when passed a url' do
it 'store the cookies set by that url' do
- Arachni::HTTP.cookies.should be_empty
+ Arachni::HTTP::Client.cookies.should be_empty
- new_session.find_login_form( url: @url + '/login' ).id.should == @id
+ subject.find_login_form( url: @url + '/login' ).coverage_id.should == @id
- Arachni::HTTP.cookies.find do |c|
+ Arachni::HTTP::Client.cookies.find do |c|
c.name == 'you_need_to' && c.value == 'preserve this'
end.should be_kind_of Arachni::Cookie
end
context 'and called without a block' do
it 'should operate in blocking mode, go through its forms and locate the login one' do
- s = new_session
- s.find_login_form( url: @url + '/login' ).id.should == @id
+ subject.find_login_form( url: @url + '/login' ).coverage_id.should == @id
end
end
context 'and called with a block' do
it 'should operate in async mode, go through its forms, locate the login one and pass it to the block' do
- s = new_session
form = nil
- s.find_login_form( url: @url + '/login' ) { |f| form = f }
- s.http.run
+ subject.find_login_form( url: @url + '/login' ) { |f| form = f }
+ subject.http.run
- form.id.should == @id
+ form.coverage_id.should == @id
end
end
end
context 'when passed an array of :inputs' do
it 'should use them to narrow down the list' do
- new_session.find_login_form( url: @url + '/multiple',
- inputs: :token ).id.should == @id
+ subject.find_login_form(
+ url: @url + '/multiple',
+ inputs: :token
+ ).coverage_id.should == @id
end
end
context 'when passed an :action' do
context Regexp do
it 'should use it to match against form actions' do
- new_session.find_login_form( url: @url + '/multiple',
- action: /login/ ).id.should == @id
+ subject.find_login_form(
+ url: @url + '/multiple',
+ action: /login/
+ ).coverage_id.should == @id
end
end
context String do
it 'should use it to match against form actions' do
- new_session.find_login_form( url: @url + '/multiple',
- action: "#{@url}/login" ).
- id.should == @id
+ subject.find_login_form(
+ url: @url + '/multiple',
+ action: "#{@url}/login"
+ ).coverage_id.should == @id
end
end
end
-
end
- describe '#login_form=' do
- it 'sets a login form' do
- s = new_session
-
- s.can_login?.should be_false
- s.has_login_sequence?.should be_false
-
- s.login_form = s.find_login_form( url: @url + '/nonce_login' ).
- update( username: 'nonce_john', password: 'nonce_doe' )
-
- # lets invalidate the form nonce now
- # (to make sure that it will be refreshed before logging in)
- s.http.get @url + '/nonce_login', async: false
-
- s.has_login_sequence?.should be_true
-
- s.set_login_check @url + '/with_nonce', 'logged-in user'
-
- s.can_login?.should be_true
- s.logged_in?.should be_false
-
- s.login
- s.logged_in?.should be_true
- end
- end
-
describe '#can_login?' do
context 'when there are no login sequences' do
it 'returns false' do
- new_session.can_login?.should be_false
+ subject.can_login?.should be_false
end
end
+
context 'when there are login sequences' do
it 'returns true' do
- s = new_session
- s.login_sequence = proc {}
- s.login_check = proc {}
- s.can_login?.should be_true
+ configured.can_login?.should be_true
end
end
end
- describe '#login' do
- context 'when there is no login capability' do
- it 'returns nil' do
- s = new_session
- s.can_login?.should be_false
- s.has_login_sequence?.should be_false
- s.login.should be_nil
- end
- end
- end
-
- describe '#logged_in?' do
- context 'when there is no login check' do
- it 'returns nil' do
- s = new_session
- s.can_login?.should be_false
- s.has_login_check?.should be_false
- s.logged_in?.should be_nil
- end
- end
- end
-
describe '#ensure_logged_in' do
context 'when the login is successful' do
it 'returns true' do
- s = new_session
- s.set_login_check @url + '/with_nonce', 'logged-in user'
- s.login_form = s.find_login_form( url: @url + '/nonce_login' ).
- update( username: 'nonce_john', password: 'nonce_doe' )
+ @opts.session.check_url = @url + '/with_nonce'
+ @opts.session.check_pattern = 'logged-in user'
- s.logged_in?.should be_false
- s.ensure_logged_in
- s.logged_in?.should be_true
+ subject.configure(
+ url: "#{@url}/nonce_login",
+ inputs: {
+ username: 'nonce_john',
+ password: 'nonce_doe'
+ }
+ )
+
+ subject.logged_in?.should be_false
+ subject.ensure_logged_in
+ subject.logged_in?.should be_true
end
end
context 'when the login fails' do
it 'returns false' do
- s = new_session
- s.set_login_check @url + '/with_nonce', 'logged-in user'
- s.login_form = s.find_login_form( url: @url + '/nonce_login' ).
- update( username: '1', password: '2' )
+ @opts.session.check_url = @url + '/with_nonce'
+ @opts.session.check_pattern = 'logged-in user'
+ subject.configure(
+ url: "#{@url}/nonce_login",
+ inputs: {
+ username: '1',
+ password: '2'
+ }
+ )
- s.logged_in?.should be_false
- s.ensure_logged_in
- s.logged_in?.should be_false
+ subject.logged_in?.should be_false
+ subject.ensure_logged_in
+ subject.logged_in?.should be_false
end
end
context 'when the login attempt fails' do
it 'retries 5 times' do
- s = new_session
- s.set_login_check @url, 'logged-in user'
- s.login_form = s.find_login_form( url: @url + '/disappearing_login' ).
- update( username: 'john', password: 'doe' )
+ @opts.session.check_url = @url
+ @opts.session.check_pattern = 'logged-in user'
- s.logged_in?.should be_false
- s.ensure_logged_in
- s.logged_in?.should be_true
+ subject.configure(
+ url: "#{@url}/disappearing_login",
+ inputs: {
+ username: 'john',
+ password: 'doe'
+ }
+ )
+
+ subject.logged_in?.should be_false
+ subject.ensure_logged_in
+ subject.logged_in?.should be_true
end
end
context 'when there is no login capability' do
it 'returns nil' do
- s = new_session
- s.can_login?.should be_false
- s.ensure_logged_in.should be_nil
+ subject.can_login?.should be_false
+ subject.ensure_logged_in.should be_nil
end
- end
- end
-
- describe '#login_sequence' do
- context 'when a block is given' do
- it 'sets it as a login sequence' do
- s = new_session
- s.login_sequence { :yeah! }
- s.login_sequence.call.should == :yeah!
- s.login.should == :yeah!
- end
- end
- end
-
- describe '#login_check' do
- context 'when a block is given' do
- it 'sets it as a login sequence' do
- s = new_session
- s.login_check { :yeah! }
- s.login_check.call.should == :yeah!
- s.logged_in?.should == :yeah!
- end
- end
- end
-
- describe '#set_login_check' do
- it 'sets a login check using a URL and regular expression' do
- s = new_session
- url = web_server_url_for( :session ) + '/'
- s.opts.url = "#{url}/congrats"
-
- s.has_login_sequence?.should be_false
- s.login_sequence = proc do
- res = s.http.get( url, async: false, follow_location: true ).response
- return false if !res
-
- login_form = s.forms_from_response( res ).first
- next false if !login_form
-
- login_form['username'] = 'john'
- login_form['password'] = 'doe'
- res = login_form.submit( async: false, update_cookies: true, follow_location: false ).response
- return false if !res
-
- true
- end
- s.has_login_sequence?.should be_true
-
- s.has_login_check?.should be_false
- s.set_login_check( url, 'logged-in user' )
- s.has_login_check?.should be_true
-
- s.logged_in?.should be_false
- s.login.should be_true
- s.logged_in?.should be_true
-
- bool = false
- s.logged_in? { |b| bool = b }
- s.http.run
- bool.should be_true
-
- not_bool = true
- s.logged_in?( no_cookiejar: true ) { |b| not_bool = b }
- s.http.run
- not_bool.should be_false
end
end
end