From dd35be60f63b715ebe3f2d28caeec9d1e5f67d15 Mon Sep 17 00:00:00 2001 From: Tasos Laskos Date: Thu, 9 Mar 2017 13:24:52 +0200 Subject: [PATCH] Check::Auditor: Removed OPTIONS [Closes #848] --- lib/arachni/check/auditor.rb | 79 ++----- .../capabilities/analyzable/signature.rb | 2 +- spec/arachni/check/auditor_spec.rb | 209 ++++-------------- spec/arachni/issue_spec.rb | 2 +- spec/support/factories/issue.rb | 3 +- 5 files changed, 65 insertions(+), 230 deletions(-) diff --git a/lib/arachni/check/auditor.rb b/lib/arachni/check/auditor.rb index 156368ac67..8455130ee2 100644 --- a/lib/arachni/check/auditor.rb +++ b/lib/arachni/check/auditor.rb @@ -307,27 +307,6 @@ def max_issues Element::LinkTemplate::DOM, Element::UIInput::DOM, Element::UIForm::DOM ] - # Default audit options. - OPTIONS = { - - # Elements to audit. - # - # If no elements have been passed to audit methods, candidates will be - # determined by {#each_candidate_element}. - elements: ELEMENTS_WITH_INPUTS, - - dom_elements: DOM_ELEMENTS_WITH_INPUTS, - - # If set to `true` the HTTP response will be analyzed for new elements. - # Be careful when enabling it, there'll be a performance penalty. - # - # If set to `false`, no training is going to occur. - # - # If set to `nil`, when the Auditor submits a form with original or - # sample values this option will be overridden to `true` - train: nil - } - # @return [Arachni::Page] # Page object to be audited. attr_reader :page @@ -487,21 +466,15 @@ def skip?( element ) # Passes each element prepared for audit to the block. # - # If no element types have been specified in `opts`, it will use the elements - # from the check's {Base.info} hash. - # - # If no elements have been specified in `opts` or {Base.info}, it will use the - # elements in {OPTIONS}. - # - # @param [Array] types - # Element types to audit (see {OPTIONS}`[:elements]`). + # It will use the elements from the check's {Base.info} hash. + # If no elements have been specified it will use {ELEMENTS_WITH_INPUTS}. # # @yield [element] - # Each candidate DOM element. - # @yieldparam [Arachni::Capabilities::Auditable::DOM] - def each_candidate_element( types = [], &block ) - types = self.class.info[:elements] if types.empty? - types = OPTIONS[:elements] if types.empty? + # Each candidate element. + # @yieldparam [Arachni::Element] + def each_candidate_element( &block ) + types = self.class.elements + types = ELEMENTS_WITH_INPUTS if types.empty? types.each do |elem| elem = elem.type @@ -538,21 +511,15 @@ def each_candidate_element( types = [], &block ) # Passes each element prepared for audit to the block. # - # If no element types have been specified in `opts`, it will use the elements - # from the check's {Base.info} hash. - # - # If no elements have been specified in `opts` or {Base.info}, it will use the - # elements in {OPTIONS}. - # - # @param [Array] types - # Element types to audit (see {OPTIONS}`[:elements]`). + # It will use the elements from the check's {Base.info} hash. + # If no elements have been specified it will use {DOM_ELEMENTS_WITH_INPUTS}. # # @yield [element] # Each candidate element. - # @yieldparam [Arachni::Element] - def each_candidate_dom_element( types = [], &block ) - types = self.class.info[:elements] if types.empty? - types = OPTIONS[:dom_elements] if types.empty? + # @yieldparam [Arachni::Element::DOM] + def each_candidate_dom_element( &block ) + types = self.class.elements + types = DOM_ELEMENTS_WITH_INPUTS if types.empty? types.each do |elem| elem = elem.type @@ -589,15 +556,13 @@ def each_candidate_dom_element( types = [], &block ) # # Uses {#each_candidate_element} to decide which elements to audit. # - # @see OPTIONS # @see Arachni::Element::Capabilities::Auditable#audit # @see #audit_signature def audit( payloads, opts = {}, &block ) - opts = OPTIONS.merge( opts ) if !block_given? audit_signature( payloads, opts ) else - each_candidate_element( opts[:elements] ) do |e| + each_candidate_element do |e| e.audit( payloads, opts, &block ) audited( e.coverage_id ) end @@ -609,11 +574,9 @@ def audit( payloads, opts = {}, &block ) # # Uses {#each_candidate_element} to decide which elements to audit. # - # @see OPTIONS # @see Arachni::Element::Capabilities::Auditable#buffered_audit def buffered_audit( payloads, opts = {}, &block ) - opts = OPTIONS.merge( opts ) - each_candidate_element( opts[:elements] ) do |e| + each_candidate_element do |e| e.buffered_audit( payloads, opts, &block ) audited( e.coverage_id ) end @@ -624,11 +587,9 @@ def buffered_audit( payloads, opts = {}, &block ) # # Uses {#each_candidate_element} to decide which elements to audit. # - # @see OPTIONS # @see Arachni::Element::Capabilities::Analyzable::Signature def audit_signature( payloads, opts = {} ) - opts = OPTIONS.merge( opts ) - each_candidate_element( opts[:elements] )do |e| + each_candidate_element do |e| e.signature_analysis( payloads, opts ) audited( e.coverage_id ) end @@ -638,11 +599,9 @@ def audit_signature( payloads, opts = {} ) # # Uses {#each_candidate_element} to decide which elements to audit. # - # @see OPTIONS # @see Arachni::Element::Capabilities::Analyzable::Differential def audit_differential( opts = {}, &block ) - opts = OPTIONS.merge( opts ) - each_candidate_element( opts[:elements] ) do |e| + each_candidate_element do |e| e.differential_analysis( opts, &block ) audited( e.coverage_id ) end @@ -652,11 +611,9 @@ def audit_differential( opts = {}, &block ) # # Uses {#each_candidate_element} to decide which elements to audit. # - # @see OPTIONS # @see Arachni::Element::Capabilities::Analyzable::Timeout def audit_timeout( payloads, opts = {} ) - opts = OPTIONS.merge( opts ) - each_candidate_element( opts[:elements] ) do |e| + each_candidate_element do |e| e.timeout_analysis( payloads, opts ) audited( e.coverage_id ) end diff --git a/lib/arachni/element/capabilities/analyzable/signature.rb b/lib/arachni/element/capabilities/analyzable/signature.rb index e53ee11222..34eb540ab6 100644 --- a/lib/arachni/element/capabilities/analyzable/signature.rb +++ b/lib/arachni/element/capabilities/analyzable/signature.rb @@ -109,7 +109,7 @@ module Signature # {Element::Capabilities::Submittable#platforms applicable platforms} # for the {Element::Capabilities::Submittable#action resource} to be audited. # @param [Hash] opts - # Options as described in {Arachni::Check::Auditor::OPTIONS} and + # Options as described in {Arachni::Element::Auditable::OPTIONS} and # {SIGNATURE_OPTIONS}. # # @return [Bool] diff --git a/spec/arachni/check/auditor_spec.rb b/spec/arachni/check/auditor_spec.rb index 9786a7cd72..f96ae7e693 100644 --- a/spec/arachni/check/auditor_spec.rb +++ b/spec/arachni/check/auditor_spec.rb @@ -428,62 +428,41 @@ def self.clear_info_cache end it 'sets the auditor' do - auditor.each_candidate_element [ Arachni::Link ] do |element| + auditor.each_candidate_element do |element| expect(element.auditor).to eq(auditor) end end - context 'when types have been provided' do - it 'provides those types of elements' do - elements = [] - auditor.each_candidate_element [ Arachni::Link, Arachni::Header ] do |element| - elements << element - end + it 'provides the types of elements specified by the check' do + auditor.class.info[:elements] = [Arachni::Link, Arachni::Form] - expect(elements).to eq((auditor.page.links | auditor.page.headers). - select { |e| e.inputs.any? }) + elements = [] + auditor.each_candidate_element do |element| + elements << element end - context 'and are not supported' do - it 'raises ArgumentError' do - expect { - auditor.each_candidate_element [Arachni::Link::DOM] - }.to raise_error ArgumentError - end - end + expect(auditor.class.elements).to eq([Arachni::Link, Arachni::Form]) + expect(elements).to eq((auditor.page.links | auditor.page.forms). + select { |e| e.inputs.any? }) end - context 'when types have not been provided' do - it 'provides the types of elements specified by the check' do - auditor.class.info[:elements] = [Arachni::Link, Arachni::Form] + + context 'and no types are specified by the check' do + it 'provides all types of elements but :inputs and :ui_forms'do + auditor.class.info[:elements].clear + + expected_elements = Arachni::Page::ELEMENTS + expected_elements.delete :ui_inputs + expected_elements.delete :ui_forms elements = [] auditor.each_candidate_element do |element| elements << element end - expect(auditor.class.elements).to eq([Arachni::Link, Arachni::Form]) - expect(elements).to eq((auditor.page.links | auditor.page.forms). + expect(elements.map { |e| "#{e.type}s".to_sym }.uniq).to eq(Arachni::Page::ELEMENTS) + expect(elements).to eq((auditor.page.elements). select { |e| e.inputs.any? }) end - - context 'and no types are specified by the check' do - it 'provides all types of elements but :inputs and :ui_forms'do - auditor.class.info[:elements].clear - - expected_elements = Arachni::Page::ELEMENTS - expected_elements.delete :ui_inputs - expected_elements.delete :ui_forms - - elements = [] - auditor.each_candidate_element do |element| - elements << element - end - - expect(elements.map { |e| "#{e.type}s".to_sym }.uniq).to eq(Arachni::Page::ELEMENTS) - expect(elements).to eq((auditor.page.elements). - select { |e| e.inputs.any? }) - end - end end end @@ -504,54 +483,33 @@ def self.clear_info_cache end end - context 'when types have been provided' do - it 'provides those types of elements' do - elements = [] - auditor.each_candidate_dom_element [ Arachni::Link::DOM ] do |element| - elements << element - end + it 'provides the types of elements specified by the check' do + auditor.class.info[:elements] = [Arachni::Form::DOM] + expect(auditor.class.elements).to eq([Arachni::Form::DOM]) - expect(elements).to be_any - expect(elements).to eq(auditor.page.links.select { |l| l.dom }.map(&:dom)) + elements = [] + auditor.each_candidate_dom_element do |element| + elements << element end - context 'and are not supported' do - it 'raises ArgumentError' do - expect { - auditor.each_candidate_dom_element [Arachni::Link] - }.to raise_error ArgumentError - end - end + expect(elements).to eq(auditor.page.forms.map(&:dom)) end - context 'when types have not been provided' do - it 'provides the types of elements specified by the check' do - auditor.class.info[:elements] = [Arachni::Form::DOM] - expect(auditor.class.elements).to eq([Arachni::Form::DOM]) + + context 'and no types are specified by the check' do + it 'provides all types of elements'do + auditor.class.info[:elements].clear elements = [] auditor.each_candidate_dom_element do |element| elements << element end - expect(elements).to eq(auditor.page.forms.map(&:dom)) - end - - context 'and no types are specified by the check' do - it 'provides all types of elements'do - auditor.class.info[:elements].clear - - elements = [] - auditor.each_candidate_dom_element do |element| - elements << element - end - - expect(elements).to eq( - (auditor.page.links.select { |l| l.dom } | - auditor.page.forms | auditor.page.cookies | - auditor.page.link_templates | auditor.page.ui_inputs | - auditor.page.ui_forms).map(&:dom) - ) - end + expect(elements).to eq( + (auditor.page.links.select { |l| l.dom } | + auditor.page.forms | auditor.page.cookies | + auditor.page.link_templates | auditor.page.ui_inputs | + auditor.page.ui_forms).map(&:dom) + ) end end end @@ -874,99 +832,20 @@ def audit( *args, &block ) end auditor.audit( @seed ){} - expect($audit_called).to eq(auditor.page.elements.map(&:class)) + expect($audit_called).to eq(auditor.class.elements) end end context 'when called without a block' do it 'delegates to #audit_signature' do - expect(auditor).to receive(:audit_signature).with( @seed, described_class::OPTIONS ) - auditor.audit( @seed ) + opts = { stuff: :here } + + expect(auditor).to receive(:audit_signature).with( @seed, opts ) + auditor.audit( @seed, opts ) end end context 'when called with options' do - describe ':elements' do - - before { auditor.load_page_from( @url + '/elem_combo' ) } - - describe 'Arachni::Element::Link' do - it 'audits links' do - auditor.audit( @seed, - format: [ Arachni::Check::Auditor::Format::STRAIGHT ], - elements: [ Arachni::Element::Link ] - ) - @framework.http.run - expect(Arachni::Data.issues.size).to eq(1) - issue = Arachni::Data.issues.first - expect(issue.vector.class).to eq(Arachni::Element::Link) - expect(issue.vector.affected_input_name).to eq('link_input') - end - end - describe 'Arachni::Element::Form' do - it 'audits forms' do - auditor.audit( @seed, - format: [ Arachni::Check::Auditor::Format::STRAIGHT ], - elements: [ Arachni::Element::Form ] - ) - @framework.http.run - expect(Arachni::Data.issues.size).to eq(1) - issue = Arachni::Data.issues.first - expect(issue.vector.class).to eq(Arachni::Element::Form) - expect(issue.vector.affected_input_name).to eq('form_input') - end - end - describe 'Arachni::Element::Cookie' do - it 'audits cookies' do - auditor.audit( @seed, - format: [ Arachni::Check::Auditor::Format::STRAIGHT ], - elements: [ Arachni::Element::Cookie ] - ) - @framework.http.run - expect(Arachni::Data.issues.size).to eq(1) - issue = Arachni::Data.issues.first - expect(issue.vector.class).to eq(Arachni::Element::Cookie) - expect(issue.vector.affected_input_name).to eq('cookie_input') - end - it 'maintains the session while auditing cookies' do - auditor.load_page_from( @url + '/session' ) - auditor.audit( @seed, - format: [ Arachni::Check::Auditor::Format::STRAIGHT ], - elements: [ Arachni::Element::Cookie ] - ) - @framework.http.run - expect(Arachni::Data.issues.size).to eq(1) - issue = Arachni::Data.issues.first - expect(issue.vector.class).to eq(Arachni::Element::Cookie) - expect(issue.vector.affected_input_name).to eq('vulnerable') - end - - end - describe 'Arachni::Element::Header' do - it 'audits headers' do - auditor.audit( @seed, - format: [ Arachni::Check::Auditor::Format::STRAIGHT ], - elements: [ Arachni::Element::Header ] - ) - @framework.http.run - expect(Arachni::Data.issues.size).to eq(1) - issue = Arachni::Data.issues.first - expect(issue.vector.class).to eq(Arachni::Element::Header) - expect(issue.vector.affected_input_name).to eq('Referer') - end - end - - context 'when using default options' do - it 'audits all element types' do - auditor.audit( @seed, - format: [ Arachni::Check::Auditor::Format::STRAIGHT ] - ) - @framework.http.run - expect(Arachni::Data.issues.size).to eq(4) - end - end - end - describe ':train' do context 'default' do it 'parses the responses of forms submitted with their default values and feed any new elements back to the framework to be audited' do @@ -1061,7 +940,7 @@ def signature_analysis( *args, &block ) end auditor.audit_signature( 'seed' ) - expect($audit_signature_called).to eq(auditor.page.elements.map(&:class)) + expect($audit_signature_called).to eq(auditor.class.elements) end end @@ -1080,7 +959,7 @@ def differential_analysis( *args, &block ) end auditor.audit_differential( { false: '0', pairs: { '1' => '2' } } ) - expect($audit_differential_called).to eq(auditor.page.elements.map(&:class)) + expect($audit_differential_called).to eq(auditor.class.elements) end end @@ -1099,7 +978,7 @@ def timeout_analysis( *args, &block ) end auditor.audit_timeout( 'seed', timeout: 1 ) - expect($audit_timeout_called).to eq(auditor.page.elements.map(&:class)) + expect($audit_timeout_called).to eq(auditor.class.elements) end end diff --git a/spec/arachni/issue_spec.rb b/spec/arachni/issue_spec.rb index 946d61fa6d..b61f61ebc2 100644 --- a/spec/arachni/issue_spec.rb +++ b/spec/arachni/issue_spec.rb @@ -412,7 +412,7 @@ targets: { 'Generic' => 'all' }, - elements: [:link, :form_dom], + elements: [:link], shortname: 'test' }, trusted: true, diff --git a/spec/support/factories/issue.rb b/spec/support/factories/issue.rb index d19fd752b4..89f9eab749 100644 --- a/spec/support/factories/issue.rb +++ b/spec/support/factories/issue.rb @@ -27,8 +27,7 @@ 'Generic' => 'all' }, elements: [ - Arachni::Element::Link, - Arachni::Element::Form::DOM + Arachni::Element::Link ], shortname: 'test' }