Class: Arachni::Issue

Inherits:
Object show all
Defined in:
lib/arachni/issue.rb,
lib/arachni/issue/severity.rb,
lib/arachni/issue/severity/base.rb

Overview

Represents a detected issue.

Author:

Defined Under Namespace

Modules: Severity

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Issue



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# 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

Instance Attribute Details

#checkHash



85
86
87
# File 'lib/arachni/issue.rb', line 85

def check
  @check
end

#cweString

Returns The CWE ID number of the issue.



56
57
58
# File 'lib/arachni/issue.rb', line 56

def cwe
  @cwe
end

#descriptionString

Note:

Should be treated as Markdown.

Returns Brief description.



26
27
28
# File 'lib/arachni/issue.rb', line 26

def description
  @description
end

#nameString



20
21
22
# File 'lib/arachni/issue.rb', line 20

def name
  @name
end

#pagePage



81
82
83
# File 'lib/arachni/issue.rb', line 81

def page
  @page
end

#platform_nameSymbol

Returns Name of the vulnerable platform.

See Also:



62
63
64
# File 'lib/arachni/issue.rb', line 62

def platform_name
  @platform_name
end

#platform_typeSymbol

Returns Type of the vulnerable platform.

See Also:



68
69
70
# File 'lib/arachni/issue.rb', line 68

def platform_type
  @platform_type
end

#proofString



93
94
95
# File 'lib/arachni/issue.rb', line 93

def proof
  @proof
end

#referencesHash



50
51
52
# File 'lib/arachni/issue.rb', line 50

def references
  @references
end

#referring_pagePage



77
78
79
# File 'lib/arachni/issue.rb', line 77

def referring_page
  @referring_page
end

#remarksHash



103
104
105
# File 'lib/arachni/issue.rb', line 103

def remarks
  @remarks
end

#remedy_codeString



36
37
38
# File 'lib/arachni/issue.rb', line 36

def remedy_code
  @remedy_code
end

#remedy_guidanceString

Note:

Should be treated as Markdown.

Returns Brief text explaining how to remedy the issue.



32
33
34
# File 'lib/arachni/issue.rb', line 32

def remedy_guidance
  @remedy_guidance
end

#severityString

Returns Severity of the issue.

See Also:



42
43
44
# File 'lib/arachni/issue.rb', line 42

def severity
  @severity
end

#signatureString



89
90
91
# File 'lib/arachni/issue.rb', line 89

def signature
  @signature
end

#tagsArray<String>



46
47
48
# File 'lib/arachni/issue.rb', line 46

def tags
  @tags
end

#trustedBool



98
99
100
# File 'lib/arachni/issue.rb', line 98

def trusted
  @trusted
end

#vectorElement::Base?



72
73
74
# File 'lib/arachni/issue.rb', line 72

def vector
  @vector
end

Class Method Details

.from_rpc_data(data) ⇒ Issue



419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# 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

Instance Method Details

#<(other) ⇒ Object



387
388
389
# File 'lib/arachni/issue.rb', line 387

def <( other )
    severity < other.severity
end

#<=>(other) ⇒ Object



383
384
385
# File 'lib/arachni/issue.rb', line 383

def <=>( other )
    severity <=> other.severity
end

#==(other) ⇒ Object



371
372
373
# File 'lib/arachni/issue.rb', line 371

def ==( other )
    hash == other.hash
end

#>(other) ⇒ Object



391
392
393
# File 'lib/arachni/issue.rb', line 391

def >( other )
    severity > other.severity
end

#active?Boolean

Returns ‘true` if the issue was discovered by manipulating an input, `false` otherwise.

See Also:



200
201
202
203
204
205
# 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) ⇒ Object

Adds a remark as a heads-up to the end user.



188
189
190
191
192
193
# 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_nameString?

Returns The name of the affected input, ‘nil` if the issue is #passive?.

See Also:



211
212
213
214
# File 'lib/arachni/issue.rb', line 211

def affected_input_name
    return if !active?
    vector.affected_input_name
end

#affected_input_valueString?

Returns The name of the affected input, ‘nil` if the issue is #passive?.

See Also:



220
221
222
223
# File 'lib/arachni/issue.rb', line 220

def affected_input_value
    return if !active?
    vector.inputs[affected_input_name]
end

#cwe_urlString



255
256
257
258
# 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

#digestInteger

Returns A hash uniquely identifying this issue.

See Also:



367
368
369
# File 'lib/arachni/issue.rb', line 367

def digest
    unique_id.persistent_hash
end

#eql?(other) ⇒ Boolean



379
380
381
# File 'lib/arachni/issue.rb', line 379

def eql?( other )
    hash == other.hash
end

#hashObject



375
376
377
# File 'lib/arachni/issue.rb', line 375

def hash
    unique_id.hash
end

#passive?Boolean

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

See Also:

  • audit?


229
230
231
# File 'lib/arachni/issue.rb', line 229

def passive?
    !active?
end

#recheck(framework = nil) ⇒ Issue?

Note:

The whole environment needs to be fresh.

Rechecks the existence of this issue.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# 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

#requestHTTP::Request



177
178
179
180
# File 'lib/arachni/issue.rb', line 177

def request
    return if !response
    response.request
end

#responseHTTP::Response



171
172
173
174
# File 'lib/arachni/issue.rb', line 171

def response
    return if !page
    page.response
end

#to_hHash Also known as: to_hash



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# 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

#to_rpc_dataHash



397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# 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?Bool

Returns ‘true` if the issue can be trusted (doesn’t require manual verification), ‘false` otherwise.

See Also:

  • #requires_verification?


238
239
240
# File 'lib/arachni/issue.rb', line 238

def trusted?
    !!@trusted
end

#unique_idString



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# 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?Boolean

See Also:



243
244
245
# File 'lib/arachni/issue.rb', line 243

def untrusted?
    !trusted?
end