site-inspector 1.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +3 -0
  6. data/Guardfile +8 -0
  7. data/README.md +175 -0
  8. data/Rakefile +8 -0
  9. data/bin/site-inspector +48 -21
  10. data/lib/site-inspector.rb +38 -613
  11. data/lib/site-inspector/cache.rb +9 -52
  12. data/lib/site-inspector/checks/check.rb +41 -0
  13. data/lib/site-inspector/checks/content.rb +67 -0
  14. data/lib/site-inspector/checks/dns.rb +129 -0
  15. data/lib/site-inspector/checks/headers.rb +83 -0
  16. data/lib/site-inspector/checks/hsts.rb +78 -0
  17. data/lib/site-inspector/checks/https.rb +40 -0
  18. data/lib/site-inspector/checks/sniffer.rb +42 -0
  19. data/lib/site-inspector/disk_cache.rb +38 -0
  20. data/lib/site-inspector/domain.rb +248 -0
  21. data/lib/site-inspector/endpoint.rb +200 -0
  22. data/lib/site-inspector/rails_cache.rb +11 -0
  23. data/lib/site-inspector/version.rb +3 -0
  24. data/script/bootstrap +1 -0
  25. data/script/cibuild +7 -0
  26. data/script/console +1 -0
  27. data/script/release +38 -0
  28. data/site-inspector.gemspec +33 -0
  29. data/spec/checks/site_inspector_endpoint_check_spec.rb +34 -0
  30. data/spec/checks/site_inspector_endpoint_content_spec.rb +89 -0
  31. data/spec/checks/site_inspector_endpoint_dns_spec.rb +167 -0
  32. data/spec/checks/site_inspector_endpoint_headers_spec.rb +74 -0
  33. data/spec/checks/site_inspector_endpoint_hsts_spec.rb +91 -0
  34. data/spec/checks/site_inspector_endpoint_https_spec.rb +48 -0
  35. data/spec/checks/site_inspector_endpoint_sniffer_spec.rb +52 -0
  36. data/spec/site_inspector_cache_spec.rb +13 -0
  37. data/spec/site_inspector_disc_cache_spec.rb +31 -0
  38. data/spec/site_inspector_domain_spec.rb +252 -0
  39. data/spec/site_inspector_endpoint_spec.rb +224 -0
  40. data/spec/site_inspector_spec.rb +46 -0
  41. data/spec/spec_helper.rb +17 -0
  42. metadata +75 -57
  43. data/lib/site-inspector/compliance.rb +0 -19
  44. data/lib/site-inspector/dns.rb +0 -92
  45. data/lib/site-inspector/headers.rb +0 -59
  46. data/lib/site-inspector/sniffer.rb +0 -26
@@ -0,0 +1,11 @@
1
+ class SiteInspector
2
+ class RailsCache
3
+ def get(request)
4
+ Rails.cache.read(request)
5
+ end
6
+
7
+ def set(request, response)
8
+ Rails.cache.write(request, response)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ class SiteInspector
2
+ VERSION = "2.0.0"
3
+ end
data/script/bootstrap ADDED
@@ -0,0 +1 @@
1
+ bundle install
data/script/cibuild ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+
3
+ set -e
4
+
5
+ bundle exec rake spec
6
+
7
+ gem build site-inspector.gemspec
data/script/console ADDED
@@ -0,0 +1 @@
1
+ bundle exec pry -r ./lib/site-inspector.rb
data/script/release ADDED
@@ -0,0 +1,38 @@
1
+ #!/bin/sh
2
+ # Tag and push a release.
3
+
4
+ set -e
5
+
6
+ # Make sure we're in the project root.
7
+
8
+ cd $(dirname "$0")/..
9
+
10
+ # Build a new gem archive.
11
+
12
+ rm -rf site-inspector-*.gem
13
+ gem build -q site-inspector.gemspec
14
+
15
+ # Make sure we're on the master branch.
16
+
17
+ (git branch | grep -q '* master') || {
18
+ echo "Only release from the master branch."
19
+ exit 1
20
+ }
21
+
22
+ # Figure out what version we're releasing.
23
+
24
+ tag=v`ls site-inspector-*.gem | sed 's/^site-inspector-\(.*\)\.gem$/\1/'`
25
+
26
+ # Make sure we haven't released this version before.
27
+
28
+ git fetch -t origin
29
+
30
+ (git tag -l | grep -q "$tag") && {
31
+ echo "Whoops, there's already a '${tag}' tag."
32
+ exit 1
33
+ }
34
+
35
+ # Tag it and bag it.
36
+
37
+ gem push site-inspector-*.gem && git tag "$tag" &&
38
+ git push origin master && git push origin "$tag"
@@ -0,0 +1,33 @@
1
+ require File.expand_path "./lib/site-inspector/version", File.dirname(__FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+
5
+ s.name = "site-inspector"
6
+ s.version = SiteInspector::VERSION
7
+ s.summary = "A Ruby port and v2 of Site Inspector (http://github.com/benbalter/site-inspector)"
8
+ s.description = "Returns information about a domain's technology and capabilities"
9
+ s.authors = "Ben Balter"
10
+ s.email = "ben@balter.com"
11
+ s.homepage = "https://github.com/benbalter/site-inspector-ruby"
12
+ s.license = "MIT"
13
+
14
+ s.files = `git ls-files -z`.split("\x0")
15
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_dependency("nokogiri", "~> 1.6")
20
+ s.add_dependency("public_suffix", "~> 1.4")
21
+ s.add_dependency("gman", "~> 4.1")
22
+ s.add_dependency("dnsruby", "~> 1.56")
23
+ s.add_dependency("sniffles", "~> 0.2")
24
+ s.add_dependency("typhoeus", "~> 0.7")
25
+ s.add_dependency("oj", "~> 2.11")
26
+ s.add_dependency("mercenary", "~> 0.3")
27
+ s.add_dependency("colorator", "~> 0.1")
28
+ s.add_development_dependency("pry", "~> 0.10")
29
+ s.add_development_dependency( "rake", "~> 10.4" )
30
+ s.add_development_dependency( "rspec", "~> 3.2")
31
+ s.add_development_dependency( "bundler", "~> 1.6" )
32
+ s.add_development_dependency( "webmock", "~> 1.2" )
33
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe SiteInspector::Endpoint::Check do
4
+
5
+ subject do
6
+ stub_request(:get, "http://example.com/").to_return(:status => 200)
7
+ endpoint = SiteInspector::Endpoint.new("http://example.com")
8
+ SiteInspector::Endpoint::Check.new(endpoint)
9
+ end
10
+
11
+ it "returns the endpoint" do
12
+ expect(subject.endpoint.class).to eql(SiteInspector::Endpoint)
13
+ end
14
+
15
+ it "returns the response" do
16
+ expect(subject.response.class).to eql(Typhoeus::Response)
17
+ end
18
+
19
+ it "returns the request" do
20
+ expect(subject.request.class).to eql(Typhoeus::Request)
21
+ end
22
+
23
+ it "returns the host" do
24
+ expect(subject.host).to eql("example.com")
25
+ end
26
+
27
+ it "returns its name" do
28
+ expect(subject.name).to eql(:check)
29
+ end
30
+
31
+ it "returns the instance name" do
32
+ expect(SiteInspector::Endpoint::Check.name).to eql(:check)
33
+ end
34
+ end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ describe SiteInspector::Endpoint::Content do
4
+
5
+ subject do
6
+ body = <<-eos
7
+ <!DOCTYPE html>
8
+ <html>
9
+ <body>
10
+ <h1>Some page</h1>
11
+ </body>
12
+ </html>
13
+ eos
14
+
15
+ stub_request(:get, "http://example.com/").
16
+ to_return(:status => 200, :body => body )
17
+ endpoint = SiteInspector::Endpoint.new("http://example.com")
18
+ SiteInspector::Endpoint::Content.new(endpoint)
19
+ end
20
+
21
+ it "returns the doc" do
22
+ expect(subject.document.class).to eql(Nokogiri::HTML::Document)
23
+ expect(subject.document.css("h1").text).to eql("Some page")
24
+ end
25
+
26
+ it "returns the body" do
27
+ expect(subject.body).to match("<h1>Some page</h1>")
28
+ end
29
+
30
+ it "returns the doctype" do
31
+ expect(subject.doctype).to eql("html")
32
+ end
33
+
34
+ it "knows when robots.txt exists" do
35
+ stub_request(:get, "http://example.com/robots.txt").
36
+ to_return(:status => 200)
37
+ expect(subject.robots_txt?).to eql(true)
38
+ end
39
+
40
+ it "knows when robots.txt doesn't exist" do
41
+ stub_request(:get, "http://example.com/robots.txt").
42
+ to_return(:status => 404)
43
+ expect(subject.robots_txt?).to eql(false)
44
+ end
45
+
46
+ it "knows when sitemap.xml exists" do
47
+ stub_request(:get, "http://example.com/sitemap.xml").
48
+ to_return(:status => 200)
49
+ expect(subject.sitemap_xml?).to eql(true)
50
+ end
51
+
52
+ it "knows when sitemap.xml exists" do
53
+ stub_request(:get, "http://example.com/sitemap.xml").
54
+ to_return(:status => 404)
55
+ expect(subject.sitemap_xml?).to eql(false)
56
+ end
57
+
58
+ it "knows when humans.txt exists" do
59
+ stub_request(:get, "http://example.com/humans.txt").
60
+ to_return(:status => 200)
61
+ expect(subject.humans_txt?).to eql(true)
62
+ end
63
+
64
+ it "knows when humans.txt doesn't exist" do
65
+ stub_request(:get, "http://example.com/humans.txt").
66
+ to_return(:status => 200)
67
+ expect(subject.humans_txt?).to eql(true)
68
+ end
69
+
70
+ context "404s" do
71
+ it "knows when an endpoint returns a proper 404" do
72
+ stub_request(:get, /http\:\/\/example.com\/.*/).
73
+ to_return(:status => 404)
74
+ expect(subject.proper_404s?).to eql(true)
75
+ end
76
+
77
+ it "knows when an endpoint doesn't return a proper 404" do
78
+ stub_request(:get, /http\:\/\/example.com\/[a-z0-9]{32}/i).
79
+ to_return(:status => 200)
80
+ expect(subject.proper_404s?).to eql(false)
81
+ end
82
+
83
+ it "generates a random path" do
84
+ path = subject.send(:random_path)
85
+ expect(path).to match /[a-z0-9]{32}/i
86
+ expect(subject.send(:random_path)).to eql(path)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+ require 'dnsruby'
3
+
4
+ describe SiteInspector::Endpoint::Dns do
5
+
6
+ subject do
7
+ stub_request(:get, "http://github.com/").to_return(:status => 200)
8
+ endpoint = SiteInspector::Endpoint.new("http://github.com")
9
+ SiteInspector::Endpoint::Dns.new(endpoint)
10
+ end
11
+
12
+ it "inits the resolver" do
13
+ expect(SiteInspector::Endpoint::Dns.resolver.class).to eql(Dnsruby::Resolver)
14
+ end
15
+
16
+ # Note: these tests makes external calls
17
+ context "live tests" do
18
+ it "it runs the query" do
19
+ expect(subject.query).not_to be_empty
20
+ end
21
+
22
+ context "resolv" do
23
+ it "returns the IP" do
24
+ expect(subject.ip).to include("192.30.252.")
25
+ end
26
+
27
+ it "returns the hostname" do
28
+ expect(subject.hostname.sld).to eql("github")
29
+
30
+ end
31
+ end
32
+ end
33
+
34
+ context "stubbed tests" do
35
+
36
+ before do
37
+ record = Dnsruby::RR.create :type => "A", :address => "1.2.3.4", :name => "test"
38
+ allow(subject).to receive(:records) { [record] }
39
+ allow(subject).to receive(:query) { [] }
40
+ end
41
+
42
+ it "returns the records" do
43
+ expect(subject.records.count).to eql(1)
44
+ expect(subject.records.first.class).to eql(Dnsruby::RR::IN::A)
45
+ end
46
+
47
+ it "knows if a record exists" do
48
+ expect(subject.has_record?("A")).to eql(true)
49
+ expect(subject.has_record?("CNAME")).to eql(false)
50
+ end
51
+
52
+ it "knows if a domain supports dnssec" do
53
+ expect(subject.dnssec?).to eql(false)
54
+
55
+ # via https://github.com/alexdalitz/dnsruby/blob/master/test/tc_dnskey.rb
56
+ input = "example.com. 86400 IN DNSKEY 256 3 5 ( AQPSKmynfzW4kyBv015MUG2DeIQ3" +
57
+ "Cbl+BBZH4b/0PY1kxkmvHjcZc8no" +
58
+ "kfzj31GajIQKY+5CptLr3buXA10h" +
59
+ "WqTkF7H6RfoRqXQeogmMHfpftf6z" +
60
+ "Mv1LyBUgia7za6ZEzOJBOztyvhjL" +
61
+ "742iU/TpPSEDhm2SNKLijfUppn1U" +
62
+ "aNvv4w== )"
63
+
64
+ record = Dnsruby::RR.create input
65
+ allow(subject).to receive(:records) { [record] }
66
+
67
+ expect(subject.dnssec?).to eql(true)
68
+ end
69
+
70
+ it "knows if a domain supports ipv6" do
71
+ expect(subject.ipv6?).to eql(false)
72
+
73
+ input = {
74
+ :type => "AAAA",
75
+ :name => "test",
76
+ :address => '102:304:506:708:90a:b0c:d0e:ff10'
77
+ }
78
+ record = Dnsruby::RR.create input
79
+ allow(subject).to receive(:records) { [record] }
80
+
81
+ expect(subject.ipv6?).to eql(true)
82
+ end
83
+
84
+ context "hostname detection" do
85
+ it "lists cnames" do
86
+ records = []
87
+
88
+ records.push Dnsruby::RR.create({
89
+ :type => "CNAME",
90
+ :domainname => "example.com",
91
+ :name => "example"
92
+ })
93
+
94
+ records.push Dnsruby::RR.create({
95
+ :type => "CNAME",
96
+ :domainname => "github.com",
97
+ :name => "github"
98
+ })
99
+
100
+ allow(subject).to receive(:records) { records }
101
+
102
+ expect(subject.cnames.count).to eql(2)
103
+ expect(subject.cnames.first.sld).to eql("example")
104
+ end
105
+
106
+ it "knows when a domain doesn't have a cdn" do
107
+ expect(subject.cdn?).to eql(false)
108
+ end
109
+
110
+ it "detects CDNs" do
111
+ records = [Dnsruby::RR.create({
112
+ :type => "CNAME",
113
+ :domainname => "foo.cloudfront.net",
114
+ :name => "example"
115
+ })]
116
+ allow(subject).to receive(:records) { records }
117
+
118
+ expect(subject.send(:detect_by_hostname, "cdn")).to eql(:cloudfront)
119
+ expect(subject.cdn).to eql(:cloudfront)
120
+ expect(subject.cdn?).to eql(true)
121
+ end
122
+
123
+ it "builds that path to a data file" do
124
+ path = subject.send(:data_path, "foo")
125
+ expected = File.expand_path "../../lib/data/foo.yml", File.dirname(__FILE__)
126
+ expect(path).to eql(expected)
127
+ end
128
+
129
+ it "loads data files" do
130
+ data = subject.send(:load_data, "cdn")
131
+ expect(data.keys).to include("cloudfront")
132
+ end
133
+
134
+ it "knows when a domain isn't cloud" do
135
+ expect(subject.cloud?).to eql(false)
136
+ end
137
+
138
+ it "detects cloud providers" do
139
+ records = [Dnsruby::RR.create({
140
+ :type => "CNAME",
141
+ :domainname => "foo.herokuapp.com",
142
+ :name => "example"
143
+ })]
144
+ allow(subject).to receive(:records) { records }
145
+
146
+ expect(subject.send(:detect_by_hostname, "cloud")).to eql(:heroku)
147
+ expect(subject.cloud_provider).to eql(:heroku)
148
+ expect(subject.cloud?).to eql(true)
149
+ end
150
+
151
+ it "knows when a domain doesn't have google apps" do
152
+ expect(subject.google_apps?).to eql(false)
153
+ end
154
+
155
+ it "knows when a domain is using google apps" do
156
+ records = [Dnsruby::RR.create({
157
+ :type => "MX",
158
+ :exchange => "mx1.google.com",
159
+ :name => "example",
160
+ :preference => 10
161
+ })]
162
+ allow(subject).to receive(:records) { records }
163
+ expect(subject.google_apps?).to eql(true)
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe SiteInspector::Endpoint::Headers do
4
+
5
+ subject do
6
+ stub_request(:get, "http://example.com/").
7
+ to_return(:status => 200, :headers => { :foo => "bar" } )
8
+ endpoint = SiteInspector::Endpoint.new("http://example.com")
9
+ SiteInspector::Endpoint::Headers.new(endpoint)
10
+ end
11
+
12
+ def stub_header(header, value)
13
+ allow(subject).to receive(:headers) { { header => value } }
14
+ end
15
+
16
+ it "parses the headers" do
17
+ expect(subject.headers.count).to eql(1)
18
+ expect(subject.headers.keys).to include("foo")
19
+ end
20
+
21
+ it "returns a header" do
22
+ expect(subject["foo"]).to eql("bar")
23
+ expect(subject.headers["foo"]).to eql("bar")
24
+ end
25
+
26
+ it "knows the server" do
27
+ stub_header "server", "foo"
28
+ expect(subject.server).to eql("foo")
29
+ end
30
+
31
+ it "knows if a server has an xss protection header" do
32
+ stub_header "x-xss-protection", "foo"
33
+ expect(subject.xss_protection).to eql("foo")
34
+ end
35
+
36
+ it "validates xss-protection" do
37
+ stub_header "x-xss-protection", "foo"
38
+ expect(subject.xss_protection?).to eql(false)
39
+
40
+ stub_header "x-xss-protection", "1; mode=block"
41
+ expect(subject.xss_protection?).to eql(true)
42
+ end
43
+
44
+ it "checks for clickjack proetection" do
45
+ expect(subject.click_jacking_protection?).to eql(false)
46
+ stub_header "x-frame-options", "foo"
47
+ expect(subject.click_jacking_protection).to eql("foo")
48
+ expect(subject.click_jacking_protection?).to eql(true)
49
+ end
50
+
51
+ it "checks for CSP" do
52
+ expect(subject.content_security_policy?).to eql(false)
53
+ stub_header "content-security-policy", "foo"
54
+ expect(subject.content_security_policy).to eql("foo")
55
+ expect(subject.content_security_policy?).to eql(true)
56
+ end
57
+
58
+ it "checks for strict-transport-security" do
59
+ expect(subject.strict_transport_security?).to eql(false)
60
+ stub_header "strict-transport-security", "foo"
61
+ expect(subject.strict_transport_security).to eql("foo")
62
+ expect(subject.strict_transport_security?).to eql(true)
63
+ end
64
+
65
+ it "knows if there are cookies" do
66
+ expect(subject.cookies?).to eql(false)
67
+ stub_header "set-cookie", "foo"
68
+ expect(subject.cookies?).to eql(true)
69
+ end
70
+
71
+ it "knows if the cookies are secure" do
72
+
73
+ end
74
+ end