class Arachni::Issue

Represents a detected issue.

@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>

Attributes

check[RW]

@return [Hash]

{Check::Base.info Information} about the check that logged the issue.
cwe[RW]

@return [String]

The CWE ID number of the issue.

@see cwe.mitre.org/

description[RW]

@note Should be treated as Markdown.

@return [String]

Brief description.
name[RW]

@return [String]

Name.
page[RW]

@return [Page]

Page proving the issue.
platform_name[RW]

@return [Symbol]

Name of the vulnerable platform.

@see Platform::Manager

platform_type[RW]

@return [Symbol]

Type of the vulnerable platform.

@see Platform::Manager

proof[RW]

@return [String]

Data that was matched by the {#signature}.
references[RW]

@return [Hash]

References related to the issue.
referring_page[RW]

@return [Page]

Page containing the {#vector} and whose audit resulted in the discovery
of the issue.
remarks[RW]

@return [Hash]

Remarks about the issue. Key is the name of the entity which
made the remark, value is an `Array` of remarks.
remedy_code[RW]

@return [String]

Code snippet demonstrating how to remedy the Issue.
remedy_guidance[RW]

@note Should be treated as Markdown.

@return [String]

Brief text explaining how to remedy the issue.
severity[RW]

@return [String]

Severity of the issue.

@see Severity

signature[RW]

@return [String]

The signature/pattern that identified the issue.
tags[RW]

@return [Array<String>]

Tags categorizing the issue.
trusted[RW]

@return [Bool]

`true` if the issue can be trusted (doesn't require manual verification),
`false` otherwise.
vector[RW]

@return [Element::Base, nil]

Instance of the relevant vector if available.

Public Class Methods

from_rpc_data( data ) click to toggle source

@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
new( options = {} ) click to toggle source

@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

element_string_to_class( element ) click to toggle source
# 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

<( other ) click to toggle source
# File lib/arachni/issue.rb, line 387
def <( other )
    severity < other.severity
end
<=>( other ) click to toggle source
# File lib/arachni/issue.rb, line 383
def <=>( other )
    severity <=> other.severity
end
==( other ) click to toggle source
# File lib/arachni/issue.rb, line 371
def ==( other )
    hash == other.hash
end
>( other ) click to toggle source
# File lib/arachni/issue.rb, line 391
def >( other )
    severity > other.severity
end
active?() click to toggle source

@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
add_remark( author, string ) click to toggle source

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
affected_input_name() click to toggle source

@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
affected_input_value() click to toggle source

@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
cwe=( id ) click to toggle source
# File lib/arachni/issue.rb, line 247
def cwe=( id )
    id = id.to_i
    return if id == 0
    @cwe = id
end
cwe_url() click to toggle source

@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
digest() click to toggle source

@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
eql?( other ) click to toggle source
# File lib/arachni/issue.rb, line 379
def eql?( other )
    hash == other.hash
end
hash() click to toggle source
# File lib/arachni/issue.rb, line 375
def hash
    unique_id.hash
end
passive?() click to toggle source

@return [Boolean]

`true` if the issue was discovered passively, `false` otherwise.

@see audit?

# File lib/arachni/issue.rb, line 229
def passive?
    !active?
end
recheck( framework = nil ) click to toggle source

@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
references=( refs ) click to toggle source
# File lib/arachni/issue.rb, line 260
def references=( refs )
    @references = (refs || {}).stringify_recursively_and_freeze
end
request() click to toggle source

@return [HTTP::Request]

# File lib/arachni/issue.rb, line 177
def request
    return if !response
    response.request
end
response() click to toggle source

@return [HTTP::Response]

# File lib/arachni/issue.rb, line 171
def response
    return if !page
    page.response
end
signature=( sig ) click to toggle source
# 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
to_h() click to toggle source

@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
Also aliased as: to_hash
to_hash()
Alias for: to_h
to_rpc_data() click to toggle source

@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
trusted?() click to toggle source

@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
unique_id() click to toggle source

@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
untrusted?() click to toggle source

@see trusted?

# File lib/arachni/issue.rb, line 243
def untrusted?
    !trusted?
end

Protected Instance Methods

unique_id=( id ) click to toggle source
# File lib/arachni/issue.rb, line 470
def unique_id=( id )
    @unique_id = id
end

Private Instance Methods

normalize_name( name ) click to toggle source
# File lib/arachni/issue.rb, line 476
def normalize_name( name )
    name.to_s.gsub( /@/, '' ).to_sym
end
try_dup( obj ) click to toggle source
# File lib/arachni/issue.rb, line 480
def try_dup( obj )
    obj.dup rescue obj
end