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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -0
- data/README.md +9 -2
- data/components/checks/active/code_injection.rb +5 -5
- data/components/checks/active/code_injection_timing.rb +3 -3
- data/components/checks/active/no_sql_injection_differential.rb +3 -2
- data/components/checks/active/os_cmd_injection.rb +11 -5
- data/components/checks/active/os_cmd_injection_timing.rb +11 -4
- data/components/checks/active/path_traversal.rb +2 -2
- data/components/checks/active/sql_injection.rb +1 -1
- data/components/checks/active/sql_injection/patterns/mssql +1 -0
- data/components/checks/active/sql_injection_differential.rb +3 -2
- data/components/checks/active/unvalidated_redirect.rb +3 -3
- data/components/checks/passive/common_directories/directories.txt +2 -0
- data/components/checks/passive/common_files/filenames.txt +1 -0
- data/lib/arachni/browser.rb +17 -1
- data/lib/arachni/check/auditor.rb +5 -2
- data/lib/arachni/check/base.rb +30 -5
- data/lib/arachni/element/capabilities/analyzable/differential.rb +2 -5
- data/lib/arachni/element/capabilities/auditable.rb +3 -1
- data/lib/arachni/element/capabilities/with_dom.rb +1 -0
- data/lib/arachni/element/capabilities/with_node.rb +1 -1
- data/lib/arachni/element/cookie.rb +2 -2
- data/lib/arachni/element/form.rb +1 -1
- data/lib/arachni/element/header.rb +2 -2
- data/lib/arachni/element/link_template.rb +1 -1
- data/lib/arachni/framework.rb +21 -1144
- data/lib/arachni/framework/parts/audit.rb +282 -0
- data/lib/arachni/framework/parts/browser.rb +132 -0
- data/lib/arachni/framework/parts/check.rb +86 -0
- data/lib/arachni/framework/parts/data.rb +158 -0
- data/lib/arachni/framework/parts/platform.rb +34 -0
- data/lib/arachni/framework/parts/plugin.rb +61 -0
- data/lib/arachni/framework/parts/report.rb +128 -0
- data/lib/arachni/framework/parts/scope.rb +40 -0
- data/lib/arachni/framework/parts/state.rb +457 -0
- data/lib/arachni/http/client.rb +33 -30
- data/lib/arachni/http/request.rb +6 -2
- data/lib/arachni/issue.rb +55 -1
- data/lib/arachni/platform/manager.rb +25 -21
- data/lib/arachni/state/framework.rb +7 -1
- data/lib/arachni/utilities.rb +10 -0
- data/lib/version +1 -1
- data/spec/arachni/browser_spec.rb +13 -0
- data/spec/arachni/check/auditor_spec.rb +1 -0
- data/spec/arachni/check/base_spec.rb +80 -0
- data/spec/arachni/element/cookie_spec.rb +2 -2
- data/spec/arachni/framework/parts/audit_spec.rb +391 -0
- data/spec/arachni/framework/parts/browser_spec.rb +26 -0
- data/spec/arachni/framework/parts/check_spec.rb +24 -0
- data/spec/arachni/framework/parts/data_spec.rb +187 -0
- data/spec/arachni/framework/parts/platform_spec.rb +62 -0
- data/spec/arachni/framework/parts/plugin_spec.rb +41 -0
- data/spec/arachni/framework/parts/report_spec.rb +66 -0
- data/spec/arachni/framework/parts/scope_spec.rb +86 -0
- data/spec/arachni/framework/parts/state_spec.rb +528 -0
- data/spec/arachni/framework_spec.rb +17 -1344
- data/spec/arachni/http/client_spec.rb +12 -7
- data/spec/arachni/issue_spec.rb +35 -0
- data/spec/arachni/platform/manager_spec.rb +2 -3
- data/spec/arachni/state/framework_spec.rb +15 -0
- data/spec/components/checks/active/code_injection_timing_spec.rb +5 -5
- data/spec/components/checks/active/no_sql_injection_differential_spec.rb +4 -0
- data/spec/components/checks/active/os_cmd_injection_spec.rb +20 -7
- data/spec/components/checks/active/os_cmd_injection_timing_spec.rb +5 -5
- data/spec/components/checks/active/sql_injection_differential_spec.rb +4 -0
- data/spec/components/checks/active/sql_injection_spec.rb +2 -3
- data/spec/support/servers/arachni/browser.rb +31 -0
- data/spec/support/servers/checks/active/code_injection.rb +1 -1
- data/spec/support/servers/checks/active/no_sql_injection_differential.rb +36 -34
- data/spec/support/servers/checks/active/os_cmd_injection.rb +6 -12
- data/spec/support/servers/checks/active/os_cmd_injection_timing.rb +9 -4
- data/spec/support/servers/checks/active/sql_injection.rb +1 -1
- data/spec/support/servers/checks/active/sql_injection_differential.rb +37 -34
- data/spec/support/shared/element/capabilities/with_node.rb +25 -0
- data/spec/support/shared/framework.rb +26 -0
- data/ui/cli/output.rb +2 -0
- data/ui/cli/rpc/server/dispatcher/option_parser.rb +1 -1
- metadata +32 -4
- 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
|