require 'spec_helper' class TrainerMockFramework attr_reader :pages attr_reader :opts attr_reader :trainer attr_accessor :sitemap def initialize( page = nil ) @page = page @pages = [] @on_audit_page = [] Arachni::HTTP.reset @trainer = Arachni::Trainer.new( self ) @opts = Arachni::Options.instance @opts.url = page.url if page @sitemap = [] end def link_count_limit_reached? @opts.link_count_limit_reached? @sitemap.size end def run @on_audit_page.each do |b| b.call @page end Arachni::HTTP.run end def on_audit_page( &block ) @on_audit_page << block end def push_to_page_queue( page ) @sitemap << page.url @pages << page end end def request( url ) Arachni::HTTP.instance.get( url.to_s, async: false ).response end describe Arachni::Trainer do before( :all ) do @url = web_server_url_for( :trainer ) Arachni::Options.audit :links, :forms, :cookies, :headers end before( :each ) do Arachni::Options.reset res = Arachni::HTTP.get( @url, async: false ).response @page = Arachni::Page.from_response( res, Arachni::Options.instance ) @framework = TrainerMockFramework.new( @page ) @trainer = @framework.trainer end describe 'HTTP requests with "train" set to' do describe 'nil' do it 'skips the Trainer' do @framework.pages.size.should == 0 Arachni::HTTP.request( @url + '/elems' ) @framework.run @framework.pages.size.should == 0 end end describe false do it 'skips the Trainer' do @framework.pages.size.should == 0 Arachni::HTTP.request( @url + '/elems', train: false ) @framework.run @framework.pages.size.should == 0 end end describe true do it 'passes the response to the Trainer' do @framework.pages.size.should == 0 Arachni::HTTP.request( @url + '/elems', train: true ) @framework.run @framework.pages.size.should == 1 end context 'when a redirection leads to new elements' do it 'passes the response to the Trainer' do @framework.pages.size.should == 0 Arachni::HTTP.request( @url + '/train/redirect', train: true ) @framework.run page = @framework.pages.first page.links.first.auditable.include?( 'msg' ).should be_true end end end end context 'when a page' do context 'has not changed' do it 'is skipped' do @framework.pages.size.should == 0 Arachni::HTTP.request( @url, train: true ) @framework.run @framework.pages.should be_empty end end context 'gets updated more than Trainer::MAX_TRAININGS_PER_URL times' do it 'is ignored' do get_response = proc do Typhoeus::Response.new( effective_url: @url, body: "Test", headers_hash: { 'Content-type' => 'text/html' }, request: Typhoeus::Request.new( @url ) ) end @trainer.page = Arachni::Page.from_response( get_response.call ) pages = [] @trainer.on_new_page { |p| pages << p } 100.times { @trainer.push( get_response.call ) } pages.size.should == Arachni::Trainer::MAX_TRAININGS_PER_URL end end context 'matches excluding criteria' do it 'is ignored' do res = Typhoeus::Response.new( effective_url: @url + '/exclude_me' ) @trainer.push( res ).should be_false end end context 'matches a redundancy filter' do it 'should not be analyzed more than the specified amount of times' do Arachni::Options.url = 'http://stuff.com' trainer = TrainerMockFramework.new.trainer get_response = proc do Typhoeus::Response.new( effective_url: 'http://stuff.com/match_this', body: "Test", headers_hash: { 'Content-type' => 'text/html' }, request: Typhoeus::Request.new( 'http://stuff.com/match_this' ) ) end trainer.page = Arachni::Page.from_response( get_response.call ) pages = [] trainer.on_new_page { |p| pages << p } Arachni::Options.redundant = { /match_this/ => 10 } 100.times { trainer.push( get_response.call ) } pages.size.should == 10 end end end context 'when the link-count-limit is exceeded, following pages' do it 'is ignored' do Arachni::Options.url = 'http://stuff.com' framework = TrainerMockFramework.new trainer = framework.trainer get_response = proc do Typhoeus::Response.new( effective_url: "http://stuff.com/#{rand( 9999 )}", body: "Test", headers_hash: { 'Content-type' => 'text/html' }, request: Typhoeus::Request.new( 'http://stuff.com/match_this' ) ) end trainer.page = Arachni::Page.from_response( get_response.call ) pages = [] trainer.on_new_page { |p| pages << p } Arachni::Options.link_count_limit = 10 100.times { trainer.push( get_response.call ) } pages.size.should == 10 end end context 'when the content-type is' do context 'text-based' do it 'returns true' do @trainer.page = @page @trainer.push( request( @url ) ).should be_true end end context 'not text-based' do it 'returns false' do ct = @url + '/non_text_content_type' @trainer.push( request( ct ) ).should be_false end end end context 'when the response contains a new' do context 'form' do it 'returns a page with the new form' do url = @url + '/new_form' @trainer.page = @page @trainer.push( request( url ) ).should be_true page = @framework.pages.first page.should be_true page.forms.size.should == 1 page.forms.first.auditable.include?( 'input2' ).should be_true end end context 'link' do it 'returns a page with the new link' do url = @url + '/new_link' @trainer.page = @page @trainer.push( request( url ) ).should be_true page = @framework.pages.first page.should be_true page.links.select { |l| l.auditable.include?( 'link_param' ) }.should be_any end end context 'cookie' do it 'returns a page with the new cookie appended' do url = @url + '/new_cookie' @trainer.page = @page @trainer.push( request( url ) ).should be_true page = @framework.pages.first page.should be_true page.cookies.last.auditable.include?( 'new_cookie' ).should be_true end end end context 'when the response is the result of a redirection' do it 'extracts query vars from the effective url' do url = @url + '/redirect?redirected=true' @trainer.page = @page @trainer.push( request( url ) ).should be_true page = @framework.pages.first page.links.last.auditable['redirected'].should == 'true' end end end