arachni 1.0.5 → 1.0.6

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/README.md +9 -2
  4. data/components/checks/active/code_injection.rb +5 -5
  5. data/components/checks/active/code_injection_timing.rb +3 -3
  6. data/components/checks/active/no_sql_injection_differential.rb +3 -2
  7. data/components/checks/active/os_cmd_injection.rb +11 -5
  8. data/components/checks/active/os_cmd_injection_timing.rb +11 -4
  9. data/components/checks/active/path_traversal.rb +2 -2
  10. data/components/checks/active/sql_injection.rb +1 -1
  11. data/components/checks/active/sql_injection/patterns/mssql +1 -0
  12. data/components/checks/active/sql_injection_differential.rb +3 -2
  13. data/components/checks/active/unvalidated_redirect.rb +3 -3
  14. data/components/checks/passive/common_directories/directories.txt +2 -0
  15. data/components/checks/passive/common_files/filenames.txt +1 -0
  16. data/lib/arachni/browser.rb +17 -1
  17. data/lib/arachni/check/auditor.rb +5 -2
  18. data/lib/arachni/check/base.rb +30 -5
  19. data/lib/arachni/element/capabilities/analyzable/differential.rb +2 -5
  20. data/lib/arachni/element/capabilities/auditable.rb +3 -1
  21. data/lib/arachni/element/capabilities/with_dom.rb +1 -0
  22. data/lib/arachni/element/capabilities/with_node.rb +1 -1
  23. data/lib/arachni/element/cookie.rb +2 -2
  24. data/lib/arachni/element/form.rb +1 -1
  25. data/lib/arachni/element/header.rb +2 -2
  26. data/lib/arachni/element/link_template.rb +1 -1
  27. data/lib/arachni/framework.rb +21 -1144
  28. data/lib/arachni/framework/parts/audit.rb +282 -0
  29. data/lib/arachni/framework/parts/browser.rb +132 -0
  30. data/lib/arachni/framework/parts/check.rb +86 -0
  31. data/lib/arachni/framework/parts/data.rb +158 -0
  32. data/lib/arachni/framework/parts/platform.rb +34 -0
  33. data/lib/arachni/framework/parts/plugin.rb +61 -0
  34. data/lib/arachni/framework/parts/report.rb +128 -0
  35. data/lib/arachni/framework/parts/scope.rb +40 -0
  36. data/lib/arachni/framework/parts/state.rb +457 -0
  37. data/lib/arachni/http/client.rb +33 -30
  38. data/lib/arachni/http/request.rb +6 -2
  39. data/lib/arachni/issue.rb +55 -1
  40. data/lib/arachni/platform/manager.rb +25 -21
  41. data/lib/arachni/state/framework.rb +7 -1
  42. data/lib/arachni/utilities.rb +10 -0
  43. data/lib/version +1 -1
  44. data/spec/arachni/browser_spec.rb +13 -0
  45. data/spec/arachni/check/auditor_spec.rb +1 -0
  46. data/spec/arachni/check/base_spec.rb +80 -0
  47. data/spec/arachni/element/cookie_spec.rb +2 -2
  48. data/spec/arachni/framework/parts/audit_spec.rb +391 -0
  49. data/spec/arachni/framework/parts/browser_spec.rb +26 -0
  50. data/spec/arachni/framework/parts/check_spec.rb +24 -0
  51. data/spec/arachni/framework/parts/data_spec.rb +187 -0
  52. data/spec/arachni/framework/parts/platform_spec.rb +62 -0
  53. data/spec/arachni/framework/parts/plugin_spec.rb +41 -0
  54. data/spec/arachni/framework/parts/report_spec.rb +66 -0
  55. data/spec/arachni/framework/parts/scope_spec.rb +86 -0
  56. data/spec/arachni/framework/parts/state_spec.rb +528 -0
  57. data/spec/arachni/framework_spec.rb +17 -1344
  58. data/spec/arachni/http/client_spec.rb +12 -7
  59. data/spec/arachni/issue_spec.rb +35 -0
  60. data/spec/arachni/platform/manager_spec.rb +2 -3
  61. data/spec/arachni/state/framework_spec.rb +15 -0
  62. data/spec/components/checks/active/code_injection_timing_spec.rb +5 -5
  63. data/spec/components/checks/active/no_sql_injection_differential_spec.rb +4 -0
  64. data/spec/components/checks/active/os_cmd_injection_spec.rb +20 -7
  65. data/spec/components/checks/active/os_cmd_injection_timing_spec.rb +5 -5
  66. data/spec/components/checks/active/sql_injection_differential_spec.rb +4 -0
  67. data/spec/components/checks/active/sql_injection_spec.rb +2 -3
  68. data/spec/support/servers/arachni/browser.rb +31 -0
  69. data/spec/support/servers/checks/active/code_injection.rb +1 -1
  70. data/spec/support/servers/checks/active/no_sql_injection_differential.rb +36 -34
  71. data/spec/support/servers/checks/active/os_cmd_injection.rb +6 -12
  72. data/spec/support/servers/checks/active/os_cmd_injection_timing.rb +9 -4
  73. data/spec/support/servers/checks/active/sql_injection.rb +1 -1
  74. data/spec/support/servers/checks/active/sql_injection_differential.rb +37 -34
  75. data/spec/support/shared/element/capabilities/with_node.rb +25 -0
  76. data/spec/support/shared/framework.rb +26 -0
  77. data/ui/cli/output.rb +2 -0
  78. data/ui/cli/rpc/server/dispatcher/option_parser.rb +1 -1
  79. metadata +32 -4
  80. data/components/checks/active/sql_injection/patterns/coldfusion +0 -1
@@ -0,0 +1,187 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arachni::Framework::Parts::Data do
4
+ include_examples 'framework'
5
+
6
+ describe '#data' do
7
+ it "returns #{Arachni::Data::Framework}" do
8
+ subject.data.should be_kind_of Arachni::Data::Framework
9
+ end
10
+ end
11
+
12
+ describe '#sitemap' do
13
+ it 'returns a hash with covered URLs and HTTP status codes' do
14
+ Arachni::Framework.new do |f|
15
+ f.options.url = "#{@url}/"
16
+ f.options.audit.elements :links, :forms, :cookies
17
+ f.checks.load :taint
18
+
19
+ f.run
20
+ f.sitemap.should == { "#{@url}/" => 200 }
21
+ end
22
+ end
23
+ end
24
+
25
+ describe '#push_to_page_queue' do
26
+ let(:page) { Arachni::Page.from_url( @url + '/train/true' ) }
27
+
28
+ it 'pushes it to the page audit queue and returns true' do
29
+ subject.options.audit.elements :links, :forms, :cookies
30
+ subject.checks.load :taint
31
+
32
+ subject.page_queue_total_size.should == 0
33
+ subject.push_to_page_queue( page ).should be_true
34
+ subject.run
35
+
36
+ subject.report.issues.size.should == 1
37
+ subject.page_queue_total_size.should > 0
38
+ end
39
+
40
+ it 'updates the #sitemap with the DOM URL' do
41
+ subject.options.audit.elements :links, :forms, :cookies
42
+ subject.checks.load :taint
43
+
44
+ subject.sitemap.should be_empty
45
+
46
+ page = Arachni::Page.from_url( @url + '/link' )
47
+ page.dom.url = @url + '/link/#/stuff'
48
+
49
+ subject.push_to_page_queue page
50
+ subject.sitemap.should include @url + '/link/#/stuff'
51
+ end
52
+
53
+ it "passes it to #{Arachni::ElementFilter}#update_from_page_cache" do
54
+ page = Arachni::Page.from_url( @url + '/link' )
55
+
56
+ Arachni::ElementFilter.should receive(:update_from_page_cache).with(page)
57
+
58
+ subject.push_to_page_queue page
59
+ end
60
+
61
+ context 'when the page has already been seen' do
62
+ it 'ignores it' do
63
+ page = Arachni::Page.from_url( @url + '/stuff' )
64
+
65
+ subject.page_queue_total_size.should == 0
66
+ subject.push_to_page_queue( page )
67
+ subject.push_to_page_queue( page )
68
+ subject.push_to_page_queue( page )
69
+ subject.page_queue_total_size.should == 1
70
+ end
71
+
72
+ it 'returns false' do
73
+ page = Arachni::Page.from_url( @url + '/stuff' )
74
+
75
+ subject.page_queue_total_size.should == 0
76
+ subject.push_to_page_queue( page ).should be_true
77
+ subject.push_to_page_queue( page ).should be_false
78
+ subject.push_to_page_queue( page ).should be_false
79
+ subject.page_queue_total_size.should == 1
80
+ end
81
+ end
82
+
83
+ context 'when #accepts_more_pages?' do
84
+ context false do
85
+ it 'returns false' do
86
+ subject.stub(:accepts_more_pages?) { false }
87
+ subject.push_to_page_queue( page ).should be_false
88
+ end
89
+ end
90
+
91
+ context true do
92
+ it 'returns true' do
93
+ subject.stub(:accepts_more_pages?) { true }
94
+ subject.push_to_page_queue( page ).should be_true
95
+ end
96
+ end
97
+ end
98
+
99
+ context "when #{Arachni::Page::Scope}#out? is true" do
100
+ it 'returns false' do
101
+ Arachni::Page::Scope.any_instance.stub(:out?) { true }
102
+ subject.push_to_page_queue( page ).should be_false
103
+ end
104
+ end
105
+
106
+ context "when #{Arachni::URI::Scope}#redundant? is true" do
107
+ it 'returns false' do
108
+ Arachni::Page::Scope.any_instance.stub(:redundant?) { true }
109
+ subject.push_to_page_queue( page ).should be_false
110
+ end
111
+ end
112
+
113
+ context "when #{Arachni::Page::Scope}#auto_redundant? is true" do
114
+ it 'returns false' do
115
+ Arachni::Page::Scope.any_instance.stub(:auto_redundant?) { true }
116
+ subject.push_to_page_queue( page ).should be_false
117
+ end
118
+ end
119
+ end
120
+
121
+ describe '#push_to_url_queue' do
122
+ it 'pushes a URL to the URL audit queue' do
123
+ subject.options.audit.elements :links, :forms, :cookies
124
+ subject.checks.load :taint
125
+
126
+ subject.url_queue_total_size.should == 0
127
+ subject.push_to_url_queue( @url + '/link' ).should be_true
128
+ subject.run
129
+
130
+ subject.report.issues.size.should == 1
131
+ subject.url_queue_total_size.should == 3
132
+ end
133
+
134
+ context 'when the URL has already been seen' do
135
+ it 'returns false' do
136
+ subject.push_to_url_queue( @url + '/link' ).should be_true
137
+ subject.push_to_url_queue( @url + '/link' ).should be_false
138
+ end
139
+
140
+ it 'ignores it' do
141
+ subject.url_queue_total_size.should == 0
142
+ subject.push_to_url_queue( @url + '/link' )
143
+ subject.push_to_url_queue( @url + '/link' )
144
+ subject.push_to_url_queue( @url + '/link' )
145
+ subject.url_queue_total_size.should == 1
146
+ end
147
+ end
148
+
149
+ context 'when #accepts_more_pages?' do
150
+ context false do
151
+ it 'returns false' do
152
+ subject.stub(:accepts_more_pages?) { false }
153
+ subject.push_to_url_queue( @url ).should be_false
154
+ end
155
+ end
156
+
157
+ context true do
158
+ it 'returns true' do
159
+ subject.stub(:accepts_more_pages?) { true }
160
+ subject.push_to_url_queue( @url ).should be_true
161
+ end
162
+ end
163
+ end
164
+
165
+ context "when #{Arachni::URI::Scope}#out? is true" do
166
+ it 'returns false' do
167
+ Arachni::URI::Scope.any_instance.stub(:out?) { true }
168
+ subject.push_to_url_queue( @url ).should be_false
169
+ end
170
+ end
171
+
172
+ context "when #{Arachni::URI::Scope}#redundant? is true" do
173
+ it 'returns false' do
174
+ Arachni::URI::Scope.any_instance.stub(:redundant?) { true }
175
+ subject.push_to_url_queue( @url ).should be_false
176
+ end
177
+ end
178
+
179
+ context "when #{Arachni::URI::Scope}#auto_redundant? is true" do
180
+ it 'returns false' do
181
+ Arachni::URI::Scope.any_instance.stub(:auto_redundant?) { true }
182
+ subject.push_to_url_queue( @url ).should be_false
183
+ end
184
+ end
185
+ end
186
+
187
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arachni::Framework::Parts::Platform do
4
+ include_examples 'framework'
5
+
6
+ describe '#list_platforms' do
7
+ it 'returns information about all valid platforms' do
8
+ subject.list_platforms.should == {
9
+ 'Operating systems' => {
10
+ unix: 'Generic Unix family',
11
+ linux: 'Linux',
12
+ bsd: 'Generic BSD family',
13
+ aix: 'IBM AIX',
14
+ solaris: 'Solaris',
15
+ windows: 'MS Windows'
16
+ },
17
+ 'Databases' => {
18
+ sql: 'Generic SQL family',
19
+ access: 'MS Access',
20
+ db2: 'DB2',
21
+ emc: 'EMC',
22
+ firebird: 'Firebird',
23
+ frontbase: 'Frontbase',
24
+ hsqldb: 'HSQLDB',
25
+ informix: 'Informix',
26
+ ingres: 'IngresDB',
27
+ interbase: 'InterBase',
28
+ maxdb: 'SaP Max DB',
29
+ mssql: 'MSSQL',
30
+ mysql: 'MySQL',
31
+ oracle: 'Oracle',
32
+ pgsql: 'Postgresql',
33
+ sqlite: 'SQLite',
34
+ sybase: 'Sybase',
35
+ nosql: 'Generic NoSQL family',
36
+ mongodb: 'MongoDB'
37
+ },
38
+ 'Web servers' => {
39
+ apache: 'Apache',
40
+ iis: 'IIS',
41
+ jetty: 'Jetty',
42
+ nginx: 'Nginx',
43
+ tomcat: 'TomCat'
44
+ },
45
+ 'Programming languages' => {
46
+ asp: 'ASP',
47
+ aspx: 'ASP.NET',
48
+ jsp: 'JSP',
49
+ perl: 'Perl',
50
+ php: 'PHP',
51
+ python: 'Python',
52
+ ruby: 'Ruby'
53
+ },
54
+ 'Frameworks' => {
55
+ rack: 'Rack'
56
+ }
57
+ }
58
+
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arachni::Framework::Parts::Plugin do
4
+ include_examples 'framework'
5
+
6
+ describe '#plugins' do
7
+ it 'provides access to the plugin manager' do
8
+ subject.plugins.is_a?( Arachni::Plugin::Manager ).should be_true
9
+ subject.plugins.available.sort.should ==
10
+ %w(wait bad with_options distributable loop default suspendable).sort
11
+ end
12
+ end
13
+
14
+ describe '#list_plugins' do
15
+ it 'returns info on all plugins' do
16
+ subject.list_plugins.size.should == subject.plugins.available.size
17
+
18
+ info = subject.list_plugins.find { |p| p[:options].any? }
19
+ plugin = subject.plugins[info[:shortname]]
20
+
21
+ plugin.info.each do |k, v|
22
+ if k == :author
23
+ info[k].should == [v].flatten
24
+ next
25
+ end
26
+
27
+ info[k].should == v
28
+ end
29
+
30
+ info[:shortname].should == plugin.shortname
31
+ end
32
+
33
+ context 'when a pattern is given' do
34
+ it 'uses it to filter out plugins that do not match it' do
35
+ subject.list_plugins( 'bad|foo' ).size == 2
36
+ subject.list_plugins( 'boo' ).size == 0
37
+ end
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arachni::Framework::Parts::Report do
4
+ include_examples 'framework'
5
+
6
+ describe '#reporters' do
7
+ it 'provides access to the reporter manager' do
8
+ subject.reporters.is_a?( Arachni::Reporter::Manager ).should be_true
9
+ subject.reporters.available.sort.should == %w(afr foo).sort
10
+ end
11
+ end
12
+
13
+ describe '#list_reporters' do
14
+ it 'returns info on all reporters' do
15
+ subject.list_reporters.size.should == subject.reporters.available.size
16
+
17
+ info = subject.list_reporters.find { |p| p[:options].any? }
18
+ report = subject.reporters[info[:shortname]]
19
+
20
+ report.info.each do |k, v|
21
+ if k == :author
22
+ info[k].should == [v].flatten
23
+ next
24
+ end
25
+
26
+ info[k].should == v
27
+ end
28
+
29
+ info[:shortname].should == report.shortname
30
+ end
31
+
32
+ context 'when a pattern is given' do
33
+ it 'uses it to filter out reporters that do not match it' do
34
+ subject.list_reporters( 'foo' ).size == 1
35
+ subject.list_reporters( 'boo' ).size == 0
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#report_as' do
41
+ before( :each ) do
42
+ reset_options
43
+ @new_framework = Arachni::Framework.new
44
+ end
45
+
46
+ context 'when passed a valid reporter name' do
47
+ it 'returns the reporter as a string' do
48
+ json = @new_framework.report_as( :json )
49
+ JSON.load( json )['issues'].size.should == @new_framework.report.issues.size
50
+ end
51
+
52
+ context 'which does not support the \'outfile\' option' do
53
+ it 'raises Arachni::Component::Options::Error::Invalid' do
54
+ expect { @new_framework.report_as( :stdout ) }.to raise_error Arachni::Component::Options::Error::Invalid
55
+ end
56
+ end
57
+ end
58
+
59
+ context 'when passed an invalid reporter name' do
60
+ it 'raises Arachni::Component::Error::NotFound' do
61
+ expect { @new_framework.report_as( :blah ) }.to raise_error Arachni::Component::Error::NotFound
62
+ end
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arachni::Framework::Parts::Scope do
4
+ include_examples 'framework'
5
+
6
+ describe '#page_limit_reached?' do
7
+ context "when the #{Arachni::OptionGroups::Scope}#page_limit has" do
8
+ context 'been reached' do
9
+ it 'returns true' do
10
+ Arachni::Framework.new do |f|
11
+ f.options.url = web_server_url_for :framework_multi
12
+ f.options.audit.elements :links
13
+ f.options.scope.page_limit = 10
14
+
15
+ f.page_limit_reached?.should be_false
16
+ f.run
17
+ f.page_limit_reached?.should be_true
18
+
19
+ f.sitemap.size.should == 10
20
+ end
21
+ end
22
+ end
23
+
24
+ context 'not been reached' do
25
+ it 'returns false' do
26
+ Arachni::Framework.new do |f|
27
+ f.options.url = web_server_url_for :framework
28
+ f.options.audit.elements :links
29
+ f.options.scope.page_limit = 100
30
+
31
+ f.checks.load :taint
32
+
33
+ f.page_limit_reached?.should be_false
34
+ f.run
35
+ f.page_limit_reached?.should be_false
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'not been set' do
41
+ it 'returns false' do
42
+ Arachni::Framework.new do |f|
43
+ f.options.url = web_server_url_for :framework
44
+ f.options.audit.elements :links
45
+
46
+ f.checks.load :taint
47
+
48
+ f.page_limit_reached?.should be_false
49
+ f.run
50
+ f.page_limit_reached?.should be_false
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ describe '#accepts_more_pages?' do
58
+ context 'when #page_limit_reached? and #crawl?' do
59
+ it 'return true' do
60
+ subject.stub(:page_limit_reached?) { false }
61
+ subject.stub(:crawl?) { true }
62
+
63
+ subject.accepts_more_pages?.should be_true
64
+ end
65
+ end
66
+
67
+ context 'when #page_limit_reached?' do
68
+ context true do
69
+ it 'returns false' do
70
+ subject.stub(:page_limit_reached?) { true }
71
+ subject.accepts_more_pages?.should be_false
72
+ end
73
+ end
74
+ end
75
+
76
+ context 'when #crawl?' do
77
+ context false do
78
+ it 'returns false' do
79
+ subject.stub(:crawl?) { false }
80
+ subject.accepts_more_pages?.should be_false
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,528 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arachni::Framework::Parts::State do
4
+ include_examples 'framework'
5
+
6
+ describe '#scanning?' do
7
+ it "delegates to #{Arachni::State::Framework}#scanning?" do
8
+ subject.state.stub(:scanning?) { :stuff }
9
+ subject.scanning?.should == :stuff
10
+ end
11
+ end
12
+
13
+ describe '#done?' do
14
+ it "delegates to #{Arachni::State::Framework}#done?" do
15
+ subject.state.stub(:done?) { :stuff }
16
+ subject.done?.should == :stuff
17
+ end
18
+ end
19
+
20
+ describe '#paused?' do
21
+ it "delegates to #{Arachni::State::Framework}#paused?" do
22
+ subject.state.stub(:paused?) { :stuff }
23
+ subject.paused?.should == :stuff
24
+ end
25
+ end
26
+
27
+ describe '#state' do
28
+ it "returns #{Arachni::State::Framework}" do
29
+ subject.state.should be_kind_of Arachni::State::Framework
30
+ end
31
+ end
32
+
33
+ describe '#abort' do
34
+ it 'aborts the system' do
35
+ @options.paths.checks = fixtures_path + '/taint_check/'
36
+
37
+ Arachni::Framework.new do |f|
38
+ f.options.url = web_server_url_for :framework_multi
39
+ f.options.audit.elements :links
40
+
41
+ f.plugins.load :wait
42
+ f.checks.load :taint
43
+
44
+ t = Thread.new do
45
+ f.run
46
+ end
47
+
48
+ sleep 0.1 while Arachni::Data.issues.size < 2
49
+
50
+ f.abort
51
+ t.join
52
+
53
+ Arachni::Data.issues.size.should < 500
54
+ end
55
+ end
56
+
57
+ it 'sets #status to :aborted' do
58
+ Arachni::Framework.new do |f|
59
+ f.options.url = web_server_url_for :framework_multi
60
+ f.options.audit.elements :links
61
+ f.checks.load :taint
62
+
63
+ t = Thread.new do
64
+ f.run
65
+ end
66
+ sleep 0.1 while f.status != :scanning
67
+
68
+ f.abort
69
+ f.status.should == :aborted
70
+
71
+ t.join
72
+ f.status.should == :aborted
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#suspend' do
78
+ it 'suspends the system' do
79
+ @options.paths.checks = fixtures_path + '/taint_check/'
80
+
81
+ Arachni::Framework.new do |f|
82
+ f.options.url = web_server_url_for :framework_multi
83
+ f.options.audit.elements :links
84
+
85
+ f.plugins.load :wait
86
+ f.checks.load :taint
87
+
88
+ t = Thread.new do
89
+ f.run
90
+ end
91
+
92
+ sleep 0.1 while Arachni::Data.issues.size < 2
93
+
94
+ @snapshot = f.suspend
95
+ t.join
96
+
97
+ Arachni::Data.issues.size.should < 500
98
+ end
99
+
100
+ Arachni::Snapshot.load( @snapshot ).should be_true
101
+ end
102
+
103
+ it 'sets #status to :suspended' do
104
+ Arachni::Framework.new do |f|
105
+ f.options.url = web_server_url_for :framework_multi
106
+ f.options.audit.elements :links
107
+ f.checks.load :taint
108
+
109
+ t = Thread.new do
110
+ f.run
111
+ end
112
+ sleep 0.1 while f.status != :scanning
113
+
114
+ @snapshot = f.suspend
115
+ f.status.should == :suspended
116
+
117
+ t.join
118
+ f.status.should == :suspended
119
+ end
120
+ end
121
+
122
+ it 'suspends plugins' do
123
+ Arachni::Options.plugins['suspendable'] = {
124
+ 'my_option' => 'my value'
125
+ }
126
+
127
+ Arachni::Framework.new do |f|
128
+ f.options.url = web_server_url_for :framework_multi
129
+ f.options.audit.elements :links
130
+
131
+ f.checks.load :taint
132
+ f.plugins.load :suspendable
133
+
134
+ t = Thread.new do
135
+ f.run
136
+ end
137
+
138
+ sleep 0.1 while f.status != :scanning
139
+
140
+ f.suspend
141
+ t.join
142
+
143
+ Arachni::State.plugins.runtime[:suspendable][:data].should == 1
144
+ end
145
+ end
146
+
147
+ it 'waits for the BrowserCluster jobs to finish'
148
+
149
+ context "when #{Arachni::OptionGroups::Snapshot}#save_path" do
150
+ context 'is a directory' do
151
+ it 'stores the snapshot under it' do
152
+ @options.paths.checks = fixtures_path + '/taint_check/'
153
+ @options.snapshot.save_path = Dir.tmpdir
154
+
155
+ Arachni::Framework.new do |f|
156
+ f.options.url = web_server_url_for :framework_multi
157
+ f.options.audit.elements :links
158
+
159
+ f.plugins.load :wait
160
+ f.checks.load :taint
161
+
162
+ t = Thread.new do
163
+ f.run
164
+ end
165
+
166
+ sleep 0.1 while Arachni::Data.issues.size < 2
167
+
168
+ @snapshot = f.suspend
169
+ t.join
170
+
171
+ Arachni::Data.issues.size.should < 500
172
+ end
173
+
174
+ File.dirname( @snapshot ).should == Dir.tmpdir
175
+ Arachni::Snapshot.load( @snapshot ).should be_true
176
+ end
177
+ end
178
+
179
+ context 'is a file path' do
180
+ it 'stores the snapshot there' do
181
+ @options.paths.checks = fixtures_path + '/taint_check/'
182
+ @options.snapshot.save_path = "#{Dir.tmpdir}/snapshot"
183
+
184
+ Arachni::Framework.new do |f|
185
+ f.options.url = web_server_url_for :framework_multi
186
+ f.options.audit.elements :links
187
+
188
+ f.plugins.load :wait
189
+ f.checks.load :taint
190
+
191
+ t = Thread.new do
192
+ f.run
193
+ end
194
+
195
+ sleep 0.1 while Arachni::Data.issues.size < 2
196
+
197
+ @snapshot = f.suspend
198
+ t.join
199
+
200
+ Arachni::Data.issues.size.should < 500
201
+ end
202
+
203
+ @snapshot.should == "#{Dir.tmpdir}/snapshot"
204
+ Arachni::Snapshot.load( @snapshot ).should be_true
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ describe '#restore' do
211
+ it 'restores a suspended scan' do
212
+ @options.paths.checks = fixtures_path + '/taint_check/'
213
+
214
+ logged_issues = 0
215
+ Arachni::Framework.new do |f|
216
+ f.options.url = web_server_url_for :framework_multi
217
+ f.options.audit.elements :links
218
+
219
+ f.plugins.load :wait
220
+ f.checks.load :taint
221
+
222
+ Arachni::Data.issues.on_new do
223
+ logged_issues += 1
224
+ end
225
+
226
+ t = Thread.new do
227
+ f.run
228
+ end
229
+
230
+ sleep 0.1 while logged_issues < 200
231
+
232
+ @snapshot = f.suspend
233
+ t.join
234
+
235
+ logged_issues.should < 500
236
+ end
237
+
238
+ reset_options
239
+ @options.paths.checks = fixtures_path + '/taint_check/'
240
+
241
+ Arachni::Framework.new do |f|
242
+ f.restore @snapshot
243
+
244
+ Arachni::Data.issues.on_new do
245
+ logged_issues += 1
246
+ end
247
+ f.run
248
+
249
+ # logged_issues.should == 500
250
+ Arachni::Data.issues.size.should == 500
251
+
252
+ f.report.plugins[:wait][:results].should == { 'stuff' => true }
253
+ end
254
+ end
255
+
256
+ it 'restores options' do
257
+ options_hash = nil
258
+
259
+ Arachni::Framework.new do |f|
260
+ f.options.url = @url + '/with_ajax'
261
+ f.options.audit.elements :links, :forms, :cookies
262
+ f.options.datastore.my_custom_option = 'my custom value'
263
+ options_hash = f.options.update( f.options.to_rpc_data ).to_h.deep_clone
264
+
265
+ f.checks.load :taint
266
+
267
+ t = Thread.new { f.run }
268
+
269
+ sleep 0.1 while f.browser_cluster.done?
270
+ @snapshot = f.suspend
271
+
272
+ t.join
273
+ end
274
+
275
+ Arachni::Framework.restore( @snapshot ) do |f|
276
+ f.options.to_h.should == options_hash.merge( checks: ['taint'] )
277
+ f.browser_job_skip_states.should be_any
278
+ end
279
+ end
280
+
281
+ it 'restores BrowserCluster skip states' do
282
+ Arachni::Framework.new do |f|
283
+ f.options.url = @url + '/with_ajax'
284
+ f.options.audit.elements :links, :forms, :cookies
285
+
286
+ f.checks.load :taint
287
+
288
+ t = Thread.new { f.run }
289
+
290
+ sleep 0.1 while f.browser_cluster.done?
291
+ @snapshot = f.suspend
292
+
293
+ t.join
294
+ end
295
+
296
+ Arachni::Framework.restore( @snapshot ) do |f|
297
+ f.browser_job_skip_states.should be_any
298
+ end
299
+ end
300
+
301
+ it 'restores loaded checks' do
302
+ Arachni::Framework.new do |f|
303
+ f.options.url = @url
304
+ f.checks.load :taint
305
+
306
+ t = Thread.new { f.run }
307
+ sleep 0.1 while f.status != :scanning
308
+
309
+ @snapshot = f.suspend
310
+
311
+ t.join
312
+ end
313
+
314
+ Arachni::Framework.restore( @snapshot ) do |f|
315
+ f.checks.loaded.should == ['taint']
316
+ end
317
+ end
318
+
319
+ it 'restores loaded plugins' do
320
+ Arachni::Framework.new do |f|
321
+ f.options.url = @url
322
+ f.plugins.load :wait
323
+
324
+ t = Thread.new { f.run }
325
+ sleep 0.1 while f.status != :scanning
326
+
327
+ @snapshot = f.suspend
328
+ t.join
329
+ end
330
+
331
+ Arachni::Framework.restore( @snapshot ) do |f|
332
+ f.plugins.loaded.should == ['wait']
333
+ end
334
+ end
335
+
336
+ it 'restores plugin states' do
337
+ Arachni::Options.plugins['suspendable'] = {
338
+ 'my_option' => 'my value'
339
+ }
340
+
341
+ Arachni::Framework.new do |f|
342
+ f.options.url = web_server_url_for :framework_multi
343
+ f.options.audit.elements :links
344
+
345
+ f.checks.load :taint
346
+ f.plugins.load :suspendable
347
+
348
+ t = Thread.new do
349
+ f.run
350
+ end
351
+
352
+ sleep 0.1 while f.status != :scanning
353
+
354
+ @snapshot = f.suspend
355
+ t.join
356
+
357
+ Arachni::State.plugins.runtime[:suspendable][:data].should == 1
358
+ end
359
+
360
+ Arachni::Framework.restore( @snapshot ) do |f|
361
+ t = Thread.new do
362
+ f.run
363
+ end
364
+
365
+ sleep 0.1 while f.status != :scanning
366
+
367
+ f.plugins.jobs[:suspendable][:instance].counter.should == 2
368
+
369
+ f.abort
370
+ t.join
371
+ end
372
+ end
373
+ end
374
+
375
+ describe '#pause' do
376
+ it 'pauses the system' do
377
+ Arachni::Framework.new do |f|
378
+ f.options.url = @url + '/elem_combo'
379
+ f.options.audit.elements :links, :forms, :cookies
380
+ f.checks.load :taint
381
+
382
+ t = Thread.new do
383
+ f.run
384
+ end
385
+
386
+ f.pause
387
+
388
+ sleep 10
389
+
390
+ f.running?.should be_true
391
+ t.kill
392
+ end
393
+ end
394
+
395
+ it 'returns an Integer request ID' do
396
+ Arachni::Framework.new do |f|
397
+ f.options.url = @url + '/elem_combo'
398
+ f.options.audit.elements :links, :forms, :cookies
399
+ f.checks.load :taint
400
+
401
+ t = Thread.new do
402
+ f.run
403
+ end
404
+
405
+ f.pause.should be_kind_of Integer
406
+
407
+ sleep 10
408
+
409
+ f.running?.should be_true
410
+ t.kill
411
+ end
412
+ end
413
+
414
+ it 'sets #status to :paused' do
415
+ Arachni::Framework.new do |f|
416
+ f.options.url = @url + '/elem_combo'
417
+ f.options.audit.elements :links, :forms, :cookies
418
+ f.checks.load :taint
419
+
420
+ t = Thread.new do
421
+ f.run
422
+ end
423
+ sleep 0.1 while f.status != :scanning
424
+
425
+ f.pause
426
+ f.status.should == :paused
427
+
428
+ t.kill
429
+ end
430
+ end
431
+ end
432
+
433
+ describe '#resume' do
434
+ it 'resumes the system' do
435
+ Arachni::Framework.new do |f|
436
+ f.options.url = @url + '/elem_combo'
437
+ f.options.audit.elements :links, :forms, :cookies
438
+ f.checks.load :taint
439
+
440
+ t = Thread.new do
441
+ f.run
442
+ end
443
+
444
+ id = f.pause
445
+
446
+ sleep 10
447
+
448
+ f.running?.should be_true
449
+ f.resume id
450
+ t.join
451
+ end
452
+ end
453
+
454
+ it 'sets #status to scanning' do
455
+ Arachni::Framework.new do |f|
456
+ f.options.url = @url + '/elem_combo'
457
+ f.options.audit.elements :links, :forms, :cookies
458
+ f.checks.load :taint
459
+
460
+ t = Thread.new do
461
+ f.run
462
+ end
463
+
464
+ id = f.pause
465
+ f.status.should == :paused
466
+
467
+ f.resume id
468
+ Timeout.timeout( 5 ) do
469
+ sleep 0.1 while f.status != :scanning
470
+ end
471
+ t.join
472
+ end
473
+ end
474
+ end
475
+
476
+ describe '#clean_up' do
477
+ it 'stops the #plugins' do
478
+ Arachni::Framework.new do |f|
479
+ f.options.url = @url + '/elem_combo'
480
+ f.plugins.load :wait
481
+
482
+ f.plugins.run
483
+ f.clean_up
484
+ f.plugins.jobs.should be_empty
485
+ end
486
+ end
487
+
488
+ it 'sets the status to cleanup' do
489
+ Arachni::Framework.new do |f|
490
+ f.options.url = @url + '/elem_combo'
491
+
492
+ f.clean_up
493
+ f.status.should == :cleanup
494
+ end
495
+ end
496
+
497
+ it 'clears the page queue' do
498
+ Arachni::Framework.new do |f|
499
+ f.options.url = @url + '/elem_combo'
500
+ f.push_to_page_queue Arachni::Page.from_url( f.options.url )
501
+
502
+ f.data.page_queue.should_not be_empty
503
+ f.clean_up
504
+ f.data.page_queue.should be_empty
505
+ end
506
+ end
507
+
508
+ it 'clears the URL queue' do
509
+ Arachni::Framework.new do |f|
510
+ f.options.url = @url + '/elem_combo'
511
+ f.push_to_url_queue f.options.url
512
+
513
+ f.data.url_queue.should_not be_empty
514
+ f.clean_up
515
+ f.data.url_queue.should be_empty
516
+ end
517
+ end
518
+
519
+ it 'sets #running? to false' do
520
+ Arachni::Framework.new do |f|
521
+ f.options.url = @url + '/elem_combo'
522
+ f.clean_up
523
+ f.should_not be_running
524
+ end
525
+ end
526
+ end
527
+
528
+ end