cms_scanner 0.0.11 → 0.0.12
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/.rubocop.yml +2 -0
- data/cms_scanner.gemspec +1 -1
- data/lib/cms_scanner/finders/finder.rb +12 -2
- data/lib/cms_scanner/finders/finder/smart_url_checker.rb +1 -20
- data/lib/cms_scanner/finders/finder/smart_url_checker/findings.rb +1 -1
- data/lib/cms_scanner/target.rb +1 -22
- data/lib/cms_scanner/target/hashes.rb +39 -0
- data/lib/cms_scanner/target/scope.rb +50 -0
- data/lib/cms_scanner/version.rb +1 -1
- data/spec/fixtures/target/scope/index.html +20 -0
- data/spec/lib/finders/finder/smart_url_checker/findings_spec.rb +32 -2
- data/spec/lib/finders/finder/smart_url_checker_spec.rb +50 -0
- data/spec/lib/target/hashes_spec.rb +90 -0
- data/spec/lib/target/scope_spec.rb +101 -0
- data/spec/lib/target_spec.rb +1 -57
- metadata +13 -7
- data/examples/views/cli/wp_custom/test.erb +0 -1
- data/examples/views/json/wp_custom/test.erb +0 -1
- data/examples/wpscan.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79429533403d4c37ac3c20f553e3b78de200666f
|
4
|
+
data.tar.gz: a295401b0ecf50612bd9c9c23f626009c03a8b76
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6fa364fe4c2c68a3ba41b833a0677f3e509b38000917ef782963d043051d5c90dc395028d284064a6be26ade3b309375cd4911edf80e0666d7f815ccf457038a
|
7
|
+
data.tar.gz: 9a01ac121b97750afaf2ff77e1e18ee588e037c5b4de279af36d3cd7c17bbb58ed1acc3d45d48b89b0910ff0cd7afaa831b0c36af7b3182160ace3aab486ca79
|
data/.rubocop.yml
CHANGED
data/cms_scanner.gemspec
CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
s.add_development_dependency 'rake', '~> 10.4'
|
32
32
|
s.add_development_dependency 'rspec', '~> 3.2'
|
33
33
|
s.add_development_dependency 'rspec-its', '~> 1.1'
|
34
|
-
s.add_development_dependency 'bundler', '~> 1.
|
34
|
+
s.add_development_dependency 'bundler', '~> 1.6'
|
35
35
|
s.add_development_dependency 'rubocop', '~> 0.28'
|
36
36
|
s.add_development_dependency 'webmock', '~> 1.20'
|
37
37
|
s.add_development_dependency 'simplecov', '~> 0.9'
|
@@ -13,6 +13,11 @@ module CMSScanner
|
|
13
13
|
@target = target
|
14
14
|
end
|
15
15
|
|
16
|
+
# @return [ String ] The titleize name of the finder
|
17
|
+
def titleize
|
18
|
+
self.class.to_s.demodulize.underscore.titleize
|
19
|
+
end
|
20
|
+
|
16
21
|
# @param [ Hash ] _opts
|
17
22
|
def passive(_opts = {})
|
18
23
|
end
|
@@ -22,8 +27,13 @@ module CMSScanner
|
|
22
27
|
end
|
23
28
|
|
24
29
|
def found_by
|
25
|
-
|
26
|
-
|
30
|
+
caller_locations.each do |call|
|
31
|
+
label = call.label
|
32
|
+
|
33
|
+
next unless label == 'aggressive' || label == 'passive'
|
34
|
+
|
35
|
+
return "#{titleize} (#{label.capitalize} Detection)"
|
36
|
+
end
|
27
37
|
end
|
28
38
|
end
|
29
39
|
end
|
@@ -24,20 +24,7 @@ module CMSScanner
|
|
24
24
|
#
|
25
25
|
# @return [ Array<String> ]
|
26
26
|
def passive_urls(_opts = {})
|
27
|
-
|
28
|
-
homepage = NS::Browser.get_and_follow_location(target.url).html
|
29
|
-
|
30
|
-
homepage.xpath(passive_urls_xpath).each do |node|
|
31
|
-
url = node['href'].strip
|
32
|
-
# case of relative URLs
|
33
|
-
url = target.url(url) unless url =~ /\Ahttps?:/i
|
34
|
-
|
35
|
-
next unless target.in_scope?(url)
|
36
|
-
|
37
|
-
urls << url
|
38
|
-
end
|
39
|
-
|
40
|
-
urls.uniq
|
27
|
+
target.in_scope_urls(NS::Browser.get_and_follow_location(target.url), passive_urls_xpath)
|
41
28
|
end
|
42
29
|
|
43
30
|
# @return [ String ]
|
@@ -62,12 +49,6 @@ module CMSScanner
|
|
62
49
|
def aggressive_urls(_opts = {})
|
63
50
|
fail NotImplementedError
|
64
51
|
end
|
65
|
-
|
66
|
-
# @return [ String ]
|
67
|
-
def found_by
|
68
|
-
"#{self.class.to_s.demodulize.underscore.titleize} " \
|
69
|
-
"(#{caller_locations[7].label.capitalize} Detection)"
|
70
|
-
end
|
71
52
|
end
|
72
53
|
end
|
73
54
|
end
|
data/lib/cms_scanner/target.rb
CHANGED
@@ -2,6 +2,7 @@ require 'cms_scanner/web_site'
|
|
2
2
|
require 'cms_scanner/target/platform'
|
3
3
|
require 'cms_scanner/target/server'
|
4
4
|
require 'cms_scanner/target/scope'
|
5
|
+
require 'cms_scanner/target/hashes'
|
5
6
|
|
6
7
|
module CMSScanner
|
7
8
|
# Target to Scan
|
@@ -18,28 +19,6 @@ module CMSScanner
|
|
18
19
|
[*opts[:scope]].each { |s| scope << s }
|
19
20
|
end
|
20
21
|
|
21
|
-
# @return [ Array<PublicSuffix::Domain, String> ]
|
22
|
-
def scope
|
23
|
-
@scope ||= Scope.new
|
24
|
-
end
|
25
|
-
|
26
|
-
# // are handled by Addressable::URI, but worngly :/
|
27
|
-
# e.g: Addressable::URI.parse('//file').host => file
|
28
|
-
#
|
29
|
-
# Idea: parse the // with PublicSuffix to see if a valid
|
30
|
-
# domain is used
|
31
|
-
#
|
32
|
-
# @param [ String ] url
|
33
|
-
#
|
34
|
-
# @return [ Boolean ] true if the url given is in scope
|
35
|
-
def in_scope?(url)
|
36
|
-
return true if url[0, 1] == '/' && url[1, 1] != '/'
|
37
|
-
|
38
|
-
scope.include?(Addressable::URI.parse(url).host)
|
39
|
-
rescue
|
40
|
-
false
|
41
|
-
end
|
42
|
-
|
43
22
|
# TODO: add a force option to re-call the #find rather than return the @interesting_files ?
|
44
23
|
#
|
45
24
|
# @param [ Hash ] opts
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module CMSScanner
|
2
|
+
# Scope system logic
|
3
|
+
class Target < WebSite
|
4
|
+
# @note Comments are deleted to avoid cache generation details
|
5
|
+
#
|
6
|
+
# @param [ Typhoeus::Response, String ] page
|
7
|
+
#
|
8
|
+
# @return [ String ] The md5sum of the page
|
9
|
+
def self.page_hash(page)
|
10
|
+
page = NS::Browser.get(page, followlocation: true) unless page.is_a?(Typhoeus::Response)
|
11
|
+
|
12
|
+
Digest::MD5.hexdigest(page.body.gsub(/<!--.*?-->/m, ''))
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [ String ] The hash of the homepage
|
16
|
+
def homepage_hash
|
17
|
+
@homepage_hash ||= Target.page_hash(url)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @note This is used to detect potential custom 404 responding with a 200
|
21
|
+
# @return [ String ] The hash of a 404
|
22
|
+
def error_404_hash
|
23
|
+
@error_404_hash ||= Target.page_hash(non_existant_page_url)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [ String ] The URL of an unlikely existant page
|
27
|
+
def non_existant_page_url
|
28
|
+
uri.join(Digest::MD5.hexdigest(rand(999_999_999).to_s) + '.html').to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param [ Typhoeus::Response, String ] page
|
32
|
+
# @return [ Boolean ] Wether or not the page is a the homepage or a 404 based on its md5sum
|
33
|
+
def homepage_or_404?(page)
|
34
|
+
md5sum = Target.page_hash(page)
|
35
|
+
|
36
|
+
md5sum == homepage_hash || md5sum == error_404_hash
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,5 +1,55 @@
|
|
1
1
|
module CMSScanner
|
2
|
+
# Scope system logic
|
2
3
|
class Target < WebSite
|
4
|
+
# @return [ Array<PublicSuffix::Domain, String> ]
|
5
|
+
def scope
|
6
|
+
@scope ||= Scope.new
|
7
|
+
end
|
8
|
+
|
9
|
+
# // are handled by Addressable::URI, but worngly :/
|
10
|
+
# e.g: Addressable::URI.parse('//file').host => file
|
11
|
+
#
|
12
|
+
# Idea: parse the // with PublicSuffix to see if a valid
|
13
|
+
# domain is used
|
14
|
+
#
|
15
|
+
# @param [ String ] url
|
16
|
+
#
|
17
|
+
# @return [ Boolean ] true if the url given is in scope
|
18
|
+
def in_scope?(url)
|
19
|
+
url.strip!
|
20
|
+
|
21
|
+
return true if url[0, 1] == '/' && url[1, 1] != '/'
|
22
|
+
|
23
|
+
scope.include?(Addressable::URI.parse(url).host)
|
24
|
+
rescue
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param [ Typhoeus::Response ] res
|
29
|
+
# @param [ String ] xpath
|
30
|
+
# @param [ Array<String> ] attributes
|
31
|
+
#
|
32
|
+
# @return [ Array<String> ] The in scope URLs detected in the response's body
|
33
|
+
def in_scope_urls(res, xpath = '//link|//script|//style|//img|//a', attributes = %w(href src))
|
34
|
+
found = []
|
35
|
+
|
36
|
+
res.html.xpath(xpath).each do |tag|
|
37
|
+
attributes.each do |attribute|
|
38
|
+
next unless in_scope?(tag[attribute])
|
39
|
+
|
40
|
+
attr_value = tag[attribute].strip
|
41
|
+
|
42
|
+
# Relative URL case (The // case is currently ignored by in_scope?)
|
43
|
+
attr_value = uri.join(attr_value).to_s unless attr_value =~ /\Ahttps?/i
|
44
|
+
|
45
|
+
yield attr_value if block_given? && !found.include?(attr_value)
|
46
|
+
found << attr_value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
found.uniq
|
51
|
+
end
|
52
|
+
|
3
53
|
# Scope Implementation
|
4
54
|
class Scope
|
5
55
|
# @return [ Array<PublicSuffix::Domain ] The valid domains in scope
|
data/lib/cms_scanner/version.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
<a href="http://e.org/f.txt">Link</a>
|
2
|
+
<a href="http://e.org/f.txt">Link</a> <!-- Duplicates should be ignored -->
|
3
|
+
|
4
|
+
<script src=" https://cdn.e.org/f2.js "></script> <!-- head & tail spaces should be removed -->
|
5
|
+
|
6
|
+
<script src="/script/s.js"></script>
|
7
|
+
|
8
|
+
<link rel="alternate" type="application/rss+xml" title="Spec" href="http://wp-lamp/robots.txt" />
|
9
|
+
|
10
|
+
<link rel="canonical" href="https://duckduckgo.com/">
|
11
|
+
|
12
|
+
<img src="http://out.of.scope.com/img.jpg" width="1000" height="288" alt="" />
|
13
|
+
|
14
|
+
<a href="">Empty Link</a>
|
15
|
+
|
16
|
+
<link rel="alternate" type="application/rss+xml" title="WordPress 4.1 » Feed" href="http://e.org/feed" />
|
17
|
+
|
18
|
+
<img src="//img.jpg" width="" height="" alt="" /> <!-- currently this should not be detected -->
|
19
|
+
|
20
|
+
<img src="//out.of.scope.com/img.jpg" width="" height="" alt="" />
|
@@ -1,9 +1,39 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'dummy_finding'
|
2
3
|
|
3
4
|
describe CMSScanner::Finders::Finder::SmartURLChecker::Findings do
|
4
5
|
subject(:findings) { described_class.new }
|
6
|
+
let(:finding) { CMSScanner::DummyFinding }
|
5
7
|
|
6
|
-
describe '
|
7
|
-
|
8
|
+
describe '#<<' do
|
9
|
+
after { expect(findings).to eq @expected }
|
10
|
+
|
11
|
+
context 'when no findings already in' do
|
12
|
+
it 'adds it' do
|
13
|
+
findings << finding.new('empty-test')
|
14
|
+
@expected = [finding.new('empty-test')]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when findings already in' do
|
19
|
+
let(:confirmed) { finding.new('confirmed', interesting_entries: entries) }
|
20
|
+
let(:entries) { %w(e1 e2) }
|
21
|
+
|
22
|
+
before { findings << finding.new('test') << confirmed }
|
23
|
+
|
24
|
+
it 'adds a confirmed result correctly' do
|
25
|
+
confirmed_dup = confirmed.dup
|
26
|
+
confirmed_dup.confidence = 100
|
27
|
+
confirmed_dup.interesting_entries = %w(e2 e3)
|
28
|
+
|
29
|
+
findings << confirmed_dup
|
30
|
+
|
31
|
+
confirmed.confirmed_by = confirmed_dup
|
32
|
+
|
33
|
+
@expected = [] << finding.new('test') << confirmed
|
34
|
+
|
35
|
+
expect(findings[1].interesting_entries).to eql(%w(e1 e2 e3))
|
36
|
+
end
|
37
|
+
end
|
8
38
|
end
|
9
39
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CMSScanner::Finders::Finder::SmartURLChecker do
|
4
|
+
# Dummy class to test the module
|
5
|
+
class DummyFinder < CMSScanner::Finders::Finder
|
6
|
+
include CMSScanner::Finders::Finder::SmartURLChecker
|
7
|
+
end
|
8
|
+
|
9
|
+
subject(:finder) { DummyFinder.new(target) }
|
10
|
+
let(:target) { CMSScanner::Target.new('http://e.org') }
|
11
|
+
|
12
|
+
before { stub_request(:get, target.url) }
|
13
|
+
|
14
|
+
context 'when methods are not implemented' do
|
15
|
+
it 'raises errors' do
|
16
|
+
expect { finder.process_urls([]) }.to raise_error NotImplementedError
|
17
|
+
expect { finder.passive }.to raise_error NotImplementedError
|
18
|
+
expect { finder.aggressive_urls }.to raise_error NotImplementedError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#aggressive' do
|
23
|
+
before { expect(finder).to receive(:aggressive_urls).and_return(%w(u1 u2 u3)) }
|
24
|
+
|
25
|
+
after do
|
26
|
+
expect(finder).to receive(:process_urls).with(@expected_urls, mode: mode)
|
27
|
+
finder.aggressive(mode: mode)
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when :mode = :mixed' do
|
31
|
+
before { expect(finder).to receive(:passive_urls).and_return(%w(u2)) }
|
32
|
+
|
33
|
+
let(:mode) { :mixed }
|
34
|
+
|
35
|
+
it 'calls #process_urls with the correct argument' do
|
36
|
+
@expected_urls = %w(u1 u3)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
[:passive, :aggressive].each do |m|
|
41
|
+
context "when :mode = #{m}" do
|
42
|
+
let(:mode) { m }
|
43
|
+
|
44
|
+
it 'calls #process_urls with the correct argument' do
|
45
|
+
@expected_urls = %w(u1 u2 u3)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CMSScanner::Target do
|
4
|
+
subject(:target) { described_class.new(url) }
|
5
|
+
let(:url) { 'http://e.org' }
|
6
|
+
|
7
|
+
def md5sum(body)
|
8
|
+
Digest::MD5.hexdigest(body)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#page_hash' do
|
12
|
+
after { expect(described_class.page_hash(page)).to eql @expected }
|
13
|
+
|
14
|
+
context 'when the page is an url' do
|
15
|
+
let(:page) { 'http://e.org/somepage.php' }
|
16
|
+
|
17
|
+
it 'returns the MD5 hash of the page' do
|
18
|
+
body = 'Hello World !'
|
19
|
+
|
20
|
+
stub_request(:get, page).to_return(body: body)
|
21
|
+
|
22
|
+
@expected = md5sum(body)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when the page is a Typhoeus::Response' do
|
27
|
+
let(:page) { Typhoeus::Response.new(body: 'Hello Example!') }
|
28
|
+
|
29
|
+
it 'returns the correct hash' do
|
30
|
+
@expected = md5sum('Hello Example!')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when there are comments' do
|
35
|
+
let(:page) do
|
36
|
+
body = "yolo\n\n<!--I should <script>no longer be</script> there -->\nworld!"
|
37
|
+
Typhoeus::Response.new(body: body)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'removes them' do
|
41
|
+
@expected = md5sum("yolo\n\n\nworld!")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#homepage_hash' do
|
47
|
+
it 'returns the MD5 hash of the homepage' do
|
48
|
+
body = 'Hello World'
|
49
|
+
|
50
|
+
stub_request(:get, target.url).to_return(body: body)
|
51
|
+
|
52
|
+
expect(target.homepage_hash).to eql md5sum(body)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#error_404_hash' do
|
57
|
+
it 'returns the md5sum of the 404 page' do
|
58
|
+
stub_request(:any, /.*/).to_return(status: 404, body: '404 page !')
|
59
|
+
|
60
|
+
expect(target.error_404_hash).to eql md5sum('404 page !')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#homepage_or_404?' do
|
65
|
+
let(:page_url) { target.url('page') }
|
66
|
+
|
67
|
+
before do
|
68
|
+
expect(target).to receive(:homepage_hash).and_return(md5sum('Home'))
|
69
|
+
expect(target).to receive(:error_404_hash).and_return(md5sum('Custom 404'))
|
70
|
+
|
71
|
+
stub_request(:get, page_url).to_return(body: body)
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when hashes do not match' do
|
75
|
+
let(:body) { 'Page!' }
|
76
|
+
|
77
|
+
it 'returns false' do
|
78
|
+
expect(target.homepage_or_404?(page_url)).to eql false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'when hashes match' do
|
83
|
+
let(:body) { 'Custom 404' }
|
84
|
+
|
85
|
+
it 'returns true' do
|
86
|
+
expect(target.homepage_or_404?(page_url)).to eql true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CMSScanner::Target do
|
4
|
+
subject(:target) { described_class.new(url, opts) }
|
5
|
+
let(:url) { 'http://e.org' }
|
6
|
+
let(:fixtures) { File.join(FIXTURES, 'target', 'scope') }
|
7
|
+
let(:opts) { { scope: nil } }
|
8
|
+
|
9
|
+
describe '#scope' do
|
10
|
+
let(:default_domains) { [PublicSuffix.parse('e.org')] }
|
11
|
+
|
12
|
+
context 'when none supplied' do
|
13
|
+
its('scope.domains') { should eq default_domains }
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when scope provided' do
|
17
|
+
let(:opts) { super().merge(scope: ['*.e.org']) }
|
18
|
+
|
19
|
+
its('scope.domains') { should eq default_domains << PublicSuffix.parse(opts[:scope].first) }
|
20
|
+
|
21
|
+
context 'when invalid domains provided' do
|
22
|
+
let(:opts) { super().merge(scope: ['wp-lamp', '192.168.1.12']) }
|
23
|
+
|
24
|
+
it 'adds them in the invalid_domains attribute' do
|
25
|
+
expect(target.scope.domains).to eq default_domains
|
26
|
+
expect(target.scope.invalid_domains).to eq opts[:scope]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#in_scope?' do
|
33
|
+
context 'when default scope (target domain)' do
|
34
|
+
[nil, '', 'http://out-of-scope.com', '//jquery.com/j.js'].each do |url|
|
35
|
+
it "returns false for #{url}" do
|
36
|
+
expect(target.in_scope?(url)).to eql false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
%w(https://e.org/file.txt http://e.org/ /relative).each do |url|
|
41
|
+
it "returns true for #{url}" do
|
42
|
+
expect(target.in_scope?(url)).to eql true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when custom scope' do
|
48
|
+
let(:opts) { { scope: ['*.e.org', '192.168.1.12'] } }
|
49
|
+
|
50
|
+
[nil, '', 'http://out-of-scope.com', '//jquery.com/j.js', 'http://192.168.1.2/'].each do |url|
|
51
|
+
it "returns false for #{url}" do
|
52
|
+
expect(target.in_scope?(url)).to eql false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
%w(https://cdn.e.org/file.txt http://www.e.org/ https://192.168.1.12/home).each do |url|
|
57
|
+
it "returns true for #{url}" do
|
58
|
+
expect(target.in_scope?(url)).to eql true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#in_scope_urls' do
|
65
|
+
let(:res) { Typhoeus::Response.new(body: File.open(File.join(fixtures, 'index.html'))) }
|
66
|
+
|
67
|
+
context 'when block given' do
|
68
|
+
it 'yield the url' do
|
69
|
+
expect { |b| target.in_scope_urls(res, &b) }
|
70
|
+
.to yield_successive_args('http://e.org/f.txt', 'http://e.org/script/s.js', 'http://e.org/feed')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when xpath argument given' do
|
75
|
+
it 'returns the expected array' do
|
76
|
+
xpath = '//link[@rel="alternate" and @type="application/rss+xml"]'
|
77
|
+
|
78
|
+
expect(target.in_scope_urls(res, xpath)).to eql(%w(http://e.org/feed))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'when no block given' do
|
83
|
+
after { expect(target.in_scope_urls(res)).to eql @expected }
|
84
|
+
|
85
|
+
context 'when default scope' do
|
86
|
+
it 'returns the expected array' do
|
87
|
+
@expected = %w(http://e.org/f.txt http://e.org/script/s.js http://e.org/feed)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'when supplied scope' do
|
92
|
+
let(:opts) { super().merge(scope: ['*.e.org', 'wp-lamp']) }
|
93
|
+
|
94
|
+
it 'returns the expected array' do
|
95
|
+
@expected = %w(http://e.org/f.txt https://cdn.e.org/f2.js http://e.org/script/s.js
|
96
|
+
http://wp-lamp/robots.txt http://e.org/feed)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/spec/lib/target_spec.rb
CHANGED
@@ -1,64 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe CMSScanner::Target do
|
4
|
-
subject(:target) { described_class.new(url
|
4
|
+
subject(:target) { described_class.new(url) }
|
5
5
|
let(:url) { 'http://e.org' }
|
6
|
-
let(:opts) { { scope: nil } }
|
7
|
-
|
8
|
-
describe '#scope' do
|
9
|
-
let(:default_domains) { [PublicSuffix.parse('e.org')] }
|
10
|
-
|
11
|
-
context 'when none supplied' do
|
12
|
-
its('scope.domains') { should eq default_domains }
|
13
|
-
end
|
14
|
-
|
15
|
-
context 'when scope provided' do
|
16
|
-
let(:opts) { super().merge(scope: ['*.e.org']) }
|
17
|
-
|
18
|
-
its('scope.domains') { should eq default_domains << PublicSuffix.parse(opts[:scope].first) }
|
19
|
-
|
20
|
-
context 'when invalid domains provided' do
|
21
|
-
let(:opts) { super().merge(scope: ['wp-lamp', '192.168.1.12']) }
|
22
|
-
|
23
|
-
it 'adds them in the invalid_domains attribute' do
|
24
|
-
expect(target.scope.domains).to eq default_domains
|
25
|
-
expect(target.scope.invalid_domains).to eq opts[:scope]
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
describe '#in_scope?' do
|
32
|
-
context 'when default scope (target domain)' do
|
33
|
-
[nil, '', 'http://out-of-scope.com', '//jquery.com/j.js'].each do |url|
|
34
|
-
it "returns false for #{url}" do
|
35
|
-
expect(target.in_scope?(url)).to eql false
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
%w(https://e.org/file.txt http://e.org/ /relative).each do |url|
|
40
|
-
it "returns true for #{url}" do
|
41
|
-
expect(target.in_scope?(url)).to eql true
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context 'when custom scope' do
|
47
|
-
let(:opts) { { scope: ['*.e.org', '192.168.1.12'] } }
|
48
|
-
|
49
|
-
[nil, '', 'http://out-of-scope.com', '//jquery.com/j.js', 'http://192.168.1.2/'].each do |url|
|
50
|
-
it "returns false for #{url}" do
|
51
|
-
expect(target.in_scope?(url)).to eql false
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
%w(https://cdn.e.org/file.txt http://www.e.org/ https://192.168.1.12/home).each do |url|
|
56
|
-
it "returns true for #{url}" do
|
57
|
-
expect(target.in_scope?(url)).to eql true
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
6
|
|
63
7
|
describe '#interesting_files' do
|
64
8
|
before do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cms_scanner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- WPScanTeam - Erwan Le Rousseau
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opt_parse_validator
|
@@ -142,14 +142,14 @@ dependencies:
|
|
142
142
|
requirements:
|
143
143
|
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: '1.
|
145
|
+
version: '1.6'
|
146
146
|
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: '1.
|
152
|
+
version: '1.6'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: rubocop
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -240,9 +240,6 @@ files:
|
|
240
240
|
- app/views/json/interesting_files/findings.erb
|
241
241
|
- app/views/json/scan_aborted.erb
|
242
242
|
- cms_scanner.gemspec
|
243
|
-
- examples/views/cli/wp_custom/test.erb
|
244
|
-
- examples/views/json/wp_custom/test.erb
|
245
|
-
- examples/wpscan.rb
|
246
243
|
- lib/cms_scanner.rb
|
247
244
|
- lib/cms_scanner/browser.rb
|
248
245
|
- lib/cms_scanner/browser/actions.rb
|
@@ -267,6 +264,7 @@ files:
|
|
267
264
|
- lib/cms_scanner/formatter/buffer.rb
|
268
265
|
- lib/cms_scanner/public_suffix/domain.rb
|
269
266
|
- lib/cms_scanner/target.rb
|
267
|
+
- lib/cms_scanner/target/hashes.rb
|
270
268
|
- lib/cms_scanner/target/platform.rb
|
271
269
|
- lib/cms_scanner/target/platform/php.rb
|
272
270
|
- lib/cms_scanner/target/scope.rb
|
@@ -312,6 +310,7 @@ files:
|
|
312
310
|
- spec/fixtures/output.txt
|
313
311
|
- spec/fixtures/target/platform/php/debug_log/debug.log
|
314
312
|
- spec/fixtures/target/platform/php/fpd/wp_rss_functions.php
|
313
|
+
- spec/fixtures/target/scope/index.html
|
315
314
|
- spec/fixtures/target/server/apache/directory_listing/2.2.16.html
|
316
315
|
- spec/fixtures/target/server/generic/server/apache/basic.txt
|
317
316
|
- spec/fixtures/target/server/generic/server/iis/basic.txt
|
@@ -332,6 +331,7 @@ files:
|
|
332
331
|
- spec/lib/controllers_spec.rb
|
333
332
|
- spec/lib/finders/confidence_spec.rb
|
334
333
|
- spec/lib/finders/finder/smart_url_checker/findings_spec.rb
|
334
|
+
- spec/lib/finders/finder/smart_url_checker_spec.rb
|
335
335
|
- spec/lib/finders/findings_spec.rb
|
336
336
|
- spec/lib/finders/independent_finders_spec.rb
|
337
337
|
- spec/lib/finders/unique_finder_spec.rb
|
@@ -339,7 +339,9 @@ files:
|
|
339
339
|
- spec/lib/formatter_spec.rb
|
340
340
|
- spec/lib/public_suffix/domain_spec.rb
|
341
341
|
- spec/lib/sub_scanner_spec.rb
|
342
|
+
- spec/lib/target/hashes_spec.rb
|
342
343
|
- spec/lib/target/platforms_spec.rb
|
344
|
+
- spec/lib/target/scope_spec.rb
|
343
345
|
- spec/lib/target/servers_spec.rb
|
344
346
|
- spec/lib/target_spec.rb
|
345
347
|
- spec/lib/web_site_spec.rb
|
@@ -423,6 +425,7 @@ test_files:
|
|
423
425
|
- spec/fixtures/output.txt
|
424
426
|
- spec/fixtures/target/platform/php/debug_log/debug.log
|
425
427
|
- spec/fixtures/target/platform/php/fpd/wp_rss_functions.php
|
428
|
+
- spec/fixtures/target/scope/index.html
|
426
429
|
- spec/fixtures/target/server/apache/directory_listing/2.2.16.html
|
427
430
|
- spec/fixtures/target/server/generic/server/apache/basic.txt
|
428
431
|
- spec/fixtures/target/server/generic/server/iis/basic.txt
|
@@ -443,6 +446,7 @@ test_files:
|
|
443
446
|
- spec/lib/controllers_spec.rb
|
444
447
|
- spec/lib/finders/confidence_spec.rb
|
445
448
|
- spec/lib/finders/finder/smart_url_checker/findings_spec.rb
|
449
|
+
- spec/lib/finders/finder/smart_url_checker_spec.rb
|
446
450
|
- spec/lib/finders/findings_spec.rb
|
447
451
|
- spec/lib/finders/independent_finders_spec.rb
|
448
452
|
- spec/lib/finders/unique_finder_spec.rb
|
@@ -450,7 +454,9 @@ test_files:
|
|
450
454
|
- spec/lib/formatter_spec.rb
|
451
455
|
- spec/lib/public_suffix/domain_spec.rb
|
452
456
|
- spec/lib/sub_scanner_spec.rb
|
457
|
+
- spec/lib/target/hashes_spec.rb
|
453
458
|
- spec/lib/target/platforms_spec.rb
|
459
|
+
- spec/lib/target/scope_spec.rb
|
454
460
|
- spec/lib/target/servers_spec.rb
|
455
461
|
- spec/lib/target_spec.rb
|
456
462
|
- spec/lib/web_site_spec.rb
|
@@ -1 +0,0 @@
|
|
1
|
-
Testing! --wpscan-option = <%= @option %>
|
@@ -1 +0,0 @@
|
|
1
|
-
"--wpscan-option": <%= @option.to_json %>,
|
data/examples/wpscan.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'cms_scanner'
|
4
|
-
|
5
|
-
# Custom WPScan Scanner
|
6
|
-
module WPScan
|
7
|
-
include CMSScanner
|
8
|
-
|
9
|
-
module Controller
|
10
|
-
# Custom WPScan Controller
|
11
|
-
class WpCustom < CMSScanner::Controller::Base
|
12
|
-
def cli_options
|
13
|
-
[
|
14
|
-
OptString.new(['--wpscan-option VALUE'])
|
15
|
-
]
|
16
|
-
end
|
17
|
-
|
18
|
-
def run
|
19
|
-
output('test', option: parsed_options[:wpscan_option])
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
WPScan::Scan.new do |s|
|
26
|
-
s.controllers << WPScan::Controller::WpCustom.new
|
27
|
-
s.views_directories << Pathname.new(__FILE__).dirname.join('views').to_s
|
28
|
-
s.run
|
29
|
-
end
|