site-inspector 3.1.0 → 3.1.1
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 +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
|