secure_headers 3.0.0.pre2 → 3.0.0.pre3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of secure_headers might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d6e439e921b5270ed007c604d2287b04a5ef691f
4
- data.tar.gz: 6ae708cd125952e4ad7f24121d1be4f5b8d54d1d
3
+ metadata.gz: 7e0a5f8e1d4955125f71ede3b2f1bd882ccaca56
4
+ data.tar.gz: 656cc8d6965a49288b7ccd2ab3fc91fb13ab9792
5
5
  SHA512:
6
- metadata.gz: a426253499c8e6e1c70d6f1f11cde022d90f4ea9dc8db1073f1055ee0c82e6be007b2746ff5e98cb4e12c0f0064b7e656c4861e97bf10fd01f156f8e78d551a1
7
- data.tar.gz: 91ef0e9f9f32d311389d35c0caf3b607da10b6e8fc029d1a4a1514c35ca2df92b1a9dbda224d6e4808d39517dc4d2ef62317bcf1c7f3d1071cd37231fe461141
6
+ metadata.gz: 4a61ccca281e3968c41aa6daf1d069b6a6aef9d796c62cc0b5c7be45007fc3ca41908f71310c289c854e386dfafbb1b96cfa5eca45fc598f43113edb8b7678f0
7
+ data.tar.gz: edd0190e206824a91e961131f8f612ac7a10559ced00c9b314cba681d579cfc27c9280205dbe687725579a7a242fcae70c6e4a279bafcb2981e63ee00679c4e6
data/README.md CHANGED
@@ -1,26 +1,31 @@
1
- # SecureHeaders [![Build Status](https://travis-ci.org/twitter/secureheaders.png?branch=master)](http://travis-ci.org/twitter/secureheaders) [![Code Climate](https://codeclimate.com/github/twitter/secureheaders.png)](https://codeclimate.com/github/twitter/secureheaders) [![Coverage Status](https://coveralls.io/repos/twitter/secureheaders/badge.png)](https://coveralls.io/r/twitter/secureheaders)
1
+ # Secure Headers [![Build Status](https://travis-ci.org/twitter/secureheaders.png?branch=master)](http://travis-ci.org/twitter/secureheaders) [![Code Climate](https://codeclimate.com/github/twitter/secureheaders.png)](https://codeclimate.com/github/twitter/secureheaders) [![Coverage Status](https://coveralls.io/repos/twitter/secureheaders/badge.png)](https://coveralls.io/r/twitter/secureheaders)
2
+
2
3
 
3
4
  **The 3.x branch was recently merged**. See the [upgrading to 3.x doc](upgrading-to-3-0.md) for instructions on how to upgrade including the differences and benefits of using the 3.x branch.
4
5
 
5
- **The [2.x branch](https://github.com/twitter/secureheaders/tree/2.x) will be maintained**. The documentation below only applies to the 2.x branch. See the 2.x [README](https://github.com/twitter/secureheaders/blob/2.x/README.md) for the old way of doing things.
6
+ **The [2.x branch](https://github.com/twitter/secureheaders/tree/2.x) will be maintained**. The documentation below only applies to the 3.x branch. See the 2.x [README](https://github.com/twitter/secureheaders/blob/2.x/README.md) for the old way of doing things.
6
7
 
7
8
  The gem will automatically apply several headers that are related to security. This includes:
8
9
  - Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. [CSP 2 Specification](http://www.w3.org/TR/CSP2/)
9
10
  - HTTP Strict Transport Security (HSTS) - Ensures the browser never visits the http version of a website. Protects from SSLStrip/Firesheep attacks. [HSTS Specification](https://tools.ietf.org/html/rfc6797)
10
11
  - X-Frame-Options (XFO) - Prevents your content from being framed and potentially clickjacked. [X-Frame-Options draft](https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-02)
11
- - X-XSS-Protection - [Cross site scripting heuristic filter for IE/Chrome](http://msdn.microsoft.com/en-us/library/dd565647\(v=vs.85\).aspx)
12
- - X-Content-Type-Options - [Prevent content type sniffing](http://msdn.microsoft.com/en-us/library/ie/gg622941\(v=vs.85\).aspx)
13
- - X-Download-Options - [Prevent file downloads opening](http://msdn.microsoft.com/en-us/library/ie/jj542450(v=vs.85).aspx)
12
+ - X-XSS-Protection - [Cross site scripting heuristic filter for IE/Chrome](https://msdn.microsoft.com/en-us/library/dd565647\(v=vs.85\).aspx)
13
+ - X-Content-Type-Options - [Prevent content type sniffing](https://msdn.microsoft.com/library/gg622941\(v=vs.85\).aspx)
14
+ - X-Download-Options - [Prevent file downloads opening](https://msdn.microsoft.com/library/jj542450(v=vs.85).aspx)
14
15
  - X-Permitted-Cross-Domain-Policies - [Restrict Adobe Flash Player's access to data](https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html)
15
16
  - Public Key Pinning - Pin certificate fingerprints in the browser to prevent man-in-the-middle attacks due to compromised Certificate Authorities. [Public Key Pinning Specification](https://tools.ietf.org/html/rfc7469)
16
17
 
17
- `secure_headers` is a library with a global config, per request overrides, and rack milddleware that enables you customize your application settings.
18
+ `secure_headers` is a library with a global config, per request overrides, and rack middleware that enables you customize your application settings.
19
+
20
+ ## Use
21
+
22
+ `gem install secure_headers`
18
23
 
19
24
  ## Configuration
20
25
 
21
26
  If you do not supply a `default` configuration, exceptions will be raised. If you would like to use a default configuration (which is fairly locked down), just call `SecureHeaders::Configuration.default` without any arguments or block.
22
27
 
23
- All `nil` values will fallback to their default value. `SecureHeaders::OPT_OUT` will disable the header entirely.
28
+ All `nil` values will fallback to their default values. `SecureHeaders::OPT_OUT` will disable the header entirely.
24
29
 
25
30
  ```ruby
26
31
  SecureHeaders::Configuration.default do |config|
@@ -31,12 +36,15 @@ SecureHeaders::Configuration.default do |config|
31
36
  config.x_download_options = "noopen"
32
37
  config.x_permitted_cross_domain_policies = "none"
33
38
  config.csp = {
39
+ # "meta" values. these will shaped the header, but the values are not included in the header.
40
+ report_only: true, # default: false
41
+ preserve_schemes: true, # default: false. Schemes are removed from host sources to save bytes and discourage mixed content.
42
+
43
+ # directive values: these values will directly translate into source directives
34
44
  default_src: %w(https: 'self'),
35
- report_only: false,
36
- frame_src: %w(*.twimg.com itunes.apple.com),
45
+ frame_src: %w('self' *.twimg.com itunes.apple.com),
37
46
  connect_src: %w(wws:),
38
47
  font_src: %w('self' data:),
39
- frame_src: %w('self'),
40
48
  img_src: %w(mycdn.com data:),
41
49
  media_src: %w(utoob.com),
42
50
  object_src: %w('self'),
@@ -129,7 +137,7 @@ end
129
137
 
130
138
  ## Per-action configuration
131
139
 
132
- You can override the settings for a given action by producing a temporary override. This approach is not recommended because the header values will be computed per request.
140
+ You can override the settings for a given action by producing a temporary override. Be aware that because of the dynamic nature of the value, the header values will be computed per request.
133
141
 
134
142
  ```ruby
135
143
  # Given a config of:
@@ -148,7 +156,7 @@ class MyController < ApplicationController
148
156
 
149
157
  # Overrides the previously set source list, override 'none' values
150
158
  # Produces: default-src 'self'; script-src s3.amazaonaws.com; object-src 'self'
151
- override_content_security_policy_directive(script_src: %w(s3.amazaonaws.com), object_src: %w('self'))
159
+ override_content_security_policy_directives(script_src: %w(s3.amazaonaws.com), object_src: %w('self'))
152
160
 
153
161
  # Global settings default to "sameorigin"
154
162
  override_x_frame_options("DENY")
@@ -157,7 +165,7 @@ class MyController < ApplicationController
157
165
 
158
166
  The following methods are available as controller instance methods. They are also available as class methods, but require you to pass in the `request` object.
159
167
  * `append_content_security_policy_directives(hash)`: appends each value to the corresponding CSP app-wide configuration.
160
- * `override_content_security_policy_directive(hash)`: merges the hash into the app-wide configuration, overwriting any previous config
168
+ * `override_content_security_policy_directives(hash)`: merges the hash into the app-wide configuration, overwriting any previous config
161
169
  * `override_x_frame_options(value)`: sets the `X-Frame-Options header` to `value`
162
170
 
163
171
  ## Appending / overriding Content Security Policy
@@ -169,7 +177,7 @@ When manipulating content security policy, there are a few things to consider. T
169
177
  The value of `default_src` is joined with the addition. Note the `https:` is carried over from the `default-src` config. If you do not want this, use `override_content_security_policy_directives` instead. To illustrate:
170
178
 
171
179
  ```ruby
172
- ::SecureHeaders::Configuration.configure do |config|
180
+ ::SecureHeaders::Configuration.default do |config|
173
181
  config.csp = {
174
182
  default_src: %w('self')
175
183
  }
@@ -181,11 +189,6 @@ Code | Result
181
189
  `append_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src 'self' mycdn.com`
182
190
  `override_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src mycdn.com`
183
191
 
184
- Code | Result
185
- ------------- | -------------
186
- `append_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src https:; script-src https: mycdn.com`
187
- `override_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src https:; script-src mycdn.com`
188
-
189
192
  #### Nonce
190
193
 
191
194
  script/style-nonce can be used to whitelist inline content. To do this, call the SecureHeaders::content_security_policy_nonce then set the nonce attributes on the various tags.
@@ -269,7 +272,7 @@ require 'secure_headers'
269
272
 
270
273
  use SecureHeaders::Middleware
271
274
 
272
- SecureHeaders::Configuration.configure do |config|
275
+ SecureHeaders::Configuration.default do |config|
273
276
  ...
274
277
  end
275
278
 
@@ -314,7 +317,7 @@ and in `config/boot.rb`:
314
317
 
315
318
  ```ruby
316
319
  def before_load
317
- SecureHeaders::Configuration.configure do |config|
320
+ SecureHeaders::Configuration.default do |config|
318
321
  ...
319
322
  end
320
323
  end
@@ -322,13 +325,14 @@ end
322
325
 
323
326
  ## Similar libraries
324
327
 
325
- * Rack [rack-secure_headers](https://github.com/harmoni/rack-secure_headers)
326
- * Node.js (express) [helmet](https://github.com/evilpacket/helmet) and [hood](https://github.com/seanmonstar/hood)
328
+ * Rack [rack-secure_headers](https://github.com/frodsan/rack-secure_headers)
329
+ * Node.js (express) [helmet](https://github.com/helmetjs/helmet) and [hood](https://github.com/seanmonstar/hood)
327
330
  * Node.js (hapi) [blankie](https://github.com/nlf/blankie)
328
331
  * J2EE Servlet >= 3.0 [headlines](https://github.com/sourceclear/headlines)
329
332
  * ASP.NET - [NWebsec](https://github.com/NWebsec/NWebsec/wiki)
330
333
  * Python - [django-csp](https://github.com/mozilla/django-csp) + [commonware](https://github.com/jsocol/commonware/); [django-security](https://github.com/sdelements/django-security)
331
334
  * Go - [secureheader](https://github.com/kr/secureheader)
335
+ * Elixir [secure_headers](https://github.com/anotherhale/secure_headers)
332
336
 
333
337
  ## License
334
338
 
@@ -47,12 +47,15 @@ module SecureHeaders
47
47
  # additions - a hash containing directives. e.g.
48
48
  # script_src: %w(another-host.com)
49
49
  def override_content_security_policy_directives(request, additions)
50
- config = config_for(request).dup
51
- if config.csp == OPT_OUT
52
- config.csp = {}
50
+ config = config_for(request)
51
+ unless CSP.idempotent_additions?(config.csp, additions)
52
+ config = config.dup
53
+ if config.csp == OPT_OUT
54
+ config.csp = {}
55
+ end
56
+ config.csp.merge!(additions)
57
+ override_secure_headers_request_config(request, config)
53
58
  end
54
- config.csp.merge!(additions)
55
- override_secure_headers_request_config(request, config)
56
59
  end
57
60
 
58
61
  # Public: appends source values to the current configuration. If no value
@@ -62,9 +65,12 @@ module SecureHeaders
62
65
  # additions - a hash containing directives. e.g.
63
66
  # script_src: %w(another-host.com)
64
67
  def append_content_security_policy_directives(request, additions)
65
- config = config_for(request).dup
66
- config.csp = CSP.combine_policies(config.csp, additions)
67
- override_secure_headers_request_config(request, config)
68
+ config = config_for(request)
69
+ unless CSP.idempotent_additions?(config.csp, additions)
70
+ config = config.dup
71
+ config.csp = CSP.combine_policies(config.csp, additions)
72
+ override_secure_headers_request_config(request, config)
73
+ end
68
74
  end
69
75
 
70
76
  # Public: override X-Frame-Options settings for this request.
@@ -145,7 +145,12 @@ module SecureHeaders
145
145
  STAR,
146
146
  DATA_PROTOCOL,
147
147
  BLOB_PROTOCOL
148
- ]
148
+ ].freeze
149
+
150
+ META_CONFIGS = [
151
+ :report_only,
152
+ :preserve_schemes
153
+ ].freeze
149
154
 
150
155
  class << self
151
156
  # Public: generate a header name, value array that is user-agent-aware.
@@ -157,13 +162,7 @@ module SecureHeaders
157
162
  [header.name, header.value]
158
163
  end
159
164
 
160
- # Public: Validates that the configuration has a valid type, or that it is a valid
161
- # source expression.
162
- #
163
- # Private: validates that a source expression:
164
- # 1. has a valid name
165
- # 2. is an array of strings
166
- # 3. does not contain any depreated, now invalid values (inline, eval, self, none)
165
+ # Public: Validates each source expression.
167
166
  #
168
167
  # Does not validate the invididual values of the source expression (e.g.
169
168
  # script_src => h*t*t*p: will not raise an exception)
@@ -171,7 +170,7 @@ module SecureHeaders
171
170
  return if config.nil? || config == OPT_OUT
172
171
  raise ContentSecurityPolicyConfigError.new(":default_src is required") unless config[:default_src]
173
172
  config.each do |key, value|
174
- if key == :report_only
173
+ if META_CONFIGS.include?(key)
175
174
  raise ContentSecurityPolicyConfigError.new("#{key} must be a boolean value") unless boolean?(value) || value.nil?
176
175
  else
177
176
  validate_directive!(key, value)
@@ -179,6 +178,17 @@ module SecureHeaders
179
178
  end
180
179
  end
181
180
 
181
+ # Public: determine if merging +additions+ will cause a change to the
182
+ # actual value of the config.
183
+ #
184
+ # e.g. config = { script_src: %w(example.org google.com)} and
185
+ # additions = { script_src: %w(google.com)} then idempotent_additions? would return
186
+ # because google.com is already in the config.
187
+ def idempotent_additions?(config, additions)
188
+ return false if config == OPT_OUT
189
+ config.to_s == combine_policies(config, additions).to_s
190
+ end
191
+
182
192
  # Public: combine the values from two different configs.
183
193
  #
184
194
  # original - the main config
@@ -208,11 +218,11 @@ module SecureHeaders
208
218
  # when each hash contains a value for a given key.
209
219
  original.merge(additions) do |directive, lhs, rhs|
210
220
  if source_list?(directive)
211
- lhs | rhs
221
+ (lhs.to_a + rhs).uniq.compact
212
222
  else
213
223
  rhs
214
224
  end
215
- end
225
+ end.reject { |_, value| value.nil? || value == [] } # this mess prevents us from adding empty directives.
216
226
  end
217
227
 
218
228
  private
@@ -275,7 +285,8 @@ module SecureHeaders
275
285
  else
276
286
  UserAgent.parse(user_agent)
277
287
  end
278
- @report_only = !!@config[:report_only]
288
+ @report_only = @config[:report_only]
289
+ @preserve_schemes = @config[:preserve_schemes]
279
290
  @script_nonce = @config[:script_nonce]
280
291
  @style_nonce = @config[:style_nonce]
281
292
  end
@@ -345,9 +356,12 @@ module SecureHeaders
345
356
  source_list.reject! { |value| value == NONE } if source_list.length > 1
346
357
 
347
358
  # remove schemes and dedup source expressions
348
- source_list = strip_source_schemes(source_list) unless directive_name == REPORT_URI
359
+ unless directive_name == REPORT_URI || @preserve_schemes
360
+ source_list = strip_source_schemes(source_list)
361
+ end
349
362
  dedup_source_list(source_list).join(" ")
350
363
  end
364
+
351
365
  [symbol_to_hyphen_case(directive_name), value].join(" ")
352
366
  end
353
367
 
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |gem|
3
3
  gem.name = "secure_headers"
4
- gem.version = "3.0.0.pre2"
4
+ gem.version = "3.0.0.pre3"
5
5
  gem.authors = ["Neil Matatall"]
6
6
  gem.email = ["neil.matatall@gmail.com"]
7
7
  gem.description = 'Security related headers all in one gem.'
@@ -23,6 +23,35 @@ module SecureHeaders
23
23
  end
24
24
 
25
25
  describe "#validate_config!" do
26
+ it "accepts all keys" do
27
+ # (pulled from README)
28
+ config = {
29
+ # "meta" values. these will shaped the header, but the values are not included in the header.
30
+ report_only: true, # default: false
31
+ preserve_schemes: true, # default: false. Schemes are removed from host sources to save bytes and discourage mixed content.
32
+
33
+ # directive values: these values will directly translate into source directives
34
+ default_src: %w(https: 'self'),
35
+ frame_src: %w('self' *.twimg.com itunes.apple.com),
36
+ connect_src: %w(wws:),
37
+ font_src: %w('self' data:),
38
+ img_src: %w(mycdn.com data:),
39
+ media_src: %w(utoob.com),
40
+ object_src: %w('self'),
41
+ script_src: %w('self'),
42
+ style_src: %w('unsafe-inline'),
43
+ base_uri: %w('self'),
44
+ child_src: %w('self'),
45
+ form_action: %w('self' github.com),
46
+ frame_ancestors: %w('none'),
47
+ plugin_types: %w(application/x-shockwave-flash),
48
+ block_all_mixed_content: true, # see [http://www.w3.org/TR/mixed-content/](http://www.w3.org/TR/mixed-content/)
49
+ report_uri: %w(https://example.com/uri-directive)
50
+ }
51
+
52
+ CSP.validate_config!(config)
53
+ end
54
+
26
55
  it "requires a :default_src value" do
27
56
  expect do
28
57
  CSP.validate_config!(script_src: %('self'))
@@ -35,6 +64,12 @@ module SecureHeaders
35
64
  end.to raise_error(ContentSecurityPolicyConfigError)
36
65
  end
37
66
 
67
+ it "requires :preserve_schemes to be a truthy value" do
68
+ expect do
69
+ CSP.validate_config!(default_opts.merge(preserve_schemes: "steve"))
70
+ end.to raise_error(ContentSecurityPolicyConfigError)
71
+ end
72
+
38
73
  it "requires :block_all_mixed_content to be a boolean value" do
39
74
  expect do
40
75
  CSP.validate_config!(default_opts.merge(block_all_mixed_content: "steve"))
@@ -109,6 +144,19 @@ module SecureHeaders
109
144
  end
110
145
  end
111
146
 
147
+ describe "#idempotent_additions?" do
148
+ specify { expect(ContentSecurityPolicy.idempotent_additions?(OPT_OUT, script_src: %w(b.com))).to be false }
149
+ specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: %w(c.com))).to be false }
150
+ specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, style_src: %w(b.com))).to be false }
151
+ specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: %w(a.com b.com c.com))).to be false }
152
+
153
+ specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: %w(b.com))).to be true }
154
+ specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: %w(b.com a.com))).to be true }
155
+ specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: %w())).to be true }
156
+ specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: [nil])).to be true }
157
+ specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, style_src: [nil])).to be true }
158
+ end
159
+
112
160
  describe "#value" do
113
161
  it "discards 'none' values if any other source expressions are present" do
114
162
  csp = ContentSecurityPolicy.new(default_opts.merge(frame_src: %w('self' 'none')))
@@ -138,6 +186,11 @@ module SecureHeaders
138
186
  expect(csp.value).to eq("default-src https:; report-uri https://example.org")
139
187
  end
140
188
 
189
+ it "does not remove schemes when :preserve_schemes is true" do
190
+ csp = ContentSecurityPolicy.new(default_src: %w(https://example.org), :preserve_schemes => true)
191
+ expect(csp.value).to eq("default-src https://example.org")
192
+ end
193
+
141
194
  it "removes nil from source lists" do
142
195
  csp = ContentSecurityPolicy.new(default_src: ["https://example.org", nil])
143
196
  expect(csp.value).to eq("default-src example.org")
data/upgrading-to-3-0.md CHANGED
@@ -11,6 +11,7 @@ Changes
11
11
  | `self`/`none` source expressions | could be `self` / `none` / `'self'` / `'none'` | Must be `'self'` or `'none'` |
12
12
  | `inline` / `eval` source expressions | could be `inline`, `eval`, `'unsafe-inline'`, or `'unsafe-eval'` | Must be `'unsafe-eval'` or `'unsafe-inline'` |
13
13
  | Per-action configuration | override [`def secure_header_options_for(header, options)`](https://github.com/twitter/secureheaders/commit/bb9ebc6c12a677aad29af8e0f08ffd1def56efec#diff-04c6e90faac2675aa89e2176d2eec7d8R111) | Use [named overrides](https://github.com/twitter/secureheaders#named-overrides) or [per-action helpers](https://github.com/twitter/secureheaders#per-action-configuration) |
14
+ | CSP/HPKP use `report_only` config that defaults to false | `enforce: false` | `report_only: false` |
14
15
 
15
16
  Migrating to 3.x from <= 2.x
16
17
  ==
@@ -18,6 +19,7 @@ Migrating to 3.x from <= 2.x
18
19
  1. Convert all headers except for CSP/HPKP using hashes to string values. The values are validated at runtime and will provide guidance on misconfigured headers.
19
20
  1. Convert all instances of `self`/`none`/`eval`/`inline` to the corresponding values in the above table.
20
21
  1. Convert all CSP space-delimited directives to an array of strings.
22
+ 1. Convert all `enforce: true|false` to `report_only: true|false`.
21
23
 
22
24
  Everything is terrible, why should I upgrade?
23
25
  ==
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secure_headers
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.pre2
4
+ version: 3.0.0.pre3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Matatall
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-22 00:00:00.000000000 Z
11
+ date: 2016-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake