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,11 @@
1
+
2
+ require 'shared_examples/browser_actions'
3
+ require 'shared_examples/formatter_buffer'
4
+ require 'shared_examples/formatter_class_methods'
5
+ require 'shared_examples/finding'
6
+ require 'shared_examples/independent_finder'
7
+ require 'shared_examples/target/platform/wordpress'
8
+ require 'shared_examples/target/platform/php'
9
+ require 'shared_examples/target/server/generic'
10
+ require 'shared_examples/target/server/apache'
11
+ require 'shared_examples/target/server/iis'
@@ -0,0 +1,32 @@
1
+
2
+ shared_examples CMSScanner::Browser::Actions do
3
+
4
+ let(:url) { 'http://example.com/file.txt' }
5
+ let(:browser) { CMSScanner::Browser }
6
+
7
+ describe '#get, #post, #head' do
8
+ [:get, :post, :head].each do |method|
9
+ it 'calls the method and returns a Typhoeus::Response' do
10
+ stub_request(method, url)
11
+
12
+ expect(browser.send(method, url)).to be_a Typhoeus::Response
13
+ end
14
+ end
15
+ end
16
+
17
+ describe '#get_and_follow_location' do
18
+ let(:redirection) { 'http://redirect.me' }
19
+
20
+ it 'follows the location' do
21
+ stub_request(:get, url).to_return(status: 301, headers: { location: redirection })
22
+ stub_request(:get, redirection).to_return(status: 200, body: 'Got me')
23
+
24
+ response = browser.get_and_follow_location(url)
25
+ expect(response).to be_a Typhoeus::Response
26
+ # Line below is not working due to an issue in Typhoeus/Webmock
27
+ # See https://github.com/typhoeus/typhoeus/issues/279
28
+ # expect(response.body).to eq 'Got me'
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,20 @@
1
+
2
+ shared_examples CMSScanner::Finders::Finding do
3
+
4
+ [:references, :confirmed_by, :interesting_entries].each do |opt|
5
+ describe "##{opt}" do
6
+ its(opt) { should eq [] }
7
+
8
+ context 'when supplied in the #new' do
9
+ let(:opts) { { opt => 'test' } }
10
+
11
+ its(opt) { should eq 'test' }
12
+ end
13
+ end
14
+ end
15
+
16
+ describe '#parse_finding_options' do
17
+ xit
18
+ end
19
+
20
+ end
@@ -0,0 +1,8 @@
1
+
2
+ shared_examples CMSScanner::Formatter::Buffer do
3
+
4
+ describe '#buffer' do
5
+ its(:buffer) { should be_empty }
6
+ end
7
+
8
+ end
@@ -0,0 +1,26 @@
1
+
2
+ shared_examples CMSScanner::Formatter::ClassMethods do
3
+ describe '#load' do
4
+ context 'w/o parameter' do
5
+ it 'loads the default formatter' do
6
+ expect(subject.load).to be_a subject::Cli
7
+ end
8
+ end
9
+
10
+ it 'loads the correct formatter' do
11
+ expect(subject.load('cli_no_colour')).to be_a subject::CliNoColour
12
+ end
13
+
14
+ it 'adds the custom_views' do
15
+ formatter = subject.load(nil, %w(/path/views1 /path2/views))
16
+
17
+ expect(formatter.views_directories).to include('/path/views1', '/path2/views')
18
+ end
19
+ end
20
+
21
+ describe '#availables' do
22
+ it 'returns the right list' do
23
+ expect(subject.availables).to match_array(%w(json cli-no-colour cli))
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+
2
+ shared_examples CMSScanner::Finders::IndependentFinder do
3
+
4
+ describe '::find' do
5
+ it 'creates a new object and call finders#find' do
6
+ created = described_class.new(target)
7
+
8
+ expect(described_class).to receive(:new).and_return(created)
9
+ expect(created).to receive(:find)
10
+
11
+ described_class.find(target)
12
+ end
13
+ end
14
+
15
+ describe '#find' do
16
+ it 'calls finders#run' do
17
+ expect(subject.finders).to receive(:run).with({})
18
+ subject.find
19
+ end
20
+ end
21
+
22
+ describe '#finders' do
23
+ its(:finders) { should be_a CMSScanner::Finders::IndependentFinders }
24
+
25
+ it 'returns the correct finders' do
26
+ finders = subject.finders
27
+
28
+ expect(finders.size).to eq expected_finders.size
29
+ expect(finders.map { |f| f.class.to_s.demodulize }).to eq expected_finders
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,58 @@
1
+
2
+ shared_examples CMSScanner::Target::Platform::PHP do
3
+
4
+ before { stub_request(:get, target.url(path)).to_return(body: body) }
5
+
6
+ describe '#debug_log?' do
7
+ let(:path) { 'd.log' }
8
+
9
+ context 'when the body matches' do
10
+ %w(debug.log).each do |file|
11
+ context "when #{file} body" do
12
+ let(:body) { File.read(File.join(fixtures, 'debug_log', file)) }
13
+
14
+ it 'returns true' do
15
+ expect(target.debug_log?(path)).to be true
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ context 'when the body does not match' do
22
+ let(:body) { '' }
23
+
24
+ it 'returns false' do
25
+ expect(target.debug_log?(path)).to be false
26
+ end
27
+ end
28
+ end
29
+
30
+ describe '#full_path_disclosure?, #full_path_disclosure_entries' do
31
+ let(:path) { 'p.php' }
32
+
33
+ context 'when the body matches a FPD' do
34
+ {
35
+ 'wp_rss_functions.php' => %w(/short-path/rss-f.php)
36
+ }
37
+ .each do |file, expected|
38
+ context "when #{file} body" do
39
+ let(:body) { File.read(File.join(fixtures, 'fpd', file)) }
40
+
41
+ it 'returns the expected array' do
42
+ expect(target.full_path_disclosure_entries(path)).to eql expected
43
+ expect(target.full_path_disclosure?(path)).to be true
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'when no FPD' do
50
+ let(:body) { '' }
51
+
52
+ it 'returns an empty array' do
53
+ expect(target.full_path_disclosure_entries(path)).to eq []
54
+ expect(target.full_path_disclosure?(path)).to be false
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,41 @@
1
+ require_relative 'wordpress/custom_directories'
2
+
3
+ shared_examples CMSScanner::Target::Platform::WordPress do
4
+
5
+ it_behaves_like 'WordPress::CustomDirectories'
6
+
7
+ describe '#wordpress?' do
8
+ let(:fixtures) { File.join(super(), 'detection') }
9
+
10
+ before do
11
+ stub_request(:get, target.url).to_return(body: File.read(File.join(fixtures, "#{body}.html")))
12
+ end
13
+
14
+ %w(default wp_includes).each do |file|
15
+ context "when a wordpress page (#{file}.html)" do
16
+ let(:body) { file }
17
+
18
+ its(:wordpress?) { should be true }
19
+ end
20
+ end
21
+
22
+ %w(not_wp).each do |file|
23
+ context "when not a wordpress page (#{file}.html)" do
24
+ let(:body) { file }
25
+
26
+ its(:wordpress?) { should be false }
27
+ end
28
+ end
29
+ end
30
+
31
+ describe '#wordpress_hosted?' do
32
+ its(:wordpress_hosted?) { should be false }
33
+
34
+ context 'when the target host matches' do
35
+ let(:url) { 'http://ex.wordpress.com' }
36
+
37
+ its(:wordpress_hosted?) { should be true }
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,50 @@
1
+
2
+ shared_examples 'WordPress::CustomDirectories' do
3
+ let(:fixtures) { File.join(super(), 'custom_directories') }
4
+
5
+ describe '#content_dir' do
6
+ {
7
+ default: 'wp-content', https: 'wp-content', custom_w_spaces: 'custom content spaces'
8
+ }
9
+ .each do |file, expected|
10
+ it "returns #{expected} for #{file}.html" do
11
+ fixture = File.join(fixtures, "#{file}.html")
12
+
13
+ stub_request(:get, target.url).to_return(body: File.read(fixture))
14
+
15
+ expect(target.content_dir).to eql expected
16
+ end
17
+ end
18
+ end
19
+
20
+ describe '#content_dir=, #plugins_dir=' do
21
+ ['wp-content' 'wp-custom'].each do |dir|
22
+ context "when content_dir = #{dir} and no plugins_dir" do
23
+ before { target.content_dir = dir }
24
+
25
+ its(:content_dir) { should eq dir.chomp('/') }
26
+ its(:plugins_dir) { should eq dir.chomp('/') + '/plugins' }
27
+ end
28
+
29
+ context "when content_dir = #{dir} and plugins_dir = #{dir}" do
30
+ before do
31
+ target.content_dir = dir
32
+ target.plugins_dir = dir
33
+ end
34
+
35
+ its(:content_dir) { should eq dir.chomp('/') }
36
+ its(:plugins_dir) { should eq dir.chomp('/') }
37
+ end
38
+ end
39
+ end
40
+
41
+ describe '#content_uri, #content_url, #plugins_uri, #plugins_url' do
42
+ before { target.content_dir = 'wp-content' }
43
+
44
+ its(:content_uri) { should eq Addressable::URI.parse("#{url}/wp-content/") }
45
+ its(:content_url) { should eq "#{url}/wp-content/" }
46
+
47
+ its(:plugins_uri) { should eq Addressable::URI.parse("#{url}/wp-content/plugins/") }
48
+ its(:plugins_url) { should eq "#{url}/wp-content/plugins/" }
49
+ end
50
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples CMSScanner::Target::Server::Apache do
4
+
5
+ describe '#server' do
6
+ its(:server) { should eq :Apache }
7
+ end
8
+
9
+ describe '#directory_listing?, #directory_listing_entries' do
10
+ before { stub_request(:get, target.url(path)).to_return(body: body, status: status) }
11
+ let(:path) { 'somedir' }
12
+
13
+ context 'when not a 200' do
14
+ let(:status) { 404 }
15
+ let(:body) { '' }
16
+
17
+ it 'returns false and an empty array' do
18
+ expect(target.directory_listing?(path)).to be false
19
+ expect(target.directory_listing_entries(path)).to eql []
20
+ end
21
+ end
22
+
23
+ context 'when 200' do
24
+ let(:status) { 200 }
25
+ let(:body) { File.read(File.join(fixtures, 'directory_listing', '2.2.16.html')) }
26
+
27
+ it 'returns true and the expected array' do
28
+ expect(target.directory_listing?(path)).to be true
29
+ expect(target.directory_listing_entries(path)).to eq %w(backup.php database-empty.php)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples CMSScanner::Target::Server::Generic do
4
+
5
+ describe '#server' do
6
+ before { stub_request(:head, target.url).to_return(headers: parse_headers_file(fixture)) }
7
+
8
+ context 'when apache headers' do
9
+ %w(basic.txt).each do |file|
10
+ context "when #{file} headers" do
11
+ let(:fixture) { File.join(fixtures, 'server', 'apache', file) }
12
+
13
+ its(:server) { should eq :Apache }
14
+ end
15
+ end
16
+ end
17
+
18
+ context 'when iis headers' do
19
+ %w(basic.txt).each do |file|
20
+ context "when #{file} headers" do
21
+ let(:fixture) { File.join(fixtures, 'server', 'iis', file) }
22
+
23
+ its(:server) { should eq :IIS }
24
+ end
25
+ end
26
+ end
27
+
28
+ context 'not detected' do
29
+ let(:fixture) { File.join(fixtures, 'server', 'not_detected.txt') }
30
+
31
+ its(:server) { should be nil }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples CMSScanner::Target::Server::IIS do
4
+
5
+ describe '#server' do
6
+ its(:server) { should eq :IIS }
7
+ end
8
+
9
+ describe '#directory_listing?, #directory_listing_entries' do
10
+ before { stub_request(:get, target.url(path)).to_return(body: body, status: status) }
11
+ let(:path) { 'dir' }
12
+
13
+ context 'when not a 200' do
14
+ let(:status) { 404 }
15
+ let(:body) { '' }
16
+
17
+ it 'returns false and an empty array' do
18
+ expect(target.directory_listing?(path)).to be false
19
+ expect(target.directory_listing_entries(path)).to eql []
20
+ end
21
+ end
22
+
23
+ context 'when 200' do
24
+ let(:status) { 200 }
25
+
26
+ %w(with_parent.html no_parent.html).each do |file|
27
+ context "when #{file} body" do
28
+ let(:body) { File.read(File.join(fixtures, 'directory_listing', file)) }
29
+
30
+ it 'returns true and the expected array' do
31
+ expect(target.directory_listing?(path)).to be true
32
+ expect(target.directory_listing_entries(path)).to eq %w(sub-dir web.config)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+
3
+ require 'simplecov'
4
+ require 'rspec/its'
5
+ require 'webmock/rspec'
6
+
7
+ if ENV['TRAVIS']
8
+ require 'coveralls'
9
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
10
+ end
11
+
12
+ SimpleCov.start do
13
+ add_filter '/spec/'
14
+ add_filter 'helper'
15
+ end
16
+
17
+ # See http://betterspecs.org/
18
+ RSpec.configure do |config|
19
+ config.expect_with :rspec do |c|
20
+ c.syntax = :expect
21
+ end
22
+ end
23
+
24
+ def count_files_in_dir(absolute_dir_path, files_pattern = '*')
25
+ Dir.glob(File.join(absolute_dir_path, files_pattern)).count
26
+ end
27
+
28
+ # Parse a file containing raw headers and return the associated Hash
29
+ # @return [ Hash ]
30
+ def parse_headers_file(filepath)
31
+ Typhoeus::Response::Header.new(File.read(filepath))
32
+ end
33
+
34
+ require 'cms_scanner'
35
+ require 'shared_examples'
36
+
37
+ SPECS = Pathname.new(__FILE__).dirname.to_s
38
+ CACHE = File.join(SPECS, 'cache')
39
+ FIXTURES = File.join(SPECS, 'fixtures')
40
+ FIXTURES_VIEWS = File.join(FIXTURES, 'views')
41
+ APP_VIEWS = File.join(CMSScanner::APP_DIR, 'views')