cms_scanner 0.0.11 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- 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
|