site-inspector 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 23a0a7c4e9875a9bec4ce8f904c75e9c29f292d4
4
+ data.tar.gz: 5fea15e6dc4a6b1b59243ae300a30a5f9cb2af5b
5
+ SHA512:
6
+ metadata.gz: 37e8b7e3f3579f266c8c61cd2d712c203f693734edd58d4fa97f1881fc856499e72205baaed781d12662f263a61d05b824deb66387808999352f821045b483fd
7
+ data.tar.gz: b4b374743289936b256fdb949537f9095521dcb6c0720abf38d5df529c61c2b8e48b80a00ca4bf0f5ff8717dd884105767c67b6a30f12ff9f664732b53d263bd
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Ben Balter
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/data/cdn.yml ADDED
@@ -0,0 +1,3 @@
1
+ #fastly: ""
2
+ cloudfront: "cloutfront.net"
3
+ akamai: "akamaitechnologies.com"
@@ -0,0 +1,6 @@
1
+ amazon: "compute-1.amazonaws.com"
2
+ Amazon GovCloud: "us-gov-west-1.compute.amazonaws.com"
3
+ linode: "lindoe.com"
4
+ rackspace: ""
5
+ heroku: "herokuapp.com"
6
+ github: "github.io"
@@ -0,0 +1,13 @@
1
+ class SiteInspectorCache
2
+ def initialize
3
+ @memory = {}
4
+ end
5
+
6
+ def get(request)
7
+ @memory[request]
8
+ end
9
+
10
+ def set(request, response)
11
+ @memory[request] = response
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ class SiteInspector
2
+
3
+ def path_exists?(path)
4
+ url = URI.join uri, path
5
+ Typhoeus::Request.get(url, followlocation: true, timeout: 10).success?
6
+ end
7
+
8
+ def slash_data?
9
+ @slash_data ||= path_exists?("/data")
10
+ end
11
+
12
+ def slash_developer?
13
+ @slash_developer ||= (path_exists?("/developer") || path_exists?("/developers"))
14
+ end
15
+
16
+ def data_dot_json?
17
+ @data_dot_json ||= path_exists?("/data.json")
18
+ end
19
+ end
@@ -0,0 +1,81 @@
1
+ class SiteInspector
2
+
3
+ def resolver
4
+ @resolver ||= Dnsruby::Resolver.new
5
+ end
6
+
7
+ def query(type="ANY")
8
+ resolver.query(domain.to_s, type).answer
9
+ rescue
10
+ []
11
+ end
12
+
13
+ def dns
14
+ @dns ||= query
15
+ end
16
+
17
+ def dnssec?
18
+ @dnssec ||= dns.any? { |record| record.type == "DNSKEY" }
19
+ end
20
+
21
+ def ipv6?
22
+ @ipv6 ||= dns.any? { |record| record.type == "AAAA" }
23
+ end
24
+
25
+ def detect_by_hostname(type)
26
+
27
+ haystack = load_data(type)
28
+ needle = haystack.find { |name, domain|
29
+ cnames.any? { |cname|
30
+ domain == cname.tld || domain == "#{cname.sld}.#{cname.tld}"
31
+ }
32
+ }
33
+
34
+ return needle[0] if needle
35
+ return false unless hostname
36
+
37
+ needle = haystack.find { |name, domain|
38
+ domain == hostname.tld || domain == "#{hostname.sld}.#{hostname.tld}"
39
+ }
40
+
41
+ needle ? needle[0] : false
42
+ end
43
+
44
+ def cdn
45
+ detect_by_hostname "cdn"
46
+ end
47
+
48
+ def cdn?
49
+ !!cdn
50
+ end
51
+
52
+ def cloud_provider
53
+ detect_by_hostname "cloud"
54
+ end
55
+
56
+ def cloud?
57
+ !!cloud_provider
58
+ end
59
+
60
+ def google_apps?
61
+ @google ||= dns.any? do |record|
62
+ record.type == "MX" && record.exchange =~ /google(mail)?\.com\.?$/
63
+ end
64
+ end
65
+
66
+ def ip
67
+ @ip ||= Resolv.getaddress domain.to_s
68
+ rescue Resolv::ResolvError
69
+ nil
70
+ end
71
+
72
+ def hostname
73
+ @hostname ||= PublicSuffix.parse(Resolv.getname(ip))
74
+ rescue Exception => e
75
+ nil
76
+ end
77
+
78
+ def cnames
79
+ @cnames ||= dns.select {|record| record.type == "CNAME" }.map { |record| PublicSuffix.parse(record.cname.to_s) }
80
+ end
81
+ end
@@ -0,0 +1,34 @@
1
+ class SiteInspector
2
+ def server
3
+ response && response.headers["Server"]
4
+ end
5
+
6
+ def xss_protection?
7
+ response && response.headers["X-XSS-Protection"] == "1; mode=block"
8
+ end
9
+
10
+ def has_cookies?
11
+ response && response.headers.include?("Set-Cookie")
12
+ end
13
+
14
+ def secure_cookies?
15
+ return nil if !response || !has_cookies?
16
+ cookie = response.headers["Set-Cookie"]
17
+ cookie = cookie.first if cookie.is_a?(Array)
18
+ marked_secure = !!(cookie.downcase =~ /secure/)
19
+ marked_http_only = !!(cookie.downcase =~ /HttpOnly/)
20
+ marked_secure and marked_http_only
21
+ end
22
+
23
+ def strict_transport_security?
24
+ response && response.headers.include?("Strict-Transport-Security")
25
+ end
26
+
27
+ def content_security_policy?
28
+ response && response.headers.include?("Content-Security-Policy")
29
+ end
30
+
31
+ def click_jacking_protection?
32
+ response && response.headers.include?("X-Frame-Options")
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ class SiteInspector
2
+ def sniff(type)
3
+ results = Sniffles.sniff(body, type).select { |name, meta| meta[:found] == true }
4
+ results.each { |name, result| result.delete :found} if results
5
+ results
6
+ end
7
+
8
+ def cms
9
+ sniff :cms
10
+ end
11
+
12
+ def analytics
13
+ sniff :analytics
14
+ end
15
+
16
+ def javascript
17
+ sniff :javascript
18
+ end
19
+
20
+ def advertising
21
+ sniff :advertising
22
+ end
23
+
24
+ end
@@ -0,0 +1,163 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+ require 'public_suffix'
4
+ require 'gman'
5
+ require 'net/http'
6
+ require "dnsruby"
7
+ require 'yaml'
8
+ require 'sniffles'
9
+ require "addressable/uri"
10
+ require 'typhoeus'
11
+ require 'json'
12
+ require 'resolv'
13
+
14
+ require_relative 'site-inspector/cache'
15
+ require_relative 'site-inspector/sniffer'
16
+ require_relative 'site-inspector/dns'
17
+ require_relative 'site-inspector/compliance'
18
+ require_relative 'site-inspector/headers'
19
+
20
+ Typhoeus::Config.cache = SiteInspectorCache.new
21
+
22
+ class SiteInspector
23
+
24
+ def initialize(domain)
25
+ domain = domain.downcase
26
+ domain = domain.sub /^http\:/, ""
27
+ domain = domain.sub /^\/+/, ""
28
+ domain = domain.sub /^www\./, ""
29
+ @uri = Addressable::URI.parse "//#{domain}"
30
+ @domain = PublicSuffix.parse @uri.host
31
+ end
32
+
33
+ def inspect
34
+ "<SiteInspector domain=\"#{domain}\">"
35
+ end
36
+
37
+ def uri(ssl=https?,www=www?)
38
+ uri = @uri.clone
39
+ uri.host = "www.#{uri.host}" if www
40
+ uri.scheme = ssl ? "https" : "http"
41
+ uri
42
+ end
43
+
44
+ def domain
45
+ non_www? ? @domain : PublicSuffix.parse("www.#{@uri.host}")
46
+ end
47
+
48
+ def request(ssl=false, www=false, followlocation=true)
49
+ Typhoeus::Request.get(uri(ssl, www), followlocation: followlocation, timeout: 10)
50
+ end
51
+
52
+ def response
53
+ @response ||= begin
54
+ if response = request(false, false) and response.success?
55
+ @non_www = true
56
+ response
57
+ elsif response = request(false, true) and response.success?
58
+ @non_www = false
59
+ response
60
+ else
61
+ false
62
+ end
63
+ end
64
+ end
65
+
66
+ def timed_out?
67
+ response && response.timed_out?
68
+ end
69
+
70
+ def doc
71
+ @doc ||= Nokogiri::HTML response.body if response
72
+ end
73
+
74
+ def body
75
+ doc.to_s
76
+ end
77
+
78
+ def load_data(name)
79
+ YAML.load_file File.expand_path "./data/#{name}.yml", File.dirname(__FILE__)
80
+ end
81
+
82
+ def government?
83
+ Gman.valid? domain.to_s
84
+ end
85
+
86
+ def https?
87
+ @https ||= request(true, www?).success?
88
+ end
89
+ alias_method :ssl?, :https?
90
+
91
+ def enforce_https?
92
+ return false unless https?
93
+ @enforce_https ||= begin
94
+ response = request(false, www?)
95
+ if response.effective_url
96
+ Addressable::URI.parse(response.effective_url).scheme == "https"
97
+ else
98
+ puts response.inspect
99
+ false
100
+ end
101
+ end
102
+ end
103
+
104
+ def www?
105
+ response && response.effective_url && !!response.effective_url.match(/https?:\/\/www\./)
106
+ end
107
+
108
+ def non_www?
109
+ response && @non_www
110
+ end
111
+
112
+ def redirect?
113
+ !!redirect
114
+ end
115
+
116
+ def redirect
117
+ @redirect ||= begin
118
+ if location = request(https?, www?, false).headers["location"]
119
+ redirect_domain = SiteInspector.new(location).domain
120
+ redirect_domain.to_s if redirect_domain.to_s != domain.to_s
121
+ end
122
+ rescue
123
+ nil
124
+ end
125
+ end
126
+
127
+ def to_json
128
+ as_json.to_json
129
+ end
130
+
131
+ def as_json
132
+ {
133
+ :domain => domain.to_s,
134
+ :uri => uri.to_s,
135
+ :government => government?,
136
+ :live => !!response,
137
+ :ssl => https?,
138
+ :enforce_https => enforce_https?,
139
+ :non_www => non_www?,
140
+ :redirect => redirect,
141
+ :ip => ip,
142
+ :hostname => hostname.to_s,
143
+ :ipv6 => ipv6?,
144
+ :dnssec => dnssec?,
145
+ :cdn => cdn,
146
+ :google_apps => google_apps?,
147
+ :could_provider => cloud_provider,
148
+ :server => server,
149
+ :cms => cms,
150
+ :analytics => analytics,
151
+ :javascript => javascript,
152
+ :advertising => advertising,
153
+ :slash_data => slash_data?,
154
+ :slash_developer => slash_developer?,
155
+ :data_dot_json => data_dot_json?,
156
+ :click_jacking_protection => click_jacking_protection?,
157
+ :content_security_policy => content_security_policy?,
158
+ :xss_protection => xss_protection?,
159
+ :secure_cookies => secure_cookies?,
160
+ :strict_transport_security => strict_transport_security?
161
+ }
162
+ end
163
+ end
metadata ADDED
@@ -0,0 +1,248 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: site-inspector
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ben Balter
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: public_suffix
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: gman
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dnsruby
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sniffles
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: typhoeus
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: shoulda
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rdoc
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: bundler
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rerun
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: vcr
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: webmock
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ description: Returns information about a domain's technology and capabilities
210
+ email: ben@balter.com
211
+ executables: []
212
+ extensions: []
213
+ extra_rdoc_files: []
214
+ files:
215
+ - LICENSE
216
+ - lib/data/cdn.yml
217
+ - lib/data/cloud.yml
218
+ - lib/site-inspector.rb
219
+ - lib/site-inspector/cache.rb
220
+ - lib/site-inspector/compliance.rb
221
+ - lib/site-inspector/dns.rb
222
+ - lib/site-inspector/headers.rb
223
+ - lib/site-inspector/sniffer.rb
224
+ homepage: https://github.com/benbalter/site-inspector-ruby
225
+ licenses:
226
+ - MIT
227
+ metadata: {}
228
+ post_install_message:
229
+ rdoc_options: []
230
+ require_paths:
231
+ - lib
232
+ required_ruby_version: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
237
+ required_rubygems_version: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - ">="
240
+ - !ruby/object:Gem::Version
241
+ version: '0'
242
+ requirements: []
243
+ rubyforge_project:
244
+ rubygems_version: 2.2.2
245
+ signing_key:
246
+ specification_version: 4
247
+ summary: A Ruby port and v2 of Site Inspector (http://github.com/benbalter/site-inspector)
248
+ test_files: []