require 'spec_helper' describe Arachni::Element::Form do it_should_behave_like 'refreshable' it_should_behave_like 'auditable', url: web_server_url_for( :form ) before( :all ) do @utils = Arachni::Module::Utilities @url = @utils.normalize_url( web_server_url_for( :form ) ) @raw = { 'attrs' => { 'method' => 'post', 'action' => @url }, 'auditable' => [ { 'type' => 'text', 'name' => 'param_name', 'value' => 'param_value' } ] } @inputs = { inputs: { 'param_name' => 'param_value' } } @form = Arachni::Element::Form.new( @url, @inputs ) @http = Arachni::HTTP.instance end it 'assigned to Arachni::Form for easy access' do Arachni::Form.should == Arachni::Element::Form end describe 'Arachni::Element::FORM' do it 'returns "form"' do Arachni::Element::FORM.should == 'form' end end describe '#new' do context 'when passed opts without a method' do it 'defaults to "get"' do Arachni::Element::Form.new( @url, @inputs ).method.should == 'get' end end context 'when passed opts without an action URL' do it 'defaults to the owner URL' do Arachni::Element::Form.new( @url ).action.should == @url end end context 'when passed opts without auditable inputs or any other expected option' do it 'uses the contents of the opts hash as auditable inputs' do e = Arachni::Element::Form.new( @url, @inputs[:inputs] ) e.auditable.should == @inputs[:inputs] end end end describe '#id' do context 'when the action it contains path parameters' do it 'ignores them' do e = Arachni::Element::Form.new( 'http://test.com/path;p=v?p1=v1&p2=v2', @inputs[:inputs] ) c = Arachni::Element::Form.new( 'http://test.com/path?p1=v1&p2=v2', @inputs[:inputs] ) e.id.should == c.id end end end describe '#field_type_for' do it 'returns a field\'s type' do e = Arachni::Element::Form.new( 'http://test.com', 'auditable' => [ { 'type' => 'password', 'name' => 'my_pass' }, { 'type' => 'hidden', 'name' => 'hidden_field' } ] ) e.field_type_for( 'my_pass' ).should == 'password' e.field_type_for( 'hidden_field' ).should == 'hidden' end end describe '#node' do it 'returns the original Nokogiri node' do html = '
' node = Arachni::Element::Form.from_document( @url, html ).first.node node.is_a?( Nokogiri::XML::Element ).should be_true node.css( 'input' ).first['name'].should == 'my_first_input' end end describe '#to_html' do context 'when there is a node' do it 'returns the original form as HTML' do html = ' ' f1 = Arachni::Element::Form.from_document( @url, html ).first f2 = Arachni::Element::Form.from_document( @url, f1.to_html ).first f2.should == f1 end end context 'when there is no node' do it 'returns nil' do Arachni::Element::Form.new( @url, @inputs[:inputs] ).to_html.should be_nil end end end describe '#requires_password?' do context 'when the form has a password field' do it 'returns true' do html = ' ' Arachni::Element::Form.from_document( @url, html ). first.requires_password?.should be_true end end context 'when the form does not have a password field' do it 'returns false' do html = ' ' Arachni::Element::Form.from_document( @url, html ). first.requires_password?.should be_false end end end describe '#original?' do context 'when the mutation' do context 'is same as the original element' do it 'returns true' do inputs = { inputs: { 'param_name' => 'param_value', 'stuff' => nil } } e = Arachni::Element::Form.new( 'http://test.com', inputs ) has_original ||= false has_sample ||= false e.mutations( 'seed' ).each do |m| m.url.should == e.url m.action.should == e.action if m.original? m.altered.should == Arachni::Element::Form::ORIGINAL_VALUES m.auditable.should == e.auditable has_original ||= true end end has_original.should be_true end end end end describe '#sample?' do context 'when the mutation' do context 'has been filled-in with sample values' do it 'returns true' do inputs = { inputs: { 'param_name' => 'param_value', 'stuff' => nil } } e = Arachni::Element::Form.new( 'http://test.com', inputs ) has_original ||= false has_sample ||= false e.mutations( 'seed' ).each do |m| m.url.should == e.url m.action.should == e.action if m.sample? m.altered.should == Arachni::Element::Form::SAMPLE_VALUES m.auditable.should == Arachni::Module::KeyFiller.fill( e.auditable ) has_sample ||= true end end has_sample.should be_true end end end end describe '#mutations' do it 'fuzzes #auditable inputs' do inputs = { inputs: { 'param_name' => 'param_value', 'stuff' => nil } } e = Arachni::Element::Form.new( 'http://test.com', inputs ) checked = false e.mutations( 'seed' ).each do |m| next if m.original? || m.sample? m.url.should == e.url m.action.should == e.action m.auditable.should_not == e.auditable checked = true end checked.should be_true end it 'sets #altered to the name of the fuzzed input' do inputs = { inputs: { 'param_name' => 'param_value', 'stuff' => nil } } e = Arachni::Element::Form.new( 'http://test.com', inputs ) checked = false e.mutations( 'seed' ).each do |m| next if m.original? || m.sample? m.url.should == e.url m.action.should == e.action m.altered.should_not == e.altered m.auditable[m.altered].should include 'seed' checked = true end checked.should be_true end context 'when it contains more than 1 password field' do it 'includes mutations which have the same values for all of them' do e = Arachni::Element::Form.new( 'http://test.com', 'auditable' => [ { 'type' => 'password', 'name' => 'my_pass' }, { 'type' => 'password', 'name' => 'my_pass_validation' } ] ) e.mutations( 'seed' ).reject do |m| m.auditable['my_pass'] != m.auditable['my_pass_validation'] end.size.should == 6 end end describe :skip_orig do it 'does not add mutations with original nor default values' do e = Arachni::Element::Form.new( 'http://test.com', @inputs ) mutations = e.mutations( @seed, skip_orig: true ) mutations.size.should == 4 mutations.reject { |m| m.mutated? }.size.should == 0 end end end describe '#nonce_name=' do it 'sets the name of the input holding the nonce' do f = Arachni::Element::Form.new( @url, nonce: 'value' ) f.nonce_name = 'nonce' f.nonce_name.should == 'nonce' end context 'when there is no input called nonce_name' do it 'raises Arachni::Element::Form::Error::FieldNotFound' do trigger = proc do Arachni::Element::Form.new( @url, name: 'value' ). nonce_name = 'stuff' end expect { trigger.call }.to raise_error Arachni::Error expect { trigger.call }.to raise_error Arachni::Element::Form::Error expect { trigger.call }.to raise_error Arachni::Element::Form::Error::FieldNotFound end end end describe '#has_nonce?' do context 'when the form has a nonce' do it 'returns true' do f = Arachni::Element::Form.new( @url, nonce: 'value' ) f.nonce_name = 'nonce' f.has_nonce?.should be_true end end context 'when the form does not have a nonce' do it 'returns false' do f = Arachni::Element::Form.new( @url, nonce: 'value' ) f.has_nonce?.should be_false end end end describe '#submit' do context 'when method is post' do it 'performs a POST HTTP request' do body_should = @form.method + @form.auditable.to_s body = nil @form.submit( remove_id: true ) { |res| body = res.body } @http.run body_should.should == body end end context 'when method is get' do it 'performs a GET HTTP request' do f = Arachni::Element::Form.new( @url, @inputs.merge( method: 'get' ) ) body_should = f.method + f.auditable.to_s body = nil f.submit( remove_id: true ).on_complete { |res| body = res.body } @http.run body_should.should == body end end context 'when the form has a nonce' do it 'refreshes its value before submitting it' do f = Arachni::Element::Form.new( @url + 'with_nonce', @inputs.merge( method: 'get', action: @url + 'get_nonce') ) f.update 'nonce' => rand( 999 ) f.nonce_name = 'nonce' body_should = f.method + f.auditable.to_s body = nil f.submit { |res| body = res.body } @http.run body.should_not == f.auditable['nonce'] body.to_i.should > 0 end end end context 'when initialized' do context 'with attributes' do describe '#simple' do it 'returns a simplified version of form attributes and auditables' do f = Arachni::Element::Form.new( @url, @raw ) f.simple.should == { 'attrs' => @raw['attrs'], 'auditable' => f.auditable } end end end context 'with hash key/pair' do describe '#simple' do it 'returns a simplified version of form attributes and auditables' do f = Arachni::Element::Form.new( @url, @inputs ) f.simple.should == { 'attrs' => { 'method' => f.method, 'action' => f.action, }, 'auditable' => f.auditable } end end end end describe '#type' do it 'is "form"' do @form.type.should == 'form' end end describe '.from_document' do context 'when the response does not contain any forms' do it 'returns an empty array' do Arachni::Element::Form.from_document( '', '' ).should be_empty end end context 'when the response contains forms' do context 'with text inputs' do it 'returns an array of forms' do html = ' ' form = Arachni::Element::Form.from_document( @url, html ).first form.action.should == @utils.normalize_url( @url + '/form_action' ) form.name.should == 'my_form' form.url.should == @url form.method.should == 'get' form.auditable.should == { 'my_first_input' => 'my_first_value', 'my_second_input' => 'my_second_value' } end end context 'with checkbox inputs' do it 'returns an array of forms' do html = ' ' form = Arachni::Element::Form.from_document( @url, html ).first form.action.should == @utils.normalize_url( @url + '/form_action' ) form.name.should == 'my_form' form.url.should == @url form.method.should == 'get' form.auditable.should == { 'vehicle' => 'Bike', 'stuff' => 'Car' } end end context 'with radio inputs' do it 'returns an array of forms' do html = ' ' form = Arachni::Element::Form.from_document( @url, html ).first form.action.should == @utils.normalize_url( @url + '/form_action' ) form.name.should == 'my_form' form.url.should == @url form.method.should == 'get' form.auditable.should == { 'my_first_input' => 'my_first_value', 'my_second_input' => 'my_second_value' } end end context 'with selects' do context 'with values' do it 'returns an array of forms' do html = ' ' form = Arachni::Element::Form.from_document( @url, html ).first form.action.should == @utils.normalize_url( @url + '/form_action' ) form.name.should == 'my_form' form.url.should == @url form.method.should == 'get' form.auditable.should == { 'manufacturer' => 'volvo', 'numbers' => '1' } end end context 'without values' do it 'uses the element texts' do html = ' ' form = Arachni::Element::Form.from_document( @url, html ).first form.action.should == @utils.normalize_url( @url + '/form_action' ) form.name.should == 'my_form' form.url.should == @url form.method.should == 'get' form.auditable.should == { 'manufacturer' => 'Volvo', 'numbers' => 'One' } end end context 'with selected options' do it 'uses their values' do html = ' ' form = Arachni::Element::Form.from_document( @url, html ).first form.action.should == @utils.normalize_url( @url + '/form_action' ) form.name.should == 'my_form' form.url.should == @url form.method.should == 'get' form.auditable.should == { 'manufacturer' => 'Saab', 'numbers' => 'Two' } end end context 'without any options' do it 'uses a nil value' do html = ' ' form = Arachni::Element::Form.from_document( @url, html ).first form.action.should == @utils.normalize_url( @url + '/form_action' ) form.name.should == 'my_form' form.url.should == @url form.method.should == 'get' form.auditable.should == { 'manufacturer' => '' } end end end context 'with a base attribute' do it 'respects it and adjust the action accordingly' do base_url = "#{@url}/this_is_the_base/" html = '