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
@@ -2,64 +2,61 @@ require 'spec_helper'
|
|
2
2
|
require 'dnsruby'
|
3
3
|
|
4
4
|
describe SiteInspector::Endpoint::Dns do
|
5
|
-
|
6
5
|
subject do
|
7
|
-
stub_request(:head,
|
8
|
-
endpoint = SiteInspector::Endpoint.new(
|
6
|
+
stub_request(:head, 'http://github.com/').to_return(status: 200)
|
7
|
+
endpoint = SiteInspector::Endpoint.new('http://github.com')
|
9
8
|
SiteInspector::Endpoint::Dns.new(endpoint)
|
10
9
|
end
|
11
10
|
|
12
|
-
it
|
11
|
+
it 'inits the resolver' do
|
13
12
|
expect(SiteInspector::Endpoint::Dns.resolver.class).to eql(Dnsruby::Resolver)
|
14
13
|
end
|
15
14
|
|
16
15
|
# Note: these tests makes external calls
|
17
|
-
context
|
18
|
-
it
|
16
|
+
context 'live tests' do
|
17
|
+
it 'it runs the query' do
|
19
18
|
expect(subject.query).not_to be_empty
|
20
19
|
end
|
21
20
|
|
22
|
-
context
|
23
|
-
it
|
24
|
-
expect(subject.ip).to include(
|
21
|
+
context 'resolv' do
|
22
|
+
it 'returns the IP' do
|
23
|
+
expect(subject.ip).to include('192.30.252.')
|
25
24
|
end
|
26
25
|
|
27
|
-
it
|
28
|
-
expect(subject.hostname.sld).to eql(
|
29
|
-
|
26
|
+
it 'returns the hostname' do
|
27
|
+
expect(subject.hostname.sld).to eql('github')
|
30
28
|
end
|
31
29
|
end
|
32
30
|
end
|
33
31
|
|
34
|
-
context
|
35
|
-
|
32
|
+
context 'stubbed tests' do
|
36
33
|
before do
|
37
|
-
record = Dnsruby::RR.create :
|
34
|
+
record = Dnsruby::RR.create type: 'A', address: '1.2.3.4', name: 'test'
|
38
35
|
allow(subject).to receive(:records) { [record] }
|
39
36
|
allow(subject).to receive(:query) { [] }
|
40
37
|
end
|
41
38
|
|
42
|
-
it
|
39
|
+
it 'returns the records' do
|
43
40
|
expect(subject.records.count).to eql(1)
|
44
41
|
expect(subject.records.first.class).to eql(Dnsruby::RR::IN::A)
|
45
42
|
end
|
46
43
|
|
47
|
-
it
|
48
|
-
expect(subject.has_record?(
|
49
|
-
expect(subject.has_record?(
|
44
|
+
it 'knows if a record exists' do
|
45
|
+
expect(subject.has_record?('A')).to eql(true)
|
46
|
+
expect(subject.has_record?('CNAME')).to eql(false)
|
50
47
|
end
|
51
48
|
|
52
|
-
it
|
49
|
+
it 'knows if a domain supports dnssec' do
|
53
50
|
expect(subject.dnssec?).to eql(false)
|
54
51
|
|
55
52
|
# via https://github.com/alexdalitz/dnsruby/blob/master/test/tc_dnskey.rb
|
56
|
-
input =
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
53
|
+
input = 'example.com. 86400 IN DNSKEY 256 3 5 ( AQPSKmynfzW4kyBv015MUG2DeIQ3' \
|
54
|
+
'Cbl+BBZH4b/0PY1kxkmvHjcZc8no' \
|
55
|
+
'kfzj31GajIQKY+5CptLr3buXA10h' \
|
56
|
+
'WqTkF7H6RfoRqXQeogmMHfpftf6z' \
|
57
|
+
'Mv1LyBUgia7za6ZEzOJBOztyvhjL' \
|
58
|
+
'742iU/TpPSEDhm2SNKLijfUppn1U' \
|
59
|
+
'aNvv4w== )'
|
63
60
|
|
64
61
|
record = Dnsruby::RR.create input
|
65
62
|
allow(subject).to receive(:records) { [record] }
|
@@ -67,13 +64,13 @@ describe SiteInspector::Endpoint::Dns do
|
|
67
64
|
expect(subject.dnssec?).to eql(true)
|
68
65
|
end
|
69
66
|
|
70
|
-
it
|
67
|
+
it 'knows if a domain supports ipv6' do
|
71
68
|
expect(subject.ipv6?).to eql(false)
|
72
69
|
|
73
70
|
input = {
|
74
|
-
:
|
75
|
-
:
|
76
|
-
:
|
71
|
+
type: 'AAAA',
|
72
|
+
name: 'test',
|
73
|
+
address: '102:304:506:708:90a:b0c:d0e:ff10'
|
77
74
|
}
|
78
75
|
record = Dnsruby::RR.create input
|
79
76
|
allow(subject).to receive(:records) { [record] }
|
@@ -85,69 +82,69 @@ describe SiteInspector::Endpoint::Dns do
|
|
85
82
|
expect(subject.localhost?).to eql(false)
|
86
83
|
end
|
87
84
|
|
88
|
-
context
|
89
|
-
it
|
85
|
+
context 'hostname detection' do
|
86
|
+
it 'lists cnames' do
|
90
87
|
records = []
|
91
88
|
|
92
|
-
records.push Dnsruby::RR.create(
|
93
|
-
:
|
94
|
-
:
|
95
|
-
:
|
96
|
-
|
89
|
+
records.push Dnsruby::RR.create(
|
90
|
+
type: 'CNAME',
|
91
|
+
domainname: 'example.com',
|
92
|
+
name: 'example'
|
93
|
+
)
|
97
94
|
|
98
|
-
records.push Dnsruby::RR.create(
|
99
|
-
:
|
100
|
-
:
|
101
|
-
:
|
102
|
-
|
95
|
+
records.push Dnsruby::RR.create(
|
96
|
+
type: 'CNAME',
|
97
|
+
domainname: 'github.com',
|
98
|
+
name: 'github'
|
99
|
+
)
|
103
100
|
|
104
101
|
allow(subject).to receive(:records) { records }
|
105
102
|
|
106
103
|
expect(subject.cnames.count).to eql(2)
|
107
|
-
expect(subject.cnames.first.sld).to eql(
|
104
|
+
expect(subject.cnames.first.sld).to eql('example')
|
108
105
|
end
|
109
106
|
|
110
107
|
it "knows when a domain doesn't have a cdn" do
|
111
108
|
expect(subject.cdn?).to eql(false)
|
112
109
|
end
|
113
110
|
|
114
|
-
it
|
115
|
-
records = [Dnsruby::RR.create(
|
116
|
-
:
|
117
|
-
:
|
118
|
-
:
|
119
|
-
|
111
|
+
it 'detects CDNs' do
|
112
|
+
records = [Dnsruby::RR.create(
|
113
|
+
type: 'CNAME',
|
114
|
+
domainname: 'foo.cloudfront.net',
|
115
|
+
name: 'example'
|
116
|
+
)]
|
120
117
|
allow(subject).to receive(:records) { records }
|
121
118
|
|
122
|
-
expect(subject.send(:detect_by_hostname,
|
119
|
+
expect(subject.send(:detect_by_hostname, 'cdn')).to eql(:cloudfront)
|
123
120
|
expect(subject.cdn).to eql(:cloudfront)
|
124
121
|
expect(subject.cdn?).to eql(true)
|
125
122
|
end
|
126
123
|
|
127
|
-
it
|
128
|
-
path = subject.send(:data_path,
|
129
|
-
expected = File.expand_path
|
124
|
+
it 'builds that path to a data file' do
|
125
|
+
path = subject.send(:data_path, 'foo')
|
126
|
+
expected = File.expand_path '../../lib/data/foo.yml', File.dirname(__FILE__)
|
130
127
|
expect(path).to eql(expected)
|
131
128
|
end
|
132
129
|
|
133
|
-
it
|
134
|
-
data = subject.send(:load_data,
|
135
|
-
expect(data.keys).to include(
|
130
|
+
it 'loads data files' do
|
131
|
+
data = subject.send(:load_data, 'cdn')
|
132
|
+
expect(data.keys).to include('cloudfront')
|
136
133
|
end
|
137
134
|
|
138
135
|
it "knows when a domain isn't cloud" do
|
139
136
|
expect(subject.cloud?).to eql(false)
|
140
137
|
end
|
141
138
|
|
142
|
-
it
|
143
|
-
records = [Dnsruby::RR.create(
|
144
|
-
:
|
145
|
-
:
|
146
|
-
:
|
147
|
-
|
139
|
+
it 'detects cloud providers' do
|
140
|
+
records = [Dnsruby::RR.create(
|
141
|
+
type: 'CNAME',
|
142
|
+
domainname: 'foo.herokuapp.com',
|
143
|
+
name: 'example'
|
144
|
+
)]
|
148
145
|
allow(subject).to receive(:records) { records }
|
149
146
|
|
150
|
-
expect(subject.send(:detect_by_hostname,
|
147
|
+
expect(subject.send(:detect_by_hostname, 'cloud')).to eql(:heroku)
|
151
148
|
expect(subject.cloud_provider).to eql(:heroku)
|
152
149
|
expect(subject.cloud?).to eql(true)
|
153
150
|
end
|
@@ -156,32 +153,30 @@ describe SiteInspector::Endpoint::Dns do
|
|
156
153
|
expect(subject.google_apps?).to eql(false)
|
157
154
|
end
|
158
155
|
|
159
|
-
it
|
160
|
-
records = [Dnsruby::RR.create(
|
161
|
-
:
|
162
|
-
:
|
163
|
-
:
|
164
|
-
:
|
165
|
-
|
156
|
+
it 'knows when a domain is using google apps' do
|
157
|
+
records = [Dnsruby::RR.create(
|
158
|
+
type: 'MX',
|
159
|
+
exchange: 'mx1.google.com',
|
160
|
+
name: 'example',
|
161
|
+
preference: 10
|
162
|
+
)]
|
166
163
|
allow(subject).to receive(:records) { records }
|
167
164
|
expect(subject.google_apps?).to eql(true)
|
168
165
|
end
|
169
166
|
end
|
170
167
|
end
|
171
168
|
|
172
|
-
context
|
173
|
-
|
169
|
+
context 'localhost' do
|
174
170
|
before do
|
175
|
-
allow(subject).to receive(:ip) {
|
171
|
+
allow(subject).to receive(:ip) { '127.0.0.1' }
|
176
172
|
end
|
177
173
|
|
178
174
|
it "knows it's a localhost address" do
|
179
175
|
expect(subject.localhost?).to eql(true)
|
180
176
|
end
|
181
177
|
|
182
|
-
it
|
183
|
-
expect(subject.to_h).to eql(
|
178
|
+
it 'returns a LocalhostError' do
|
179
|
+
expect(subject.to_h).to eql(error: SiteInspector::Endpoint::Dns::LocalhostError)
|
184
180
|
end
|
185
|
-
|
186
181
|
end
|
187
182
|
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe SiteInspector::Endpoint::Headers do
|
4
|
-
|
5
4
|
subject do
|
6
|
-
stub_request(:head,
|
7
|
-
to_return(:
|
8
|
-
endpoint = SiteInspector::Endpoint.new(
|
5
|
+
stub_request(:head, 'http://example.com/')
|
6
|
+
.to_return(status: 200, headers: { foo: 'bar' })
|
7
|
+
endpoint = SiteInspector::Endpoint.new('http://example.com')
|
9
8
|
SiteInspector::Endpoint::Headers.new(endpoint)
|
10
9
|
end
|
11
10
|
|
@@ -13,52 +12,52 @@ describe SiteInspector::Endpoint::Headers do
|
|
13
12
|
allow(subject).to receive(:headers) { { header => value } }
|
14
13
|
end
|
15
14
|
|
16
|
-
it
|
15
|
+
it 'parses the headers' do
|
17
16
|
expect(subject.headers.count).to eql(1)
|
18
|
-
expect(subject.headers.keys).to include(
|
17
|
+
expect(subject.headers.keys).to include('foo')
|
19
18
|
end
|
20
19
|
|
21
|
-
it
|
22
|
-
expect(subject[
|
23
|
-
expect(subject.headers[
|
20
|
+
it 'returns a header' do
|
21
|
+
expect(subject['foo']).to eql('bar')
|
22
|
+
expect(subject.headers['foo']).to eql('bar')
|
24
23
|
end
|
25
24
|
|
26
|
-
it
|
27
|
-
stub_header
|
28
|
-
expect(subject.server).to eql(
|
25
|
+
it 'knows the server' do
|
26
|
+
stub_header 'server', 'foo'
|
27
|
+
expect(subject.server).to eql('foo')
|
29
28
|
end
|
30
29
|
|
31
|
-
it
|
32
|
-
stub_header
|
33
|
-
expect(subject.xss_protection).to eql(
|
30
|
+
it 'knows if a server has an xss protection header' do
|
31
|
+
stub_header 'x-xss-protection', 'foo'
|
32
|
+
expect(subject.xss_protection).to eql('foo')
|
34
33
|
end
|
35
34
|
|
36
|
-
it
|
37
|
-
stub_header
|
35
|
+
it 'validates xss-protection' do
|
36
|
+
stub_header 'x-xss-protection', 'foo'
|
38
37
|
expect(subject.xss_protection?).to eql(false)
|
39
38
|
|
40
|
-
stub_header
|
39
|
+
stub_header 'x-xss-protection', '1; mode=block'
|
41
40
|
expect(subject.xss_protection?).to eql(true)
|
42
41
|
end
|
43
42
|
|
44
|
-
it
|
43
|
+
it 'checks for clickjack proetection' do
|
45
44
|
expect(subject.click_jacking_protection?).to eql(false)
|
46
|
-
stub_header
|
47
|
-
expect(subject.click_jacking_protection).to eql(
|
45
|
+
stub_header 'x-frame-options', 'foo'
|
46
|
+
expect(subject.click_jacking_protection).to eql('foo')
|
48
47
|
expect(subject.click_jacking_protection?).to eql(true)
|
49
48
|
end
|
50
49
|
|
51
|
-
it
|
50
|
+
it 'checks for CSP' do
|
52
51
|
expect(subject.content_security_policy?).to eql(false)
|
53
|
-
stub_header
|
54
|
-
expect(subject.content_security_policy).to eql(
|
52
|
+
stub_header 'content-security-policy', 'foo'
|
53
|
+
expect(subject.content_security_policy).to eql('foo')
|
55
54
|
expect(subject.content_security_policy?).to eql(true)
|
56
55
|
end
|
57
56
|
|
58
|
-
it
|
57
|
+
it 'checks for strict-transport-security' do
|
59
58
|
expect(subject.strict_transport_security?).to eql(false)
|
60
|
-
stub_header
|
61
|
-
expect(subject.strict_transport_security).to eql(
|
59
|
+
stub_header 'strict-transport-security', 'foo'
|
60
|
+
expect(subject.strict_transport_security).to eql('foo')
|
62
61
|
expect(subject.strict_transport_security?).to eql(true)
|
63
62
|
end
|
64
63
|
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe SiteInspector::Endpoint::Hsts do
|
4
|
-
|
5
4
|
subject do
|
6
|
-
headers = {
|
7
|
-
stub_request(:head,
|
8
|
-
to_return(:
|
9
|
-
endpoint = SiteInspector::Endpoint.new(
|
5
|
+
headers = { 'strict-transport-security' => 'max-age=31536000; includeSubDomains;' }
|
6
|
+
stub_request(:head, 'http://example.com/')
|
7
|
+
.to_return(status: 200, headers: headers)
|
8
|
+
endpoint = SiteInspector::Endpoint.new('http://example.com')
|
10
9
|
SiteInspector::Endpoint::Hsts.new(endpoint)
|
11
10
|
end
|
12
11
|
|
@@ -14,77 +13,77 @@ describe SiteInspector::Endpoint::Hsts do
|
|
14
13
|
allow(subject).to receive(:header) { value }
|
15
14
|
end
|
16
15
|
|
17
|
-
it
|
16
|
+
it 'returns the headers' do
|
18
17
|
expect(subject.send(:headers).class).to eql(SiteInspector::Endpoint::Headers)
|
19
18
|
end
|
20
19
|
|
21
|
-
it
|
22
|
-
expect(subject.send(:header)).to eql(
|
20
|
+
it 'returns the HSTS header' do
|
21
|
+
expect(subject.send(:header)).to eql('max-age=31536000; includeSubDomains;')
|
23
22
|
end
|
24
23
|
|
25
|
-
it
|
24
|
+
it 'it parses the directives' do
|
26
25
|
expect(subject.send(:directives).count).to eql(2)
|
27
|
-
expect(subject.send(:directives).first).to eql(
|
28
|
-
expect(subject.send(:directives).last).to eql(
|
26
|
+
expect(subject.send(:directives).first).to eql('max-age=31536000')
|
27
|
+
expect(subject.send(:directives).last).to eql('includeSubDomains')
|
29
28
|
end
|
30
29
|
|
31
|
-
it
|
30
|
+
it 'parses pairs' do
|
32
31
|
expect(subject.send(:pairs).keys).to include(:"max-age")
|
33
|
-
expect(subject.send(:pairs)[:"max-age"]).to eql(
|
32
|
+
expect(subject.send(:pairs)[:"max-age"]).to eql('31536000')
|
34
33
|
end
|
35
34
|
|
36
|
-
it
|
35
|
+
it 'knows if the header is valid' do
|
37
36
|
expect(subject.valid?).to eql(true)
|
38
37
|
|
39
|
-
allow(subject).to receive(:pairs) { [
|
38
|
+
allow(subject).to receive(:pairs) { ['fo o' => 'bar'] }
|
40
39
|
expect(subject.valid?).to eql(false)
|
41
40
|
|
42
|
-
allow(subject).to receive(:pairs) { ["fo'o" =>
|
41
|
+
allow(subject).to receive(:pairs) { ["fo'o" => 'bar'] }
|
43
42
|
expect(subject.valid?).to eql(false)
|
44
43
|
end
|
45
44
|
|
46
|
-
it
|
47
|
-
expect(subject.max_age).to eql(
|
45
|
+
it 'knows the max age' do
|
46
|
+
expect(subject.max_age).to eql(31_536_000)
|
48
47
|
end
|
49
48
|
|
50
|
-
it
|
49
|
+
it 'knows if subdomains are included' do
|
51
50
|
expect(subject.include_subdomains?).to eql(true)
|
52
|
-
allow(subject).to receive(:pairs) { {:
|
51
|
+
allow(subject).to receive(:pairs) { { foo: 'bar' } }
|
53
52
|
expect(subject.include_subdomains?).to eql(false)
|
54
53
|
end
|
55
54
|
|
56
55
|
it "knows if it's preloaded" do
|
57
56
|
expect(subject.preload?).to eql(false)
|
58
|
-
allow(subject).to receive(:pairs) { {:
|
57
|
+
allow(subject).to receive(:pairs) { { preload: nil } }
|
59
58
|
expect(subject.preload?).to eql(true)
|
60
59
|
end
|
61
60
|
|
62
61
|
it "knows if it's enabled" do
|
63
62
|
expect(subject.enabled?).to eql(true)
|
64
63
|
|
65
|
-
allow(subject).to receive(:pairs) { {
|
64
|
+
allow(subject).to receive(:pairs) { { "max-age": 0 } }
|
66
65
|
expect(subject.preload?).to eql(false)
|
67
66
|
|
68
|
-
allow(subject).to receive(:pairs) { {:
|
67
|
+
allow(subject).to receive(:pairs) { { foo: 'bar' } }
|
69
68
|
expect(subject.preload?).to eql(false)
|
70
69
|
end
|
71
70
|
|
72
71
|
it "knows if it's preload ready" do
|
73
72
|
expect(subject.preload_ready?).to eql(false)
|
74
73
|
|
75
|
-
pairs = {
|
74
|
+
pairs = { "max-age": 10_886_401, preload: nil, includesubdomains: nil }
|
76
75
|
allow(subject).to receive(:pairs) { pairs }
|
77
76
|
expect(subject.preload_ready?).to eql(true)
|
78
77
|
|
79
|
-
pairs = {
|
78
|
+
pairs = { "max-age": 10_886_401, includesubdomains: nil }
|
80
79
|
allow(subject).to receive(:pairs) { pairs }
|
81
80
|
expect(subject.preload_ready?).to eql(false)
|
82
81
|
|
83
|
-
pairs = {
|
82
|
+
pairs = { "max-age": 10_886_401, preload: nil, includesubdomains: nil }
|
84
83
|
allow(subject).to receive(:pairs) { pairs }
|
85
84
|
expect(subject.preload_ready?).to eql(true)
|
86
85
|
|
87
|
-
pairs = {
|
86
|
+
pairs = { "max-age": 5, preload: nil, includesubdomains: nil }
|
88
87
|
allow(subject).to receive(:pairs) { pairs }
|
89
88
|
expect(subject.preload_ready?).to eql(false)
|
90
89
|
end
|