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
@@ -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
|