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
@@ -69,7 +69,7 @@ module Master
69
69
  return false
70
70
  end
71
71
 
72
- instance_info = instance_info.symbolize_keys
72
+ instance_info = instance_info.my_symbolize_keys
73
73
 
74
74
  fail "Instance info does not contain a 'url' key." if !instance_info[:url]
75
75
  fail "Instance info does not contain a 'token' key." if !instance_info[:token]
@@ -86,7 +86,7 @@ module MultiInstance
86
86
  # @return [Hash]
87
87
  # Progress data.
88
88
  def progress( opts = {}, &block )
89
- opts = opts.symbolize_keys
89
+ opts = opts.my_symbolize_keys
90
90
 
91
91
  include_statistics = opts[:statistics].nil? ? true : opts[:statistics]
92
92
  include_slaves = opts[:slaves].nil? ? true : opts[:slaves]
@@ -153,7 +153,7 @@ module MultiInstance
153
153
  slave_data.compact!
154
154
 
155
155
  slave_data.each do |slave|
156
- slave = slave.symbolize_keys
156
+ slave = slave.my_symbolize_keys
157
157
 
158
158
  if include_errors
159
159
  data[:errors] |= slave[:errors]
@@ -532,7 +532,7 @@ class Instance
532
532
  end
533
533
 
534
534
  # Normalize this sucker to have symbols as keys.
535
- opts = opts.symbolize_keys( false )
535
+ opts = opts.my_symbolize_keys( false )
536
536
 
537
537
  slaves = opts.delete(:slaves) || []
538
538
  spawn_count = opts[:spawns]
@@ -750,7 +750,7 @@ class Instance
750
750
  parsed[q.to_sym] = nil
751
751
 
752
752
  when Hash
753
- parsed.merge!( q.symbolize_keys )
753
+ parsed.merge!( q.my_symbolize_keys )
754
754
  end
755
755
  end
756
756
 
@@ -758,7 +758,7 @@ class Instance
758
758
  parsed[w.to_sym] = nil
759
759
 
760
760
  when Hash
761
- parsed.merge!( w.symbolize_keys )
761
+ parsed.merge!( w.my_symbolize_keys )
762
762
  end
763
763
  end
764
764
 
@@ -806,6 +806,14 @@ class Instance
806
806
 
807
807
  # Starts RPC service.
808
808
  def run
809
+ Reactor.global.on_error do |_, e|
810
+ print_error "Arachni::Reactor: #{e}"
811
+
812
+ e.backtrace.each do |l|
813
+ print_error "Arachni::Reactor: #{l}"
814
+ end
815
+ end
816
+
809
817
  print_status 'Starting the server...'
810
818
  @server.start
811
819
  end
@@ -19,10 +19,11 @@ class Hash
19
19
  #
20
20
  # @return [Hash]
21
21
  # Hash with +self+'s keys recursively converted to strings.
22
- def stringify_keys( recursively = true )
22
+ def my_stringify_keys( recursively = true )
23
23
  stringified = {}
24
24
  each do |k, v|
25
- stringified[k.to_s] = (recursively && v.is_a?( Hash ) ? v.stringify_keys : v)
25
+ stringified[k.to_s] = (recursively && v.is_a?( Hash ) ?
26
+ v.my_stringify_keys : v)
26
27
  end
27
28
  stringified
28
29
  end
@@ -34,20 +35,20 @@ class Hash
34
35
  #
35
36
  # @return [Hash]
36
37
  # Hash with +self+'s keys recursively converted to symbols.
37
- def symbolize_keys( recursively = true )
38
+ def my_symbolize_keys( recursively = true )
38
39
  symbolize = {}
39
40
  each do |k, v|
40
41
  k = k.respond_to?(:to_sym) ? k.to_sym : k
41
42
 
42
43
  symbolize[k] = (recursively && v.is_a?( Hash ) ?
43
- v.symbolize_keys : v)
44
+ v.my_symbolize_keys : v)
44
45
  end
45
46
  symbolize
46
47
  end
47
48
 
48
49
  # @return [Hash]
49
50
  # Hash with +self+'s keys and values recursively converted to strings.
50
- def stringify
51
+ def my_stringify
51
52
  apply_recursively(:to_s)
52
53
  end
53
54
 
@@ -79,7 +80,7 @@ class Hash
79
80
  # @return [Hash]
80
81
  # Self with the keys and values converted to lower-case strings.
81
82
  def downcase
82
- stringify_keys.inject({}) do |h, (k, v)|
83
+ my_stringify_keys.inject({}) do |h, (k, v)|
83
84
  k = k.downcase if k.is_a?( String )
84
85
  v = v.downcase if v.is_a?( String )
85
86
  h[k] = v
@@ -100,6 +100,7 @@ class Framework
100
100
  suspending_plugins: 'Suspending plugins.',
101
101
  saving_snapshot: 'Saving snapshot at: %s',
102
102
  snapshot_location: 'Snapshot location: %s',
103
+ browser_cluster_startup: 'Initialising the browser cluster.',
103
104
  browser_cluster_shutdown: 'Shutting down the browser cluster.',
104
105
  clearing_queues: 'Clearing the audit queues.',
105
106
  waiting_for_plugins: 'Waiting for the plugins to finish.',
@@ -1 +1 @@
1
- 1.0
1
+ 1.0.1
@@ -1797,6 +1797,20 @@ describe Arachni::Browser do
1797
1797
 
1798
1798
  transition.options[:cookies].should == cookie
1799
1799
  end
1800
+
1801
+ context 'when auditing existing cookies' do
1802
+ it 'preserves the HttpOnly attribute' do
1803
+ @browser.goto( @url )
1804
+ @browser.cookies.size.should == 1
1805
+
1806
+ cookies = { @browser.cookies.first.name => 'updated' }
1807
+ @browser.goto( @url, cookies: cookies )
1808
+
1809
+ @browser.cookies.first.value == 'updated'
1810
+ @browser.cookies.first.should be_http_only
1811
+ end
1812
+ end
1813
+
1800
1814
  end
1801
1815
 
1802
1816
  describe :take_snapshot do
@@ -2269,6 +2283,17 @@ describe Arachni::Browser do
2269
2283
  cookie.name.should == 'This name should be updated; and properly escaped'
2270
2284
  cookie.value.should == 'This value should be updated; and properly escaped'
2271
2285
  end
2286
+
2287
+ it 'preserves the HttpOnly attribute' do
2288
+ @browser.load @url
2289
+ @browser.cookies.first.should be_http_only
2290
+ end
2291
+
2292
+ context 'when no page is available' do
2293
+ it 'returns an empty Array' do
2294
+ @browser.cookies.should be_empty
2295
+ end
2296
+ end
2272
2297
  end
2273
2298
 
2274
2299
  describe '#snapshot_id' do
@@ -301,7 +301,7 @@ describe Arachni::Component::Manager do
301
301
  'opt_opt' => 'opt_opt value',
302
302
  'default_opt' => 'value2'
303
303
  }
304
- @components.prepare_options( c, @components[c], opts ).should == opts.symbolize_keys
304
+ @components.prepare_options( c, @components[c], opts ).should == opts.my_symbolize_keys
305
305
  end
306
306
 
307
307
  context 'with missing options' do
@@ -199,7 +199,7 @@ describe Arachni::Element::Cookie do
199
199
 
200
200
  describe '#encode' do
201
201
  it 'encodes the string in a way that makes is suitable to be included in a cookie header' do
202
- described_class.encode( 'some stuff ;%=' ).should == 'some+stuff+%3B%25='
202
+ described_class.encode( 'some stuff \'";%=' ).should == 'some+stuff+%27%22%3B%25='
203
203
  end
204
204
  end
205
205
 
@@ -214,7 +214,7 @@ describe Arachni::Element::Cookie do
214
214
  )
215
215
 
216
216
  c.to_set_cookie.should ==
217
- 'blah%3Dha%25=some+stuff+%3B; Path=/; Domain=.127.0.0.2; Secure; HttpOnly'
217
+ 'blah%3Dha%25=some+stuff+%3B; Path=/; Domain=127.0.0.2; Secure; HttpOnly'
218
218
  described_class.from_set_cookie( url, c.to_set_cookie ).first.should == c
219
219
 
220
220
  c = described_class.new(
@@ -226,7 +226,7 @@ describe Arachni::Element::Cookie do
226
226
 
227
227
  described_class.from_set_cookie( url, c.to_set_cookie ).first.should == c
228
228
  c.to_set_cookie.should ==
229
- 'blah%3Dha%25=some+stuff+%3B; Path=/stuff; Domain=.127.0.0.2'
229
+ 'blah%3Dha%25=some+stuff+%3B; Path=/stuff; Domain=127.0.0.2'
230
230
  end
231
231
  end
232
232
 
@@ -450,12 +450,12 @@ describe Arachni::HTTP::Request do
450
450
  described_class.new(
451
451
  url: url,
452
452
  proxy: 'http://stuff/',
453
- proxy_type: 'http'
453
+ proxy_type: :http
454
454
  )
455
455
  end
456
456
 
457
457
  it 'forwards it' do
458
- subject.options[:proxytype].should == 'http'
458
+ subject.options[:proxytype].should == :http
459
459
  end
460
460
  end
461
461
  end
@@ -486,7 +486,7 @@ describe Arachni::HTTP::Request do
486
486
  context "and #{Arachni::OptionGroups::HTTP}#proxy_type" do
487
487
  it 'forwards it' do
488
488
  Arachni::Options.http.proxy_type = 'http'
489
- subject.options[:proxytype].should == 'http'
489
+ subject.options[:proxytype].should == :http
490
490
  end
491
491
  end
492
492
  end
@@ -214,14 +214,14 @@ describe Arachni::OptionGroups::Scope do
214
214
  values = { /redundant_path_patterns/ => 1 }
215
215
  subject.redundant_path_patterns = values
216
216
 
217
- data['redundant_path_patterns'].should == values.stringify
217
+ data['redundant_path_patterns'].should == values.my_stringify
218
218
  end
219
219
 
220
220
  it "converts 'url_rewrites' to strings" do
221
221
  values = { /url_rewrites/ => 'test' }
222
222
  subject.url_rewrites = values
223
223
 
224
- data['url_rewrites'].should == values.stringify
224
+ data['url_rewrites'].should == values.my_stringify
225
225
  end
226
226
 
227
227
  %w(exclude_path_patterns exclude_content_patterns include_path_patterns).each do |k|
@@ -517,6 +517,13 @@ describe Arachni::Parser do
517
517
  subject.link_vars.should == { 'id' => '13' }
518
518
  end
519
519
  end
520
+
521
+ context 'when the URL cannot be parsed' do
522
+ it 'returns an empty array' do
523
+ subject.url = nil
524
+ subject.link_vars.should == {}
525
+ end
526
+ end
520
527
  end
521
528
 
522
529
  end
@@ -23,7 +23,7 @@ describe Arachni::Reporter::Manager do
23
23
  options = { 'outfile' => 'stuff' }
24
24
  reporter = @reporters.run( :foo, report, options )
25
25
 
26
- reporter.options.should == options.symbolize_keys(false)
26
+ reporter.options.should == options.my_symbolize_keys(false)
27
27
  end
28
28
  end
29
29
  end
@@ -97,6 +97,8 @@ describe Arachni::RPC::Server::Dispatcher::Node do
97
97
  c = @get_node.call
98
98
 
99
99
  n.add_neighbour( c.url )
100
+ sleep 1
101
+
100
102
  c.neighbours.should == [n.url]
101
103
  n.neighbours.should == [c.url]
102
104
 
@@ -226,7 +226,7 @@ describe 'Arachni::RPC::Server::Framework' do
226
226
  data.keys.sort.should == @progress_keys
227
227
 
228
228
  data[:statistics].keys.should == instance.framework.statistics.keys
229
- data[:messages].should be_any
229
+ data[:messages].should be_empty
230
230
  data[:status].should be_true
231
231
  data[:busy].nil?.should be_false
232
232
  data[:issues].should be_any
@@ -23,14 +23,14 @@ describe Hash do
23
23
  }
24
24
  end
25
25
 
26
- describe '#stringify_keys' do
26
+ describe '#my_stringify_keys' do
27
27
  it 'recursively converts keys to strings' do
28
- with_symbols.stringify_keys.should == with_strings
28
+ with_symbols.my_stringify_keys.should == with_strings
29
29
  end
30
30
 
31
31
  context 'when the recursive is set to false' do
32
32
  it 'only converts the keys at depth 1' do
33
- with_symbols.stringify_keys( false ).should == {
33
+ with_symbols.my_stringify_keys( false ).should == {
34
34
  'stuff' => 'blah',
35
35
  'more' => {
36
36
  stuff: {
@@ -42,14 +42,14 @@ describe Hash do
42
42
  end
43
43
  end
44
44
 
45
- describe '#symbolize_keys' do
45
+ describe '#my_symbolize_keys' do
46
46
  it 'recursively converts keys to symbols' do
47
- with_strings.symbolize_keys.should == with_symbols
47
+ with_strings.my_symbolize_keys.should == with_symbols
48
48
  end
49
49
 
50
50
  context 'when the recursive is set to false' do
51
51
  it 'only converts the keys at depth 1' do
52
- with_strings.symbolize_keys( false ).should == {
52
+ with_strings.my_symbolize_keys( false ).should == {
53
53
  stuff: 'blah',
54
54
  more: {
55
55
  'stuff' => {
@@ -75,14 +75,14 @@ describe Hash do
75
75
  end
76
76
  end
77
77
 
78
- describe '#stringify' do
78
+ describe '#my_stringify' do
79
79
  it 'returns a Hash with keys and values recursively converted to strings' do
80
80
  {
81
81
  test: 'blah',
82
82
  another_hash: {
83
83
  stuff: 'test'
84
84
  }
85
- }.stringify.should == {
85
+ }.my_stringify.should == {
86
86
  'test' => 'blah',
87
87
  'another_hash' => {
88
88
  'stuff' => 'test'
@@ -4,7 +4,7 @@ require 'sinatra/contrib'
4
4
  get '/' do
5
5
  response.set_cookie( 'cookie', {
6
6
  value: 'value',
7
- domain: nil
7
+ domain: '.localhost'
8
8
  })
9
9
  response.set_cookie( 'cookie2', {
10
10
  value: 'value2',
@@ -15,7 +15,7 @@ shared_examples_for 'inputtable' do |options = {}|
15
15
  end
16
16
  end
17
17
 
18
- let(:sym_key_inputs) { inputs.symbolize_keys }
18
+ let(:sym_key_inputs) { inputs.my_symbolize_keys }
19
19
 
20
20
  let(:keys) do
21
21
  subject.inputs.keys
@@ -140,7 +140,7 @@ shared_examples_for 'inputtable' do |options = {}|
140
140
  context 'when it has the given inputs (names and values)' do
141
141
  it 'returns true' do
142
142
  subject.has_inputs?( subject.inputs ).should be_true
143
- subject.has_inputs?( subject.inputs.symbolize_keys ).should be_true
143
+ subject.has_inputs?( subject.inputs.my_symbolize_keys ).should be_true
144
144
  end
145
145
  end
146
146
  context 'when it does not have the given inputs' do
@@ -29,9 +29,12 @@ module Utilities
29
29
  issue_cnt = issues.count
30
30
  issues.each.with_index do |issue, i|
31
31
  meth = input = ''
32
+
32
33
  if issue.active?
33
34
  input = " input `#{issue.vector.affected_input_name}`"
34
35
  meth = " using #{issue.vector.method.to_s.upcase}"
36
+ elsif issue.vector.respond_to?( :inputs )
37
+ input = " with inputs `#{issue.vector.inputs.keys.join(', ')}`"
35
38
  end
36
39
 
37
40
  cnt = "#{i + 1} |".rjust( issue_cnt.to_s.size + 2 )
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arachni
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.0'
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tasos Laskos
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-30 00:00:00.000000000 Z
11
+ date: 2014-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - '='
88
88
  - !ruby/object:Gem::Version
89
- version: 0.2.0
89
+ version: 0.2.1.1
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - '='
95
95
  - !ruby/object:Gem::Version
96
- version: 0.2.0
96
+ version: 0.2.1.1
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: typhoeus
99
99
  requirement: !ruby/object:Gem::Requirement