cms_scanner 0.0.2

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 (147) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +6 -0
  5. data/.travis.yml +14 -0
  6. data/Gemfile +6 -0
  7. data/README.md +20 -0
  8. data/Rakefile +9 -0
  9. data/app/app.rb +4 -0
  10. data/app/controllers.rb +2 -0
  11. data/app/controllers/core.rb +46 -0
  12. data/app/controllers/core/cli_options.rb +68 -0
  13. data/app/controllers/interesting_files.rb +12 -0
  14. data/app/finders.rb +1 -0
  15. data/app/finders/interesting_files.rb +21 -0
  16. data/app/finders/interesting_files/fantastico_fileslist.rb +23 -0
  17. data/app/finders/interesting_files/headers.rb +15 -0
  18. data/app/finders/interesting_files/robots_txt.rb +22 -0
  19. data/app/finders/interesting_files/search_replace_db_2.rb +28 -0
  20. data/app/finders/interesting_files/xml_rpc.rb +62 -0
  21. data/app/formatters.rb +3 -0
  22. data/app/formatters/cli.rb +18 -0
  23. data/app/formatters/cli_no_colour.rb +15 -0
  24. data/app/formatters/json.rb +12 -0
  25. data/app/models.rb +5 -0
  26. data/app/models/fantastico_fileslist.rb +20 -0
  27. data/app/models/headers.rb +37 -0
  28. data/app/models/interesting_file.rb +30 -0
  29. data/app/models/robots_txt.rb +20 -0
  30. data/app/models/xml_rpc.rb +35 -0
  31. data/app/views/cli/core/finished.erb +4 -0
  32. data/app/views/cli/core/started.erb +3 -0
  33. data/app/views/cli/interesting_files/findings.erb +19 -0
  34. data/app/views/cli/scan_aborted.erb +4 -0
  35. data/app/views/json/core/finished.erb +3 -0
  36. data/app/views/json/core/started.erb +3 -0
  37. data/app/views/json/interesting_files/findings.erb +1 -0
  38. data/app/views/json/scan_aborted.erb +4 -0
  39. data/cms_scanner.gemspec +37 -0
  40. data/examples/views/cli/wp_custom/test.erb +1 -0
  41. data/examples/views/json/wp_custom/test.erb +1 -0
  42. data/examples/wpscan.rb +29 -0
  43. data/lib/cms_scanner.rb +71 -0
  44. data/lib/cms_scanner/browser.rb +68 -0
  45. data/lib/cms_scanner/browser/actions.rb +48 -0
  46. data/lib/cms_scanner/browser/options.rb +53 -0
  47. data/lib/cms_scanner/cache/file_store.rb +75 -0
  48. data/lib/cms_scanner/cache/typhoeus.rb +21 -0
  49. data/lib/cms_scanner/controller.rb +90 -0
  50. data/lib/cms_scanner/controllers.rb +34 -0
  51. data/lib/cms_scanner/errors/auth_errors.rb +15 -0
  52. data/lib/cms_scanner/finders.rb +5 -0
  53. data/lib/cms_scanner/finders/finder.rb +27 -0
  54. data/lib/cms_scanner/finders/finding.rb +32 -0
  55. data/lib/cms_scanner/finders/findings.rb +25 -0
  56. data/lib/cms_scanner/finders/independent_finder.rb +30 -0
  57. data/lib/cms_scanner/finders/independent_finders.rb +41 -0
  58. data/lib/cms_scanner/formatter.rb +118 -0
  59. data/lib/cms_scanner/formatter/buffer.rb +15 -0
  60. data/lib/cms_scanner/target.rb +33 -0
  61. data/lib/cms_scanner/target/platform.rb +2 -0
  62. data/lib/cms_scanner/target/platform/php.rb +39 -0
  63. data/lib/cms_scanner/target/platform/wordpress.rb +35 -0
  64. data/lib/cms_scanner/target/platform/wordpress/custom_directories.rb +62 -0
  65. data/lib/cms_scanner/target/server.rb +3 -0
  66. data/lib/cms_scanner/target/server/apache.rb +43 -0
  67. data/lib/cms_scanner/target/server/generic.rb +34 -0
  68. data/lib/cms_scanner/target/server/iis.rb +48 -0
  69. data/lib/cms_scanner/version.rb +4 -0
  70. data/lib/cms_scanner/web_site.rb +68 -0
  71. data/lib/helper.rb +24 -0
  72. data/spec/app/controllers/core_spec.rb +152 -0
  73. data/spec/app/controllers/interesting_files_spec.rb +50 -0
  74. data/spec/app/finders/interesting_files/fantastico_fileslist_spec.rb +68 -0
  75. data/spec/app/finders/interesting_files/headers_spec.rb +38 -0
  76. data/spec/app/finders/interesting_files/robots_txt_spec.rb +56 -0
  77. data/spec/app/finders/interesting_files/search_replace_db_2_spec.rb +55 -0
  78. data/spec/app/finders/interesting_files/xml_rpc_spec.rb +138 -0
  79. data/spec/app/finders/interesting_files_spec.rb +13 -0
  80. data/spec/app/formatters/cli_no_colour_spec.rb +17 -0
  81. data/spec/app/formatters/cli_spec.rb +21 -0
  82. data/spec/app/formatters/json_spec.rb +33 -0
  83. data/spec/app/models/fantastico_fileslist_spec.rb +32 -0
  84. data/spec/app/models/headers_spec.rb +52 -0
  85. data/spec/app/models/interesting_file_spec.rb +51 -0
  86. data/spec/app/models/robots_txt_spec.rb +28 -0
  87. data/spec/app/models/xml_rpc_spec.rb +47 -0
  88. data/spec/cache/.gitignore +4 -0
  89. data/spec/dummy_finders.rb +41 -0
  90. data/spec/fixtures/interesting_files/fantastico_fileslist/fantastico_fileslist.txt +12 -0
  91. data/spec/fixtures/interesting_files/file.txt +4 -0
  92. data/spec/fixtures/interesting_files/headers/interesting.txt +14 -0
  93. data/spec/fixtures/interesting_files/headers/no_interesting.txt +12 -0
  94. data/spec/fixtures/interesting_files/robots_txt/robots.txt +10 -0
  95. data/spec/fixtures/interesting_files/search_replace_db_2/searchreplacedb2.php +188 -0
  96. data/spec/fixtures/interesting_files/xml_rpc/homepage_in_scope_pingback.html +7 -0
  97. data/spec/fixtures/interesting_files/xml_rpc/homepage_out_of_scope_pingback.html +7 -0
  98. data/spec/fixtures/interesting_files/xml_rpc/xmlrpc.php +1 -0
  99. data/spec/fixtures/output.txt +0 -0
  100. data/spec/fixtures/target/platform/php/debug_log/debug.log +2 -0
  101. data/spec/fixtures/target/platform/php/fpd/wp_rss_functions.php +2 -0
  102. data/spec/fixtures/target/platform/wordpress/custom_directories/custom_w_spaces.html +10 -0
  103. data/spec/fixtures/target/platform/wordpress/custom_directories/default.html +14 -0
  104. data/spec/fixtures/target/platform/wordpress/custom_directories/https.html +12 -0
  105. data/spec/fixtures/target/platform/wordpress/detection/default.html +4 -0
  106. data/spec/fixtures/target/platform/wordpress/detection/not_wp.html +8 -0
  107. data/spec/fixtures/target/platform/wordpress/detection/wp_includes.html +3 -0
  108. data/spec/fixtures/target/server/apache/directory_listing/2.2.16.html +15 -0
  109. data/spec/fixtures/target/server/generic/server/apache/basic.txt +5 -0
  110. data/spec/fixtures/target/server/generic/server/iis/basic.txt +6 -0
  111. data/spec/fixtures/target/server/generic/server/not_detected.txt +3 -0
  112. data/spec/fixtures/target/server/iis/directory_listing/no_parent.html +3 -0
  113. data/spec/fixtures/target/server/iis/directory_listing/with_parent.html +3 -0
  114. data/spec/fixtures/views/base/ctrl/local.erb +1 -0
  115. data/spec/fixtures/views/base/ctrl/test.erb +3 -0
  116. data/spec/fixtures/views/base/global.erb +1 -0
  117. data/spec/fixtures/views/base/test.erb +2 -0
  118. data/spec/fixtures/views/based_format/test.erb +1 -0
  119. data/spec/fixtures/views/json/render_me.erb +4 -0
  120. data/spec/lib/browser_spec.rb +141 -0
  121. data/spec/lib/cache/file_store_spec.rb +101 -0
  122. data/spec/lib/cache/typhoeus_spec.rb +30 -0
  123. data/spec/lib/cms_scanner_spec.rb +45 -0
  124. data/spec/lib/controller_spec.rb +23 -0
  125. data/spec/lib/controllers_spec.rb +52 -0
  126. data/spec/lib/finders/findings_spec.rb +49 -0
  127. data/spec/lib/finders/independent_finders_spec.rb +98 -0
  128. data/spec/lib/formatter_spec.rb +136 -0
  129. data/spec/lib/sub_scanner_spec.rb +27 -0
  130. data/spec/lib/target/platforms_spec.rb +13 -0
  131. data/spec/lib/target/servers_spec.rb +13 -0
  132. data/spec/lib/target_spec.rb +50 -0
  133. data/spec/lib/web_site_spec.rb +124 -0
  134. data/spec/shared_examples.rb +11 -0
  135. data/spec/shared_examples/browser_actions.rb +32 -0
  136. data/spec/shared_examples/finding.rb +20 -0
  137. data/spec/shared_examples/formatter_buffer.rb +8 -0
  138. data/spec/shared_examples/formatter_class_methods.rb +26 -0
  139. data/spec/shared_examples/independent_finder.rb +33 -0
  140. data/spec/shared_examples/target/platform/php.rb +58 -0
  141. data/spec/shared_examples/target/platform/wordpress.rb +41 -0
  142. data/spec/shared_examples/target/platform/wordpress/custom_directories.rb +50 -0
  143. data/spec/shared_examples/target/server/apache.rb +33 -0
  144. data/spec/shared_examples/target/server/generic.rb +34 -0
  145. data/spec/shared_examples/target/server/iis.rb +38 -0
  146. data/spec/spec_helper.rb +41 -0
  147. metadata +432 -0
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'dummy_finders'
3
+
4
+ describe CMSScanner::Finders::Findings do
5
+
6
+ subject(:findings) { described_class.new }
7
+ let(:dummy) { CMSScanner::DummyFinding }
8
+
9
+ describe '#<<' do
10
+ after { expect(findings).to eq @expected }
11
+
12
+ context 'when empty array' do
13
+ it 'adds it' do
14
+ findings << 'empty-test'
15
+ @expected = %w(empty-test)
16
+ end
17
+ end
18
+
19
+ context 'when not empty' do
20
+ let(:confirmed) { dummy.new('confirmed') }
21
+
22
+ before { findings << dummy.new('test') << confirmed }
23
+
24
+ it 'adds a confirmed result correctly' do
25
+ confirmed_dup = confirmed.dup
26
+ confirmed_dup.confidence = 100
27
+
28
+ findings << dummy.new('test2')
29
+ findings << confirmed_dup
30
+
31
+ confirmed.confirmed_by = confirmed_dup
32
+
33
+ @expected = [] << dummy.new('test') << confirmed << dummy.new('test2')
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '#+' do
39
+ after { expect(findings).to eq @expected }
40
+
41
+ it 'adds it/them' do
42
+ # Dummy assignement to avoid the 'Operator used in void context'
43
+ _ = findings + %w(test1 test2)
44
+
45
+ @expected = %w(test1 test2)
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+ require 'dummy_finders'
3
+
4
+ describe CMSScanner::Finders::IndependentFinders do
5
+
6
+ subject(:finders) { described_class.new }
7
+
8
+ describe '#run' do
9
+ let(:target) { 'target' }
10
+ let(:finding) { CMSScanner::DummyFinding }
11
+ let(:expected_aggressive) { finding.new('test', found_by: 'override', confidence: 100) }
12
+ let(:expected_passive) do
13
+ [
14
+ finding.new('test', found_by: 'DummyFinder (passive detection)'),
15
+ finding.new('spotted', found_by: 'NoAggressiveResult (passive detection)', confidence: 10)
16
+ ]
17
+ end
18
+
19
+ before do
20
+ finders << CMSScanner::Finders::DummyFinder.new(target) <<
21
+ CMSScanner::Finders::NoAggressiveResult.new(target)
22
+
23
+ @found = finders.run(mode: mode)
24
+
25
+ expect(@found).to be_a(CMSScanner::Finders::Findings)
26
+
27
+ @found.each { |f| expect(f).to be_a finding }
28
+ end
29
+
30
+ context 'when :passive mode' do
31
+ let(:mode) { :passive }
32
+
33
+ it 'returns 2 results' do
34
+ expect(@found.size).to eq 2
35
+ expect(@found.first).to eql expected_passive.first
36
+ expect(@found.last).to eql expected_passive.last
37
+ end
38
+ end
39
+
40
+ context 'when :aggressive mode' do
41
+ let(:mode) { :aggressive }
42
+
43
+ it 'returns 1 result' do
44
+ expect(@found.size).to eq 1
45
+ expect(@found.first).to eql expected_aggressive
46
+ end
47
+ end
48
+
49
+ context 'when :mixed mode' do
50
+ let(:mode) { :mixed }
51
+
52
+ it 'returns 2 results' do
53
+ expect(@found.size).to eq 2
54
+ expect(@found.first).to eql expected_passive.first
55
+ expect(@found.first.confirmed_by).to eql [expected_aggressive]
56
+ expect(@found.last).to eql expected_passive.last
57
+ end
58
+ end
59
+
60
+ context 'when multiple results returned' do
61
+
62
+ end
63
+ end
64
+
65
+ describe '#symbols_from_mode' do
66
+ after { expect(finders.symbols_from_mode(@mode)).to eq @expected }
67
+
68
+ context 'when :mixed' do
69
+ it 'returns [:passive, :aggressive]' do
70
+ @mode = :mixed
71
+ @expected = [:passive, :aggressive]
72
+ end
73
+ end
74
+
75
+ context 'when :passive or :aggresssive' do
76
+ [:passive, :aggressive].each do |symbol|
77
+ it 'returns it in an array' do
78
+ @mode = symbol
79
+ @expected = [*symbol]
80
+ end
81
+ end
82
+ end
83
+
84
+ context 'otherwise' do
85
+ it 'returns []' do
86
+ @mode = :unallowed
87
+ @expected = []
88
+ end
89
+ end
90
+ end
91
+
92
+ describe '#findings' do
93
+ it 'returns a Findings object' do
94
+ expect(finders.findings).to be_a CMSScanner::Finders::Findings
95
+ end
96
+ end
97
+
98
+ end
@@ -0,0 +1,136 @@
1
+ require 'spec_helper'
2
+
3
+ # Test Module to check the correct inclusion of
4
+ # the class methods
5
+ module OtherFormatter
6
+ include CMSScanner::Formatter
7
+ end
8
+
9
+ [CMSScanner::Formatter, OtherFormatter].each do |f|
10
+ describe "#{f}" do
11
+ subject(:formatter) { f }
12
+ it_behaves_like CMSScanner::Formatter::ClassMethods
13
+ end
14
+ end
15
+
16
+ module CMSScanner
17
+ module Formatter
18
+ module Spec
19
+ # Base Format Test Class
20
+ class BasedFormat < Base
21
+ def base_format
22
+ 'base'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ describe CMSScanner::Formatter::Base do
30
+
31
+ subject(:formatter) { described_class.new }
32
+
33
+ describe '#format' do
34
+ its(:format) { should eq 'base' }
35
+ end
36
+
37
+ describe '#render, output' do
38
+ before { formatter.views_directories << FIXTURES_VIEWS }
39
+
40
+ it 'renders the global template and does not override the @views_directories' do
41
+ expect($stdout).to receive(:puts)
42
+ .with("It Works!\nViews Dirs: #{formatter.views_directories}")
43
+
44
+ formatter.output('@test', test: 'Works!', views_directories: 'owned')
45
+ end
46
+
47
+ context 'when global and local rendering are used inside a template' do
48
+ it 'renders them correcly' do
49
+ rendered = formatter.render('test', { var: 'Works' }, 'ctrl')
50
+
51
+ expect(rendered).to eq "Test: Works\nLocal View\nGlobal View"
52
+ end
53
+ end
54
+
55
+ it 'raises an error if the controller_name is nil and tpl is not a global one' do
56
+ expect { formatter.output('test') }.to raise_error('The controller_name can not be nil')
57
+ end
58
+ end
59
+
60
+ describe '#view_path' do
61
+ before do
62
+ formatter.views_directories << FIXTURES_VIEWS
63
+ formatter.render('local', {}, 'ctrl') # Used to set the @controller_name
64
+ end
65
+
66
+ context 'when the tpl format is invalid' do
67
+ let(:tpl) { '../try-this' }
68
+
69
+ it 'raises an error' do
70
+ expect { formatter.view_path(tpl) }.to raise_error("Wrong tpl format: 'ctrl/#{tpl}'")
71
+ end
72
+ end
73
+
74
+ context 'when the tpl is not found' do
75
+ let(:tpl) { 'not_there' }
76
+
77
+ it 'raises an error' do
78
+ expect { formatter.view_path(tpl) }.to raise_error("View not found for base/ctrl/#{tpl}")
79
+ end
80
+ end
81
+
82
+ context 'when the tpl is found' do
83
+ after { expect(formatter.view_path(@tpl)).to eq @expected }
84
+
85
+ context 'if it\'s a global tpl' do
86
+ it 'returns its path' do
87
+ @expected = File.join(FIXTURES_VIEWS, 'base', 'test.erb')
88
+ @tpl = '@test'
89
+ end
90
+ end
91
+
92
+ context 'if it\s a local tpl' do
93
+ it 'retuns its path' do
94
+ @expected = File.join(FIXTURES_VIEWS, 'base', 'ctrl', 'local.erb')
95
+ @tpl = 'local'
96
+ end
97
+ end
98
+ end
99
+
100
+ context 'when base_format' do
101
+ subject(:formatter) { CMSScanner::Formatter::Spec::BasedFormat.new }
102
+
103
+ after { expect(formatter.view_path(@tpl)).to eq @expected }
104
+
105
+ context 'when the ovverided view exists' do
106
+ it 'returns it' do
107
+ @expected = File.join(FIXTURES_VIEWS, 'based_format', 'test.erb')
108
+ @tpl = '@test'
109
+ end
110
+ end
111
+
112
+ it 'returns the base views otherwise' do
113
+ @expected = File.join(FIXTURES_VIEWS, 'base', 'ctrl', 'local.erb')
114
+ @tpl = 'local'
115
+ end
116
+ end
117
+
118
+ end
119
+
120
+ describe '#views_directories' do
121
+ let(:default_directories) { [APP_VIEWS] }
122
+
123
+ context 'when default directories' do
124
+ its(:views_directories) { should eq(default_directories) }
125
+ end
126
+
127
+ context 'when adding directories' do
128
+ it 'adds them' do
129
+ formatter.views_directories << 'testing'
130
+
131
+ expect(formatter.views_directories).to eq(default_directories << 'testing')
132
+ end
133
+ end
134
+ end
135
+
136
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ # Module included the CMSScanner to test its correct inclusion
4
+ module SubScanner
5
+ include CMSScanner
6
+
7
+ # This Target class should be called in the CMSScanner::Controller::Base
8
+ # instead of the CMSScanner::Target
9
+ class Target < CMSScanner::Target
10
+ def new_method
11
+ 'working'
12
+ end
13
+ end
14
+ end
15
+
16
+ describe SubScanner::Scan do
17
+ subject(:scanner) { described_class.new }
18
+ let(:controller) { SubScanner::Controller }
19
+
20
+ it 'loads the overrided Target class' do
21
+ target = scanner.controllers.first.target
22
+
23
+ expect(target).to be_a SubScanner::Target
24
+ expect(target.respond_to?(:new_method)).to eq true
25
+ expect(target.new_method).to eq 'working'
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ [:WordPress, :PHP].each do |platform|
4
+ describe CMSScanner::Target do
5
+ subject(:target) do
6
+ described_class.new(url).extend(described_class::Platform.const_get(platform))
7
+ end
8
+ let(:url) { 'http://ex.lo' }
9
+ let(:fixtures) { File.join(FIXTURES, 'target', 'platform', platform.to_s.downcase) }
10
+
11
+ it_behaves_like described_class::Platform.const_get(platform)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ [:Generic, :Apache, :IIS].each do |server|
4
+ describe CMSScanner::Target do
5
+ subject(:target) do
6
+ described_class.new(url).extend(described_class::Server.const_get(server))
7
+ end
8
+ let(:url) { 'http://ex.lo' }
9
+ let(:fixtures) { File.join(FIXTURES, 'target', 'server', server.to_s.downcase) }
10
+
11
+ it_behaves_like described_class::Server.const_get(server)
12
+ end
13
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe CMSScanner::Target do
4
+
5
+ subject(:target) { described_class.new(url) }
6
+ let(:url) { 'http://ex.lo' }
7
+
8
+ describe '#in_scope?' do
9
+ after { expect(target.in_scope?(@url)).to eq @expected }
10
+
11
+ [nil, '', 'http://out-of-scope.com', '//jquery.com/j.js'].each do |url|
12
+ it "returns false for #{url}" do
13
+ @url = url
14
+ @expected = false
15
+ end
16
+ end
17
+
18
+ %w(https://ex.lo/file.txt http://ex.lo/ /relative).each do |url|
19
+ it "returns true for #{url}" do
20
+ @url = url
21
+ @expected = true
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '#interesting_files' do
27
+ before do
28
+ expect(CMSScanner::Finders::InterestingFiles).to receive(:find).and_return(stubbed)
29
+ end
30
+
31
+ context 'when no findings' do
32
+ let(:stubbed) { [] }
33
+
34
+ its(:interesting_files) { should eq stubbed }
35
+ end
36
+
37
+ context 'when findings' do
38
+ let(:stubbed) { ['yolo'] }
39
+
40
+ it 'allows findings to be added with <<' do
41
+ expect(target.interesting_files).to eq stubbed
42
+
43
+ target.interesting_files << 'other-finding'
44
+
45
+ expect(target.interesting_files).to eq(stubbed << 'other-finding')
46
+ end
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe CMSScanner::WebSite do
4
+
5
+ subject(:web_site) { described_class.new(url) }
6
+ let(:url) { 'http://ex.lo' }
7
+
8
+ describe '#url=' do
9
+ context 'when the url is incorrect' do
10
+
11
+ after do
12
+ expect { web_site.url = @url }.to raise_error Addressable::URI::InvalidURIError
13
+ end
14
+
15
+ it 'raises an error if empty' do
16
+ @url = ''
17
+ end
18
+
19
+ it 'raises an error if wrong format' do
20
+ @url = 'jj'
21
+ end
22
+ end
23
+
24
+ context 'when valid' do
25
+ it 'creates an Addressable object and adds a traling slash' do
26
+ web_site.url = 'http://site.com'
27
+
28
+ expect(web_site.url).to eq('http://site.com/')
29
+ expect(web_site.uri).to be_a Addressable::URI
30
+ end
31
+ end
32
+ end
33
+
34
+ describe '#url' do
35
+ context 'when no path argument' do
36
+ its(:url) { should eql 'http://ex.lo/' }
37
+ end
38
+
39
+ context 'when a path argument' do
40
+ it 'appends the path' do
41
+ expect(web_site.url('file.txt')).to eql "#{url}/file.txt"
42
+ end
43
+
44
+ context 'when relative path' do
45
+ let(:url) { 'http://ex.lo/dir/' }
46
+
47
+ it 'appends it from the host/domain' do
48
+ expect(web_site.url('/sub/file.txt')).to eql 'http://ex.lo/sub/file.txt'
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ describe '#online?' do
55
+ context 'when online' do
56
+ before { stub_request(:get, url).to_return(status: 200) }
57
+
58
+ it { should be_online }
59
+ end
60
+
61
+ context 'when offline' do
62
+ before { stub_request(:get, url).to_return(status: 0) }
63
+
64
+ it { should_not be_online }
65
+ end
66
+ end
67
+
68
+ describe '#http_auth?' do
69
+ context 'when http auth' do
70
+ before { stub_request(:get, url).to_return(status: 401) }
71
+
72
+ it { should be_http_auth }
73
+ end
74
+
75
+ context 'when no http auth' do
76
+ before { stub_request(:get, url).to_return(status: 200) }
77
+
78
+ it { should_not be_http_auth }
79
+ end
80
+ end
81
+
82
+ describe '#proxy_auth?' do
83
+ # Handled in app/controllers/core_spec
84
+ end
85
+
86
+ describe '#redirection' do
87
+ it 'returns nil if no redirection detected' do
88
+ stub_request(:get, web_site.url).to_return(status: 200, body: '')
89
+
90
+ expect(web_site.redirection).to be_nil
91
+ end
92
+
93
+ [301, 302].each do |status_code|
94
+ it "returns http://new-location.com if the status code is #{status_code}" do
95
+ new_location = 'http://new-location.com'
96
+
97
+ stub_request(:get, web_site.url)
98
+ .to_return(status: status_code, headers: { location: new_location })
99
+
100
+ stub_request(:get, new_location).to_return(status: 200)
101
+
102
+ expect(web_site.redirection).to eq new_location
103
+ end
104
+ end
105
+
106
+ context 'when multiple redirections' do
107
+ it 'returns the last redirection' do
108
+ first_redirection = 'www.redirection.com'
109
+ last_redirection = 'redirection.com'
110
+
111
+ stub_request(:get, web_site.url)
112
+ .to_return(status: 301, headers: { location: first_redirection })
113
+
114
+ stub_request(:get, first_redirection)
115
+ .to_return(status: 302, headers: { location: last_redirection })
116
+
117
+ stub_request(:get, last_redirection).to_return(status: 200)
118
+
119
+ expect(web_site.redirection).to eq last_redirection
120
+ end
121
+ end
122
+ end
123
+
124
+ end