arachni 1.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -0
  3. data/README.md +2 -2
  4. data/arachni.gemspec +1 -1
  5. data/components/checks/active/code_injection_php_input_wrapper.rb +8 -3
  6. data/components/checks/active/file_inclusion.rb +7 -3
  7. data/components/checks/active/path_traversal.rb +7 -3
  8. data/components/checks/passive/grep/cookie_set_for_parent_domain.rb +5 -3
  9. data/components/plugins/cookie_collector.rb +1 -1
  10. data/components/plugins/proxy.rb +4 -3
  11. data/components/plugins/vector_feed.rb +1 -1
  12. data/components/reporters/html/default/issue.erb +5 -0
  13. data/components/reporters/stdout.rb +4 -0
  14. data/lib/arachni/browser.rb +25 -6
  15. data/lib/arachni/browser/element_locator.rb +1 -1
  16. data/lib/arachni/browser/javascript/taint_tracer.rb +3 -3
  17. data/lib/arachni/browser/javascript/taint_tracer/frame.rb +1 -1
  18. data/lib/arachni/browser/javascript/taint_tracer/frame/called_function.rb +1 -1
  19. data/lib/arachni/browser/javascript/taint_tracer/sink/base.rb +1 -1
  20. data/lib/arachni/check/auditor.rb +2 -0
  21. data/lib/arachni/component/manager.rb +2 -2
  22. data/lib/arachni/component/options/base.rb +2 -2
  23. data/lib/arachni/element/base.rb +2 -2
  24. data/lib/arachni/element/cookie.rb +4 -4
  25. data/lib/arachni/element/form.rb +1 -1
  26. data/lib/arachni/element/generic_dom.rb +1 -1
  27. data/lib/arachni/framework.rb +9 -1
  28. data/lib/arachni/http/client.rb +2 -0
  29. data/lib/arachni/http/request.rb +2 -2
  30. data/lib/arachni/http/response.rb +1 -1
  31. data/lib/arachni/issue.rb +2 -2
  32. data/lib/arachni/option_group.rb +1 -1
  33. data/lib/arachni/option_groups/input.rb +1 -1
  34. data/lib/arachni/option_groups/scope.rb +1 -1
  35. data/lib/arachni/page.rb +1 -1
  36. data/lib/arachni/page/dom/transition.rb +3 -3
  37. data/lib/arachni/parser.rb +3 -1
  38. data/lib/arachni/platform/list.rb +1 -1
  39. data/lib/arachni/report.rb +1 -1
  40. data/lib/arachni/rpc/client/instance/framework.rb +6 -6
  41. data/lib/arachni/rpc/client/instance/service.rb +7 -7
  42. data/lib/arachni/rpc/server/dispatcher.rb +18 -5
  43. data/lib/arachni/rpc/server/dispatcher/node.rb +13 -6
  44. data/lib/arachni/rpc/server/framework/distributor.rb +1 -1
  45. data/lib/arachni/rpc/server/framework/master.rb +1 -1
  46. data/lib/arachni/rpc/server/framework/multi_instance.rb +2 -2
  47. data/lib/arachni/rpc/server/instance.rb +11 -3
  48. data/lib/arachni/ruby/hash.rb +7 -6
  49. data/lib/arachni/state/framework.rb +1 -0
  50. data/lib/version +1 -1
  51. data/spec/arachni/browser_spec.rb +25 -0
  52. data/spec/arachni/component/manager_spec.rb +1 -1
  53. data/spec/arachni/element/cookie_spec.rb +3 -3
  54. data/spec/arachni/http/request_spec.rb +3 -3
  55. data/spec/arachni/option_groups/scope_spec.rb +2 -2
  56. data/spec/arachni/parser_spec.rb +7 -0
  57. data/spec/arachni/reporter/manager_spec.rb +1 -1
  58. data/spec/arachni/rpc/server/dispatcher/node_spec.rb +2 -0
  59. data/spec/arachni/rpc/server/framework_spec.rb +1 -1
  60. data/spec/arachni/ruby/hash_spec.rb +8 -8
  61. data/spec/support/servers/checks/passive/grep/cookie_set_for_parent_domain.rb +1 -1
  62. data/spec/support/shared/element/capabilities/inputtable.rb +2 -2
  63. data/ui/cli/utilities.rb +3 -0
  64. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0bbd91e8fb1bcbcf5164c22e187397214e1894ac
4
- data.tar.gz: 16adc7946a2fa8da04a50b2417cd8b731d291652
3
+ metadata.gz: a4f1fba6f25f0dd437f9d31c82b3f036d4b87ec9
4
+ data.tar.gz: eaa9814da188578b10283e23101525828d42b17a
5
5
  SHA512:
6
- metadata.gz: 913913c30128c176a2e55614ee95530343fd6f300cdf3008cbd6df69cf93b78c2b9c87ea748ae330524061a32a160563fb1aafdfc2b06ad4dcf853e5f9eb8b50
7
- data.tar.gz: 59bd68596db84ed440707cde96554f13d6397a52ca0025f9019073ba250bf0e32296adbebd50ac099c02262bff40ac5c27d9d3bcffeb0d2afe74666577816b3d
6
+ metadata.gz: a98cde207400471e0ba80cffc9fce6ce46348d43e76c509cbd06f5c7e6b4867f62f9e5382bedaa7b500d731a82428cde04d23db0ea72a4cfceb9087c8fe8062a
7
+ data.tar.gz: a66344c76f782b7042f003c0e7b525b5ea173d2f72257d77f9a4673be35280624d4c73a5f874c157b4b66fd5b3eb46a0196480d9b91379639da6cf26e288c045
@@ -1,5 +1,52 @@
1
1
  # ChangeLog
2
2
 
3
+ ## 1.0.1 _(September 7, 2014)_
4
+
5
+ - `RPC::Server::Dispatcher`
6
+ - Check for Instance status via the bind address, not the external one.
7
+ - Added more status and debugging messages
8
+ - Fixed RPC connection leak when in Grid configuration.
9
+ - `Node`
10
+ - Don't raise error if the initial neighbour is unreachable, just add
11
+ it to the dead list as usual.
12
+ - `Browser`
13
+ - Fixed issue causing the removal of cookie HttpOnly flags.
14
+ - `Parser`
15
+ - `#link_vars` -- Return empty `Hash` when dealing with unparsable URL.
16
+ - `HTTP::Client`
17
+ - Debugging messages now include the `HTTP::Request#performer`.
18
+ - `HTTP::Request`
19
+ - `#to_typhoeus` -- Converted proxy type to `Symbol` to prevent the option
20
+ from being ignored.
21
+ - `UI::CLI::Utilities`
22
+ - `#print_issues` -- Updated to include all inputs of the given vector in
23
+ the message, if the issue is passive.
24
+ - `Check::Auditor`
25
+ - `#log` -- Updated to include all inputs of the given vector in the success
26
+ message, if the issue is passive.
27
+ - `Element::Cookie`
28
+ - `.encode` -- Encode `'` and `"`.
29
+ - `Hash` -- Renamed added methods to avoid clashes with `ActiveSupport`.
30
+ - `stringify_keys` => `my_stringify_keys`
31
+ - `symbolize_keys` => `my_symbolize_keys`
32
+ - `stringify` => `my_stringify`
33
+ - Plugins
34
+ - `proxy` -- Show control panel URL in output.
35
+ - Reporters
36
+ - `stdout`
37
+ - Updated to print out information about all available vector inputs.
38
+ - `html`
39
+ - Updated to include information about all available vector inputs in
40
+ issue title for passive issues.
41
+ - Checks
42
+ - Active
43
+ - `code_injection_php_input_wrapper` -- Fixed `nil` error when
44
+ manipulating mutations.
45
+ - `file_inclusion` -- Fixed `nil` error when manipulating mutations.
46
+ - `path_traversal` -- Fixed `nil` error when manipulating mutations.
47
+ - Passive
48
+ - `cookie_set_for_parent_domain` -- Only check `HTTP::Response` cookies.
49
+
3
50
  ## 1.0 _(August 29, 2014)_
4
51
 
5
52
  - Executables:
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  * Arachni's license has changed, please see the _LICENSE_ file before working
4
4
  with the project.
5
- * v1.0 is not backwards compatible.
5
+ * v1.0 is not backwards compatible with v0.4.
6
6
 
7
7
  <hr/>
8
8
 
@@ -11,7 +11,7 @@
11
11
  <table>
12
12
  <tr>
13
13
  <th>Version</th>
14
- <td>1.0</td>
14
+ <td>1.0.1</td>
15
15
  </tr>
16
16
  <tr>
17
17
  <th>Homepage</th>
@@ -61,7 +61,7 @@ Gem::Specification.new do |s|
61
61
  end
62
62
 
63
63
  # RPC client/server implementation.
64
- s.add_dependency 'arachni-rpc', '0.2.0'
64
+ s.add_dependency 'arachni-rpc', '0.2.1.1'
65
65
 
66
66
  # HTTP client.
67
67
  s.add_dependency 'typhoeus', '0.6.9'
@@ -6,9 +6,9 @@
6
6
  web site for more information on licensing and terms of use.
7
7
  =end
8
8
 
9
- # @see OWASP https://www.owasp.org/index.php/Top_10_2007-Malicious_File_Execution
10
9
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
11
- # @version 0.1
10
+ # @version 0.1.1
11
+ # @see OWASP https://www.owasp.org/index.php/Top_10_2007-Malicious_File_Execution
12
12
  class Arachni::Checks::CodeInjectionPhpInputWrapper < Arachni::Check::Base
13
13
 
14
14
  def self.options
@@ -20,6 +20,11 @@ class Arachni::Checks::CodeInjectionPhpInputWrapper < Arachni::Check::Base
20
20
  # Add one more mutation (on the fly) which will include the extension
21
21
  # of the original value (if that value was a filename) after a null byte.
22
22
  each_mutation: proc do |mutation|
23
+ next if !mutation.affected_input_value ||
24
+ (mutation.is_a?( Arachni::Form ) &&
25
+ (mutation.mutation_with_original_values? ||
26
+ mutation.mutation_with_sample_values?))
27
+
23
28
  # Don't bother if the current element type can't carry nulls.
24
29
  next if !mutation.valid_input_value_data?( "\0" )
25
30
 
@@ -51,7 +56,7 @@ to try and load it.
51
56
  },
52
57
  elements: [ Element::Form, Element::Link, Element::Cookie, Element::Header ],
53
58
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com> ',
54
- version: '0.1',
59
+ version: '0.1.1',
55
60
  platforms: [:php],
56
61
 
57
62
  issue: {
@@ -9,8 +9,7 @@
9
9
  # File inclusion check.
10
10
  #
11
11
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
12
- #
13
- # @version 0.1.1
12
+ # @version 0.1.2
14
13
  #
15
14
  # @see http://cwe.mitre.org/data/definitions/98.html
16
15
  # @see https://www.owasp.org/index.php/PHP_File_Inclusion
@@ -51,6 +50,11 @@ class Arachni::Checks::FileInclusion < Arachni::Check::Base
51
50
  # Add one more mutation (on the fly) which will include the extension
52
51
  # of the original value (if that value was a filename) after a null byte.
53
52
  each_mutation: proc do |mutation|
53
+ next if !mutation.affected_input_value ||
54
+ (mutation.is_a?( Arachni::Form ) &&
55
+ (mutation.mutation_with_original_values? ||
56
+ mutation.mutation_with_sample_values?))
57
+
54
58
  # Don't bother if the current element type can't carry nulls.
55
59
  next if !mutation.valid_input_value_data?( "\0" )
56
60
 
@@ -101,7 +105,7 @@ content or errors in the HTTP response body.
101
105
  elements: [ Element::Form, Element::Link, Element::Cookie,
102
106
  Element::Header, Element::LinkTemplate ],
103
107
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com> ',
104
- version: '0.1.1',
108
+ version: '0.1.2',
105
109
  platforms: options[:regexp].keys,
106
110
 
107
111
  issue: {
@@ -9,8 +9,7 @@
9
9
  # Path Traversal check.
10
10
  #
11
11
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
12
- #
13
- # @version 0.4.1
12
+ # @version 0.4.2
14
13
  #
15
14
  # @see http://cwe.mitre.org/data/definitions/22.html
16
15
  # @see http://www.owasp.org/index.php/Path_Traversal
@@ -40,6 +39,11 @@ class Arachni::Checks::PathTraversal < Arachni::Check::Base
40
39
  # Add one more mutation (on the fly) which will include the extension
41
40
  # of the original value (if that value was a filename) after a null byte.
42
41
  each_mutation: proc do |mutation|
42
+ next if !mutation.affected_input_value ||
43
+ (mutation.is_a?( Arachni::Form ) &&
44
+ (mutation.mutation_with_original_values? ||
45
+ mutation.mutation_with_sample_values?))
46
+
43
47
  # Don't bother if the current element type can't carry nulls.
44
48
  next if !mutation.valid_input_value_data?( "\0" )
45
49
 
@@ -111,7 +115,7 @@ of relevant content in the HTML responses.
111
115
  elements: [ Element::Form, Element::Link, Element::Cookie,
112
116
  Element::Header, Element::LinkTemplate ],
113
117
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com> ',
114
- version: '0.4.1',
118
+ version: '0.4.2',
115
119
  platforms: payloads.keys,
116
120
 
117
121
  issue: {
@@ -7,11 +7,13 @@
7
7
  =end
8
8
 
9
9
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
10
- # @version 0.1
10
+ # @version 0.1.1
11
11
  class Arachni::Checks::CookieSetForParentDomain < Arachni::Check::Base
12
12
 
13
13
  def run
14
- page.cookies.each do |cookie|
14
+ return if !page.parser
15
+
16
+ page.parser.cookies.each do |cookie|
15
17
  next if !cookie.domain.start_with?( '.' ) || audited?( cookie.name )
16
18
 
17
19
  log( vector: cookie )
@@ -25,7 +27,7 @@ class Arachni::Checks::CookieSetForParentDomain < Arachni::Check::Base
25
27
  description: %q{Logs cookies that are accessible by all subdomains.},
26
28
  elements: [ Element::Cookie ],
27
29
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>',
28
- version: '0.1',
30
+ version: '0.1.1',
29
31
 
30
32
  issue: {
31
33
  name: %q{Cookie set for parent domain},
@@ -44,7 +44,7 @@ class Arachni::Plugins::CookieCollector < Arachni::Plugin::Base
44
44
 
45
45
  @cookies << {
46
46
  'time' => Time.now.to_s,
47
- 'response' => response_hash.stringify_keys,
47
+ 'response' => response_hash.my_stringify_keys,
48
48
  'cookies' => cookies
49
49
  }
50
50
  end
@@ -16,7 +16,7 @@ require 'ostruct'
16
16
  #
17
17
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
18
18
  #
19
- # @version 0.3
19
+ # @version 0.3.1
20
20
  class Arachni::Plugins::Proxy < Arachni::Plugin::Base
21
21
 
22
22
  BASEDIR = "#{File.dirname( __FILE__ )}/proxy/"
@@ -58,7 +58,8 @@ class Arachni::Plugins::Proxy < Arachni::Plugin::Base
58
58
  def run
59
59
  print_status "Listening on: http://#{@server[:BindAddress]}:#{@server[:Port]}"
60
60
 
61
- print_status "Shutdown URL: #{url_for( :shutdown )}"
61
+ print_info "Control panel URL: #{url_for( :panel )}"
62
+ print_info "Shutdown URL: #{url_for( :shutdown )}"
62
63
  print_info 'The scan will resume once you visit the shutdown URL.'
63
64
 
64
65
  print_info
@@ -463,7 +464,7 @@ a way to restrict usage enough to avoid users unwittingly interfering with each
463
464
  others' sessions.
464
465
  },
465
466
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>',
466
- version: '0.3',
467
+ version: '0.3.1',
467
468
  options: [
468
469
  Options::Port.new( :port,
469
470
  description: 'Port to bind to.',
@@ -58,7 +58,7 @@ class Arachni::Plugins::VectorFeed < Arachni::Plugin::Base
58
58
  page_buffer = []
59
59
  print_status "Imported #{feed.size} vectors."
60
60
  feed.each do |obj|
61
- vector = (obj.respond_to?( :value ) ? obj.value : obj).symbolize_keys( false )
61
+ vector = (obj.respond_to?( :value ) ? obj.value : obj).my_symbolize_keys( false )
62
62
 
63
63
  exception_jail false do
64
64
  if page?( vector )
@@ -10,6 +10,11 @@
10
10
  <% if issue.active? %>
11
11
  input
12
12
  <code><%= issue.affected_input_name %></code>
13
+ <% elsif issue.vector.respond_to? :inputs %>
14
+ with inputs
15
+ <% issue.vector.inputs.keys.map do |iname| %>
16
+ <code><%= iname %></code>
17
+ <% end.join( ', ') %>
13
18
  <% end %>
14
19
 
15
20
  <% if issue.variations.first.request %>
@@ -109,6 +109,10 @@ class Arachni::Reporters::Stdout < Arachni::Reporter::Base
109
109
  print_info "Input name: #{issue.affected_input_name}"
110
110
  end
111
111
 
112
+ if issue.vector.respond_to? :inputs
113
+ print_info "All inputs: #{issue.vector.inputs.keys.join(', ')}"
114
+ end
115
+
112
116
  print_line
113
117
  print_info "Tags: #{issue.tags.join(', ')}" if issue.tags.is_a?( Array )
114
118
  print_line
@@ -308,6 +308,7 @@ class Browser
308
308
 
309
309
  @javascript.wait_till_ready
310
310
  wait_for_timers
311
+
311
312
  wait_for_pending_requests
312
313
  end
313
314
 
@@ -553,7 +554,7 @@ class Browser
553
554
  event = event.to_s.downcase.sub( /^on/, '' ).to_sym
554
555
  locator = nil
555
556
 
556
- options[:inputs] = options[:inputs].stringify if options[:inputs]
557
+ options[:inputs] = options[:inputs].my_stringify if options[:inputs]
557
558
 
558
559
  if element.is_a? ElementLocator
559
560
  locator = element
@@ -792,10 +793,22 @@ class Browser
792
793
  # @return [Array<Cookie>]
793
794
  # Browser cookies.
794
795
  def cookies
796
+ js_cookies = begin
797
+ # Watir doesn't tell us if cookies are HttpOnly, so we need to figure
798
+ # this out ourselves, by checking for JS visibility.
799
+ javascript.run( 'return document.cookie' )
800
+ # We may not have a page.
801
+ rescue Selenium::WebDriver::Error::UnknownError
802
+ ''
803
+ end
804
+
795
805
  watir.cookies.to_a.map do |c|
796
- c[:path] = '/' if c[:path] == '//'
797
- c[:name] = Cookie.decode( c[:name].to_s )
798
- c[:value] = Cookie.decode( c[:value].to_s )
806
+ original_name = c[:name].to_s
807
+
808
+ c[:path] = '/' if c[:path] == '//'
809
+ c[:name] = Cookie.decode( c[:name].to_s )
810
+ c[:value] = Cookie.decode( c[:value].to_s )
811
+ c[:httponly] = !js_cookies.include?( original_name )
799
812
 
800
813
  Cookie.new c.merge( url: @last_url )
801
814
  end
@@ -944,7 +957,8 @@ class Browser
944
957
  self.class.executable,
945
958
  "--webdriver=#{port}",
946
959
  "--proxy=http://#{@proxy.address}/",
947
- '--ignore-ssl-errors=true'
960
+ '--ignore-ssl-errors=true',
961
+ "--debug=#{!!debug?}"
948
962
  )
949
963
  @process.detach = true
950
964
 
@@ -1065,7 +1079,12 @@ class Browser
1065
1079
  set_cookies[cookie.name] = cookie
1066
1080
  end
1067
1081
  cookies.each do |name, value|
1068
- set_cookies[name] = Cookie.new( url: url, inputs: { name => value } )
1082
+ if set_cookies[name]
1083
+ set_cookies[name] = set_cookies[name].dup
1084
+ set_cookies[name].update( name => value )
1085
+ else
1086
+ set_cookies[name] = Cookie.new( url: url, inputs: { name => value } )
1087
+ end
1069
1088
  end
1070
1089
 
1071
1090
  url = "#{url}/set-cookies-#{request_token}"
@@ -91,7 +91,7 @@ class ElementLocator
91
91
  # @return [Hash]
92
92
  # Data representing this instance that are suitable the RPC transmission.
93
93
  def to_rpc_data
94
- to_h.stringify_keys
94
+ to_h.my_stringify_keys
95
95
  end
96
96
 
97
97
  # @param [Hash] data {#to_rpc_data}
@@ -74,9 +74,9 @@ class TaintTracer < Proxy
74
74
  return [] if !data
75
75
 
76
76
  data.map do |entry|
77
- Sink::DataFlow.new( (entry['data'] || {}).symbolize_keys( false ).merge(
77
+ Sink::DataFlow.new( (entry['data'] || {}).my_symbolize_keys( false ).merge(
78
78
  trace: [entry['trace']].flatten.compact.
79
- map { |h| Frame.new h.symbolize_keys( false ) }
79
+ map { |h| Frame.new h.my_symbolize_keys( false ) }
80
80
  )
81
81
  )
82
82
  end
@@ -88,7 +88,7 @@ class TaintTracer < Proxy
88
88
  data.map do |entry|
89
89
  Sink::ExecutionFlow.new( entry.merge(
90
90
  trace: [entry['trace']].flatten.compact.
91
- map { |h| Frame.new h.symbolize_keys( false ) }
91
+ map { |h| Frame.new h.my_symbolize_keys( false ) }
92
92
  )
93
93
  )
94
94
  end
@@ -35,7 +35,7 @@ class Frame
35
35
  @function = CalledFunction.new( options.delete(:function) )
36
36
  end
37
37
 
38
- options.symbolize_keys(false).each do |k, v|
38
+ options.my_symbolize_keys(false).each do |k, v|
39
39
  send( "#{k}=", v )
40
40
  end
41
41
  end
@@ -28,7 +28,7 @@ class CalledFunction
28
28
  attr_accessor :arguments
29
29
 
30
30
  def initialize( options = {} )
31
- options.symbolize_keys(false).each do |k, v|
31
+ options.my_symbolize_keys(false).each do |k, v|
32
32
  send( "#{k}=", v )
33
33
  end
34
34
  end
@@ -20,7 +20,7 @@ class Base
20
20
  attr_accessor :trace
21
21
 
22
22
  def initialize( options = {} )
23
- options.symbolize_keys(false).each do |k, v|
23
+ options.my_symbolize_keys(false).each do |k, v|
24
24
  send( "#{k}=", v )
25
25
  end
26
26
 
@@ -271,6 +271,8 @@ module Auditor
271
271
 
272
272
  if active
273
273
  msg << " input '#{vector.affected_input_name}'"
274
+ elsif vector.respond_to?( :inputs )
275
+ msg << " with inputs '#{vector.inputs.keys.join(', ')}'"
274
276
  end
275
277
 
276
278
  print_ok "#{msg} with action #{vector.action}"
@@ -172,7 +172,7 @@ class Manager < Hash
172
172
  return {} if !info.include?( :options ) || info[:options].empty?
173
173
 
174
174
  user_opts ||= {}
175
- user_opts = user_opts.symbolize_keys(false)
175
+ user_opts = user_opts.my_symbolize_keys(false)
176
176
 
177
177
  options = {}
178
178
  errors = {}
@@ -209,7 +209,7 @@ class Manager < Hash
209
209
  format_error_string( component_name, errors )
210
210
  end
211
211
 
212
- options.symbolize_keys( false )
212
+ options.my_symbolize_keys( false )
213
213
  end
214
214
 
215
215
  # It parses the component array making sure that its structure is valid
@@ -117,7 +117,7 @@ class Arachni::Component::Options::Base
117
117
  # @return [Hash]
118
118
  # Data representing this instance that are suitable the RPC transmission.
119
119
  def to_rpc_data
120
- to_h.merge( class: self.class.to_s ).stringify_keys
120
+ to_h.merge( class: self.class.to_s ).my_stringify_keys
121
121
  end
122
122
 
123
123
  # @param [Hash] data {#to_rpc_data}
@@ -127,7 +127,7 @@ class Arachni::Component::Options::Base
127
127
  data.delete('class')
128
128
  name = data.delete('name')
129
129
 
130
- new name, data.symbolize_keys(false)
130
+ new name, data.my_symbolize_keys(false)
131
131
  end
132
132
 
133
133
  def ==( option )