site-inspector 3.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +34 -0
- data/.ruby-version +1 -1
- data/Gemfile +1 -1
- data/Guardfile +1 -1
- data/README.md +6 -1
- data/Rakefile +2 -2
- data/bin/site-inspector +15 -15
- data/lib/cliver/dependency_ext.rb +21 -0
- data/lib/site-inspector.rb +13 -11
- data/lib/site-inspector/checks/accessibility.rb +27 -17
- data/lib/site-inspector/checks/check.rb +1 -3
- data/lib/site-inspector/checks/content.rb +6 -6
- data/lib/site-inspector/checks/cookies.rb +6 -8
- data/lib/site-inspector/checks/dns.rb +21 -20
- data/lib/site-inspector/checks/headers.rb +12 -13
- data/lib/site-inspector/checks/hsts.rb +8 -9
- data/lib/site-inspector/checks/https.rb +3 -5
- data/lib/site-inspector/checks/sniffer.rb +8 -9
- data/lib/site-inspector/domain.rb +28 -32
- data/lib/site-inspector/endpoint.rb +31 -32
- data/lib/site-inspector/version.rb +1 -1
- data/script/cibuild +3 -1
- data/script/pa11y-version +9 -0
- data/site-inspector.gemspec +25 -25
- data/spec/checks/site_inspector_endpoint_accessibility_spec.rb +31 -30
- data/spec/checks/site_inspector_endpoint_check_spec.rb +10 -11
- data/spec/checks/site_inspector_endpoint_content_spec.rb +43 -44
- data/spec/checks/site_inspector_endpoint_cookies_spec.rb +30 -31
- data/spec/checks/site_inspector_endpoint_dns_spec.rb +72 -77
- data/spec/checks/site_inspector_endpoint_headers_spec.rb +26 -27
- data/spec/checks/site_inspector_endpoint_hsts_spec.rb +26 -27
- data/spec/checks/site_inspector_endpoint_https_spec.rb +11 -12
- data/spec/checks/site_inspector_endpoint_sniffer_spec.rb +56 -57
- data/spec/site_inspector_cache_spec.rb +6 -6
- data/spec/site_inspector_disk_cache_spec.rb +9 -9
- data/spec/site_inspector_domain_spec.rb +132 -136
- data/spec/site_inspector_endpoint_spec.rb +108 -108
- data/spec/site_inspector_spec.rb +17 -18
- data/spec/spec_helper.rb +3 -3
- metadata +21 -3
@@ -4,15 +4,15 @@ class SiteInspector
|
|
4
4
|
class LocalhostError < StandardError; end
|
5
5
|
|
6
6
|
def self.resolver
|
7
|
-
require
|
7
|
+
require 'dnsruby'
|
8
8
|
@resolver ||= begin
|
9
9
|
resolver = Dnsruby::Resolver.new
|
10
|
-
resolver.config.nameserver = [
|
10
|
+
resolver.config.nameserver = ['8.8.8.8', '8.8.4.4']
|
11
11
|
resolver
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def query(type=
|
15
|
+
def query(type = 'ANY')
|
16
16
|
SiteInspector::Endpoint::Dns.resolver.query(host.to_s, type).answer
|
17
17
|
rescue Dnsruby::ResolvTimeout, Dnsruby::ServFail, Dnsruby::NXDomain
|
18
18
|
[]
|
@@ -22,20 +22,21 @@ class SiteInspector
|
|
22
22
|
@records ||= query
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
25
|
+
def record?(type)
|
26
26
|
records.any? { |record| record.type == type } || query(type).count != 0
|
27
27
|
end
|
28
|
+
alias_method :has_record?, :record?
|
28
29
|
|
29
30
|
def dnssec?
|
30
|
-
@dnssec ||= has_record?
|
31
|
+
@dnssec ||= has_record? 'DNSKEY'
|
31
32
|
end
|
32
33
|
|
33
34
|
def ipv6?
|
34
|
-
@ipv6 ||= has_record?
|
35
|
+
@ipv6 ||= has_record? 'AAAA'
|
35
36
|
end
|
36
37
|
|
37
38
|
def cdn
|
38
|
-
detect_by_hostname
|
39
|
+
detect_by_hostname 'cdn'
|
39
40
|
end
|
40
41
|
|
41
42
|
def cdn?
|
@@ -43,7 +44,7 @@ class SiteInspector
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def cloud_provider
|
46
|
-
detect_by_hostname
|
47
|
+
detect_by_hostname 'cloud'
|
47
48
|
end
|
48
49
|
|
49
50
|
def cloud?
|
@@ -52,7 +53,7 @@ class SiteInspector
|
|
52
53
|
|
53
54
|
def google_apps?
|
54
55
|
@google ||= records.any? do |record|
|
55
|
-
record.type ==
|
56
|
+
record.type == 'MX' && record.exchange.to_s =~ /google(mail)?\.com\.?\z/i
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
@@ -75,7 +76,7 @@ class SiteInspector
|
|
75
76
|
end
|
76
77
|
|
77
78
|
def cnames
|
78
|
-
@cnames ||= records.select { |record| record.type ==
|
79
|
+
@cnames ||= records.select { |record| record.type == 'CNAME' }.map do |record|
|
79
80
|
PublicSuffix.parse(record.cname.to_s)
|
80
81
|
end
|
81
82
|
end
|
@@ -85,15 +86,15 @@ class SiteInspector
|
|
85
86
|
end
|
86
87
|
|
87
88
|
def to_h
|
88
|
-
return { :
|
89
|
+
return { error: LocalhostError } if localhost?
|
89
90
|
{
|
90
|
-
:dnssec
|
91
|
-
:ipv6
|
92
|
-
:cdn
|
93
|
-
:
|
94
|
-
:google_apps
|
95
|
-
:hostname
|
96
|
-
:ip
|
91
|
+
dnssec: dnssec?,
|
92
|
+
ipv6: ipv6?,
|
93
|
+
cdn: cdn,
|
94
|
+
cloud_provider: cloud_provider,
|
95
|
+
google_apps: google_apps?,
|
96
|
+
hostname: hostname,
|
97
|
+
ip: ip
|
97
98
|
}
|
98
99
|
end
|
99
100
|
|
@@ -115,7 +116,7 @@ class SiteInspector
|
|
115
116
|
|
116
117
|
def detect_by_hostname(type)
|
117
118
|
haystack = load_data(type)
|
118
|
-
needle = haystack.find do |
|
119
|
+
needle = haystack.find do |_name, domain|
|
119
120
|
cnames.any? do |cname|
|
120
121
|
domain == cname.tld || domain == "#{cname.sld}.#{cname.tld}"
|
121
122
|
end
|
@@ -124,7 +125,7 @@ class SiteInspector
|
|
124
125
|
return needle[0].to_sym if needle
|
125
126
|
return nil unless hostname
|
126
127
|
|
127
|
-
needle = haystack.find do |
|
128
|
+
needle = haystack.find do |_name, domain|
|
128
129
|
domain == hostname.tld || domain == "#{hostname.sld}.#{hostname.tld}"
|
129
130
|
end
|
130
131
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
class SiteInspector
|
2
2
|
class Endpoint
|
3
3
|
class Headers < Check
|
4
|
-
|
5
4
|
# TODO: kill this
|
6
5
|
def strict_transport_security?
|
7
6
|
!!strict_transport_security
|
@@ -19,33 +18,33 @@ class SiteInspector
|
|
19
18
|
|
20
19
|
# TODO: kill this
|
21
20
|
def strict_transport_security
|
22
|
-
headers[
|
21
|
+
headers['strict-transport-security']
|
23
22
|
end
|
24
23
|
|
25
24
|
def content_security_policy
|
26
|
-
headers[
|
25
|
+
headers['content-security-policy']
|
27
26
|
end
|
28
27
|
|
29
28
|
def click_jacking_protection
|
30
|
-
headers[
|
29
|
+
headers['x-frame-options']
|
31
30
|
end
|
32
31
|
|
33
32
|
def server
|
34
|
-
headers[
|
33
|
+
headers['server']
|
35
34
|
end
|
36
35
|
|
37
36
|
def xss_protection
|
38
|
-
headers[
|
37
|
+
headers['x-xss-protection']
|
39
38
|
end
|
40
39
|
|
41
40
|
# more specific checks than presence of headers
|
42
41
|
def xss_protection?
|
43
|
-
xss_protection ==
|
42
|
+
xss_protection == '1; mode=block'
|
44
43
|
end
|
45
44
|
|
46
45
|
# Returns an array of hashes of downcased key/value header pairs (or an empty hash)
|
47
46
|
def all
|
48
|
-
@all ||= (response && response.headers) ? Hash[response.headers.map{ |k,v| [k.downcase,v] }] : {}
|
47
|
+
@all ||= (response && response.headers) ? Hash[response.headers.map { |k, v| [k.downcase, v] }] : {}
|
49
48
|
end
|
50
49
|
alias_method :headers, :all
|
51
50
|
|
@@ -55,11 +54,11 @@ class SiteInspector
|
|
55
54
|
|
56
55
|
def to_h
|
57
56
|
{
|
58
|
-
:
|
59
|
-
:content_security_policy
|
60
|
-
:click_jacking_protection
|
61
|
-
:server
|
62
|
-
:xss_protection
|
57
|
+
strict_transport_security: strict_transport_security || false,
|
58
|
+
content_security_policy: content_security_policy || false,
|
59
|
+
click_jacking_protection: click_jacking_protection || false,
|
60
|
+
server: server,
|
61
|
+
xss_protection: xss_protection || false
|
63
62
|
}
|
64
63
|
end
|
65
64
|
end
|
@@ -3,7 +3,6 @@ class SiteInspector
|
|
3
3
|
# Utility parser for HSTS headers.
|
4
4
|
# RFC: http://tools.ietf.org/html/rfc6797
|
5
5
|
class Hsts < Check
|
6
|
-
|
7
6
|
def valid?
|
8
7
|
return false unless header
|
9
8
|
pairs.none? { |key, value| "#{key}#{value}" =~ /[\s\'\"]/ }
|
@@ -28,17 +27,17 @@ class SiteInspector
|
|
28
27
|
|
29
28
|
# Google's minimum max-age for automatic preloading
|
30
29
|
def preload_ready?
|
31
|
-
include_subdomains?
|
30
|
+
include_subdomains? && preload? && max_age >= 10_886_400
|
32
31
|
end
|
33
32
|
|
34
33
|
def to_h
|
35
34
|
{
|
36
|
-
valid:
|
37
|
-
max_age:
|
35
|
+
valid: valid?,
|
36
|
+
max_age: max_age,
|
38
37
|
include_subdomains: include_subdomains?,
|
39
|
-
preload:
|
40
|
-
enabled:
|
41
|
-
preload_ready:
|
38
|
+
preload: preload?,
|
39
|
+
enabled: enabled?,
|
40
|
+
preload_ready: preload_ready?
|
42
41
|
}
|
43
42
|
end
|
44
43
|
|
@@ -49,7 +48,7 @@ class SiteInspector
|
|
49
48
|
end
|
50
49
|
|
51
50
|
def header
|
52
|
-
@header ||= headers[
|
51
|
+
@header ||= headers['strict-transport-security']
|
53
52
|
end
|
54
53
|
|
55
54
|
def directives
|
@@ -60,7 +59,7 @@ class SiteInspector
|
|
60
59
|
@pairs ||= begin
|
61
60
|
pairs = {}
|
62
61
|
directives.each do |directive|
|
63
|
-
key, value = directive.downcase.split(
|
62
|
+
key, value = directive.downcase.split('=')
|
64
63
|
|
65
64
|
if value =~ /\".*\"/
|
66
65
|
value = value.sub(/^\"/, '')
|
@@ -1,9 +1,8 @@
|
|
1
1
|
class SiteInspector
|
2
2
|
class Endpoint
|
3
3
|
class Https < Check
|
4
|
-
|
5
4
|
def scheme?
|
6
|
-
scheme ==
|
5
|
+
scheme == 'https'
|
7
6
|
end
|
8
7
|
|
9
8
|
def valid?
|
@@ -24,8 +23,8 @@ class SiteInspector
|
|
24
23
|
|
25
24
|
def to_h
|
26
25
|
{
|
27
|
-
valid:
|
28
|
-
return_code: response.return_code
|
26
|
+
valid: valid?,
|
27
|
+
return_code: response.return_code
|
29
28
|
}
|
30
29
|
end
|
31
30
|
|
@@ -34,7 +33,6 @@ class SiteInspector
|
|
34
33
|
def scheme
|
35
34
|
@scheme ||= request.base_url.scheme
|
36
35
|
end
|
37
|
-
|
38
36
|
end
|
39
37
|
end
|
40
38
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
class SiteInspector
|
2
2
|
class Endpoint
|
3
3
|
class Sniffer < Check
|
4
|
-
|
5
4
|
OPEN_SOURCE_FRAMEWORKS = [
|
6
5
|
# Sniffles
|
7
6
|
:drupal,
|
@@ -20,9 +19,9 @@ class SiteInspector
|
|
20
19
|
cms = sniff :cms
|
21
20
|
return cms unless cms.nil?
|
22
21
|
return :expression_engine if endpoint.cookies.any? { |c| c.keys.first =~ /^exp_/ }
|
23
|
-
return :php if endpoint.cookies[
|
24
|
-
return :coldfusion if endpoint.cookies[
|
25
|
-
return :cowboy if endpoint.headers.server.to_s.downcase ==
|
22
|
+
return :php if endpoint.cookies['PHPSESSID']
|
23
|
+
return :coldfusion if endpoint.cookies['CFID'] && endpoint.cookies['CFTOKEN']
|
24
|
+
return :cowboy if endpoint.headers.server.to_s.downcase == 'cowboy'
|
26
25
|
nil
|
27
26
|
end
|
28
27
|
|
@@ -44,10 +43,10 @@ class SiteInspector
|
|
44
43
|
|
45
44
|
def to_h
|
46
45
|
{
|
47
|
-
:
|
48
|
-
:
|
49
|
-
:
|
50
|
-
:
|
46
|
+
framework: framework,
|
47
|
+
analytics: analytics,
|
48
|
+
javascript: javascript,
|
49
|
+
advertising: advertising
|
51
50
|
}
|
52
51
|
end
|
53
52
|
|
@@ -55,7 +54,7 @@ class SiteInspector
|
|
55
54
|
|
56
55
|
def sniff(type)
|
57
56
|
require 'sniffles'
|
58
|
-
results = Sniffles.sniff(endpoint.content.body, type).select { |
|
57
|
+
results = Sniffles.sniff(endpoint.content.body, type).select { |_name, meta| meta[:found] }
|
59
58
|
results.keys.first if results
|
60
59
|
rescue
|
61
60
|
nil
|
@@ -1,23 +1,22 @@
|
|
1
1
|
class SiteInspector
|
2
2
|
class Domain
|
3
|
-
|
4
3
|
attr_reader :host
|
5
4
|
|
6
5
|
def initialize(host)
|
7
6
|
host = host.downcase
|
8
|
-
host = host.sub
|
9
|
-
host = host.sub
|
10
|
-
host = host.sub
|
7
|
+
host = host.sub(/^https?\:/, '')
|
8
|
+
host = host.sub(%r{^/+}, '')
|
9
|
+
host = host.sub(/^www\./, '')
|
11
10
|
uri = Addressable::URI.parse "//#{host}"
|
12
11
|
@host = uri.host
|
13
12
|
end
|
14
13
|
|
15
14
|
def endpoints
|
16
15
|
@endpoints ||= [
|
17
|
-
Endpoint.new("https://#{host}", :
|
18
|
-
Endpoint.new("https://www.#{host}", :
|
19
|
-
Endpoint.new("http://#{host}", :
|
20
|
-
Endpoint.new("http://www.#{host}", :
|
16
|
+
Endpoint.new("https://#{host}", domain: self),
|
17
|
+
Endpoint.new("https://www.#{host}", domain: self),
|
18
|
+
Endpoint.new("http://#{host}", domain: self),
|
19
|
+
Endpoint.new("http://www.#{host}", domain: self)
|
21
20
|
]
|
22
21
|
end
|
23
22
|
|
@@ -37,16 +36,15 @@ class SiteInspector
|
|
37
36
|
|
38
37
|
# Does *any* endpoint return a 200 or 300 response code?
|
39
38
|
def up?
|
40
|
-
endpoints.any?
|
39
|
+
endpoints.any?(&:up?)
|
41
40
|
end
|
42
41
|
|
43
42
|
# Does *any* endpoint respond to HTTP?
|
44
43
|
# TODO: needs to allow an invalid chain.
|
45
44
|
def responds?
|
46
|
-
endpoints.any?
|
45
|
+
endpoints.any?(&:responds?)
|
47
46
|
end
|
48
47
|
|
49
|
-
|
50
48
|
# TODO: These weren't present before, and may not be useful.
|
51
49
|
# Can you connect to www?
|
52
50
|
def www?
|
@@ -84,7 +82,7 @@ class SiteInspector
|
|
84
82
|
# TODO: don't need to require that the HTTPS cert is valid for this purpose.
|
85
83
|
def enforces_https?
|
86
84
|
return false unless https?
|
87
|
-
endpoints.select
|
85
|
+
endpoints.select(&:http?).all? { |e| !e.up? || (e.redirect && e.redirect.https?) }
|
88
86
|
end
|
89
87
|
|
90
88
|
# we can say that a canonical HTTPS site "defaults" to HTTPS,
|
@@ -93,7 +91,7 @@ class SiteInspector
|
|
93
91
|
#
|
94
92
|
# TODO: not implemented.
|
95
93
|
def defaults_https?
|
96
|
-
|
94
|
+
fail 'Not implemented. Halp?'
|
97
95
|
end
|
98
96
|
|
99
97
|
# HTTPS is "downgraded" if both:
|
@@ -128,10 +126,10 @@ class SiteInspector
|
|
128
126
|
return false unless www?
|
129
127
|
|
130
128
|
# Are both root endpoints down?
|
131
|
-
return true if endpoints.select
|
129
|
+
return true if endpoints.select(&:root?).all? { |e| !e.up? }
|
132
130
|
|
133
131
|
# Does either root endpoint redirect to a www endpoint?
|
134
|
-
endpoints.select
|
132
|
+
endpoints.select(&:root?).any? { |e| e.redirect && e.redirect.www? }
|
135
133
|
end
|
136
134
|
|
137
135
|
# A domain is "canonically" at https if:
|
@@ -159,10 +157,10 @@ class SiteInspector
|
|
159
157
|
return false unless https?
|
160
158
|
|
161
159
|
# Both http endpoints are down
|
162
|
-
return true if endpoints.select
|
160
|
+
return true if endpoints.select(&:http?).all? { |e| !e.up? }
|
163
161
|
|
164
162
|
# at least one http endpoint redirects immediately to https
|
165
|
-
endpoints.select
|
163
|
+
endpoints.select(&:http?).any? { |e| e.redirect && e.redirect.https? }
|
166
164
|
end
|
167
165
|
|
168
166
|
# A domain redirects if
|
@@ -175,7 +173,7 @@ class SiteInspector
|
|
175
173
|
|
176
174
|
# The first endpoint to respond with a redirect
|
177
175
|
def redirect
|
178
|
-
endpoints.find
|
176
|
+
endpoints.find(&:external_redirect?)
|
179
177
|
end
|
180
178
|
|
181
179
|
# HSTS on the canonical domain?
|
@@ -223,7 +221,7 @@ class SiteInspector
|
|
223
221
|
# :all - return information about all endpoints
|
224
222
|
#
|
225
223
|
# Returns a complete hash of the domain's information
|
226
|
-
def to_h(options={})
|
224
|
+
def to_h(options = {})
|
227
225
|
prefetch
|
228
226
|
|
229
227
|
hash = {
|
@@ -244,19 +242,17 @@ class SiteInspector
|
|
244
242
|
canonical_endpoint: canonical_endpoint.to_h(options)
|
245
243
|
}
|
246
244
|
|
247
|
-
if options[
|
248
|
-
hash.merge!({
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
}
|
259
|
-
})
|
245
|
+
if options['all']
|
246
|
+
hash.merge!(endpoints: {
|
247
|
+
https: {
|
248
|
+
root: endpoints[0].to_h(options),
|
249
|
+
www: endpoints[1].to_h(options)
|
250
|
+
},
|
251
|
+
http: {
|
252
|
+
root: endpoints[2].to_h(options),
|
253
|
+
www: endpoints[3].to_h(options)
|
254
|
+
}
|
255
|
+
})
|
260
256
|
end
|
261
257
|
|
262
258
|
hash
|
@@ -17,12 +17,12 @@ class SiteInspector
|
|
17
17
|
# endpoint - (string) the endpoint to query (e.g., `https://example.com`)
|
18
18
|
# options - A hash of options
|
19
19
|
# domain - the parent domain object, if passed, facilitates caching of redirects
|
20
|
-
def initialize(host, options={})
|
20
|
+
def initialize(host, options = {})
|
21
21
|
@uri = Addressable::URI.parse(host.downcase)
|
22
22
|
# The root URL always has an implict path of "/", even if not requested
|
23
23
|
# Make it explicit to facilitate caching and prevent a potential redirect
|
24
|
-
@uri.path =
|
25
|
-
@host = uri.host.sub(/^www\./,
|
24
|
+
@uri.path = '/'
|
25
|
+
@host = uri.host.sub(/^www\./, '')
|
26
26
|
@checks = {}
|
27
27
|
@domain = options[:domain]
|
28
28
|
end
|
@@ -72,24 +72,24 @@ class SiteInspector
|
|
72
72
|
|
73
73
|
# Does the endpoint return a 2xx or 3xx response code?
|
74
74
|
def up?
|
75
|
-
response && response_code.start_with?(
|
75
|
+
response && response_code.start_with?('2') || response_code.start_with?('3')
|
76
76
|
end
|
77
77
|
|
78
78
|
# Does the server respond at all?
|
79
79
|
def responds?
|
80
|
-
|
80
|
+
response.code != 0 && !timed_out?
|
81
81
|
end
|
82
82
|
|
83
83
|
# If the domain is a redirect, what's the first endpoint we're redirected to?
|
84
84
|
def redirect
|
85
|
-
return unless response && response_code.start_with?(
|
85
|
+
return unless response && response_code.start_with?('3')
|
86
86
|
|
87
87
|
@redirect ||= begin
|
88
|
-
redirect = Addressable::URI.parse(headers[
|
88
|
+
redirect = Addressable::URI.parse(headers['location'])
|
89
89
|
|
90
90
|
# This is a relative redirect, but we still need the absolute URI
|
91
91
|
if redirect.relative?
|
92
|
-
redirect.path = "/#{redirect.path}" unless redirect.path[0] ==
|
92
|
+
redirect.path = "/#{redirect.path}" unless redirect.path[0] == '/'
|
93
93
|
redirect.host = host
|
94
94
|
redirect.scheme = scheme
|
95
95
|
end
|
@@ -116,12 +116,12 @@ class SiteInspector
|
|
116
116
|
return redirect unless redirect.redirect?
|
117
117
|
|
118
118
|
@resolves_to ||= begin
|
119
|
-
response = request(:
|
119
|
+
response = request(followlocation: true)
|
120
120
|
|
121
121
|
# Workaround for Webmock not playing nicely with Typhoeus redirects
|
122
122
|
if response.mock?
|
123
|
-
if response.headers[
|
124
|
-
url = response.headers[
|
123
|
+
if response.headers['Location']
|
124
|
+
url = response.headers['Location']
|
125
125
|
else
|
126
126
|
url = response.request.url
|
127
127
|
end
|
@@ -142,7 +142,7 @@ class SiteInspector
|
|
142
142
|
end
|
143
143
|
|
144
144
|
def inspect
|
145
|
-
"#<SiteInspector::Endpoint uri=\"#{uri
|
145
|
+
"#<SiteInspector::Endpoint uri=\"#{uri}\">"
|
146
146
|
end
|
147
147
|
|
148
148
|
# Returns information about the endpoint
|
@@ -154,37 +154,39 @@ class SiteInspector
|
|
154
154
|
# a hash of check symbols and bools representing which checks should be run
|
155
155
|
#
|
156
156
|
# Returns the hash representing the endpoint and its checks
|
157
|
-
def to_h(options={})
|
157
|
+
def to_h(options = {})
|
158
158
|
hash = {
|
159
|
-
uri:
|
160
|
-
host:
|
161
|
-
www:
|
162
|
-
https:
|
163
|
-
scheme:
|
164
|
-
up:
|
165
|
-
responds:
|
166
|
-
timed_out:
|
167
|
-
redirect:
|
168
|
-
external_redirect: external_redirect
|
159
|
+
uri: uri.to_s,
|
160
|
+
host: host,
|
161
|
+
www: www?,
|
162
|
+
https: https?,
|
163
|
+
scheme: scheme,
|
164
|
+
up: up?,
|
165
|
+
responds: responds?,
|
166
|
+
timed_out: timed_out?,
|
167
|
+
redirect: redirect?,
|
168
|
+
external_redirect: external_redirect?
|
169
169
|
}
|
170
170
|
|
171
171
|
# Either they've specifically asked for a check, or we throw everything at them
|
172
172
|
checks = SiteInspector::Endpoint.checks.select { |c| options.keys.include?(c.name) }
|
173
173
|
checks = SiteInspector::Endpoint.checks if checks.empty?
|
174
174
|
|
175
|
-
Parallel.each(checks, :
|
176
|
-
hash[check.name] =
|
175
|
+
Parallel.each(checks, in_threads: 4) do |check|
|
176
|
+
hash[check.name] = send(check.name).to_h
|
177
177
|
end
|
178
178
|
|
179
179
|
hash
|
180
180
|
end
|
181
181
|
|
182
182
|
def self.checks
|
183
|
-
|
183
|
+
return @checks if defined? @checks
|
184
|
+
@checks = ObjectSpace.each_object(Class).select { |klass| klass < Check }.select(&:enabled?)
|
184
185
|
end
|
185
186
|
|
186
187
|
def method_missing(method_sym, *arguments, &block)
|
187
|
-
|
188
|
+
check = SiteInspector::Endpoint.checks.find { |c| c.name == method_sym }
|
189
|
+
if check
|
188
190
|
@checks[method_sym] ||= check.new(self)
|
189
191
|
else
|
190
192
|
super
|
@@ -209,11 +211,8 @@ class SiteInspector
|
|
209
211
|
# Try to return the existing endpoint, rather than create a new one
|
210
212
|
def find_or_create_by_uri(uri)
|
211
213
|
uri = Addressable::URI.parse(uri.downcase)
|
212
|
-
|
213
|
-
|
214
|
-
else
|
215
|
-
Endpoint.new(uri.to_s)
|
216
|
-
end
|
214
|
+
cached_endpoint = domain.endpoints.find { |e| e.uri.to_s == uri.to_s } if domain
|
215
|
+
cached_endpoint || Endpoint.new(uri.to_s)
|
217
216
|
end
|
218
217
|
end
|
219
218
|
end
|