site-inspector 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5eab4885077637bf3251776f685d29b3cde53a3f
4
- data.tar.gz: de588b40792c8fb27be03badd85df8c7b74c3d6b
3
+ metadata.gz: b994e74251bb7b1be968d5bbad80273ee3ffea24
4
+ data.tar.gz: e104a5856e1710233df4da02b234935e56910f3b
5
5
  SHA512:
6
- metadata.gz: c23e39615a372abb0ce5958d1ed7abdbfb7d98c436b1cdfd21ec9072471f0eed3c0365641d3211945212309205b6501cb59b2f7b158017ef082f71b72c738d02
7
- data.tar.gz: 0dc5713aa8301858af5e0c8941aa12c05140837e33151d650475aed15d39794d950ea90c1152799ec6ad04b9d9739f0cd14a634abc69ae9ff48145a17f0da7fd
6
+ metadata.gz: c68e92c9e4c7b82aa7f19fb56f625f0656104b026ef81ede02b8f9dff90b430fc1af16062bb0fa8d337ad53f410ee563c127de9c594a3b47ce4a93723b668b90
7
+ data.tar.gz: 2e07f88d11a2c116289bfeb3e9050ad59b7314523aedeb45cf7d4b4042912c31e2ac4e80828ed27a272cec8952726ed02735c2af4022821255d3685c38478c61
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  /*.gem
3
3
  vendor
4
4
  Gemfile.lock
5
-
6
5
  /.env
7
6
  /tmp
7
+ /cache
8
+ node_modules
data/.travis.yml CHANGED
@@ -3,4 +3,7 @@ rvm:
3
3
  - 2.1
4
4
  script: "script/cibuild"
5
5
  sudo: false
6
- cache: bundler
6
+ cache:
7
+ - bundler
8
+ - node_modules
9
+ before_script: npm install
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A Ruby Gem to sniff information about a domain's technology and capabilities.
4
4
 
5
- [![Gem Version](https://badge.fury.io/rb/site-inspector.svg)](http://badge.fury.io/rb/site-inspector) [![Build Status](https://travis-ci.org/benbalter/site-inspector-ruby.svg)](https://travis-ci.org/benbalter/site-inspector-ruby)
5
+ [![Gem Version](https://badge.fury.io/rb/site-inspector.svg)](http://badge.fury.io/rb/site-inspector) [![Build Status](https://travis-ci.org/benbalter/site-inspector.svg)](https://travis-ci.org/benbalter/site-inspector)
6
6
 
7
7
  ## Demo
8
8
 
@@ -87,6 +87,15 @@ Options:
87
87
 
88
88
  Each endpoint also returns the following checks:
89
89
 
90
+ #### Accessibility
91
+
92
+ Uses the `pa11y` CLI to run automated accessibility tests. Requires `node`. To install `pally`: `[sudo] npm install -g pa11y`.
93
+
94
+ * `section508` - Tests against the Section508 standard
95
+ * `wcag2a` - Tests against the WCAG2A standard
96
+ * `wcag2aa` - Tests against the WCAG2AA standard
97
+ * `wcag2aaa` - Tests against the WCAG2AAA standard
98
+
90
99
  #### Content
91
100
 
92
101
  * `doctype` - The HTML doctype returned
@@ -136,7 +145,7 @@ Each endpoint also returns the following checks:
136
145
 
137
146
  ## Adding your own check
138
147
 
139
- [Checks](https://github.com/benbalter/site-inspector-ruby/tree/master/lib/site-inspector/checks) are special classes that are children of [`SiteInspector::Endpoint::Check`](https://github.com/benbalter/site-inspector-ruby/blob/master/lib/site-inspector/checks/check.rb). You can implement your own check like this:
148
+ [Checks](https://github.com/benbalter/site-inspector/tree/master/lib/site-inspector/checks) are special classes that are children of [`SiteInspector::Endpoint::Check`](https://github.com/benbalter/site-inspector/blob/master/lib/site-inspector/checks/check.rb). You can implement your own check like this:
140
149
 
141
150
  ```ruby
142
151
  class SiteInspector
@@ -0,0 +1,114 @@
1
+ require 'json'
2
+ require 'open3'
3
+
4
+ class SiteInspector
5
+ class Endpoint
6
+ class Accessibility < Check
7
+ class Pa11yError < RuntimeError; end
8
+
9
+ STANDARDS = {
10
+ section508: 'Section508', # Default standard
11
+ wcag2a: 'WCAG2A',
12
+ wcag2aa: 'WCAG2AA',
13
+ wcag2aaa: 'WCAG2AAA'
14
+ }
15
+
16
+ DEFAULT_LEVEL = :error
17
+
18
+ class << self
19
+ def pa11y_version
20
+ output, status = Open3.capture2e("pa11y", "--version")
21
+ output.strip if status == 0
22
+ end
23
+
24
+ def pa11y?
25
+ !!(Cliver.detect('pa11y'))
26
+ end
27
+ alias_method :enabled?, :pa11y?
28
+ end
29
+
30
+ def level
31
+ @level ||= DEFAULT_LEVEL
32
+ end
33
+
34
+ def level=(level)
35
+ raise ArgumentError, "Invalid level '#{level}'" unless [:error, :warning, :notice].include?(level)
36
+ @level = level
37
+ end
38
+
39
+ def standard?(standard)
40
+ STANDARDS.keys.include?(standard)
41
+ end
42
+
43
+ def standard
44
+ @standard ||= STANDARDS.keys.first
45
+ end
46
+
47
+ def standard=(standard)
48
+ raise ArgumentError, "Unknown standard '#{standard}'" unless standard?(standard)
49
+ @standard = standard
50
+ end
51
+
52
+ def valid?
53
+ check[:valid] if check
54
+ end
55
+
56
+ def errors
57
+ check[:results].count { |r| r["type"] == "error" } if check
58
+ end
59
+
60
+ def check
61
+ @check ||= pa11y(standard)
62
+ rescue Pa11yError
63
+ nil
64
+ end
65
+ alias_method :to_h, :check
66
+
67
+ def method_missing(method_sym, *arguments, &block)
68
+ if standard?(method_sym)
69
+ pa11y(method_sym)
70
+ else
71
+ super
72
+ end
73
+ end
74
+
75
+ def respond_to?(method_sym, include_private = false)
76
+ if standard?(method_sym)
77
+ true
78
+ else
79
+ super
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def pa11y(standard)
86
+ Cliver.assert('pa11y')
87
+ raise ArgumentError, "Unknown standard '#{standard}'" unless standard?(standard)
88
+
89
+ args = [
90
+ "--standard", STANDARDS[standard],
91
+ "--reporter", "json",
92
+ "--level", level.to_s,
93
+ endpoint.uri.to_s
94
+ ]
95
+ output, status = run_command(args)
96
+
97
+ # Pa11y exit codes: https://github.com/nature/pa11y#exit-codes
98
+ # 0: No errors, 1: Technical error within pa11y, 2: accessibility error (configurable via --level)
99
+ raise Pa11yError if status == 1
100
+
101
+ {
102
+ valid: status == 0,
103
+ results: JSON.parse(output)
104
+ }
105
+ rescue Pa11yError, JSON::ParserError
106
+ raise Pa11yError, "Command `pa11y #{args.join(" ")}` failed: #{output}"
107
+ end
108
+
109
+ def run_command(args)
110
+ Open3.capture2e("pa11y", *args)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -36,6 +36,10 @@ class SiteInspector
36
36
  def self.name
37
37
  self.to_s.split('::').last.downcase.to_sym
38
38
  end
39
+
40
+ def self.enabled?
41
+ true
42
+ end
39
43
  end
40
44
  end
41
45
  end
@@ -3,7 +3,7 @@ class SiteInspector
3
3
  class Content < Check
4
4
  # Given a path (e.g, "/data"), check if the given path exists on the canonical endpoint
5
5
  def path_exists?(path)
6
- endpoint.request(path: path, followlocation: true).success?
6
+ endpoint.up? && endpoint.request(path: path, followlocation: true).success?
7
7
  end
8
8
 
9
9
  def document
@@ -17,22 +17,23 @@ class SiteInspector
17
17
  end
18
18
 
19
19
  def robots_txt?
20
- @bodts_txt ||= path_exists?("robots.txt")
20
+ @bodts_txt ||= path_exists?("robots.txt") if proper_404s?
21
21
  end
22
22
 
23
23
  def sitemap_xml?
24
- @sitemap_xml ||= path_exists?("sitemap.xml")
24
+ @sitemap_xml ||= path_exists?("sitemap.xml") if proper_404s?
25
25
  end
26
26
 
27
27
  def humans_txt?
28
- @humans_txt ||= path_exists?("humans.txt")
28
+ @humans_txt ||= path_exists?("humans.txt") if proper_404s?
29
29
  end
30
30
 
31
31
  def doctype
32
- document.internal_subset.name
32
+ document.internal_subset.external_id
33
33
  end
34
34
 
35
35
  def prefetch
36
+ return unless endpoint.up?
36
37
  options = SiteInspector.typhoeus_defaults.merge(followlocation: true)
37
38
  ["robots.txt", "sitemap.xml", "humans.txt", random_path].each do |path|
38
39
  request = Typhoeus::Request.new(URI.join(endpoint.uri, path), options)
@@ -0,0 +1,45 @@
1
+ class SiteInspector
2
+ class Endpoint
3
+ class Cookies < Check
4
+
5
+ def any?(&block)
6
+ if cookie_header.nil? || cookie_header.empty?
7
+ false
8
+ elsif block_given?
9
+ all.any? { |cookie| block.call(cookie) }
10
+ else
11
+ true
12
+ end
13
+ end
14
+ alias_method :cookies?, :any?
15
+
16
+ def all
17
+ @cookies ||= cookie_header.map { |c| CGI::Cookie::parse(c) } if cookies?
18
+ end
19
+
20
+ def [](key)
21
+ all.find { |cookie| cookie.keys.first == key } if cookies?
22
+ end
23
+
24
+ def secure?
25
+ pairs = cookie_header.join("; ").split("; ") # CGI::Cookies#Parse doesn't seem to like secure headers
26
+ pairs.any? { |c| c.downcase == "secure" } && pairs.any? { |c| c.downcase == "httponly" }
27
+ end
28
+
29
+ def to_h
30
+ {
31
+ :cookie? => any?,
32
+ :secure? => secure?
33
+ }
34
+ end
35
+
36
+ private
37
+
38
+ def cookie_header
39
+ # Cookie header may be an array or string, always return an array
40
+ [endpoint.headers.all["set-cookie"]].flatten.compact
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -2,12 +2,6 @@ class SiteInspector
2
2
  class Endpoint
3
3
  class Headers < Check
4
4
 
5
- # cookies can have multiple set-cookie headers, so this detects
6
- # whether cookies are set, but not all their values.
7
- def cookies?
8
- !!headers["set-cookie"]
9
- end
10
-
11
5
  # TODO: kill this
12
6
  def strict_transport_security?
13
7
  !!strict_transport_security
@@ -49,13 +43,6 @@ class SiteInspector
49
43
  xss_protection == "1; mode=block"
50
44
  end
51
45
 
52
- def secure_cookies?
53
- return false if !cookies?
54
- cookie = headers["set-cookie"]
55
- cookie = cookie.first if cookie.is_a?(Array)
56
- !!(cookie =~ /(; secure.*; httponly|; httponly.*; secure)/i)
57
- end
58
-
59
46
  # Returns an array of hashes of downcased key/value header pairs (or an empty hash)
60
47
  def all
61
48
  @all ||= (response && response.headers) ? Hash[response.headers.map{ |k,v| [k.downcase,v] }] : {}
@@ -68,14 +55,11 @@ class SiteInspector
68
55
 
69
56
  def to_h
70
57
  {
71
- :cookies => cookies?,
72
58
  :strict_transport_security => strict_transport_security || false,
73
59
  :content_security_policy => content_security_policy || false,
74
60
  :click_jacking_protection => click_jacking_protection || false,
75
- :click_jacking_protection => click_jacking_protection || false,
76
61
  :server => server,
77
62
  :xss_protection => xss_protection || false,
78
- :secure_cookies => secure_cookies?
79
63
  }
80
64
  end
81
65
  end
@@ -2,8 +2,31 @@ class SiteInspector
2
2
  class Endpoint
3
3
  class Sniffer < Check
4
4
 
5
- def cms
6
- sniff :cms
5
+ OPEN_SOURCE_FRAMEWORKS = [
6
+ # Sniffles
7
+ :drupal,
8
+ :joomla,
9
+ :movabletype,
10
+ :phpbb,
11
+ :wordpress,
12
+
13
+ # Internal
14
+ :php,
15
+ :expression_engine,
16
+ :cowboy
17
+ ]
18
+
19
+ def framework
20
+ cms = sniff :cms
21
+ return cms unless cms.nil?
22
+ return :expression_engine if endpoint.cookies.any? { |c| c.keys.first =~ /^exp_/ }
23
+ return :php if endpoint.cookies["PHPSESSID"]
24
+ return :cowboy if endpoint.headers.server.to_s.downcase == "cowboy"
25
+ nil
26
+ end
27
+
28
+ def open_source?
29
+ OPEN_SOURCE_FRAMEWORKS.include?(framework)
7
30
  end
8
31
 
9
32
  def analytics
@@ -20,7 +43,7 @@ class SiteInspector
20
43
 
21
44
  def to_h
22
45
  {
23
- :cms => cms,
46
+ :framework => framework,
24
47
  :analytics => analytics,
25
48
  :javascript => javascript,
26
49
  :advertising => advertising
@@ -31,9 +54,8 @@ class SiteInspector
31
54
 
32
55
  def sniff(type)
33
56
  require 'sniffles'
34
- results = Sniffles.sniff(endpoint.content.body, type).select { |name, meta| meta[:found] == true }
35
- results.each { |name, result| result.delete :found} if results
36
- results
57
+ results = Sniffles.sniff(endpoint.content.body, type).select { |name, meta| meta[:found] }
58
+ results.keys.first if results
37
59
  rescue
38
60
  nil
39
61
  end
@@ -31,8 +31,10 @@ class SiteInspector
31
31
 
32
32
  private
33
33
 
34
+ # The `request` is a Typhoeus::Request, which provides a
35
+ # unique `cache_key` string for exactly this sort of thing.
34
36
  def path(request)
35
- File.join(@dir, request)
37
+ File.join(@dir, request.cache_key)
36
38
  end
37
39
  end
38
40
  end
@@ -32,12 +32,20 @@ class SiteInspector
32
32
  Gman.valid? host
33
33
  end
34
34
 
35
- # Does *any* endpoint return a 200 response code?
35
+ # Does *any* endpoint return a 200 or 300 response code?
36
36
  def up?
37
37
  endpoints.any? { |e| e.up? }
38
38
  end
39
39
 
40
- # Does any www endpoint return a 200 response code?
40
+ # Does *any* endpoint respond to HTTP?
41
+ # TODO: needs to allow an invalid chain.
42
+ def responds?
43
+ endpoints.any? { |e| e.responds? }
44
+ end
45
+
46
+
47
+ # TODO: These weren't present before, and may not be useful.
48
+ # Can you connect to www?
41
49
  def www?
42
50
  endpoints.any? { |e| e.www? && e.up? }
43
51
  end
@@ -51,11 +59,13 @@ class SiteInspector
51
59
  #
52
60
  # * Either of the HTTPS endpoints is listening, and doesn't have
53
61
  # an invalid hostname.
62
+ #
63
+ # TODO: needs to allow an invalid chain.
54
64
  def https?
55
65
  endpoints.any? { |e| e.https? && e.up? && e.https.valid? }
56
66
  end
57
67
 
58
- # HTTPS is enforced if one of the HTTPS endpoints is "live",
68
+ # HTTPS is enforced if one of the HTTPS endpoints is "up",
59
69
  # and if both *HTTP* endpoints are either:
60
70
  #
61
71
  # * down, or
@@ -66,14 +76,19 @@ class SiteInspector
66
76
  # * an HTTP redirect can go to HTTPS on another domain, as long
67
77
  # as it's immediate.
68
78
  # * a domain with an invalid cert can still be enforcing HTTPS.
79
+ #
80
+ # TODO: need to ensure the redirect *immediately* goes to HTTPS.
81
+ # TODO: don't need to require that the HTTPS cert is valid for this purpose.
69
82
  def enforces_https?
70
83
  return false unless https?
71
- endpoints.select { |e| e.http? }.all? { |e| e.down? || (e.redirect && e.redirect.https?) }
84
+ endpoints.select { |e| e.http? }.all? { |e| !e.up? || (e.redirect && e.redirect.https?) }
72
85
  end
73
86
 
74
87
  # we can say that a canonical HTTPS site "defaults" to HTTPS,
75
88
  # even if it doesn't *strictly* enforce it (e.g. having a www
76
89
  # subdomain first to go HTTP root before HTTPS root).
90
+ #
91
+ # TODO: not implemented.
77
92
  def defaults_https?
78
93
  raise "Not implemented. Halp?"
79
94
  end
@@ -82,9 +97,11 @@ class SiteInspector
82
97
  #
83
98
  # * HTTPS is supported, and
84
99
  # * The 'canonical' endpoint gets an immediate internal redirect to HTTP.
100
+ #
101
+ # TODO: the redirect must be internal.
85
102
  def downgrades_https?
86
103
  return false unless https?
87
- canonical_endpoint.redirect && canonical_endpoint.redirect.http?
104
+ canonical_endpoint.redirect? && canonical_endpoint.redirect.http?
88
105
  end
89
106
 
90
107
  # A domain is "canonically" at www if:
@@ -108,7 +125,7 @@ class SiteInspector
108
125
  return false unless www?
109
126
 
110
127
  # Are both root endpoints down?
111
- return true if endpoints.select { |e| e.root? }.all? { |e| e.down? }
128
+ return true if endpoints.select { |e| e.root? }.all? { |e| !e.up? }
112
129
 
113
130
  # Does either root endpoint redirect to a www endpoint?
114
131
  endpoints.select { |e| e.root? }.any? { |e| e.redirect && e.redirect.www? }
@@ -139,7 +156,7 @@ class SiteInspector
139
156
  return false unless https?
140
157
 
141
158
  # Both http endpoints are down
142
- return true if endpoints.select { |e| e.http? }.all? { |e| e.down? }
159
+ return true if endpoints.select { |e| e.http? }.all? { |e| !e.up? }
143
160
 
144
161
  # at least one http endpoint redirects immediately to https
145
162
  endpoints.select { |e| e.http? }.any? { |e| e.redirect && e.redirect.https? }
@@ -150,7 +167,7 @@ class SiteInspector
150
167
  # 2. All endpoints are either down or an external redirect
151
168
  def redirect?
152
169
  return false unless redirect
153
- endpoints.all? { |e| e.down? || e.external_redirect? }
170
+ endpoints.all? { |e| !e.up? || e.external_redirect? }
154
171
  end
155
172
 
156
173
  # The first endpoint to respond with a redirect
@@ -205,10 +222,11 @@ class SiteInspector
205
222
  # Returns a complete hash of the domain's information
206
223
  def to_h(options={})
207
224
  prefetch
208
-
225
+
209
226
  hash = {
210
227
  host: host,
211
228
  up: up?,
229
+ responds: responds?,
212
230
  www: www?,
213
231
  root: root?,
214
232
  https: https?,
@@ -220,7 +238,7 @@ class SiteInspector
220
238
  hsts: hsts?,
221
239
  hsts_subdomains: hsts_subdomains?,
222
240
  hsts_preload_ready: hsts_preload_ready?,
223
- canoncial_endpoint: canonical_endpoint.to_h(options)
241
+ canonical_endpoint: canonical_endpoint.to_h(options)
224
242
  }
225
243
 
226
244
  if options["all"]
@@ -56,11 +56,6 @@ class SiteInspector
56
56
  @response ||= request
57
57
  end
58
58
 
59
- # Does the server return any response? (including 50x)
60
- def response?
61
- response.code != 0 && !timed_out?
62
- end
63
-
64
59
  def response_code
65
60
  response.response_code.to_s if response
66
61
  end
@@ -74,8 +69,9 @@ class SiteInspector
74
69
  response && response_code.start_with?("2") || response_code.start_with?("3")
75
70
  end
76
71
 
77
- def down?
78
- !up?
72
+ # Does the server respond at all?
73
+ def responds?
74
+ response.code != 0 && !timed_out?
79
75
  end
80
76
 
81
77
  # If the domain is a redirect, what's the first endpoint we're redirected to?
@@ -155,6 +151,7 @@ class SiteInspector
155
151
  https: https?,
156
152
  scheme: scheme,
157
153
  up: up?,
154
+ responds: responds?,
158
155
  timed_out: timed_out?,
159
156
  redirect: redirect?,
160
157
  external_redirect: external_redirect?,
@@ -172,7 +169,7 @@ class SiteInspector
172
169
  end
173
170
 
174
171
  def self.checks
175
- ObjectSpace.each_object(Class).select { |klass| klass < Check }
172
+ ObjectSpace.each_object(Class).select { |klass| klass < Check }.select { |check| check.enabled? }
176
173
  end
177
174
 
178
175
  def method_missing(method_sym, *arguments, &block)
@@ -1,3 +1,3 @@
1
1
  class SiteInspector
2
- VERSION = "2.0.0"
2
+ VERSION = "3.0.0"
3
3
  end
@@ -2,18 +2,22 @@ require 'open-uri'
2
2
  require 'addressable/uri'
3
3
  require 'public_suffix'
4
4
  require 'typhoeus'
5
+ require 'cliver'
6
+ require 'cgi'
5
7
 
6
8
  require_relative 'site-inspector/cache'
7
9
  require_relative 'site-inspector/disk_cache'
8
10
  require_relative 'site-inspector/rails_cache'
9
11
  require_relative 'site-inspector/domain'
10
12
  require_relative 'site-inspector/checks/check'
13
+ require_relative 'site-inspector/checks/accessibility'
11
14
  require_relative 'site-inspector/checks/content'
12
15
  require_relative 'site-inspector/checks/dns'
13
16
  require_relative 'site-inspector/checks/headers'
14
17
  require_relative 'site-inspector/checks/hsts'
15
18
  require_relative 'site-inspector/checks/https'
16
19
  require_relative 'site-inspector/checks/sniffer'
20
+ require_relative 'site-inspector/checks/cookies'
17
21
  require_relative 'site-inspector/endpoint'
18
22
  require_relative 'site-inspector/version'
19
23
 
@@ -46,7 +50,7 @@ class SiteInspector
46
50
  :timeout => SiteInspector.timeout,
47
51
  :accept_encoding => "gzip",
48
52
  :headers => {
49
- "User-Agent" => "Mozilla/5.0 (compatible; SiteInspector/#{SiteInspector::VERSION}; +https://github.com/benbalter/site-inspector-ruby)"
53
+ "User-Agent" => "Mozilla/5.0 (compatible; SiteInspector/#{SiteInspector::VERSION}; +https://github.com/benbalter/site-inspector)"
50
54
  }
51
55
  }
52
56
  end
data/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "site-inspector",
3
+ "version": "2.0.0",
4
+ "description": "Returns information about a domain's technology and capabilities",
5
+ "main": "site-inspector",
6
+ "dependencies": {
7
+ "pa11y": "^2.1.0"
8
+ },
9
+ "devDependencies": {},
10
+ "scripts": {
11
+ "test": "script/cibuild"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/benbalter/site-inspector.git"
16
+ },
17
+ "author": "",
18
+ "license": "MIT",
19
+ "bugs": {
20
+ "url": "https://github.com/benbalter/site-inspector/issues"
21
+ },
22
+ "homepage": "https://github.com/benbalter/site-inspector#readme"
23
+ }
data/script/bootstrap CHANGED
@@ -1 +1,2 @@
1
1
  bundle install
2
+ npm install
data/script/cibuild CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  set -e
4
4
 
5
+ echo "Pa11y version: $(pa11y --version)"
6
+
5
7
  bundle exec rake spec
6
8
 
7
9
  gem build site-inspector.gemspec
@@ -4,11 +4,11 @@ Gem::Specification.new do |s|
4
4
 
5
5
  s.name = "site-inspector"
6
6
  s.version = SiteInspector::VERSION
7
- s.summary = "A Ruby port and v2 of Site Inspector (http://github.com/benbalter/site-inspector)"
7
+ s.summary = "A Ruby port and v2 of Site Inspector (https://github.com/benbalter/site-inspector)"
8
8
  s.description = "Returns information about a domain's technology and capabilities"
9
9
  s.authors = "Ben Balter"
10
10
  s.email = "ben@balter.com"
11
- s.homepage = "https://github.com/benbalter/site-inspector-ruby"
11
+ s.homepage = "https://github.com/benbalter/site-inspector"
12
12
  s.license = "MIT"
13
13
 
14
14
  s.files = `git ls-files -z`.split("\x0")
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
25
25
  s.add_dependency("oj", "~> 2.11")
26
26
  s.add_dependency("mercenary", "~> 0.3")
27
27
  s.add_dependency("colorator", "~> 0.1")
28
+ s.add_dependency("cliver", "~> 0.3")
28
29
  s.add_development_dependency("pry", "~> 0.10")
29
30
  s.add_development_dependency( "rake", "~> 10.4" )
30
31
  s.add_development_dependency( "rspec", "~> 3.2")