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.



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/arachni/issue.rb', line 117

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

#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:



450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
# File 'lib/arachni/issue.rb', line 450

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
    instance
end

Instance Method Details

#==(other) ⇒ Object



411
412
413
# File 'lib/arachni/issue.rb', line 411

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:



165
166
167
168
169
170
171
# File 'lib/arachni/issue.rb', line 165

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.



153
154
155
156
157
158
# File 'lib/arachni/issue.rb', line 153

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:



177
178
179
180
181
182
183
184
185
# File 'lib/arachni/issue.rb', line 177

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:



367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/arachni/issue.rb', line 367

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
end

#cwe_urlString

Returns CWE reference URL.

Returns:



217
218
219
220
# File 'lib/arachni/issue.rb', line 217

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:



326
327
328
# File 'lib/arachni/issue.rb', line 326

def digest
    unique_id.persistent_hash
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


419
420
421
# File 'lib/arachni/issue.rb', line 419

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

#hashObject



415
416
417
# File 'lib/arachni/issue.rb', line 415

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?


191
192
193
# File 'lib/arachni/issue.rb', line 191

def passive?
    !active?
end

#requestHTTP::Request

Returns:



142
143
144
145
# File 'lib/arachni/issue.rb', line 142

def request
    return if !response
    response.request
end

#responseHTTP::Response

Returns:



136
137
138
139
# File 'lib/arachni/issue.rb', line 136

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.



332
333
334
# File 'lib/arachni/issue.rb', line 332

def solo?
    @variation.nil?
end

#to_hHash Also known as: to_hash

Returns:



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
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
# File 'lib/arachni/issue.rb', line 246

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



425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/arachni/issue.rb', line 425

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

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



407
408
409
# File 'lib/arachni/issue.rb', line 407

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



388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/arachni/issue.rb', line 388

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

    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?


200
201
202
# File 'lib/arachni/issue.rb', line 200

def trusted?
    !!@trusted
end

#unique_idString

Returns A string uniquely identifying this issue.

Returns:

  • (String)

    A string uniquely identifying this issue.



316
317
318
319
320
# File 'lib/arachni/issue.rb', line 316

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:



205
206
207
# File 'lib/arachni/issue.rb', line 205

def untrusted?
    !trusted?
end

#variation?Bool

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

Returns:

  • (Bool)

    ‘true` if `self` is a variation.



338
339
340
# File 'lib/arachni/issue.rb', line 338

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.



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/arachni/issue.rb', line 347

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
end