site-inspector 3.1.1 → 3.2.0
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 +5 -5
- data/.gitignore +1 -1
- data/.rubocop.yml +18 -10
- data/.rubocop_todo.yml +139 -0
- data/.ruby-version +1 -1
- data/Gemfile +4 -0
- data/Guardfile +2 -0
- data/Rakefile +2 -0
- data/bin/site-inspector +7 -6
- data/lib/cliver/dependency_ext.rb +6 -3
- data/lib/site-inspector.rb +18 -11
- data/lib/site-inspector/cache.rb +2 -0
- data/lib/site-inspector/checks/accessibility.rb +30 -22
- data/lib/site-inspector/checks/check.rb +4 -2
- data/lib/site-inspector/checks/content.rb +15 -4
- data/lib/site-inspector/checks/cookies.rb +5 -3
- data/lib/site-inspector/checks/dns.rb +13 -11
- data/lib/site-inspector/checks/headers.rb +8 -6
- data/lib/site-inspector/checks/hsts.rb +16 -12
- data/lib/site-inspector/checks/https.rb +3 -1
- data/lib/site-inspector/checks/sniffer.rb +10 -7
- data/lib/site-inspector/checks/wappalyzer.rb +62 -0
- data/lib/site-inspector/checks/whois.rb +36 -0
- data/lib/site-inspector/disk_cache.rb +2 -0
- data/lib/site-inspector/domain.rb +36 -30
- data/lib/site-inspector/endpoint.rb +22 -23
- data/lib/site-inspector/rails_cache.rb +2 -0
- data/lib/site-inspector/version.rb +3 -1
- data/package-lock.json +505 -0
- data/package.json +1 -1
- data/script/pa11y-version +1 -0
- data/site-inspector.gemspec +24 -17
- data/spec/checks/site_inspector_endpoint_accessibility_spec.rb +15 -13
- data/spec/checks/site_inspector_endpoint_check_spec.rb +9 -7
- data/spec/checks/site_inspector_endpoint_content_spec.rb +30 -21
- data/spec/checks/site_inspector_endpoint_cookies_spec.rb +17 -15
- data/spec/checks/site_inspector_endpoint_dns_spec.rb +42 -40
- data/spec/checks/site_inspector_endpoint_headers_spec.rb +12 -10
- data/spec/checks/site_inspector_endpoint_hsts_spec.rb +27 -25
- data/spec/checks/site_inspector_endpoint_https_spec.rb +12 -10
- data/spec/checks/site_inspector_endpoint_sniffer_spec.rb +33 -31
- data/spec/checks/site_inspector_endpoint_wappalyzer_spec.rb +34 -0
- data/spec/checks/site_inspector_endpoint_whois_spec.rb +26 -0
- data/spec/fixtures/wappalyzer.json +125 -0
- data/spec/site_inspector_cache_spec.rb +2 -0
- data/spec/site_inspector_disk_cache_spec.rb +8 -6
- data/spec/site_inspector_domain_spec.rb +34 -34
- data/spec/site_inspector_endpoint_spec.rb +44 -43
- data/spec/site_inspector_spec.rb +15 -13
- data/spec/spec_helper.rb +2 -0
- metadata +125 -55
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class SiteInspector
|
2
4
|
class Endpoint
|
3
5
|
class Check
|
@@ -40,11 +42,11 @@ class SiteInspector
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def enabled?
|
43
|
-
|
45
|
+
!!@@enabled
|
44
46
|
end
|
45
47
|
|
46
48
|
def enabled=(value)
|
47
|
-
@@enabled = !!
|
49
|
+
@@enabled = !!value
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class SiteInspector
|
2
4
|
class Endpoint
|
3
5
|
class Content < Check
|
@@ -16,7 +18,7 @@ class SiteInspector
|
|
16
18
|
require 'nokogiri'
|
17
19
|
@doc ||= Nokogiri::HTML response.body if response
|
18
20
|
end
|
19
|
-
|
21
|
+
alias doc document
|
20
22
|
|
21
23
|
def body
|
22
24
|
@body ||= document.to_s.force_encoding('UTF-8').encode('UTF-8', invalid: :replace, replace: '')
|
@@ -38,8 +40,16 @@ class SiteInspector
|
|
38
40
|
document.internal_subset.external_id
|
39
41
|
end
|
40
42
|
|
43
|
+
def generator
|
44
|
+
@generator ||= begin
|
45
|
+
tag = document.at('meta[name="generator"]')
|
46
|
+
tag['content'] if tag
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
41
50
|
def prefetch
|
42
51
|
return unless endpoint.up?
|
52
|
+
|
43
53
|
options = SiteInspector.typhoeus_defaults.merge(followlocation: true)
|
44
54
|
['robots.txt', 'sitemap.xml', 'humans.txt', random_path].each do |path|
|
45
55
|
request = Typhoeus::Request.new(URI.join(endpoint.uri, path), options)
|
@@ -55,10 +65,11 @@ class SiteInspector
|
|
55
65
|
def to_h
|
56
66
|
prefetch
|
57
67
|
{
|
58
|
-
doctype:
|
68
|
+
doctype: doctype,
|
69
|
+
generator: generator,
|
59
70
|
sitemap_xml: sitemap_xml?,
|
60
|
-
robots_txt:
|
61
|
-
humans_txt:
|
71
|
+
robots_txt: robots_txt?,
|
72
|
+
humans_txt: humans_txt?,
|
62
73
|
proper_404s: proper_404s?
|
63
74
|
}
|
64
75
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class SiteInspector
|
2
4
|
class Endpoint
|
3
5
|
class Cookies < Check
|
@@ -5,12 +7,12 @@ class SiteInspector
|
|
5
7
|
if cookie_header.nil? || cookie_header.empty?
|
6
8
|
false
|
7
9
|
elsif block_given?
|
8
|
-
all.any?
|
10
|
+
all.any?(&block)
|
9
11
|
else
|
10
12
|
true
|
11
13
|
end
|
12
14
|
end
|
13
|
-
|
15
|
+
alias cookies? any?
|
14
16
|
|
15
17
|
def all
|
16
18
|
@cookies ||= cookie_header.map { |c| CGI::Cookie.parse(c) } if cookies?
|
@@ -22,7 +24,7 @@ class SiteInspector
|
|
22
24
|
|
23
25
|
def secure?
|
24
26
|
pairs = cookie_header.join('; ').split('; ') # CGI::Cookies#Parse doesn't seem to like secure headers
|
25
|
-
pairs.any? { |c| c.
|
27
|
+
pairs.any? { |c| c.casecmp('secure').zero? } && pairs.any? { |c| c.casecmp('httponly').zero? }
|
26
28
|
end
|
27
29
|
|
28
30
|
def to_h
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class SiteInspector
|
2
4
|
class Endpoint
|
3
5
|
class Dns < Check
|
@@ -25,7 +27,7 @@ class SiteInspector
|
|
25
27
|
def record?(type)
|
26
28
|
records.any? { |record| record.type == type } || query(type).count != 0
|
27
29
|
end
|
28
|
-
|
30
|
+
alias has_record? record?
|
29
31
|
|
30
32
|
def dnssec?
|
31
33
|
@dnssec ||= has_record? 'DNSKEY'
|
@@ -52,7 +54,7 @@ class SiteInspector
|
|
52
54
|
end
|
53
55
|
|
54
56
|
def google_apps?
|
55
|
-
@
|
57
|
+
@google_apps ||= records.any? do |record|
|
56
58
|
record.type == 'MX' && record.exchange.to_s =~ /google(mail)?\.com\.?\z/i
|
57
59
|
end
|
58
60
|
end
|
@@ -62,7 +64,6 @@ class SiteInspector
|
|
62
64
|
end
|
63
65
|
|
64
66
|
def ip
|
65
|
-
require 'resolv'
|
66
67
|
@ip ||= Resolv.getaddress host
|
67
68
|
rescue Resolv::ResolvError
|
68
69
|
nil
|
@@ -87,14 +88,15 @@ class SiteInspector
|
|
87
88
|
|
88
89
|
def to_h
|
89
90
|
return { error: LocalhostError } if localhost?
|
91
|
+
|
90
92
|
{
|
91
|
-
dnssec:
|
92
|
-
ipv6:
|
93
|
-
cdn:
|
93
|
+
dnssec: dnssec?,
|
94
|
+
ipv6: ipv6?,
|
95
|
+
cdn: cdn,
|
94
96
|
cloud_provider: cloud_provider,
|
95
|
-
google_apps:
|
96
|
-
hostname:
|
97
|
-
ip:
|
97
|
+
google_apps: google_apps?,
|
98
|
+
hostname: hostname,
|
99
|
+
ip: ip
|
98
100
|
}
|
99
101
|
end
|
100
102
|
|
@@ -118,7 +120,7 @@ class SiteInspector
|
|
118
120
|
haystack = load_data(type)
|
119
121
|
needle = haystack.find do |_name, domain|
|
120
122
|
cnames.any? do |cname|
|
121
|
-
|
123
|
+
[cname.tld, "#{cname.sld}.#{cname.tld}"].include? domain
|
122
124
|
end
|
123
125
|
end
|
124
126
|
|
@@ -126,7 +128,7 @@ class SiteInspector
|
|
126
128
|
return nil unless hostname
|
127
129
|
|
128
130
|
needle = haystack.find do |_name, domain|
|
129
|
-
|
131
|
+
[hostname.tld, "#{hostname.sld}.#{hostname.tld}"].include? domain
|
130
132
|
end
|
131
133
|
|
132
134
|
needle ? needle[0].to_sym : nil
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class SiteInspector
|
2
4
|
class Endpoint
|
3
5
|
class Headers < Check
|
@@ -44,9 +46,9 @@ class SiteInspector
|
|
44
46
|
|
45
47
|
# Returns an array of hashes of downcased key/value header pairs (or an empty hash)
|
46
48
|
def all
|
47
|
-
@all ||=
|
49
|
+
@all ||= response&.headers ? response.headers.transform_keys(&:downcase) : {}
|
48
50
|
end
|
49
|
-
|
51
|
+
alias headers all
|
50
52
|
|
51
53
|
def [](header)
|
52
54
|
headers[header]
|
@@ -55,10 +57,10 @@ class SiteInspector
|
|
55
57
|
def to_h
|
56
58
|
{
|
57
59
|
strict_transport_security: strict_transport_security || false,
|
58
|
-
content_security_policy:
|
59
|
-
click_jacking_protection:
|
60
|
-
server:
|
61
|
-
xss_protection:
|
60
|
+
content_security_policy: content_security_policy || false,
|
61
|
+
click_jacking_protection: click_jacking_protection || false,
|
62
|
+
server: server,
|
63
|
+
xss_protection: xss_protection || false
|
62
64
|
}
|
63
65
|
end
|
64
66
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class SiteInspector
|
2
4
|
class Endpoint
|
3
5
|
# Utility parser for HSTS headers.
|
@@ -5,7 +7,8 @@ class SiteInspector
|
|
5
7
|
class Hsts < Check
|
6
8
|
def valid?
|
7
9
|
return false unless header
|
8
|
-
|
10
|
+
|
11
|
+
pairs.none? { |key, value| "#{key}#{value}" =~ /[\s'"]/ }
|
9
12
|
end
|
10
13
|
|
11
14
|
def max_age
|
@@ -13,16 +16,17 @@ class SiteInspector
|
|
13
16
|
end
|
14
17
|
|
15
18
|
def include_subdomains?
|
16
|
-
pairs.
|
19
|
+
pairs.key?(:includesubdomains)
|
17
20
|
end
|
18
21
|
|
19
22
|
def preload?
|
20
|
-
pairs.
|
23
|
+
pairs.key?(:preload)
|
21
24
|
end
|
22
25
|
|
23
26
|
def enabled?
|
24
27
|
return false unless max_age
|
25
|
-
|
28
|
+
|
29
|
+
max_age.positive?
|
26
30
|
end
|
27
31
|
|
28
32
|
# Google's minimum max-age for automatic preloading
|
@@ -32,12 +36,12 @@ class SiteInspector
|
|
32
36
|
|
33
37
|
def to_h
|
34
38
|
{
|
35
|
-
valid:
|
36
|
-
max_age:
|
39
|
+
valid: valid?,
|
40
|
+
max_age: max_age,
|
37
41
|
include_subdomains: include_subdomains?,
|
38
|
-
preload:
|
39
|
-
enabled:
|
40
|
-
preload_ready:
|
42
|
+
preload: preload?,
|
43
|
+
enabled: enabled?,
|
44
|
+
preload_ready: preload_ready?
|
41
45
|
}
|
42
46
|
end
|
43
47
|
|
@@ -61,9 +65,9 @@ class SiteInspector
|
|
61
65
|
directives.each do |directive|
|
62
66
|
key, value = directive.downcase.split('=')
|
63
67
|
|
64
|
-
if
|
65
|
-
value = value.sub(
|
66
|
-
value = value.sub(
|
68
|
+
if /".*"/.match?(value)
|
69
|
+
value = value.sub(/^"/, '')
|
70
|
+
value = value.sub(/"$/, '')
|
67
71
|
end
|
68
72
|
|
69
73
|
pairs[key.to_sym] = value
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class SiteInspector
|
2
4
|
class Endpoint
|
3
5
|
class Sniffer < Check
|
@@ -13,7 +15,7 @@ class SiteInspector
|
|
13
15
|
:php,
|
14
16
|
:expression_engine,
|
15
17
|
:cowboy
|
16
|
-
]
|
18
|
+
].freeze
|
17
19
|
|
18
20
|
def framework
|
19
21
|
cms = sniff :cms
|
@@ -21,7 +23,8 @@ class SiteInspector
|
|
21
23
|
return :expression_engine if endpoint.cookies.any? { |c| c.keys.first =~ /^exp_/ }
|
22
24
|
return :php if endpoint.cookies['PHPSESSID']
|
23
25
|
return :coldfusion if endpoint.cookies['CFID'] && endpoint.cookies['CFTOKEN']
|
24
|
-
return :cowboy if endpoint.headers.server.to_s.
|
26
|
+
return :cowboy if endpoint.headers.server.to_s.casecmp('cowboy').zero?
|
27
|
+
|
25
28
|
nil
|
26
29
|
end
|
27
30
|
|
@@ -43,9 +46,9 @@ class SiteInspector
|
|
43
46
|
|
44
47
|
def to_h
|
45
48
|
{
|
46
|
-
framework:
|
47
|
-
analytics:
|
48
|
-
javascript:
|
49
|
+
framework: framework,
|
50
|
+
analytics: analytics,
|
51
|
+
javascript: javascript,
|
49
52
|
advertising: advertising
|
50
53
|
}
|
51
54
|
end
|
@@ -55,8 +58,8 @@ class SiteInspector
|
|
55
58
|
def sniff(type)
|
56
59
|
require 'sniffles'
|
57
60
|
results = Sniffles.sniff(endpoint.content.body, type).select { |_name, meta| meta[:found] }
|
58
|
-
results
|
59
|
-
rescue
|
61
|
+
results&.keys&.first
|
62
|
+
rescue StandardError
|
60
63
|
nil
|
61
64
|
end
|
62
65
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SiteInspector
|
4
|
+
class Endpoint
|
5
|
+
class Wappalyzer < Check
|
6
|
+
ENDPOINT = 'https://api.wappalyzer.com/lookup/v2/'
|
7
|
+
|
8
|
+
def to_h
|
9
|
+
return {} unless data['technologies']
|
10
|
+
|
11
|
+
@to_h ||= begin
|
12
|
+
technologies = {}
|
13
|
+
data['technologies'].each do |t|
|
14
|
+
category = t['categories'].first
|
15
|
+
category = category ? category['name'] : 'Other'
|
16
|
+
technologies[category] ||= []
|
17
|
+
technologies[category].push t['name']
|
18
|
+
end
|
19
|
+
|
20
|
+
technologies
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def request
|
27
|
+
@request ||= begin
|
28
|
+
options = SiteInspector.typhoeus_defaults
|
29
|
+
headers = options[:headers].merge({ "x-api-key": api_key })
|
30
|
+
options = options.merge(method: :get, headers: headers)
|
31
|
+
Typhoeus::Request.new(url, options)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def data
|
36
|
+
return {} unless api_key && api_key != ''
|
37
|
+
|
38
|
+
@data ||= begin
|
39
|
+
SiteInspector.hydra.queue(request)
|
40
|
+
SiteInspector.hydra.run
|
41
|
+
|
42
|
+
response = request.response
|
43
|
+
if response.success?
|
44
|
+
JSON.parse(response.body).first
|
45
|
+
else
|
46
|
+
{}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def url
|
52
|
+
url = Addressable::URI.parse(ENDPOINT)
|
53
|
+
url.query_values = { urls: endpoint.uri }
|
54
|
+
url
|
55
|
+
end
|
56
|
+
|
57
|
+
def api_key
|
58
|
+
@api_key ||= ENV['WAPPALYZER_API_KEY']
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SiteInspector
|
4
|
+
class Endpoint
|
5
|
+
class Whois < Check
|
6
|
+
def domain
|
7
|
+
@domain ||= whois.lookup host
|
8
|
+
end
|
9
|
+
|
10
|
+
def ip
|
11
|
+
@ip ||= whois.lookup ip_address
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_h
|
15
|
+
{
|
16
|
+
domain: record_to_h(domain),
|
17
|
+
ip: record_to_h(ip)
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def record_to_h(record)
|
24
|
+
record.content.scan(/^\s*(.*?):\s*(.*?)\r?\n/).to_h
|
25
|
+
end
|
26
|
+
|
27
|
+
def ip_address
|
28
|
+
@ip_address ||= Resolv.getaddress host
|
29
|
+
end
|
30
|
+
|
31
|
+
def whois
|
32
|
+
@whois ||= ::Whois::Client.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class SiteInspector
|
2
4
|
class Domain
|
3
5
|
attr_reader :host
|
4
6
|
|
5
7
|
def initialize(host)
|
6
8
|
host = host.downcase
|
7
|
-
host = host.sub(/^https
|
9
|
+
host = host.sub(/^https?:/, '')
|
8
10
|
host = host.sub(%r{^/+}, '')
|
9
11
|
host = host.sub(/^www\./, '')
|
10
12
|
uri = Addressable::URI.parse "//#{host}"
|
@@ -82,7 +84,8 @@ class SiteInspector
|
|
82
84
|
# TODO: don't need to require that the HTTPS cert is valid for this purpose.
|
83
85
|
def enforces_https?
|
84
86
|
return false unless https?
|
85
|
-
|
87
|
+
|
88
|
+
endpoints.select(&:http?).all? { |e| !e.up? || e.redirect&.https? }
|
86
89
|
end
|
87
90
|
|
88
91
|
# we can say that a canonical HTTPS site "defaults" to HTTPS,
|
@@ -91,7 +94,7 @@ class SiteInspector
|
|
91
94
|
#
|
92
95
|
# TODO: not implemented.
|
93
96
|
def defaults_https?
|
94
|
-
|
97
|
+
raise 'Not implemented. Halp?'
|
95
98
|
end
|
96
99
|
|
97
100
|
# HTTPS is "downgraded" if both:
|
@@ -102,6 +105,7 @@ class SiteInspector
|
|
102
105
|
# TODO: the redirect must be internal.
|
103
106
|
def downgrades_https?
|
104
107
|
return false unless https?
|
108
|
+
|
105
109
|
canonical_endpoint.redirect? && canonical_endpoint.redirect.http?
|
106
110
|
end
|
107
111
|
|
@@ -129,7 +133,7 @@ class SiteInspector
|
|
129
133
|
return true if endpoints.select(&:root?).all? { |e| !e.up? }
|
130
134
|
|
131
135
|
# Does either root endpoint redirect to a www endpoint?
|
132
|
-
endpoints.select(&:root?).any? { |e| e.redirect
|
136
|
+
endpoints.select(&:root?).any? { |e| e.redirect&.www? }
|
133
137
|
end
|
134
138
|
|
135
139
|
# A domain is "canonically" at https if:
|
@@ -160,7 +164,7 @@ class SiteInspector
|
|
160
164
|
return true if endpoints.select(&:http?).all? { |e| !e.up? }
|
161
165
|
|
162
166
|
# at least one http endpoint redirects immediately to https
|
163
|
-
endpoints.select(&:http?).any? { |e| e.redirect
|
167
|
+
endpoints.select(&:http?).any? { |e| e.redirect&.https? }
|
164
168
|
end
|
165
169
|
|
166
170
|
# A domain redirects if
|
@@ -168,6 +172,7 @@ class SiteInspector
|
|
168
172
|
# 2. All endpoints are either down or an external redirect
|
169
173
|
def redirect?
|
170
174
|
return false unless redirect
|
175
|
+
|
171
176
|
endpoints.all? { |e| !e.up? || e.external_redirect? }
|
172
177
|
end
|
173
178
|
|
@@ -178,7 +183,7 @@ class SiteInspector
|
|
178
183
|
|
179
184
|
# HSTS on the canonical domain?
|
180
185
|
def hsts?
|
181
|
-
canonical_endpoint.hsts
|
186
|
+
canonical_endpoint.hsts&.enabled?
|
182
187
|
end
|
183
188
|
|
184
189
|
def hsts_subdomains?
|
@@ -187,6 +192,7 @@ class SiteInspector
|
|
187
192
|
|
188
193
|
def hsts_preload_ready?
|
189
194
|
return false unless hsts_subdomains?
|
195
|
+
|
190
196
|
endpoints.find { |e| e.root? && e.https? }.hsts.preload_ready?
|
191
197
|
end
|
192
198
|
|
@@ -225,40 +231,40 @@ class SiteInspector
|
|
225
231
|
prefetch
|
226
232
|
|
227
233
|
hash = {
|
228
|
-
host:
|
229
|
-
up:
|
230
|
-
responds:
|
231
|
-
www:
|
232
|
-
root:
|
233
|
-
https:
|
234
|
-
enforces_https:
|
235
|
-
downgrades_https:
|
236
|
-
canonically_www:
|
237
|
-
canonically_https:
|
238
|
-
redirect:
|
239
|
-
hsts:
|
240
|
-
hsts_subdomains:
|
234
|
+
host: host,
|
235
|
+
up: up?,
|
236
|
+
responds: responds?,
|
237
|
+
www: www?,
|
238
|
+
root: root?,
|
239
|
+
https: https?,
|
240
|
+
enforces_https: enforces_https?,
|
241
|
+
downgrades_https: downgrades_https?,
|
242
|
+
canonically_www: canonically_www?,
|
243
|
+
canonically_https: canonically_https?,
|
244
|
+
redirect: redirect?,
|
245
|
+
hsts: hsts?,
|
246
|
+
hsts_subdomains: hsts_subdomains?,
|
241
247
|
hsts_preload_ready: hsts_preload_ready?,
|
242
248
|
canonical_endpoint: canonical_endpoint.to_h(options)
|
243
249
|
}
|
244
250
|
|
245
251
|
if options['all']
|
246
|
-
hash
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
252
|
+
hash[:endpoints] = {
|
253
|
+
https: {
|
254
|
+
root: endpoints[0].to_h(options),
|
255
|
+
www: endpoints[1].to_h(options)
|
256
|
+
},
|
257
|
+
http: {
|
258
|
+
root: endpoints[2].to_h(options),
|
259
|
+
www: endpoints[3].to_h(options)
|
260
|
+
}
|
261
|
+
}
|
256
262
|
end
|
257
263
|
|
258
264
|
hash
|
259
265
|
end
|
260
266
|
|
261
|
-
def to_json
|
267
|
+
def to_json(*_args)
|
262
268
|
to_h.to_json
|
263
269
|
end
|
264
270
|
end
|