rack_csrf 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|