site-inspector 2.0.0 → 3.0.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 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")