rack_csrf 2.3.0 → 2.4.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.
- data/Changelog.md +13 -0
- data/Gemfile +12 -0
- data/LICENSE.rdoc +1 -1
- data/README.rdoc +61 -1
- data/Rakefile +11 -7
- data/VERSION +1 -1
- data/examples/cuba/Gemfile +1 -1
- data/examples/innate/Gemfile +1 -1
- data/examples/rack/Gemfile +1 -1
- data/examples/sinatra/Gemfile +1 -1
- data/features/check_only_some_specific_requests.feature +22 -0
- data/features/custom_http_methods.feature +46 -0
- data/features/empty_responses.feature +47 -0
- data/features/inspecting_also_get_requests.feature +14 -0
- data/features/raising_exception.feature +27 -0
- data/features/skip_if_block_passes.feature +40 -0
- data/features/skip_some_routes.feature +3 -3
- data/features/step_definitions/request_steps.rb +39 -7
- data/features/step_definitions/setup_steps.rb +56 -14
- data/features/variation_on_header_name.feature +35 -0
- data/lib/rack/csrf.rb +40 -13
- data/rack_csrf.gemspec +16 -7
- data/spec/csrf_spec.rb +105 -59
- metadata +57 -24
@@ -0,0 +1,40 @@
|
|
1
|
+
Feature: Skipping the check if a block passes
|
2
|
+
|
3
|
+
Scenario Outline: Skipping check for requests with specific header
|
4
|
+
Given a rack with the anti-CSRF middleware and the :skip_if option
|
5
|
+
| name | value |
|
6
|
+
| token | skip |
|
7
|
+
| User-Agent | MSIE |
|
8
|
+
When it receives a request with headers <name> = <value> without the CSRF token or header
|
9
|
+
Then it lets it pass untouched
|
10
|
+
|
11
|
+
Examples:
|
12
|
+
| name | value |
|
13
|
+
| token | skip |
|
14
|
+
| User-Agent | MSIE |
|
15
|
+
|
16
|
+
Scenario Outline: Not skipping check for requests without specific header
|
17
|
+
Given a rack with the anti-CSRF middleware and the :skip_if option
|
18
|
+
| name | value |
|
19
|
+
| token | skip |
|
20
|
+
| User-Agent | MSIE |
|
21
|
+
When it receives a request with headers <name> = <value> without the CSRF token or header
|
22
|
+
Then it responds with 403
|
23
|
+
|
24
|
+
Examples:
|
25
|
+
| name | value |
|
26
|
+
| token | forward |
|
27
|
+
| User-Agent | WebKit |
|
28
|
+
| User-Agent | Mozilla |
|
29
|
+
|
30
|
+
Scenario Outline: Skipping check for requests with :skip and :skip_if options
|
31
|
+
Given a rack with the anti-CSRF middleware and both the :skip and :skip_if options
|
32
|
+
| name | value | path |
|
33
|
+
| token | skip | POST:/ |
|
34
|
+
When it receives a request with headers <name> = <value>, <method>, and without the CSRF token or header
|
35
|
+
Then it lets it pass untouched
|
36
|
+
|
37
|
+
Examples:
|
38
|
+
| name | value | method |
|
39
|
+
| token | skip | POST |
|
40
|
+
| token | pass | POST |
|
@@ -8,7 +8,7 @@ Feature: Skipping the check for some specific routes
|
|
8
8
|
| POST:/not_.*\.json |
|
9
9
|
| DELETE:/cars/.*\.xml |
|
10
10
|
| PATCH:/this/one/too |
|
11
|
-
When it receives a <method> request for <path> without the CSRF token
|
11
|
+
When it receives a <method> request for <path> without the CSRF token or header
|
12
12
|
Then it lets it pass untouched
|
13
13
|
|
14
14
|
Examples:
|
@@ -28,7 +28,7 @@ Feature: Skipping the check for some specific routes
|
|
28
28
|
| POST:/not_.*\.json |
|
29
29
|
| DELETE:/cars/.*\.xml |
|
30
30
|
| PATCH:/this/one/too |
|
31
|
-
When it receives a <method> request for <path> without the CSRF token
|
31
|
+
When it receives a <method> request for <path> without the CSRF token or header
|
32
32
|
Then it responds with 403
|
33
33
|
And the response body is empty
|
34
34
|
|
@@ -63,7 +63,7 @@ Feature: Skipping the check for some specific routes
|
|
63
63
|
| PUT:/ |
|
64
64
|
| DELETE:/ |
|
65
65
|
| PATCH:/ |
|
66
|
-
When it receives a <method> request with neither PATH_INFO nor CSRF token
|
66
|
+
When it receives a <method> request with neither PATH_INFO nor CSRF token or header
|
67
67
|
Then it lets it pass untouched
|
68
68
|
|
69
69
|
Examples:
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Yes, they're not as DRY as possible, but I think they're more readable than
|
2
2
|
# a single step definition with a few captures and more complex checkings.
|
3
3
|
|
4
|
-
When /^it receives a (.*) request without the CSRF token$/ do |http_method|
|
4
|
+
When /^it receives a (.*) request without the CSRF (?:token|header)$/ do |http_method|
|
5
5
|
begin
|
6
6
|
@browser.request '/', :method => http_method
|
7
7
|
rescue Exception => e
|
@@ -9,7 +9,7 @@ When /^it receives a (.*) request without the CSRF token$/ do |http_method|
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
When /^it receives a (.*) request for (.+) without the CSRF token$/ do |http_method, path|
|
12
|
+
When /^it receives a (.*) request for (.+) without the CSRF (?:token|header|token or header)$/ do |http_method, path|
|
13
13
|
begin
|
14
14
|
@browser.request path, :method => http_method
|
15
15
|
rescue Exception => e
|
@@ -18,24 +18,56 @@ When /^it receives a (.*) request for (.+) without the CSRF token$/ do |http_met
|
|
18
18
|
end
|
19
19
|
|
20
20
|
When /^it receives a (.*) request with the right CSRF token$/ do |http_method|
|
21
|
-
@browser.request '/', :method
|
22
|
-
|
23
|
-
|
21
|
+
@browser.request '/', :method => http_method,
|
22
|
+
'rack.session' => {Rack::Csrf.key => 'right_token'},
|
23
|
+
:params => {Rack::Csrf.field => 'right_token'}
|
24
|
+
end
|
25
|
+
|
26
|
+
When /^it receives a (.*) request with the right CSRF header$/ do |http_method|
|
27
|
+
@browser.request '/', :method => http_method,
|
28
|
+
'rack.session' => {Rack::Csrf.key => 'right_token'},
|
29
|
+
Rack::Csrf.rackified_header => 'right_token'
|
24
30
|
end
|
25
31
|
|
26
32
|
When /^it receives a (.*) request with the wrong CSRF token$/ do |http_method|
|
33
|
+
begin
|
34
|
+
@browser.request '/', :method => http_method,
|
35
|
+
'rack.session' => {Rack::Csrf.key => 'right_token'},
|
36
|
+
:params => {Rack::Csrf.field => 'wrong_token'}
|
37
|
+
rescue Exception => e
|
38
|
+
@exception = e
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
When /^it receives a (.*) request with the wrong CSRF header/ do |http_method|
|
27
43
|
begin
|
28
44
|
@browser.request '/', :method => http_method,
|
29
|
-
|
45
|
+
Rack::Csrf.rackified_header => 'right_token'
|
30
46
|
rescue Exception => e
|
31
47
|
@exception = e
|
32
48
|
end
|
33
49
|
end
|
34
50
|
|
35
|
-
When /^it receives a (.*) request with neither PATH_INFO nor CSRF token$/ do |http_method|
|
51
|
+
When /^it receives a (.*) request with neither PATH_INFO nor CSRF token or header$/ do |http_method|
|
36
52
|
begin
|
37
53
|
@browser.request '/doesntmatter', :method => http_method, 'PATH_INFO' => ''
|
38
54
|
rescue Exception => e
|
39
55
|
@exception = e
|
40
56
|
end
|
41
57
|
end
|
58
|
+
|
59
|
+
When /^it receives a request with headers (.+) = ([^ ]+) without the CSRF token or header$/ do |name, value|
|
60
|
+
begin
|
61
|
+
@browser.request '/', Hash[:method, 'POST', name, value]
|
62
|
+
rescue Exception => e
|
63
|
+
@exception = e
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
When /^it receives a request with headers (.+) = ([^,]+), (.+), and without the CSRF token or header$/ do |name, value, method|
|
68
|
+
begin
|
69
|
+
@browser.request '/', Hash[:method, method, name, value]
|
70
|
+
rescue Exception => e
|
71
|
+
@exception = e
|
72
|
+
end
|
73
|
+
end
|
@@ -8,38 +8,53 @@ end
|
|
8
8
|
# a single step definition with a few captures and more complex checkings.
|
9
9
|
|
10
10
|
Given /^a rack with the anti\-CSRF middleware$/ do
|
11
|
-
|
12
|
-
|
11
|
+
step 'a rack with the session middleware'
|
12
|
+
step 'I insert the anti-CSRF middleware'
|
13
13
|
end
|
14
14
|
|
15
15
|
Given /^a rack with the anti\-CSRF middleware and the :raise option$/ do
|
16
|
-
|
17
|
-
|
16
|
+
step 'a rack with the session middleware'
|
17
|
+
step 'I insert the anti-CSRF middleware with the :raise option'
|
18
18
|
end
|
19
19
|
|
20
20
|
Given /^a rack with the anti\-CSRF middleware and the :skip option$/ do |table|
|
21
|
-
|
22
|
-
|
21
|
+
step 'a rack with the session middleware'
|
22
|
+
step 'I insert the anti-CSRF middleware with the :skip option', table
|
23
|
+
end
|
24
|
+
|
25
|
+
Given /^a rack with the anti\-CSRF middleware and the :skip_if option$/ do |table|
|
26
|
+
step 'a rack with the session middleware'
|
27
|
+
step 'I insert the anti-CSRF middleware with the :skip_if option', table
|
28
|
+
end
|
29
|
+
|
30
|
+
Given /^a rack with the anti\-CSRF middleware and both the :skip and :skip_if options$/ do |table|
|
31
|
+
step 'a rack with the session middleware'
|
32
|
+
step 'I insert the anti-CSRF middleware with the :skip and :skip_if options', table
|
23
33
|
end
|
24
34
|
|
25
35
|
Given /^a rack with the anti\-CSRF middleware and the :field option$/ do
|
26
|
-
|
27
|
-
|
36
|
+
step 'a rack with the session middleware'
|
37
|
+
step 'I insert the anti-CSRF middleware with the :field option'
|
28
38
|
end
|
29
39
|
|
30
40
|
Given /^a rack with the anti\-CSRF middleware and the :key option$/ do
|
31
|
-
|
32
|
-
|
41
|
+
step 'a rack with the session middleware'
|
42
|
+
step 'I insert the anti-CSRF middleware with the :key option'
|
43
|
+
end
|
44
|
+
|
45
|
+
Given /^a rack with the anti\-CSRF middleware and the :header option$/ do
|
46
|
+
step 'a rack with the session middleware'
|
47
|
+
step 'I insert the anti-CSRF middleware with the :header option'
|
33
48
|
end
|
34
49
|
|
35
50
|
Given /^a rack with the anti\-CSRF middleware and the :check_also option$/ do |table|
|
36
|
-
|
37
|
-
|
51
|
+
step 'a rack with the session middleware'
|
52
|
+
step 'I insert the anti-CSRF middleware with the :check_also option', table
|
38
53
|
end
|
39
54
|
|
40
55
|
Given /^a rack with the anti\-CSRF middleware and the :check_only option$/ do |table|
|
41
|
-
|
42
|
-
|
56
|
+
step 'a rack with the session middleware'
|
57
|
+
step 'I insert the anti-CSRF middleware with the :check_only option', table
|
43
58
|
end
|
44
59
|
|
45
60
|
# Yes, they're not as DRY as possible, but I think they're more readable than
|
@@ -64,6 +79,27 @@ When /^I insert the anti\-CSRF middleware with the :skip option$/ do |table|
|
|
64
79
|
@browser = Rack::Test::Session.new(Rack::MockSession.new(@app))
|
65
80
|
end
|
66
81
|
|
82
|
+
When /^I insert the anti\-CSRF middleware with the :skip_if option$/ do |table|
|
83
|
+
skippable = table.hashes.collect {|t| t.values}
|
84
|
+
@rack_builder.use Rack:: Csrf, :skip_if => Proc.new { |request|
|
85
|
+
skippable.any? { |name, value| request.env[name] == value }
|
86
|
+
}
|
87
|
+
@app = toy_app
|
88
|
+
@browser = Rack::Test::Session.new(Rack::MockSession.new(@app))
|
89
|
+
end
|
90
|
+
|
91
|
+
When /^I insert the anti\-CSRF middleware with the :skip and :skip_if options$/ do |table|
|
92
|
+
data = table.hashes.collect {|t| t.values}[0]
|
93
|
+
headers = data[0..1]
|
94
|
+
skippable = data[2]
|
95
|
+
|
96
|
+
@rack_builder.use Rack:: Csrf, :skip => [skippable], :skip_if => Proc.new { |request|
|
97
|
+
skippable.any? { |name, value| request.env[name] == value }
|
98
|
+
}
|
99
|
+
@app = toy_app
|
100
|
+
@browser = Rack::Test::Session.new(Rack::MockSession.new(@app))
|
101
|
+
end
|
102
|
+
|
67
103
|
When /^I insert the anti\-CSRF middleware with the :field option$/ do
|
68
104
|
@rack_builder.use Rack::Csrf, :field => 'fantasy_name'
|
69
105
|
@app = toy_app
|
@@ -76,6 +112,12 @@ When /^I insert the anti\-CSRF middleware with the :key option$/ do
|
|
76
112
|
@browser = Rack::Test::Session.new(Rack::MockSession.new(@app))
|
77
113
|
end
|
78
114
|
|
115
|
+
When /^I insert the anti\-CSRF middleware with the :header option$/ do
|
116
|
+
@rack_builder.use Rack::Csrf, :header => 'fantasy_name'
|
117
|
+
@app = toy_app
|
118
|
+
@browser = Rack::Test::Session.new(Rack::MockSession.new(@app))
|
119
|
+
end
|
120
|
+
|
79
121
|
When /^I insert the anti\-CSRF middleware with the :check_also option$/ do |table|
|
80
122
|
check_also = table.hashes.collect {|t| t.values}.flatten
|
81
123
|
@rack_builder.use Rack::Csrf, :check_also => check_also
|
@@ -0,0 +1,35 @@
|
|
1
|
+
Feature: Customization of the header name
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given a rack with the anti-CSRF middleware and the :header option
|
5
|
+
|
6
|
+
Scenario: GET request with the right CSRF header in custom field
|
7
|
+
When it receives a GET request with the right CSRF header
|
8
|
+
Then it lets it pass untouched
|
9
|
+
|
10
|
+
Scenario: GET request with the wrong CSRF header in custom field
|
11
|
+
When it receives a GET request with the wrong CSRF header
|
12
|
+
Then it lets it pass untouched
|
13
|
+
|
14
|
+
Scenario Outline: Handling request with the right CSRF header in custom field
|
15
|
+
When it receives a <method> request with the right CSRF header
|
16
|
+
Then it lets it pass untouched
|
17
|
+
|
18
|
+
Examples:
|
19
|
+
| method |
|
20
|
+
| POST |
|
21
|
+
| PUT |
|
22
|
+
| DELETE |
|
23
|
+
| PATCH |
|
24
|
+
|
25
|
+
Scenario Outline: Handling request with the wrong CSRF header in custom field
|
26
|
+
When it receives a <method> request with the wrong CSRF header
|
27
|
+
Then it responds with 403
|
28
|
+
And the response body is empty
|
29
|
+
|
30
|
+
Examples:
|
31
|
+
| method |
|
32
|
+
| POST |
|
33
|
+
| PUT |
|
34
|
+
| DELETE |
|
35
|
+
| PATCH |
|
data/lib/rack/csrf.rb
CHANGED
@@ -10,20 +10,23 @@ module Rack
|
|
10
10
|
class SessionUnavailable < StandardError; end
|
11
11
|
class InvalidCsrfToken < StandardError; end
|
12
12
|
|
13
|
-
@@field
|
14
|
-
@@
|
13
|
+
@@field = '_csrf'
|
14
|
+
@@header = 'X_CSRF_TOKEN'
|
15
|
+
@@key = 'csrf.token'
|
15
16
|
|
16
17
|
def initialize(app, opts = {})
|
17
18
|
@app = app
|
18
19
|
|
19
|
-
@raisable
|
20
|
-
@
|
21
|
-
@
|
22
|
-
|
23
|
-
@@
|
20
|
+
@raisable = opts[:raise] || false
|
21
|
+
@skip_list = (opts[:skip] || []).map {|r| /\A#{r}\Z/i}
|
22
|
+
@skip_if = opts[:skip_if] if opts[:skip_if]
|
23
|
+
@check_only_list = (opts[:check_only] || []).map {|r| /\A#{r}\Z/i}
|
24
|
+
@@field = opts[:field] if opts[:field]
|
25
|
+
@@header = opts[:header] if opts[:header]
|
26
|
+
@@key = opts[:key] if opts[:key]
|
24
27
|
|
25
28
|
standard_http_methods = %w(POST PUT DELETE PATCH)
|
26
|
-
check_also
|
29
|
+
check_also = opts[:check_also] || []
|
27
30
|
@http_methods = (standard_http_methods + check_also).flatten.uniq
|
28
31
|
end
|
29
32
|
|
@@ -35,7 +38,8 @@ module Rack
|
|
35
38
|
req = Rack::Request.new(env)
|
36
39
|
untouchable = skip_checking(req) ||
|
37
40
|
!@http_methods.include?(req.request_method) ||
|
38
|
-
req.params[self.class.field] == env['rack.session'][self.class.key]
|
41
|
+
req.params[self.class.field] == env['rack.session'][self.class.key] ||
|
42
|
+
req.env[self.class.rackified_header] == env['rack.session'][self.class.key]
|
39
43
|
if untouchable
|
40
44
|
@app.call(env)
|
41
45
|
else
|
@@ -52,6 +56,10 @@ module Rack
|
|
52
56
|
@@field
|
53
57
|
end
|
54
58
|
|
59
|
+
def self.header
|
60
|
+
@@header
|
61
|
+
end
|
62
|
+
|
55
63
|
def self.token(env)
|
56
64
|
env['rack.session'][key] ||= SecureRandom.base64(32)
|
57
65
|
end
|
@@ -60,21 +68,40 @@ module Rack
|
|
60
68
|
%Q(<input type="hidden" name="#{field}" value="#{token(env)}" />)
|
61
69
|
end
|
62
70
|
|
71
|
+
def self.metatag(env, options = {})
|
72
|
+
name = options.delete(:name) || '_csrf'
|
73
|
+
%Q(<meta name="#{name}" content="#{token(env)}" />)
|
74
|
+
end
|
75
|
+
|
63
76
|
class << self
|
64
77
|
alias_method :csrf_key, :key
|
65
78
|
alias_method :csrf_field, :field
|
79
|
+
alias_method :csrf_header, :header
|
66
80
|
alias_method :csrf_token, :token
|
67
81
|
alias_method :csrf_tag, :tag
|
82
|
+
alias_method :csrf_metatag, :metatag
|
68
83
|
end
|
69
84
|
|
70
85
|
protected
|
71
86
|
|
87
|
+
# Returns the custom header's name adapted to current standards.
|
88
|
+
def self.rackified_header
|
89
|
+
"HTTP_#{@@header.gsub('-','_').upcase}"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns +true+ if the given request appears in the <b>skip list</b> or
|
93
|
+
# the <b>conditional skipping code</b> return true or, when the <b>check
|
94
|
+
# only list</b> is not empty (i.e., we are working in the "reverse mode"
|
95
|
+
# triggered by the +check_only+ option), it does not appear in the
|
96
|
+
# <b>check only list.</b>
|
72
97
|
def skip_checking request
|
73
|
-
|
74
|
-
|
75
|
-
|
98
|
+
to_be_skipped = any? @skip_list, request
|
99
|
+
to_be_skipped ||= @skip_if && @skip_if.call(request)
|
100
|
+
to_be_checked = any? @check_only_list, request
|
101
|
+
to_be_skipped || (!@check_only_list.empty? && !to_be_checked)
|
76
102
|
end
|
77
|
-
|
103
|
+
|
104
|
+
# Returns +true+ when the given list "includes" the request.
|
78
105
|
def any? list, request
|
79
106
|
pi = request.path_info.empty? ? '/' : request.path_info
|
80
107
|
list.any? do |route|
|
data/rack_csrf.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "rack_csrf"
|
8
|
-
s.version = "2.
|
8
|
+
s.version = "2.4.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Emanuele Vicentini"]
|
12
|
-
s.date = "
|
12
|
+
s.date = "2012-02-28"
|
13
13
|
s.description = "Anti-CSRF Rack middleware"
|
14
14
|
s.email = "emanuele.vicentini@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.files = [
|
20
20
|
".rspec",
|
21
21
|
"Changelog.md",
|
22
|
+
"Gemfile",
|
22
23
|
"LICENSE.rdoc",
|
23
24
|
"README.rdoc",
|
24
25
|
"Rakefile",
|
@@ -63,6 +64,7 @@ Gem::Specification.new do |s|
|
|
63
64
|
"features/inspecting_also_get_requests.feature",
|
64
65
|
"features/raising_exception.feature",
|
65
66
|
"features/setup.feature",
|
67
|
+
"features/skip_if_block_passes.feature",
|
66
68
|
"features/skip_some_routes.feature",
|
67
69
|
"features/step_definitions/request_steps.rb",
|
68
70
|
"features/step_definitions/response_steps.rb",
|
@@ -70,6 +72,7 @@ Gem::Specification.new do |s|
|
|
70
72
|
"features/support/env.rb",
|
71
73
|
"features/support/fake_session.rb",
|
72
74
|
"features/variation_on_field_name.feature",
|
75
|
+
"features/variation_on_header_name.feature",
|
73
76
|
"features/variation_on_key_name.feature",
|
74
77
|
"lib/rack/csrf.rb",
|
75
78
|
"lib/rack/vendor/securerandom.rb",
|
@@ -79,10 +82,10 @@ Gem::Specification.new do |s|
|
|
79
82
|
]
|
80
83
|
s.homepage = "https://github.com/baldowl/rack_csrf"
|
81
84
|
s.licenses = ["MIT"]
|
82
|
-
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rack::Csrf 2.
|
85
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rack::Csrf 2.4.0", "--main", "README.rdoc"]
|
83
86
|
s.require_paths = ["lib"]
|
84
87
|
s.rubyforge_project = "rackcsrf"
|
85
|
-
s.rubygems_version = "1.8.
|
88
|
+
s.rubygems_version = "1.8.17"
|
86
89
|
s.summary = "Anti-CSRF Rack middleware"
|
87
90
|
|
88
91
|
if s.respond_to? :specification_version then
|
@@ -90,23 +93,29 @@ Gem::Specification.new do |s|
|
|
90
93
|
|
91
94
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
92
95
|
s.add_runtime_dependency(%q<rack>, [">= 0.9"])
|
93
|
-
s.add_development_dependency(%q<
|
96
|
+
s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
|
97
|
+
s.add_development_dependency(%q<cucumber>, [">= 1.1.1"])
|
94
98
|
s.add_development_dependency(%q<rack-test>, [">= 0"])
|
95
99
|
s.add_development_dependency(%q<rspec>, [">= 2.0.0"])
|
96
100
|
s.add_development_dependency(%q<rdoc>, [">= 2.4.2"])
|
101
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
97
102
|
else
|
98
103
|
s.add_dependency(%q<rack>, [">= 0.9"])
|
99
|
-
s.add_dependency(%q<
|
104
|
+
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
105
|
+
s.add_dependency(%q<cucumber>, [">= 1.1.1"])
|
100
106
|
s.add_dependency(%q<rack-test>, [">= 0"])
|
101
107
|
s.add_dependency(%q<rspec>, [">= 2.0.0"])
|
102
108
|
s.add_dependency(%q<rdoc>, [">= 2.4.2"])
|
109
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
103
110
|
end
|
104
111
|
else
|
105
112
|
s.add_dependency(%q<rack>, [">= 0.9"])
|
106
|
-
s.add_dependency(%q<
|
113
|
+
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
114
|
+
s.add_dependency(%q<cucumber>, [">= 1.1.1"])
|
107
115
|
s.add_dependency(%q<rack-test>, [">= 0"])
|
108
116
|
s.add_dependency(%q<rspec>, [">= 2.0.0"])
|
109
117
|
s.add_dependency(%q<rdoc>, [">= 2.4.2"])
|
118
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
110
119
|
end
|
111
120
|
end
|
112
121
|
|