require 'spec_helper' describe Arachni::Browser do before( :all ) do @url = Arachni::Utilities.normalize_url( web_server_url_for( :browser ) ) end before( :each ) do clear_hit_count @browser = described_class.new end after( :each ) do Arachni::Options.reset Arachni::Framework.reset @browser.shutdown clear_hit_count end let(:subject) { @browser } let(:ua) { Arachni::Options.http.user_agent } def transitions_from_array( transitions ) transitions.map do |t| element, event = t.first.to_a options = {} if element == :page && event == :load options.merge!( url: @browser.watir.url, cookies: {} ) end if element.is_a? Hash element = described_class::ElementLocator.new( element ) end Arachni::Page::DOM::Transition.new( element, event, options ).complete end end def hit_count Typhoeus::Request.get( "#{@url}/hit-count" ).body.to_i end def image_hit_count Typhoeus::Request.get( "#{@url}/image-hit-count" ).body.to_i end def clear_hit_count Typhoeus::Request.get( "#{@url}/clear-hit-count" ) end it 'supports HTTPS' do url = web_server_url_for( :browser_https ) @browser.start_capture pages = @browser.load( url ).flush_pages pages_should_have_form_with_input( pages, 'ajax-token' ) pages_should_have_form_with_input( pages, 'by-ajax' ) end describe '.has_executable?' do context 'when there is no executable browser' do it 'returns false' do allow(Selenium::WebDriver::PhantomJS).to receive(:path){ false } expect(described_class.has_executable?).to be_falsey end end context 'when there is an executable browser' do it 'returns true' do allow(Selenium::WebDriver::PhantomJS).to receive(:path){ __FILE__ } expect(described_class.has_executable?).to be_truthy end end end describe '.executable' do it 'returns the path to the browser executable' do stub = __FILE__ allow(Selenium::WebDriver::PhantomJS).to receive(:path){ stub } expect(described_class.executable).to eq(stub) end end describe '#initialize' do describe :concurrency do it 'sets the HTTP request concurrency' end describe :ignore_scope do context true do it 'ignores scope restrictions' do @browser.shutdown @browser = described_class.new( ignore_scope: true ) Arachni::Options.scope.exclude_path_patterns << /sleep/ subject.load @url + '/ajax_sleep' expect(subject.to_page).to be_truthy end end context false do it 'enforces scope restrictions' do @browser.shutdown @browser = described_class.new( ignore_scope: false ) Arachni::Options.scope.exclude_path_patterns << /sleep/ subject.load @url + '/ajax_sleep' expect(subject.to_page.code).to eq(0) end end context :default do it 'enforces scope restrictions' do @browser.shutdown @browser = described_class.new( ignore_scope: false ) Arachni::Options.scope.exclude_path_patterns << /sleep/ subject.load @url + '/ajax_sleep' expect(subject.to_page.code).to eq(0) end end end describe :width do it 'sets the window width' do @browser.shutdown width = 100 @browser = described_class.new( width: width ) expect(subject.javascript.run('return window.innerWidth')).to eq(width) end it 'defaults to 1600' do expect(subject.javascript.run('return window.innerWidth')).to eq(1600) end end describe :height do it 'sets the window height' do @browser.shutdown height = 100 @browser = described_class.new( height: height ) expect(subject.javascript.run('return window.innerHeight')).to eq(height) end it 'defaults to 1200' do expect(subject.javascript.run('return window.innerHeight')).to eq(1200) end end describe :store_pages do describe 'default' do it 'stores snapshot pages' do @browser.shutdown @browser = described_class.new expect(@browser.load( @url + '/explore' ).flush_pages).to be_any end it 'stores captured pages' do @browser.shutdown @browser = described_class.new @browser.start_capture expect(@browser.load( @url + '/with-ajax' ).flush_pages).to be_any end end describe true do it 'stores snapshot pages' do @browser.shutdown @browser = described_class.new( store_pages: true ) expect(@browser.load( @url + '/explore' ).trigger_events.flush_pages).to be_any end it 'stores captured pages' do @browser.shutdown @browser = described_class.new( store_pages: true ) @browser.start_capture expect(@browser.load( @url + '/with-ajax' ).flush_pages).to be_any end end describe false do it 'stores snapshot pages' do @browser.shutdown @browser = described_class.new( store_pages: false ) expect(@browser.load( @url + '/explore' ).trigger_events.flush_pages).to be_empty end it 'stores captured pages' do @browser.shutdown @browser = described_class.new( store_pages: false ) @browser.start_capture expect(@browser.load( @url + '/with-ajax' ).flush_pages).to be_empty end end end context 'when browser process spawn fails' do it "raises #{described_class::Error::Spawn}" do allow_any_instance_of(described_class).to receive(:spawn_phantomjs) { nil } expect { described_class.new }.to raise_error described_class::Error::Spawn end end end describe '#source_with_line_numbers' do it 'prefixes each source code line with a number' do subject.load @url lines = subject.source.lines.to_a expect(lines).to be_any subject.source_with_line_numbers.lines.each.with_index do |l, i| expect(l).to eq("#{i+1} - #{lines[i]}") end end end describe '#load_delay' do it 'returns nil' do subject.load @url expect(subject.load_delay).to be_nil end context 'when the page has JS timeouts' do it 'returns the maximum time the browser should wait for the page based on Timeout' do subject.load( "#{@url}load_delay" ) expect(subject.load_delay).to eq(2000) end end end describe '#wait_for_timers' do it 'returns' do subject.load @url expect(subject.wait_for_timers).to be_nil end context 'when the page has JS timeouts' do it 'waits for them to complete' do subject.load( "#{@url}load_delay" ) seconds = subject.load_delay / 1000 time = Time.now subject.wait_for_timers expect(Time.now - time).to be > seconds end it "caps them at #{Arachni::OptionGroups::HTTP}#request_timeout" do subject.load( "#{@url}load_delay" ) Arachni::Options.http.request_timeout = 100 time = Time.now subject.wait_for_timers expect(Time.now - time).to be < 0.2 end end end describe '#capture_snapshot' do let(:sink_url) do "#{@url}script_sink?input=#{@browser.javascript.log_execution_flow_sink_stub(1)}" end let(:ajax_url) do "#{@url}with-ajax" end let(:captured) { subject.capture_snapshot } context 'when a snapshot has not been previously seen' do before :each do subject.load( @url + '/with-ajax', take_snapshot: false ) end it 'calls #on_new_page callbacks' do received = [] subject.on_new_page do |page| received << page end expect(captured).to eq(received) end context '#store_pages?' do context true do subject { @browser.shutdown; @browser = described_class.new( store_pages: true )} it 'stores it in #page_snapshots' do captured = subject.capture_snapshot expect(subject.page_snapshots).to eq(captured) end it 'returns it' do expect(captured.size).to eq(1) expect(captured.first).to eq(subject.to_page) end end context false do subject { @browser.shutdown; @browser = described_class.new( store_pages: false ) } it 'does not store it' do subject.capture_snapshot expect(subject.page_snapshots).to be_empty end it 'returns an empty array' do expect(captured).to be_empty end end end end context 'when a snapshot has already been seen' do before :each do subject.load( @url + '/with-ajax', take_snapshot: false ) end it 'ignores it' do expect(subject.capture_snapshot).to be_any expect(subject.capture_snapshot).to be_empty end end context 'when a snapshot has sink data' do before :each do subject.load sink_url, take_snapshot: false end it 'calls #on_new_page_with_sink callbacks' do sinks = [] subject.on_new_page_with_sink do |page| sinks << page.dom.execution_flow_sinks end subject.capture_snapshot expect(sinks.size).to eq(1) end context 'and has already been seen' do it 'calls #on_new_page_with_sink callbacks' do sinks = [] subject.on_new_page_with_sink do |page| sinks << page.dom.execution_flow_sinks end subject.capture_snapshot subject.capture_snapshot expect(sinks.size).to eq(2) end end context '#store_pages?' do context true do subject { @browser.shutdown; @browser = described_class.new( store_pages: true )} it 'stores it in #page_snapshots_with_sinks' do subject.capture_snapshot expect(subject.page_snapshots_with_sinks).to be_any end end context false do subject { @browser.shutdown; @browser = described_class.new( store_pages: false )} it 'does not store it in #page_snapshots_with_sinks' do subject.capture_snapshot expect(subject.page_snapshots_with_sinks).to be_empty end end end end context 'when a transition has been given' do before :each do subject.load( ajax_url, take_snapshot: false ) end it 'pushes it to the existing transitions' do transition = { stuff: :here } captured = subject.capture_snapshot( stuff: :here ) expect(captured.first.dom.transitions).to include transition end end context 'when there are multiple windows open' do before :each do subject.load( ajax_url, take_snapshot: false ) end it 'captures snapshots from all windows' do subject.javascript.run( 'window.open()' ) subject.watir.windows.last.use subject.load sink_url, take_snapshot: false expect(subject.capture_snapshot.map(&:url).sort).to eq( [ajax_url, sink_url].sort ) end end context 'when an error occurs' do it 'ignores it' do allow(subject.watir).to receive(:windows) { raise } expect(subject.capture_snapshot( blah: :stuff )).to be_empty end end end describe '#flush_page_snapshots_with_sinks' do it 'returns pages with data-flow sink data' do @browser.load "#{@url}/lots_of_sinks?input=#{@browser.javascript.log_data_flow_sink_stub( function: { name: 'blah' } )}" @browser.explore_and_flush expect(@browser.page_snapshots_with_sinks.map(&:dom).map(&:data_flow_sinks)).to eq( @browser.flush_page_snapshots_with_sinks.map(&:dom).map(&:data_flow_sinks) ) end it 'returns pages with execution-flow sink data' do @browser.load "#{@url}/lots_of_sinks?input=#{@browser.javascript.log_execution_flow_sink_stub( function: { name: 'blah' } )}" @browser.explore_and_flush expect(@browser.page_snapshots_with_sinks.map(&:dom).map(&:execution_flow_sinks)).to eq( @browser.flush_page_snapshots_with_sinks.map(&:dom).map(&:execution_flow_sinks) ) end it 'empties the data-flow sink page buffer' do @browser.load "#{@url}/lots_of_sinks?input=#{@browser.javascript.log_data_flow_sink_stub( function: { name: 'blah' } )}" @browser.explore_and_flush @browser.flush_page_snapshots_with_sinks.map(&:dom).map(&:data_flow_sinks) expect(@browser.page_snapshots_with_sinks).to be_empty end it 'empties the execution-flow sink page buffer' do @browser.load "#{@url}/lots_of_sinks?input=#{@browser.javascript.log_execution_flow_sink_stub( function: { name: 'blah' } )}" @browser.explore_and_flush @browser.flush_page_snapshots_with_sinks.map(&:dom).map(&:execution_flow_sinks) expect(@browser.page_snapshots_with_sinks).to be_empty end end describe '#on_new_page_with_sink' do it 'assigns blocks to handle each page with execution-flow sink data' do @browser.load "#{@url}/lots_of_sinks?input=#{@browser.javascript.log_execution_flow_sink_stub( function: { name: 'blah' } )}" sinks = [] @browser.on_new_page_with_sink do |page| sinks << page.dom.execution_flow_sinks end @browser.explore_and_flush expect(sinks.size).to eq(2) expect(sinks).to eq(@browser.page_snapshots_with_sinks.map(&:dom). map(&:execution_flow_sinks)) end it 'assigns blocks to handle each page with data-flow sink data' do @browser.javascript.taint = 'taint' @browser.load "#{@url}/lots_of_sinks?input=#{@browser.javascript.log_data_flow_sink_stub( @browser.javascript.taint, function: { name: 'blah' } )}" sinks = [] @browser.on_new_page_with_sink do |page| sinks << page.dom.data_flow_sinks end @browser.explore_and_flush expect(sinks.size).to eq(2) expect(sinks).to eq(@browser.page_snapshots_with_sinks.map(&:dom). map(&:data_flow_sinks)) end end describe '#on_fire_event' do it 'gets called before each event is triggered' do @browser.load "#{@url}/trigger_events" calls = [] @browser.on_fire_event do |element, event| calls << [element.opening_tag, event] end @browser.fire_event @browser.watir.div( id: 'my-div' ), :click @browser.fire_event @browser.watir.div( id: 'my-div' ), :mouseover expect(calls).to eq([ [ "
", :click ], [ "
", :mouseover ] ]) end end describe '#on_new_page' do it 'is passed each snapshot' do pages = [] @browser.on_new_page { |page| pages << page } expect(@browser.load( @url + '/explore' ).trigger_events. page_snapshots).to eq(pages) end it 'is passed each request capture' do pages = [] @browser.on_new_page { |page| pages << page } @browser.start_capture # Last page will be the root snapshot so ignore it. expect(@browser.load( @url + '/with-ajax' ).captured_pages).to eq(pages[0...2]) end end describe '#on_response' do context 'when a response is preloaded' do it 'is passed each response' do responses = [] @browser.on_response { |response| responses << response } @browser.preload Arachni::HTTP::Client.get( @url, mode: :sync ) @browser.goto @url response = responses.first expect(response).to be_kind_of Arachni::HTTP::Response expect(response.url).to eq(@url) end end context 'when a response is cached' do it 'is passed each response' do responses = [] @browser.on_response { |response| responses << response } @browser.cache Arachni::HTTP::Client.get( @url, mode: :sync ) @browser.goto @url response = responses.first expect(response).to be_kind_of Arachni::HTTP::Response expect(response.url).to eq(@url) end end context 'when a request is performed by the browser' do it 'is passed each response' do responses = [] @browser.on_response { |response| responses << response } @browser.goto @url response = responses.first expect(response).to be_kind_of Arachni::HTTP::Response expect(response.url).to eq(@url) end end end describe '#explore_and_flush' do it 'handles deep DOM/page transitions' do url = @url + '/deep-dom' pages = @browser.load( url ).explore_and_flush pages_should_have_form_with_input pages, 'by-ajax' expect(pages.map(&:dom).map(&:transitions)).to eq([ [ { :page => :load }, { "#{@url}deep-dom" => :request }, { "#{@url}level2" => :request } ], [ { :page => :load }, { "#{@url}deep-dom" => :request }, { "#{@url}level2" => :request }, { { tag_name: 'a', attributes: { 'onmouseover' => 'writeButton();', 'href' => 'javascript:level3();' } } => :mouseover } ], [ { :page => :load }, { "#{@url}deep-dom" => :request }, { "#{@url}level2" => :request }, { { tag_name: 'a', attributes: { 'onmouseover' => 'writeButton();', 'href' => 'javascript:level3();' } } => :click }, { "#{@url}level4" => :request } ], [ { :page => :load }, { "#{@url}deep-dom" => :request }, { "#{@url}level2" => :request }, { { tag_name: 'a', attributes: { 'onmouseover' => 'writeButton();', 'href' => 'javascript:level3();' } } => :mouseover }, { { tag_name: 'button', attributes: { 'onclick' => 'writeUserAgent();', } } => :click } ], [ { :page => :load }, { "#{@url}deep-dom" => :request }, { "#{@url}level2" => :request }, { { tag_name: 'a', attributes: { 'onmouseover' => 'writeButton();', 'href' => 'javascript:level3();' } } => :click }, { "#{@url}level4" => :request }, { { tag_name: 'div', attributes: { 'onclick' => 'level6();', 'id' => 'level5' } } => :click }, { "#{@url}level6" => :request } ] ].map { |transitions| transitions_from_array( transitions ) }) end context 'with a depth argument' do it 'does not go past the given DOM depth' do pages = @browser.load( @url + '/deep-dom' ).explore_and_flush(2) expect(pages.map(&:dom).map(&:transitions)).to eq([ [ { :page => :load }, { "#{@url}deep-dom" => :request }, { "#{@url}level2" => :request } ], [ { :page => :load }, { "#{@url}deep-dom" => :request }, { "#{@url}level2" => :request }, { { tag_name: 'a', attributes: { 'onmouseover' => 'writeButton();', 'href' => 'javascript:level3();' } } => :mouseover } ], [ { :page => :load }, { "#{@url}deep-dom" => :request }, { "#{@url}level2" => :request }, { { tag_name: 'a', attributes: { 'onmouseover' => 'writeButton();', 'href' => 'javascript:level3();' } } => :click }, { "#{@url}level4" => :request } ] ].map { |transitions| transitions_from_array( transitions ) }) end end end describe '#page_snapshots_with_sinks' do it 'returns execution-flow sink data' do @browser.load "#{@url}/lots_of_sinks?input=#{@browser.javascript.log_execution_flow_sink_stub(1)}" @browser.explore_and_flush pages = @browser.page_snapshots_with_sinks doms = pages.map(&:dom) expect(doms.size).to eq(2) expect(doms[0].transitions).to eq(transitions_from_array([ { page: :load }, { "#{@url}lots_of_sinks?input=#{@browser.javascript.log_execution_flow_sink_stub(1)}" => :request }, { { tag_name: 'a', attributes: { 'href' => '#', 'onmouseover' => "onClick2('blah1', 'blah2', 'blah3');" } } => :mouseover } ])) expect(doms[0].execution_flow_sinks.size).to eq(2) entry = doms[0].execution_flow_sinks[0] expect(entry.data).to eq([1]) expect(entry.trace.size).to eq(3) expect(entry.trace[0].function.name).to eq('onClick') expect(entry.trace[0].function.source).to start_with 'function onClick' expect(@browser.source.split("\n")[entry.trace[0].line]).to include 'log_execution_flow_sink(1)' expect(entry.trace[0].function.arguments).to eq([1, 2]) expect(entry.trace[1].function.name).to eq('onClick2') expect(entry.trace[1].function.source).to start_with 'function onClick2' expect(@browser.source.split("\n")[entry.trace[1].line]).to include 'onClick' expect(entry.trace[1].function.arguments).to eq(%w(blah1 blah2 blah3)) expect(entry.trace[2].function.name).to eq('onmouseover') expect(entry.trace[2].function.source).to start_with 'function onmouseover' event = entry.trace[2].function.arguments.first link = "Blah" expect(event['target']).to eq(link) expect(event['srcElement']).to eq(link) expect(event['type']).to eq('mouseover') entry = doms[0].execution_flow_sinks[1] expect(entry.data).to eq([1]) expect(entry.trace.size).to eq(4) expect(entry.trace[0].function.name).to eq('onClick3') expect(entry.trace[0].function.source).to start_with 'function onClick3' expect(@browser.source.split("\n")[entry.trace[0].line]).to include 'log_execution_flow_sink(1)' expect(entry.trace[0].function.arguments).to be_empty expect(entry.trace[1].function.name).to eq('onClick') expect(entry.trace[1].function.source).to start_with 'function onClick' expect(@browser.source.split("\n")[entry.trace[1].line]).to include 'onClick3' expect(entry.trace[1].function.arguments).to eq([1, 2]) expect(entry.trace[2].function.name).to eq('onClick2') expect(entry.trace[2].function.source).to start_with 'function onClick2' expect(@browser.source.split("\n")[entry.trace[2].line]).to include 'onClick' expect(entry.trace[2].function.arguments).to eq(%w(blah1 blah2 blah3)) expect(entry.trace[3].function.name).to eq('onmouseover') expect(entry.trace[3].function.source).to start_with 'function onmouseover' event = entry.trace[3].function.arguments.first link = "Blah" expect(event['target']).to eq(link) expect(event['srcElement']).to eq(link) expect(event['type']).to eq('mouseover') expect(doms[1].transitions).to eq(transitions_from_array([ { page: :load }, { "#{@url}lots_of_sinks?input=#{@browser.javascript.log_execution_flow_sink_stub(1)}" => :request }, { { tag_name: 'form', attributes: { 'id' => 'my_form', 'onsubmit' => "onClick('some-arg', 'arguments-arg', 'here-arg'); return false;" } } => :submit } ])) expect(doms[1].execution_flow_sinks.size).to eq(2) entry = doms[1].execution_flow_sinks[0] expect(entry.data).to eq([1]) expect(entry.trace.size).to eq(2) expect(entry.trace[0].function.name).to eq('onClick') expect(entry.trace[0].function.source).to start_with 'function onClick' expect(@browser.source.split("\n")[entry.trace[0].line]).to include 'log_execution_flow_sink(1)' expect(entry.trace[0].function.arguments).to eq(%w(some-arg arguments-arg here-arg)) expect(entry.trace[1].function.name).to eq('onsubmit') expect(entry.trace[1].function.source).to start_with 'function onsubmit' expect(@browser.source.split("\n")[entry.trace[1].line]).to include 'onClick' event = entry.trace[1].function.arguments.first form = "
\n
" expect(event['target']).to eq(form) expect(event['srcElement']).to eq(form) expect(event['type']).to eq('submit') entry = doms[1].execution_flow_sinks[1] expect(entry.data).to eq([1]) expect(entry.trace.size).to eq(3) expect(entry.trace[0].function.name).to eq('onClick3') expect(entry.trace[0].function.source).to start_with 'function onClick3' expect(@browser.source.split("\n")[entry.trace[0].line]).to include 'log_execution_flow_sink(1)' expect(entry.trace[0].function.arguments).to be_empty expect(entry.trace[1].function.name).to eq('onClick') expect(entry.trace[1].function.source).to start_with 'function onClick' expect(@browser.source.split("\n")[entry.trace[1].line]).to include 'onClick3()' expect(entry.trace[1].function.arguments).to eq(%w(some-arg arguments-arg here-arg)) expect(entry.trace[2].function.name).to eq('onsubmit') expect(entry.trace[2].function.source).to start_with 'function onsubmit' expect(@browser.source.split("\n")[entry.trace[2].line]).to include 'onClick(' event = entry.trace[2].function.arguments.first form = "
\n
" expect(event['target']).to eq(form) expect(event['srcElement']).to eq(form) expect(event['type']).to eq('submit') end it 'returns data-flow sink data' do @browser.javascript.taint = 'taint' @browser.load "#{@url}/lots_of_sinks?input=#{@browser.javascript.log_data_flow_sink_stub( @browser.javascript.taint, function: 'blah' )}" @browser.explore_and_flush pages = @browser.page_snapshots_with_sinks doms = pages.map(&:dom) expect(doms.size).to eq(2) expect(doms[0].data_flow_sinks.size).to eq(2) entry = doms[0].data_flow_sinks[0] expect(entry.function).to eq('blah') expect(entry.trace.size).to eq(3) expect(entry.trace[0].function.name).to eq('onClick') expect(entry.trace[0].function.source).to start_with 'function onClick' expect(@browser.source.split("\n")[entry.trace[0].line]).to include 'log_data_flow_sink(' expect(entry.trace[0].function.arguments).to eq([1, 2]) expect(entry.trace[1].function.name).to eq('onClick2') expect(entry.trace[1].function.source).to start_with 'function onClick2' expect(@browser.source.split("\n")[entry.trace[1].line]).to include 'onClick' expect(entry.trace[1].function.arguments).to eq(%w(blah1 blah2 blah3)) expect(entry.trace[2].function.name).to eq('onmouseover') expect(entry.trace[2].function.source).to start_with 'function onmouseover' event = entry.trace[2].function.arguments.first link = "Blah" expect(event['target']).to eq(link) expect(event['srcElement']).to eq(link) expect(event['type']).to eq('mouseover') entry = doms[0].data_flow_sinks[1] expect(entry.function).to eq('blah') expect(entry.trace.size).to eq(4) expect(entry.trace[0].function.name).to eq('onClick3') expect(entry.trace[0].function.source).to start_with 'function onClick3' expect(@browser.source.split("\n")[entry.trace[0].line]).to include 'log_data_flow_sink(' expect(entry.trace[0].function.arguments).to be_empty expect(entry.trace[1].function.name).to eq('onClick') expect(entry.trace[1].function.source).to start_with 'function onClick' expect(@browser.source.split("\n")[entry.trace[1].line]).to include 'onClick3' expect(entry.trace[1].function.arguments).to eq([1, 2]) expect(entry.trace[2].function.name).to eq('onClick2') expect(entry.trace[2].function.source).to start_with 'function onClick2' expect(@browser.source.split("\n")[entry.trace[2].line]).to include 'onClick' expect(entry.trace[2].function.arguments).to eq(%w(blah1 blah2 blah3)) expect(entry.trace[3].function.name).to eq('onmouseover') expect(entry.trace[3].function.source).to start_with 'function onmouseover' event = entry.trace[3].function.arguments.first link = "Blah" expect(event['target']).to eq(link) expect(event['srcElement']).to eq(link) expect(event['type']).to eq('mouseover') expect(doms[1].data_flow_sinks.size).to eq(2) entry = doms[1].data_flow_sinks[0] expect(entry.function).to eq('blah') expect(entry.trace.size).to eq(2) expect(entry.trace[0].function.name).to eq('onClick') expect(entry.trace[0].function.source).to start_with 'function onClick' expect(@browser.source.split("\n")[entry.trace[0].line]).to include 'log_data_flow_sink(' expect(entry.trace[0].function.arguments).to eq(%w(some-arg arguments-arg here-arg)) expect(entry.trace[1].function.name).to eq('onsubmit') expect(entry.trace[1].function.source).to start_with 'function onsubmit' expect(@browser.source.split("\n")[entry.trace[1].line]).to include 'onClick' event = entry.trace[1].function.arguments.first form = "
\n
" expect(event['target']).to eq(form) expect(event['srcElement']).to eq(form) expect(event['type']).to eq('submit') entry = doms[1].data_flow_sinks[1] expect(entry.function).to eq('blah') expect(entry.trace.size).to eq(3) expect(entry.trace[0].function.name).to eq('onClick3') expect(entry.trace[0].function.source).to start_with 'function onClick3' expect(@browser.source.split("\n")[entry.trace[0].line]).to include 'log_data_flow_sink(' expect(entry.trace[0].function.arguments).to be_empty expect(entry.trace[1].function.name).to eq('onClick') expect(entry.trace[1].function.source).to start_with 'function onClick' expect(@browser.source.split("\n")[entry.trace[1].line]).to include 'onClick3()' expect(entry.trace[1].function.arguments).to eq(%w(some-arg arguments-arg here-arg)) expect(entry.trace[2].function.name).to eq('onsubmit') expect(entry.trace[2].function.source).to start_with 'function onsubmit' expect(@browser.source.split("\n")[entry.trace[2].line]).to include 'onClick(' event = entry.trace[2].function.arguments.first form = "
\n
" expect(event['target']).to eq(form) expect(event['srcElement']).to eq(form) expect(event['type']).to eq('submit') end describe 'when store_pages: false' do it 'does not store pages' do @browser.shutdown @browser = @browser.class.new( store_pages: false ) @browser.load "#{@url}/lots_of_sinks?input=#{@browser.javascript.log_execution_flow_sink_stub(1)}" @browser.explore_and_flush expect(@browser.page_snapshots_with_sinks).to be_empty end end end describe '#response' do it "returns the #{Arachni::HTTP::Response} for the loaded page" do @browser.load @url browser_response = @browser.response browser_request = browser_response.request raw_response = Arachni::HTTP::Client.get( @url, mode: :sync ) raw_request = raw_response.request expect(browser_response.url).to eq(raw_response.url) [:url, :method].each do |attribute| expect(browser_request.send(attribute)).to eq(raw_request.send(attribute)) end end context "when the response takes more than #{Arachni::OptionGroups::HTTP}#request_timeout" do it 'returns nil' end context 'when the resource is out-of-scope' do it 'returns nil' do Arachni::Options.url = @url @browser.load 'http://google.com/' expect(@browser.response).to be_nil end end end describe '#to_page' do it "converts the working window to an #{Arachni::Page}" do ua = Arachni::Options.http.user_agent @browser.load( @url ) page = @browser.to_page expect(page).to be_kind_of Arachni::Page expect(ua).not_to be_empty expect(page.response.body).not_to include( ua ) expect(page.body).to include( ua ) end it "assigns the proper #{Arachni::Page::DOM}#digest" do @browser.load( @url ) expect(@browser.to_page.dom.instance_variable_get(:@digest)).to eq( '