class Arachni::Issue
Represents a detected issue.
@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>
Attributes
@return [Hash]
{Check::Base.info Information} about the check that logged the issue.
@note Should be treated as Markdown.
@return [String]
Brief description.
@return [String]
Name.
@return [Page]
Page proving the issue.
@return [String]
Data that was matched by the {#signature}.
@return [Hash]
References related to the issue.
@return [Page]
Page containing the {#vector} and whose audit resulted in the discovery of the issue.
@return [Hash]
Remarks about the issue. Key is the name of the entity which made the remark, value is an `Array` of remarks.
@return [String]
Code snippet demonstrating how to remedy the Issue.
@note Should be treated as Markdown.
@return [String]
Brief text explaining how to remedy the issue.
@return [String]
The signature/pattern that identified the issue.
@return [Bool]
`true` if the issue can be trusted (doesn't require manual verification), `false` otherwise.
@return [Element::Base, nil]
Instance of the relevant vector if available.
Public Class Methods
@param [Hash] data
{#to_rpc_data}
@return [Issue]
# File lib/arachni/issue.rb, line 419 def self.from_rpc_data( data ) instance = allocate data.each do |name, value| value = case name when 'vector' element_string_to_class( value.delete('class') ).from_rpc_data( value ) when 'check' if value['elements'] value['elements'] = (value['elements'].map do |class_name| element_string_to_class( class_name ) end) end value.my_symbolize_keys(false) when 'remarks' value.my_symbolize_keys when 'platform_name', 'platform_type' next if !value value.to_sym when 'severity' next if value.to_s.empty? Severity.const_get( value.upcase.to_sym ) when 'page', 'referring_page' Arachni::Page.from_rpc_data( value ) else value end instance.instance_variable_set( "@#{name}", value ) end instance end
@param [Hash] options
Configuration hash holding instance attributes.
# File lib/arachni/issue.rb, line 107 def initialize( options = {} ) # Make sure we're dealing with UTF-8 data. options = options.recode options.each do |k, v| send( "#{k.to_s.downcase}=", v ) end fail ArgumentError, 'Missing :vector' if !@vector @remarks ||= {} @trusted = true if @trusted.nil? @references ||= {} @tags ||= [] end
Protected Class Methods
# File lib/arachni/issue.rb, line 462 def self.element_string_to_class( element ) parent = Arachni::Element element.gsub( "#{parent}::", '' ).split( '::' ).each do |klass| parent = parent.const_get( klass ) end parent end
Public Instance Methods
# File lib/arachni/issue.rb, line 387 def <( other ) severity < other.severity end
# File lib/arachni/issue.rb, line 383 def <=>( other ) severity <=> other.severity end
# File lib/arachni/issue.rb, line 371 def ==( other ) hash == other.hash end
# File lib/arachni/issue.rb, line 391 def >( other ) severity > other.severity end
@return [Boolean]
`true` if the issue was discovered by manipulating an input, `false` otherwise.
@see passive?
# File lib/arachni/issue.rb, line 200 def active? !!( (vector.respond_to?( :affected_input_name ) && vector.affected_input_name) && (vector.respond_to?( :seed ) && vector.seed) ) end
Adds a remark as a heads-up to the end user.
@param [String, Symbol] author
Component which made the remark.
@param [String] string
Remark.
# File lib/arachni/issue.rb, line 188 def add_remark( author, string ) fail ArgumentError, 'Author cannot be blank.' if author.to_s.empty? fail ArgumentError, 'String cannot be blank.' if string.to_s.empty? (@remarks[author] ||= []) << string end
@return [String, nil]
The name of the affected input, `nil` if the issue is {#passive?}.
@see passive?
# File lib/arachni/issue.rb, line 211 def affected_input_name return if !active? vector.affected_input_name end
@return [String, nil]
The name of the affected input, `nil` if the issue is {#passive?}.
@see passive?
# File lib/arachni/issue.rb, line 220 def affected_input_value return if !active? vector.inputs[affected_input_name] end
# File lib/arachni/issue.rb, line 247 def cwe=( id ) id = id.to_i return if id == 0 @cwe = id end
@return [String]
{#cwe CWE} reference URL.
# File lib/arachni/issue.rb, line 255 def cwe_url return if !cwe @cwe_url ||= "http://cwe.mitre.org/data/definitions/#{cwe}.html".freeze end
@return [Integer]
A hash uniquely identifying this issue.
@see unique_id
# File lib/arachni/issue.rb, line 367 def digest unique_id.persistent_hash end
# File lib/arachni/issue.rb, line 379 def eql?( other ) hash == other.hash end
# File lib/arachni/issue.rb, line 375 def hash unique_id.hash end
@return [Boolean]
`true` if the issue was discovered passively, `false` otherwise.
@see audit?
# File lib/arachni/issue.rb, line 229 def passive? !active? end
@note The whole environment needs to be fresh.
Rechecks the existence of this issue.
@param [Framework.nil] framework
{Framework} to use, if `nil` is given a new {Framework} will be instantiated and used.
@return [Issue,nil]
Fresh {Issue} if the issue still exists, `nil` otherwise.
# File lib/arachni/issue.rb, line 133 def recheck( framework = nil ) original_options = Options.to_h new_issue = nil checker = proc do |f| if active? referring_page.update_element_audit_whitelist vector f.options.audit.elements vector.class.type f.options.audit.include_vector_patterns = [affected_input_name] end f.options.url = referring_page.url f.checks.load( check[:shortname] ) f.plugins.load( f.options.plugins.keys ) f.push_to_page_queue referring_page # Needs to happen **AFTER** the push to page queue. f.options.scope.do_not_crawl f.run new_issue = Data.issues[digest] end if framework checker.call framework else Framework.new( &checker ) end new_issue ensure Options.reset Options.set original_options end
# File lib/arachni/issue.rb, line 260 def references=( refs ) @references = (refs || {}).stringify_recursively_and_freeze end
@return [HTTP::Request]
# File lib/arachni/issue.rb, line 177 def request return if !response response.request end
@return [HTTP::Response]
# File lib/arachni/issue.rb, line 171 def response return if !page page.response end
# File lib/arachni/issue.rb, line 283 def signature=( sig ) @signature = case sig when Regexp sig.source when String, nil sig else sig.to_s end end
@return [Hash]
# File lib/arachni/issue.rb, line 295 def to_h h = {} self.instance_variables.each do |var| h[normalize_name( var )] = try_dup( instance_variable_get( var ) ) end h[:vector] = vector.to_h h.delete( :unique_id ) h[:vector][:affected_input_name] = affected_input_name h[:digest] = digest h[:severity] = severity.to_sym h[:cwe_url] = cwe_url if cwe_url # Since we're doing the whole cross-platform hash thing better switch # the Element classes in the check's info data to symbols. h[:check][:elements] ||= [] h[:check][:elements] = h[:check][:elements].map(&:type) if page dom_h = page.dom.to_h dom_h.delete(:skip_states) h[:page] = { body: page.body, dom: dom_h } end if referring_page referring_page_dom_h = referring_page.dom.to_h referring_page_dom_h.delete(:skip_states) h[:referring_page] = { body: referring_page.body, dom: referring_page_dom_h } end h[:response] = response.to_h if response h[:request] = request.to_h if request h end
@return [Hash]
Data representing this instance that are suitable the RPC transmission.
# File lib/arachni/issue.rb, line 397 def to_rpc_data data = {} instance_variables.each do |ivar| data[ivar.to_s.gsub('@','')] = instance_variable_get( ivar ).to_rpc_data_or_self end if data['check'] && data['check'][:elements] data['check'] = data['check'].dup data['check'][:elements] = data['check'][:elements].map(&:to_s) end data['unique_id'] = unique_id data['digest'] = digest data['severity'] = data['severity'].to_s data end
@return [Bool]
`true` if the issue can be trusted (doesn't require manual verification), `false` otherwise.
@see requires_verification?
# File lib/arachni/issue.rb, line 238 def trusted? !!@trusted end
@return [String]
A string uniquely identifying this issue.
# File lib/arachni/issue.rb, line 345 def unique_id return @unique_id if @unique_id vector_info = if passive? proof else if vector.respond_to?( :method ) vector.method end end.to_s.dup if vector.respond_to?( :affected_input_name ) vector_info << ":#{vector.affected_input_name}" end "#{name}:#{vector_info}:#{vector.action.split( '?' ).first}" end
@see trusted?
# File lib/arachni/issue.rb, line 243 def untrusted? !trusted? end
Protected Instance Methods
# File lib/arachni/issue.rb, line 470 def unique_id=( id ) @unique_id = id end
Private Instance Methods
# File lib/arachni/issue.rb, line 476 def normalize_name( name ) name.to_s.gsub( /@/, '' ).to_sym end
# File lib/arachni/issue.rb, line 480 def try_dup( obj ) obj.dup rescue obj end