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

Constant Summary collapse

VARIATION_ATTRIBUTES =

Attributes removed from a parent issue (i.e. an issues with variations) and solely populating variations.

Set.new([
    :@page, :@referring_page, :@proof, :@signature, :@remarks, :@trusted
])

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Issue

Returns a new instance of Issue.

Parameters:

  • options (Hash) (defaults to: {})

    Configuration hash holding instance attributes.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/arachni/issue.rb', line 121

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       ||= []
    @variations ||= []
    @variation    = nil
    @parent       = nil
end

Instance Attribute Details

#checkHash

Returns Information about the check that logged the issue.

Returns:



91
92
93
# File 'lib/arachni/issue.rb', line 91

def check
  @check
end

#cweString

Returns The CWE ID number of the issue.

Returns:

  • (String)

    The CWE ID number of the issue.

See Also:



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

def cwe
  @cwe
end

#descriptionString

Note:

Should be treated as Markdown.

Returns Brief description.

Returns:

  • (String)

    Brief description.



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

def description
  @description
end

#nameString

Returns Name.

Returns:



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

def name
  @name
end

#pagePage

Returns Page proving the issue.

Returns:

  • (Page)

    Page proving the issue.



87
88
89
# File 'lib/arachni/issue.rb', line 87

def page
  @page
end

#parentIssue?

Returns Parent of variation.

Returns:

  • (Issue, nil)

    Parent of variation.



117
118
119
# File 'lib/arachni/issue.rb', line 117

def parent
  @parent
end

#platform_nameSymbol

Returns Name of the vulnerable platform.

Returns:

  • (Symbol)

    Name of the vulnerable platform.

See Also:



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

def platform_name
  @platform_name
end

#platform_typeSymbol

Returns Type of the vulnerable platform.

Returns:

  • (Symbol)

    Type of the vulnerable platform.

See Also:



74
75
76
# File 'lib/arachni/issue.rb', line 74

def platform_type
  @platform_type
end

#proofString

Returns Data that was matched by the #signature.

Returns:



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

def proof
  @proof
end

#referencesHash

Returns References related to the issue.

Returns:

  • (Hash)

    References related to the issue.



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

def references
  @references
end

#referring_pagePage

Returns Page containing the #vector and whose audit resulted in the discovery of the issue.

Returns:

  • (Page)

    Page containing the #vector and whose audit resulted in the discovery of the issue.



83
84
85
# File 'lib/arachni/issue.rb', line 83

def referring_page
  @referring_page
end

#remarksHash

Returns Remarks about the issue. Key is the name of the entity which made the remark, value is an ‘Array` of remarks.

Returns:

  • (Hash)

    Remarks about the issue. Key is the name of the entity which made the remark, value is an ‘Array` of remarks.



109
110
111
# File 'lib/arachni/issue.rb', line 109

def remarks
  @remarks
end

#remedy_codeString

Returns Code snippet demonstrating how to remedy the Issue.

Returns:

  • (String)

    Code snippet demonstrating how to remedy the Issue.



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

def remedy_code
  @remedy_code
end

#remedy_guidanceString

Note:

Should be treated as Markdown.

Returns Brief text explaining how to remedy the issue.

Returns:

  • (String)

    Brief text explaining how to remedy the issue.



38
39
40
# File 'lib/arachni/issue.rb', line 38

def remedy_guidance
  @remedy_guidance
end

#severityString

Returns Severity of the issue.

Returns:

  • (String)

    Severity of the issue.

See Also:



48
49
50
# File 'lib/arachni/issue.rb', line 48

def severity
  @severity
end

#signatureString

Returns The signature/pattern that identified the issue.

Returns:

  • (String)

    The signature/pattern that identified the issue.



95
96
97
# File 'lib/arachni/issue.rb', line 95

def signature
  @signature
end

#tagsArray<String>

Returns Tags categorizing the issue.

Returns:



52
53
54
# File 'lib/arachni/issue.rb', line 52

def tags
  @tags
end

#trustedBool

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

Returns:

  • (Bool)

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



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

def trusted
  @trusted
end

#variationsArray<Issue>

Returns Variations of this issue.

Returns:



113
114
115
# File 'lib/arachni/issue.rb', line 113

def variations
  @variations
end

#vectorElement::Base?

Returns Instance of the relevant vector if available.

Returns:

  • (Element::Base, nil)

    Instance of the relevant vector if available.



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

def vector
  @vector
end

Class Method Details

.from_rpc_data(data) ⇒ Issue

Parameters:

Returns:



497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
# File 'lib/arachni/issue.rb', line 497

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 'variations'
                        value.map { |i| from_rpc_data i }

                    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

    if instance.variations
        instance.variations.each do |v|
            v.parent = instance
        end
    end

    instance
end

Instance Method Details

#==(other) ⇒ Object



456
457
458
# File 'lib/arachni/issue.rb', line 456

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

#active?Boolean

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

Returns:

  • (Boolean)

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

See Also:



205
206
207
208
209
210
211
# File 'lib/arachni/issue.rb', line 205

def active?
    if variations && variations.any?
        return variations.first.active?
    end

    !!(vector.respond_to?( :affected_input_name ) && vector.affected_input_name)
end

#add_remark(author, string) ⇒ Object

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

Parameters:

  • author (String, Symbol)

    Component which made the remark.

  • string (String)

    Remark.



193
194
195
196
197
198
# File 'lib/arachni/issue.rb', line 193

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?.

Returns:

  • (String, nil)

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

See Also:



217
218
219
220
221
222
223
224
225
# File 'lib/arachni/issue.rb', line 217

def affected_input_name
    return if !active?

    if variations && variations.any?
        return variations.first.vector.affected_input_name
    end

    vector.affected_input_name
end

#as_variationIssue

Returns A copy of ‘self` with specific details only and the mutated #vector.

Returns:



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/arachni/issue.rb', line 410

def as_variation
    issue = self.deep_clone

    instance_variables.each do |k|
        next if k == :@vector || VARIATION_ATTRIBUTES.include?( k ) ||
            !issue.instance_variable_defined?( k )

        issue.remove_instance_variable k
    end

    issue.unique_id = unique_id
    issue.variation = true
    issue.parent    = self
    issue
end

#cwe_urlString

Returns CWE reference URL.

Returns:



257
258
259
260
# File 'lib/arachni/issue.rb', line 257

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.

Returns:

  • (Integer)

    A hash uniquely identifying this issue.

See Also:



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

def digest
    unique_id.persistent_hash
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


464
465
466
# File 'lib/arachni/issue.rb', line 464

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

#hashObject



460
461
462
# File 'lib/arachni/issue.rb', line 460

def hash
    unique_id.hash
end

#passive?Boolean

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

Returns:

  • (Boolean)

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

See Also:

  • audit?


231
232
233
# File 'lib/arachni/issue.rb', line 231

def passive?
    !active?
end

#recheck(framework = nil) ⇒ Issue?

Note:

The whole environment needs to be fresh.

Rechecks the existence of this issue.

Parameters:

  • framework (Framework.nil) (defaults to: nil)

    Framework to use, if ‘nil` is given a new Framework will be instantiated and used.

Returns:



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/arachni/issue.rb', line 150

def recheck( framework = nil )
    new_issue = nil
    checker = proc do |f|
        referring_page.update_element_audit_whitelist vector

        f.options.url = referring_page.url
        f.options.audit.elements vector.class.type

        f.checks.load( parent ? parent.check[:shortname] : check[:shortname] )
        f.push_to_page_queue referring_page

        f.run

        new_issue = Data.issues[digest]
    end

    if framework
        checker.call framework
    else
        Arachni::Framework.new( &checker )
    end

    new_issue
end

#requestHTTP::Request

Returns:



182
183
184
185
# File 'lib/arachni/issue.rb', line 182

def request
    return if !response
    response.request
end

#responseHTTP::Response

Returns:



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

def response
    return if !page
    page.response
end

#solo?Bool

Returns ‘true` if the issue neither has nor is a variation, `false` otherwise.

Returns:

  • (Bool)

    ‘true` if the issue neither has nor is a variation, `false` otherwise.



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

def solo?
    @variation.nil?
end

#to_hHash Also known as: to_hash

Returns:



286
287
288
289
290
291
292
293
294
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
341
342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/arachni/issue.rb', line 286

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 )

    if solo?
        h.delete( :variation )
    else
        if variation?
            h[:vector].delete :html
            h[:vector].delete :type
            h[:vector].delete :url
            h[:vector].delete :action
            h[:vector].delete :default_inputs
            h[:vector].delete :affected_input_name
        else
            h[:vector][:inputs] = h[:vector].delete( :default_inputs )
            h[:vector][:affected_input_name] = affected_input_name
        end
    end

    if !variation? || solo?
        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)

        h[:variations] = @variations.map(&:to_h)
    end

    if variation? || solo?
        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
    end

    h.delete :parent

    h
end

#to_rpc_dataHash

Returns Data representing this instance that are suitable the RPC transmission.

Returns:

  • (Hash)

    Data representing this instance that are suitable the RPC transmission.



470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'lib/arachni/issue.rb', line 470

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

    data.delete 'parent'

    if data['check'] && data['check'][:elements]
        data['check'] = data['check'].dup
        data['check'][:elements] = data['check'][:elements].map(&:to_s)
    end

    if data['variations']
        data['variations'] = data['variations'].map(&:to_rpc_data)
    end

    data['digest']   = digest
    data['severity'] = data['severity'].to_s

    data
end

#to_solo(issue) ⇒ Issue

Copy of ‘self` as a solo issue.

Parameters:

  • issue (Issue)

    Parent issue.

Returns:

  • (Issue)

    Solo issue, with generic vulnerability data filled in from ‘issue`.



452
453
454
# File 'lib/arachni/issue.rb', line 452

def to_solo( issue )
    deep_clone.to_solo!( issue )
end

#to_solo!(issue) ⇒ Issue

Converts ‘self` to a solo issue, in place.

Parameters:

  • issue (Issue)

    Parent issue.

Returns:

  • (Issue)

    Solo issue, with generic vulnerability data filled in from ‘issue`.



432
433
434
435
436
437
438
439
440
441
442
443
444
# File 'lib/arachni/issue.rb', line 432

def to_solo!( issue )
    issue.instance_variables.each do |k|
        next if k == :@variations || k == :@vector || k == :@trusted
        next if (val = issue.instance_variable_get(k)).nil?
        instance_variable_set( k, val )
    end

    @variations = []
    @variation  = nil
    @parent     = nil

    self
end

#trusted?Bool

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

Returns:

  • (Bool)

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

See Also:

  • #requires_verification?


240
241
242
# File 'lib/arachni/issue.rb', line 240

def trusted?
    !!@trusted
end

#unique_idString

Returns A string uniquely identifying this issue.

Returns:

  • (String)

    A string uniquely identifying this issue.



358
359
360
361
362
# File 'lib/arachni/issue.rb', line 358

def unique_id
    return @unique_id if @unique_id
    vector_info = active? ? "#{vector.method}:#{vector.affected_input_name}:" : nil
    "#{name}:#{vector_info}#{vector.action.split( '?' ).first}"
end

#untrusted?Boolean

Returns:

  • (Boolean)

See Also:



245
246
247
# File 'lib/arachni/issue.rb', line 245

def untrusted?
    !trusted?
end

#variation?Bool

Returns ‘true` if `self` is a variation.

Returns:

  • (Bool)

    ‘true` if `self` is a variation.



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

def variation?
    !!@variation
end

#with_variationsIssue

Returns A copy of ‘self` without specific details and an empty array of #variations to be populated.

Also, the #vector attribute will hold the original, non-mutated vector.

Returns:

  • (Issue)

    A copy of ‘self` without specific details and an empty array of #variations to be populated.

    Also, the #vector attribute will hold the original, non-mutated vector.



389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/arachni/issue.rb', line 389

def with_variations
    issue = self.deep_clone

    instance_variables.each do |k|
        next if k == :@trusted || !VARIATION_ATTRIBUTES.include?( k ) ||
            !issue.instance_variable_defined?( k )

        issue.remove_instance_variable k
    end

    issue.vector.reset

    issue.unique_id = unique_id
    issue.variation = false
    issue.parent    = nil
    issue
end