safe_cookies 0.1.5 → 0.2.2
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 +5 -13
- data/.gitignore +1 -0
- data/README.md +94 -56
- data/lib/safe_cookies.rb +45 -33
- data/lib/safe_cookies/configuration.rb +14 -2
- data/lib/safe_cookies/cookie_path_fix.rb +27 -22
- data/lib/safe_cookies/helpers.rb +18 -13
- data/lib/safe_cookies/util.rb +18 -13
- data/lib/safe_cookies/version.rb +1 -1
- data/safe_cookies.gemspec +1 -0
- data/spec/cookie_path_fix_spec.rb +18 -9
- data/spec/safe_cookies_spec.rb +123 -61
- data/spec/util_spec.rb +27 -0
- metadata +18 -16
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
OGNlMjkzMmI2MTFhZTFlNzA3MDZmMmYxOWQ3YmQ1ZGE4Njg0OWZlOA==
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7175011cd4a253c98779e5fdb5d221ea99dbbe5708e3a9dce37547ac89d29361
|
4
|
+
data.tar.gz: 08e2daf10fdd7ec2a115162969eaffd241edca9f82671a146bfafd381bd9f7a3
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
YmQ3YjJjZDBhYTU3ZjBhNzgzMGYwNzdmNzcyZTFmYWY1NzQ0N2RhZDUyYzA1
|
11
|
-
NjJlZDc4NjdiMWU2M2NhOWY5YzBmNWFkNWNmY2Y1OTgxNjI1YjQ=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
Yjg4YTA3OGYyY2RhYmJkZTAyYmNiNjE2ZGRjNTc4MTA0NDJjMTc1ZjA3Mjcy
|
14
|
-
MjFhYjlhOGY1ZjQyYTE3OTEzODE3ZmU3YmE5YzIxOTNhOWE4NTg5M2M3ZTk4
|
15
|
-
ZWMzMzVmODk4MWE3MDdjN2RkZjI1N2UwN2EyOTVlNmM0MmMwMjk=
|
6
|
+
metadata.gz: 8f9932ab978f4cdeabc001314496ffed4214f9a966b45847e246c2c78730b1388ee1169a4def1864edb78481fbb30cefbffe29fd366f1a3abe207d107175c681
|
7
|
+
data.tar.gz: d42dbf95c6d38e99c1e0f356f31925ef6da59ba465a768ad5d25f573a02bb75e671330dbe6dfc4a52bf039879eab7ee53fe371d50d7e540ddaa5f87cd93aa8f6
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,89 +1,127 @@
|
|
1
|
+
# This gem is no longer maintained!
|
2
|
+
|
3
|
+
Read about [reasons and alternatives](https://makandracards.com/makandra/53693-rails-making-all-cookies-secure-to-pass-a-security-audit).
|
4
|
+
|
5
|
+
|
6
|
+
--------------------
|
7
|
+
|
1
8
|
# SafeCookies
|
2
9
|
|
3
|
-
This
|
10
|
+
This gem has a middleware that will make all cookies secure, by setting the
|
11
|
+
`HttpOnly` and the `secure` flag for all cookies the application sets on the
|
12
|
+
client.
|
13
|
+
|
14
|
+
Making a cookie `HttpOnly` prevents Javascripts from seeing it, which really
|
15
|
+
should be the default. It makes it way harder to steal cookie information via
|
16
|
+
malicious Javascript.
|
17
|
+
|
18
|
+
Making a cookie `secure` tells the browser to only send the cookie over HTTPS
|
19
|
+
connections, protecting it from being sniffed by a man-in-the-middle. (Setting a
|
20
|
+
[HSTS](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security) header
|
21
|
+
achieves the same, but Safari < 7 and IE < 11 don't speak HSTS.)
|
22
|
+
|
23
|
+
SafeCookies will *additionally* rewrite all cookies the user is sending. **But**
|
24
|
+
it can only do so, if the cookie was registered before (see below). It will rewrite
|
25
|
+
user cookies only once per user.
|
4
26
|
|
5
|
-
* set all new application cookies 'HttpOnly', unless specified otherwise
|
6
|
-
* set all new application cookies 'secure', if the request came via HTTPS and not specified otherwise
|
7
|
-
* rewrite request cookies, setting both flags as above
|
8
27
|
|
9
28
|
## Installation
|
10
29
|
|
11
|
-
Add
|
30
|
+
1. Add `gem 'safe_cookies'` to your application's Gemfile, then run `bundle install`.
|
12
31
|
|
13
|
-
|
32
|
+
2. Add a configuration block in an initializer (e.g. `config/initializers/safe_cookies.rb`):
|
14
33
|
|
15
|
-
|
34
|
+
SafeCookies.configure do |config|
|
35
|
+
# configuration ...
|
36
|
+
end
|
16
37
|
|
17
|
-
|
38
|
+
3. Register the middleware:
|
18
39
|
|
19
|
-
|
40
|
+
Rails 3+: add the following lines to the application block in `config/application.rb`:
|
20
41
|
|
21
|
-
|
42
|
+
require 'safe_cookies'
|
43
|
+
config.middleware.insert_before ActionDispatch::Cookies, SafeCookies::Middleware
|
22
44
|
|
45
|
+
Rails 2: add the following lines to the initializer block in `config/environment.rb`:
|
23
46
|
|
24
|
-
|
47
|
+
require 'safe_cookies'
|
48
|
+
config.middleware.insert_before ActionController::Session::CookieStore, SafeCookies::Middleware
|
25
49
|
|
26
|
-
|
27
|
-
|
50
|
+
Now all new cookies will be made `secure` and `HttpOnly`. But what about cookies
|
51
|
+
already out there?
|
28
52
|
|
29
|
-
class Application < Rails::Application
|
30
|
-
# ...
|
31
|
-
config.middleware.insert_before ActionDispatch::Cookies, SafeCookies::Middleware
|
32
|
-
end
|
33
53
|
|
34
|
-
|
54
|
+
## Updating existing cookies
|
55
|
+
|
56
|
+
Unfortunately, [the client won't ever tell us](http://tools.ietf.org/html/rfc6265#section-4.2.2)
|
57
|
+
if it stores a cookie with flags such as `secure` or which expiry date it is
|
58
|
+
stored with. Therefore, in order to make the middleware retroactively secure
|
59
|
+
cookies owned by the client, you need to register each of those cookies with
|
60
|
+
the middleware, specifying their properties.
|
35
61
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
62
|
+
Carefully scan your app for cookies you are using. There's no easy way to find
|
63
|
+
out if you missed one (but see below for some help the gem provides).
|
64
|
+
|
65
|
+
SafeCookies.configure do |config|
|
66
|
+
config.register_cookie 'remember_token', :expire_after => 1.year
|
67
|
+
config.register_cookie 'last_action', :expire_after => 30.days, :path => '/commerce'
|
40
68
|
end
|
41
69
|
|
42
|
-
|
43
|
-
|
44
|
-
|
70
|
+
Available options are: `:expire_after` (required)`, :path, :secure, :http_only`.
|
71
|
+
For cookies with "session" expiry, set `:expire_after => nil`.
|
72
|
+
|
73
|
+
|
74
|
+
## Having a cookie non-secure or non-HttpOnly
|
75
|
+
|
76
|
+
Tell SafeCookies which cookies not to make `secure` or `HttpOnly` by registering
|
77
|
+
them, just like above:
|
45
78
|
|
46
79
|
SafeCookies.configure do |config|
|
47
|
-
config.register_cookie
|
48
|
-
config.register_cookie
|
49
|
-
config.register_cookie :default_language, :expire_after => 10.years, :secure => false
|
50
|
-
config.register_cookie :javascript_data, :expire_after => 1.day, :http_only => false
|
80
|
+
config.register_cookie 'default_language', :expire_after => 10.years, :secure => false
|
81
|
+
config.register_cookie 'javascript_data', :expire_after => 1.day, :http_only => false
|
51
82
|
end
|
52
83
|
|
53
|
-
This will have the `default_language` cookie not made secure, the `javascript_data` cookie
|
54
|
-
not made http-only. It will rewrite the `remember_token` with an expiry of one year and the
|
55
|
-
`last_action` cookie with an expiry of 30 days, making both of them secure and http-only.
|
56
|
-
Available options are: `:expire_after (required), :path, :secure, :http_only`.
|
57
84
|
|
58
|
-
|
59
|
-
|
85
|
+
## Finding unregistered user cookies
|
86
|
+
|
87
|
+
There are lots of cookies your application receives that you never did set.
|
88
|
+
However, if you want to know about any unknown cookies touching your
|
89
|
+
application, SafeCookies gives you two tools.
|
90
|
+
|
91
|
+
1) If you set `config.log_unknown_cookies = true` in the configuration block, all
|
92
|
+
unknown cookies will be written to the Rails log. When you start implementing
|
93
|
+
the middleware, closely watch it to find cookies you forgot to register.
|
94
|
+
|
95
|
+
2) You may overwrite `SafeCookies::Middleware#handle_unknown_cookies(cookies)`
|
96
|
+
in the configuration block for customized behaviour (like, notifying you per
|
97
|
+
email).
|
98
|
+
|
99
|
+
To ignore cookies that are irrelevant to you, you may configure them to be
|
100
|
+
ignored. Use the `config.ignore_cookie` directive, which takes either a String
|
101
|
+
or a Regex parameter. *Be careful when using regular expressions!*
|
60
102
|
|
61
103
|
|
62
|
-
##
|
104
|
+
## Fixing cookie paths
|
63
105
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
Unregistered cookies cannot be secured.
|
106
|
+
In August 2013 we noticed a bug in SafeCookies < 0.1.4, by which secured cookies
|
107
|
+
would be set for the current "directory" (see comments in `cookie_path_fix.rb`)
|
108
|
+
instead of root (which usually is what you want). Users would get multiple
|
109
|
+
cookies for that domain, leading to issues like being unable to sign in.
|
69
110
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
111
|
+
The configuration option `config.fix_paths` turns on fixing this error. It
|
112
|
+
expects an option `:for_cookies_secured_before => Time.parse('some minutes after
|
113
|
+
you will have deployed')` which reflects the point of time from which SafeCookies
|
114
|
+
can expect cookies to be set with the correct path. It will only rewrite cookies
|
115
|
+
with a new path if it had set them before that point of time.
|
75
116
|
|
76
|
-
You should not ignore an unregistered cookie, but instead register it.
|
77
117
|
|
118
|
+
## Development
|
78
119
|
|
79
|
-
|
120
|
+
- Tests live in `spec`.
|
121
|
+
- You can run specs from the project root by saying `bundle exec rake`.
|
80
122
|
|
81
|
-
|
82
|
-
current "directory" (see comments in `cookie_path_fix.rb`) instead of root (which usually is what you want).
|
83
|
-
Users would get multiple cookies for that domain, leading to issues like being unable to sign in.
|
123
|
+
If you would like to contribute:
|
84
124
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
paths by rewriting all cookies that it has already secured, but only if the were secured before the time
|
89
|
-
you specified.
|
125
|
+
- Fork the repository.
|
126
|
+
- Push your changes **with passing specs**.
|
127
|
+
- Send us a pull request.
|
data/lib/safe_cookies.rb
CHANGED
@@ -5,12 +5,11 @@ require "safe_cookies/helpers"
|
|
5
5
|
require "safe_cookies/util"
|
6
6
|
require "safe_cookies/version"
|
7
7
|
require "rack"
|
8
|
+
require "time" # add Time#rfc2822
|
8
9
|
|
9
10
|
# Naming:
|
10
11
|
# - application_cookies: cookies received from the application. The 'Set-Cookie' header is a string
|
11
12
|
# - request_cookies: cookies received from the client. Rack::Request#cookies returns a Hash of { 'name' => 'value' }
|
12
|
-
# - response_cookies: cookies to be sent to the client
|
13
|
-
# (= application_cookies + any cookies set in the middleware)
|
14
13
|
|
15
14
|
module SafeCookies
|
16
15
|
|
@@ -19,6 +18,7 @@ module SafeCookies
|
|
19
18
|
STORE_COOKIE_NAME = '_safe_cookies__known_cookies'
|
20
19
|
SECURED_COOKIE_NAME = 'secured_old_cookies'
|
21
20
|
HELPER_COOKIES_LIFETIME = 10 * 365 * 24 * 60 * 60 # 10 years
|
21
|
+
|
22
22
|
|
23
23
|
class Middleware
|
24
24
|
|
@@ -30,22 +30,23 @@ module SafeCookies
|
|
30
30
|
|
31
31
|
def initialize(app)
|
32
32
|
@app = app
|
33
|
-
@
|
33
|
+
@config = SafeCookies.configuration or raise "Don't know what to do without configuration"
|
34
34
|
end
|
35
35
|
|
36
36
|
def call(env)
|
37
37
|
reset_instance_variables
|
38
38
|
|
39
39
|
@request = Rack::Request.new(env)
|
40
|
-
|
40
|
+
check_if_request_has_unknown_cookies
|
41
41
|
|
42
|
+
# call the next middleware up the stack
|
42
43
|
status, @headers, body = @app.call(env)
|
43
44
|
cache_application_cookies_string
|
44
45
|
|
45
|
-
|
46
|
-
rewrite_application_cookies
|
46
|
+
enhance_application_cookies!
|
47
47
|
store_application_cookie_names
|
48
|
-
|
48
|
+
|
49
|
+
delete_cookies_on_bad_path if fix_cookie_paths?
|
49
50
|
rewrite_request_cookies unless cookies_have_been_rewritten_before?
|
50
51
|
|
51
52
|
[ status, @headers, body ]
|
@@ -53,31 +54,27 @@ module SafeCookies
|
|
53
54
|
|
54
55
|
private
|
55
56
|
|
57
|
+
# Instance variables survive requests because the middleware is a singleton.
|
56
58
|
def reset_instance_variables
|
57
|
-
@request, @headers, @
|
59
|
+
@request, @headers, @application_cookies_string = nil
|
58
60
|
end
|
59
61
|
|
60
|
-
def
|
62
|
+
def check_if_request_has_unknown_cookies
|
61
63
|
request_cookie_names = request_cookies.keys.map(&:to_s)
|
62
64
|
unknown_cookie_names = request_cookie_names - known_cookie_names
|
63
65
|
|
64
66
|
if unknown_cookie_names.any?
|
65
|
-
|
66
|
-
|
67
|
-
end
|
67
|
+
message = "Request for '#{@request.url}' had unknown cookies: #{unknown_cookie_names.join(', ')}"
|
68
|
+
log(message) if @config.log_unknown_cookies
|
68
69
|
|
69
|
-
|
70
|
-
if @application_cookies
|
71
|
-
application_cookie_names = @application_cookies.scan(COOKIE_NAME_REGEX)
|
72
|
-
application_cookie_names.each do |cookie|
|
73
|
-
request_cookies.delete(cookie)
|
74
|
-
end
|
70
|
+
handle_unknown_cookies(unknown_cookie_names)
|
75
71
|
end
|
76
72
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
73
|
+
|
74
|
+
# Overwrites @header['Set-Cookie']!
|
75
|
+
def enhance_application_cookies!
|
76
|
+
if @application_cookies_string
|
77
|
+
cookies = @application_cookies_string.split("\n")
|
81
78
|
|
82
79
|
# On Rack 1.1, cookie values sometimes contain trailing newlines.
|
83
80
|
# Example => ["foo=1; path=/\n", "bar=2; path=/"]
|
@@ -96,36 +93,51 @@ module SafeCookies
|
|
96
93
|
@headers['Set-Cookie'] = cookies.join("\n")
|
97
94
|
end
|
98
95
|
end
|
99
|
-
|
96
|
+
|
97
|
+
# Store the names of cookies that are set by the application.
|
98
|
+
# (We are already securing those and will not need to rewrite them.)
|
100
99
|
def store_application_cookie_names
|
101
|
-
if @
|
102
|
-
application_cookie_names = stored_application_cookie_names + @
|
100
|
+
if @application_cookies_string
|
101
|
+
application_cookie_names = stored_application_cookie_names + @application_cookies_string.scan(COOKIE_NAME_REGEX)
|
103
102
|
application_cookies_string = application_cookie_names.uniq.join(KNOWN_COOKIES_DIVIDER)
|
104
103
|
|
105
104
|
set_cookie!(STORE_COOKIE_NAME, application_cookies_string, :expire_after => HELPER_COOKIES_LIFETIME)
|
106
105
|
end
|
107
106
|
end
|
108
107
|
|
109
|
-
# This method takes
|
108
|
+
# This method takes the cookies sent with the request and rewrites them,
|
110
109
|
# making them both secure and http-only (unless specified otherwise in
|
111
110
|
# the configuration).
|
112
111
|
# With the SECURED_COOKIE_NAME cookie we remember the exact time that we
|
113
112
|
# rewrote the cookies.
|
114
113
|
def rewrite_request_cookies
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
114
|
+
rewritable_cookies = rewritable_request_cookies
|
115
|
+
|
116
|
+
# don't rewrite request cookies that the application is setting in the response
|
117
|
+
if @application_cookies_string
|
118
|
+
app_cookie_names = @application_cookies_string.scan(COOKIE_NAME_REGEX)
|
119
|
+
Util.except!(rewritable_cookies, *app_cookie_names)
|
120
|
+
end
|
121
|
+
|
122
|
+
if rewritable_cookies.any?
|
123
|
+
rewritable_cookies.each do |cookie_name, value|
|
124
|
+
options = @config.registered_cookies[cookie_name]
|
119
125
|
set_cookie!(cookie_name, value, options)
|
120
126
|
end
|
121
127
|
|
122
|
-
|
123
|
-
set_cookie!(SECURED_COOKIE_NAME, formatted_now, :expire_after => HELPER_COOKIES_LIFETIME)
|
128
|
+
set_cookie!(SECURED_COOKIE_NAME, Time.now.gmtime.rfc2822, :expire_after => HELPER_COOKIES_LIFETIME)
|
124
129
|
end
|
125
130
|
end
|
126
131
|
|
132
|
+
# API method
|
127
133
|
def handle_unknown_cookies(cookie_names)
|
128
|
-
|
134
|
+
end
|
135
|
+
|
136
|
+
def log(error_message)
|
137
|
+
message = '** [SafeCookies] '
|
138
|
+
message << error_message
|
139
|
+
|
140
|
+
Rails.logger.error(message) if defined?(Rails)
|
129
141
|
end
|
130
142
|
|
131
143
|
end
|
@@ -13,12 +13,15 @@ module SafeCookies
|
|
13
13
|
end
|
14
14
|
|
15
15
|
class Configuration
|
16
|
-
|
16
|
+
attr_accessor :log_unknown_cookies
|
17
|
+
attr_reader :registered_cookies, :fix_cookie_paths, :correct_cookie_paths_timestamp,
|
18
|
+
:ignored_cookies
|
17
19
|
|
18
20
|
def initialize
|
19
21
|
self.registered_cookies = {}
|
20
22
|
self.insecure_cookies = []
|
21
23
|
self.scriptable_cookies = []
|
24
|
+
self.ignored_cookies = []
|
22
25
|
end
|
23
26
|
|
24
27
|
# Register cookies you expect to receive. The middleware will rewrite all
|
@@ -45,6 +48,14 @@ module SafeCookies
|
|
45
48
|
scriptable_cookies << name if options[:http_only] == false
|
46
49
|
end
|
47
50
|
|
51
|
+
# Ignore cookies that you don't control like this:
|
52
|
+
#
|
53
|
+
# ignore_cookie 'ignored_cookie'
|
54
|
+
# ignore_cookie /^__utm/
|
55
|
+
def ignore_cookie(name_or_regex)
|
56
|
+
self.ignored_cookies << name_or_regex
|
57
|
+
end
|
58
|
+
|
48
59
|
def fix_paths(options = {})
|
49
60
|
options.has_key?(:for_cookies_secured_before) or raise MissingOptionError.new("Was told to fix paths without the :for_cookies_secured_before timestamp.")
|
50
61
|
|
@@ -63,7 +74,8 @@ module SafeCookies
|
|
63
74
|
private
|
64
75
|
|
65
76
|
attr_accessor :insecure_cookies, :scriptable_cookies
|
66
|
-
attr_writer :registered_cookies, :fix_cookie_paths, :correct_cookie_paths_timestamp
|
77
|
+
attr_writer :registered_cookies, :fix_cookie_paths, :correct_cookie_paths_timestamp,
|
78
|
+
:ignored_cookies
|
67
79
|
|
68
80
|
end
|
69
81
|
|
@@ -2,39 +2,37 @@ module SafeCookies
|
|
2
2
|
module CookiePathFix
|
3
3
|
|
4
4
|
# Previously, the SafeCookies gem would not set a path when rewriting
|
5
|
-
# cookies. Browsers then would assume and store the current "directory"
|
6
|
-
# leading to multiple cookies per domain.
|
5
|
+
# cookies. Browsers then would assume and store the current "directory"
|
6
|
+
# (see below), leading to multiple cookies per domain.
|
7
|
+
#
|
8
|
+
# If the cookies were secured before the configured datetime, this method
|
9
|
+
# instructs the client to delete all cookies it sent with the request and
|
10
|
+
# that we are able to rewrite, plus the SECURED_COOKIE_NAME helper cookie.
|
7
11
|
#
|
8
|
-
# If cookies had been secured before the configured datetime, the method
|
9
|
-
# `fix_cookie_paths` deletes all cookies coming with the request, and the
|
10
|
-
# SECURED_COOKIE_NAME helper cookie.
|
11
12
|
# The middleware still sees the request cookies and will rewrite them as
|
12
|
-
# if it hadn't seen them before
|
13
|
-
|
14
|
-
def
|
15
|
-
|
16
|
-
delete_cookie_for_current_directory(registered_cookie)
|
17
|
-
end
|
13
|
+
# if it hadn't seen them before, setting them on the correct path (root,
|
14
|
+
# by default).
|
15
|
+
def delete_cookies_on_bad_path
|
16
|
+
rewritable_request_cookies.keys.each &method(:delete_cookie_for_current_directory)
|
18
17
|
delete_cookie_for_current_directory(SafeCookies::SECURED_COOKIE_NAME)
|
19
18
|
|
20
|
-
# Delete this cookie here, so the middleware
|
21
|
-
|
19
|
+
# Delete this cookie here, so the middleware believes it hasn't secured
|
20
|
+
# the cookies yet.
|
21
|
+
@request.cookies.delete(SafeCookies::SECURED_COOKIE_NAME)
|
22
22
|
end
|
23
23
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def fix_cookie_paths?
|
27
|
-
@
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
cookies_have_been_rewritten_before? and cookies_need_path_fix
|
27
|
+
@config.fix_cookie_paths &&
|
28
|
+
cookies_have_been_rewritten_before? &&
|
29
|
+
(secured_old_cookies_timestamp < @config.correct_cookie_paths_timestamp)
|
32
30
|
end
|
33
31
|
|
34
32
|
# Delete cookies by giving them an expiry in the past,
|
35
33
|
# cf. https://tools.ietf.org/html/rfc6265#section-4.1.2.
|
36
34
|
#
|
37
|
-
# Most
|
35
|
+
# Most importantly, as specified in
|
38
36
|
# https://tools.ietf.org/html/rfc6265#section-4.1.2.4 and in section 5.1.4,
|
39
37
|
# cookies set without a path will be set for the current "directory", that is:
|
40
38
|
#
|
@@ -51,16 +49,23 @@ module SafeCookies
|
|
51
49
|
end
|
52
50
|
|
53
51
|
def current_directory_is_root?
|
54
|
-
|
52
|
+
# in words: "there are not three slashes before any query params"
|
53
|
+
!@request.path[%r(^/[^/]+/[^\?]+), 0]
|
55
54
|
end
|
56
55
|
|
57
56
|
def secured_old_cookies_timestamp
|
58
|
-
|
57
|
+
@request.cookies.has_key?(SafeCookies::SECURED_COOKIE_NAME) or return nil
|
58
|
+
|
59
|
+
Time.rfc2822(@request.cookies[SafeCookies::SECURED_COOKIE_NAME])
|
59
60
|
rescue ArgumentError
|
60
61
|
# If we cannot parse the secured_old_cookies time,
|
61
62
|
# assume it was before we noticed the bug to ensure
|
62
63
|
# broken cookie paths will be fixed.
|
63
|
-
|
64
|
+
#
|
65
|
+
# One reason to get here is that Rack::Utils.rfc2822 produces an invalid
|
66
|
+
# datetime string in Rack v1.1, writing the date with dashes
|
67
|
+
# (e.g. '04-Nov-2013').
|
68
|
+
Time.parse "2013-08-25 00:00"
|
64
69
|
end
|
65
70
|
|
66
71
|
end
|
data/lib/safe_cookies/helpers.rb
CHANGED
@@ -3,14 +3,18 @@ module SafeCookies
|
|
3
3
|
|
4
4
|
KNOWN_COOKIES_DIVIDER = '|'
|
5
5
|
|
6
|
+
# Since we have to operate on and modify the actual @headers hash that the
|
7
|
+
# application returns, cache the @headers['Set-Cookie'] string so that
|
8
|
+
# later on, we still know what the application did set.
|
6
9
|
def cache_application_cookies_string
|
7
10
|
cookies = @headers['Set-Cookie']
|
8
11
|
# Rack 1.1 returns an Array
|
9
12
|
cookies = cookies.join("\n") if cookies.is_a?(Array)
|
10
13
|
|
11
14
|
if cookies and cookies.length > 0
|
12
|
-
@
|
15
|
+
@application_cookies_string = cookies
|
13
16
|
end
|
17
|
+
# else, @application_cookies_string will be `nil`
|
14
18
|
end
|
15
19
|
|
16
20
|
def secure(cookie)
|
@@ -40,42 +44,43 @@ module SafeCookies
|
|
40
44
|
options[:secure] = should_be_secure?(name)
|
41
45
|
options[:httponly] = should_be_http_only?(name)
|
42
46
|
|
47
|
+
# Rack magic
|
43
48
|
Rack::Utils.set_cookie_header!(@headers, name, options)
|
44
49
|
end
|
45
50
|
|
46
51
|
|
47
52
|
# getters
|
53
|
+
|
54
|
+
# returns the request cookies minus ignored cookies
|
55
|
+
def request_cookies
|
56
|
+
Util.except!(@request.cookies.dup, *@config.ignored_cookies)
|
57
|
+
end
|
48
58
|
|
49
59
|
def stored_application_cookie_names
|
50
|
-
store_cookie =
|
60
|
+
store_cookie = @request.cookies[STORE_COOKIE_NAME] || ""
|
51
61
|
store_cookie.split(KNOWN_COOKIES_DIVIDER)
|
52
62
|
end
|
53
63
|
|
54
|
-
|
55
|
-
|
56
|
-
Util.slice(@configuration.registered_cookies, *request_cookies.keys)
|
64
|
+
def rewritable_request_cookies
|
65
|
+
Util.slice(request_cookies, *@config.registered_cookies.keys)
|
57
66
|
end
|
58
67
|
|
59
68
|
def known_cookie_names
|
60
69
|
known = [STORE_COOKIE_NAME, SECURED_COOKIE_NAME]
|
61
70
|
known += stored_application_cookie_names
|
62
|
-
known += @
|
63
|
-
end
|
64
|
-
|
65
|
-
def request_cookies
|
66
|
-
@request.cookies
|
71
|
+
known += @config.registered_cookies.keys
|
67
72
|
end
|
68
73
|
|
69
74
|
|
70
75
|
# boolean
|
71
76
|
|
72
77
|
def cookies_have_been_rewritten_before?
|
73
|
-
|
78
|
+
@request.cookies.has_key? SECURED_COOKIE_NAME
|
74
79
|
end
|
75
80
|
|
76
81
|
def should_be_secure?(cookie)
|
77
82
|
cookie_name = cookie.split('=').first.strip
|
78
|
-
ssl? and not @
|
83
|
+
ssl? and not @config.insecure_cookie?(cookie_name)
|
79
84
|
end
|
80
85
|
|
81
86
|
def ssl?
|
@@ -89,7 +94,7 @@ module SafeCookies
|
|
89
94
|
|
90
95
|
def should_be_http_only?(cookie)
|
91
96
|
cookie_name = cookie.split('=').first.strip
|
92
|
-
not @
|
97
|
+
not @config.scriptable_cookie?(cookie_name)
|
93
98
|
end
|
94
99
|
|
95
100
|
end
|
data/lib/safe_cookies/util.rb
CHANGED
@@ -1,17 +1,22 @@
|
|
1
|
-
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
}
|
1
|
+
class SafeCookies::Util
|
2
|
+
class << self
|
3
|
+
|
4
|
+
def slice(hash, *allowed_keys)
|
5
|
+
sliced_hash = hash.select { |key, _value|
|
6
|
+
allowed_keys.include? key
|
7
|
+
}
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
# Normalize the result of Hash#select
|
10
|
+
# (Ruby 1.8 returns an Array, Ruby 1.9 returns a Hash)
|
11
|
+
Hash[sliced_hash]
|
12
|
+
end
|
13
|
+
|
14
|
+
# rejected_keys may be of type String or Regex
|
15
|
+
def except!(hash, *rejected_keys)
|
16
|
+
hash.delete_if do |key, _value|
|
17
|
+
rejected_keys.any? { |rejected| rejected === key }
|
13
18
|
end
|
14
|
-
|
15
19
|
end
|
20
|
+
|
16
21
|
end
|
17
|
-
end
|
22
|
+
end
|
data/lib/safe_cookies/version.rb
CHANGED
data/safe_cookies.gemspec
CHANGED
@@ -7,6 +7,7 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.description = %q{Make all cookies `secure` and `HttpOnly`.}
|
8
8
|
gem.summary = %q{Make all cookies `secure` and `HttpOnly`.}
|
9
9
|
gem.homepage = "http://www.makandra.de"
|
10
|
+
gem.license = "MIT"
|
10
11
|
|
11
12
|
gem.files = `git ls-files`.split($\)
|
12
13
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
@@ -3,7 +3,7 @@ require 'cgi'
|
|
3
3
|
|
4
4
|
describe SafeCookies::Middleware do
|
5
5
|
|
6
|
-
describe 'cookie path fix' do
|
6
|
+
describe 'cookie path fix,' do
|
7
7
|
|
8
8
|
subject { described_class.new(app) }
|
9
9
|
let(:app) { stub 'application' }
|
@@ -15,12 +15,11 @@ describe SafeCookies::Middleware do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def set_default_request_cookies(secured_at = Time.parse('2040-01-01 00:00'))
|
18
|
-
|
19
|
-
set_request_cookies(env, 'cookie_to_update=some_data', "secured_old_cookies=#{CGI::escape(secured_old_cookies_time)}")
|
18
|
+
set_request_cookies(env, 'cookie_to_update=some_data', "secured_old_cookies=#{CGI::escape(secured_at.gmtime.rfc2822)}")
|
20
19
|
end
|
21
20
|
|
22
21
|
|
23
|
-
context 'rewriting previously secured cookies' do
|
22
|
+
context 'rewriting previously secured cookies,' do
|
24
23
|
|
25
24
|
before do
|
26
25
|
SafeCookies.configure do |config|
|
@@ -109,11 +108,21 @@ describe SafeCookies::Middleware do
|
|
109
108
|
headers['Set-Cookie'].should =~ %r(secured_old_cookies=\w+.*path=/;)
|
110
109
|
end
|
111
110
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
111
|
+
context 'unparseable secured_old_cookies timestamp,' do
|
112
|
+
|
113
|
+
before do
|
114
|
+
set_request_cookies(env, 'cookie_to_update=some_data', 'secured_old_cookies=rubbish')
|
115
|
+
code, @headers, response = subject.call(env)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'rewrites cookies anyway' do
|
119
|
+
@headers['Set-Cookie'].should include('cookie_to_update=some_data;')
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'sets a new, parseable secured_old_cookies timestamp' do
|
123
|
+
@headers['Set-Cookie'].should include("secured_old_cookies=#{CGI::escape @now.gmtime.rfc2822}")
|
124
|
+
end
|
125
|
+
|
117
126
|
end
|
118
127
|
|
119
128
|
end
|
data/spec/safe_cookies_spec.rb
CHANGED
@@ -7,7 +7,7 @@ describe SafeCookies::Middleware do
|
|
7
7
|
let(:app) { stub 'application' }
|
8
8
|
let(:env) { { 'HTTPS' => 'on' } }
|
9
9
|
|
10
|
-
it '
|
10
|
+
it 'rewrites registered request cookies as secure and http-only, but only once' do
|
11
11
|
SafeCookies.configure do |config|
|
12
12
|
config.register_cookie('foo', :expire_after => 3600)
|
13
13
|
end
|
@@ -35,7 +35,7 @@ describe SafeCookies::Middleware do
|
|
35
35
|
headers['Set-Cookie'].to_s.should == ''
|
36
36
|
end
|
37
37
|
|
38
|
-
it '
|
38
|
+
it 'doesn’t make cookies secure if the request was not secure' do
|
39
39
|
stub_app_call(app, :application_cookies => 'filter-settings=sort_by_date')
|
40
40
|
env['HTTPS'] = 'off'
|
41
41
|
|
@@ -43,7 +43,7 @@ describe SafeCookies::Middleware do
|
|
43
43
|
headers['Set-Cookie'].should include("filter-settings=sort_by_date")
|
44
44
|
headers['Set-Cookie'].should_not match(/\bsecure\b/i)
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
it 'expires the secured_old_cookies helper cookie in ten years' do
|
48
48
|
Timecop.freeze(Time.parse('2013-09-17 17:53'))
|
49
49
|
|
@@ -59,43 +59,47 @@ describe SafeCookies::Middleware do
|
|
59
59
|
headers['Set-Cookie'].should =~ /secured_old_cookies.*expires=Fri, 15 Sep 2023 \d\d:\d\d:\d\d/
|
60
60
|
end
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
context 'cookie attributes' do
|
63
|
+
|
64
|
+
it 'sets cookies on the root path' do
|
65
|
+
SafeCookies.configure do |config|
|
66
|
+
config.register_cookie('my_old_cookie', :expire_after => 3600)
|
67
|
+
end
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
+
set_request_cookies(env, 'my_old_cookie=foobar')
|
70
|
+
stub_app_call(app)
|
69
71
|
|
70
|
-
|
72
|
+
code, headers, response = subject.call(env)
|
71
73
|
|
72
|
-
|
73
|
-
|
74
|
-
|
74
|
+
cookies = headers['Set-Cookie'].split("\n")
|
75
|
+
cookies.each do |cookie|
|
76
|
+
cookie.should include('; path=/;')
|
77
|
+
end
|
75
78
|
end
|
76
|
-
end
|
77
79
|
|
78
|
-
|
79
|
-
|
80
|
+
it 'should not alter cookie attributes coming from the application' do
|
81
|
+
stub_app_call(app, :application_cookies => 'cookie=data; path=/; expires=next_week')
|
80
82
|
|
81
|
-
|
82
|
-
|
83
|
-
|
83
|
+
code, headers, response = subject.call(env)
|
84
|
+
headers['Set-Cookie'].should =~ %r(cookie=data; path=/; expires=next_week; secure; HttpOnly)
|
85
|
+
end
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
+
it 'should respect cookie attributes set in the configuration' do
|
88
|
+
Timecop.freeze
|
87
89
|
|
88
|
-
|
89
|
-
|
90
|
-
|
90
|
+
SafeCookies.configure do |config|
|
91
|
+
config.register_cookie('foo', :expire_after => 3600, :path => '/special/path')
|
92
|
+
end
|
91
93
|
|
92
|
-
|
93
|
-
|
94
|
-
|
94
|
+
stub_app_call(app)
|
95
|
+
set_request_cookies(env, 'foo=bar')
|
96
|
+
env['PATH_INFO'] = '/special/path/subfolder'
|
95
97
|
|
96
|
-
|
97
|
-
|
98
|
-
|
98
|
+
code, headers, response = subject.call(env)
|
99
|
+
expected_expiry = (Time.now + 3600).gmtime.rfc2822 # a special date format needed here
|
100
|
+
headers['Set-Cookie'].should =~ %r(foo=bar; path=/special/path; expires=#{expected_expiry}; secure; HttpOnly)
|
101
|
+
end
|
102
|
+
|
99
103
|
end
|
100
104
|
|
101
105
|
context 'cookies set by the application' do
|
@@ -173,49 +177,44 @@ describe SafeCookies::Middleware do
|
|
173
177
|
headers['Set-Cookie'].should =~ /js-data=json;.* secure/
|
174
178
|
headers['Set-Cookie'].should_not =~ /js-data=json;.* HttpOnly/
|
175
179
|
end
|
176
|
-
|
180
|
+
|
177
181
|
end
|
178
182
|
|
179
|
-
context '
|
183
|
+
context 'ignored cookies' do
|
180
184
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
expect{ subject.call(env) }.to raise_error(SafeCookies::UnknownCookieError)
|
185
|
+
before do
|
186
|
+
stub_app_call(app)
|
187
|
+
set_request_cookies(env, '__utma=123', '__utmz=456')
|
185
188
|
end
|
186
|
-
|
187
|
-
it 'should not raise an error if the (unregistered) cookie was initially set by the application' do
|
188
|
-
# application sets cookie
|
189
|
-
stub_app_call(app, :application_cookies => 'foo=bar; path=/some/path; secure')
|
190
|
-
|
191
|
-
code, headers, response = subject.call(env)
|
192
189
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
other_app = stub('application')
|
199
|
-
other_subject = described_class.new(other_app)
|
200
|
-
|
201
|
-
stub_app_call(other_app)
|
202
|
-
set_request_cookies(env, *received_cookies)
|
190
|
+
it 'does not rewrite ignored cookies given as string' do
|
191
|
+
SafeCookies.configure do |config|
|
192
|
+
config.ignore_cookie '__utma'
|
193
|
+
config.ignore_cookie '__utmz'
|
194
|
+
end
|
203
195
|
|
204
|
-
|
196
|
+
code, headers, response = subject.call(env)
|
197
|
+
headers['Set-Cookie'].should_not =~ /__utm/
|
205
198
|
end
|
206
|
-
|
207
|
-
it '
|
199
|
+
|
200
|
+
it 'does not rewrite ignored cookies given as regex' do
|
208
201
|
SafeCookies.configure do |config|
|
209
|
-
config.
|
202
|
+
config.ignore_cookie /^__utm/
|
210
203
|
end
|
211
|
-
|
212
|
-
stub_app_call(app)
|
213
|
-
set_request_cookies(env, 'foo=bar')
|
214
204
|
|
215
|
-
subject.call(env)
|
205
|
+
code, headers, response = subject.call(env)
|
206
|
+
headers['Set-Cookie'].should_not =~ /__utm/
|
216
207
|
end
|
208
|
+
|
209
|
+
end
|
217
210
|
|
218
|
-
|
211
|
+
|
212
|
+
# The unknown cookies mechanism was more important when we were sending
|
213
|
+
# notifications on encountering unknown cookies. Perhaps, it will regain
|
214
|
+
# importance in the future.
|
215
|
+
context 'when a request has unknown cookies,' do
|
216
|
+
|
217
|
+
it 'allows overwriting the handling mechanism' do
|
219
218
|
stub_app_call(app)
|
220
219
|
set_request_cookies(env, 'foo=bar')
|
221
220
|
|
@@ -227,6 +226,69 @@ describe SafeCookies::Middleware do
|
|
227
226
|
subject.instance_variable_get('@custom_method_called').should == true
|
228
227
|
end
|
229
228
|
|
229
|
+
context 'and it is configured to log unknown cookies' do
|
230
|
+
|
231
|
+
before do
|
232
|
+
SafeCookies.configure do |config|
|
233
|
+
config.log_unknown_cookies = true
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'logs an error message' do
|
238
|
+
stub_app_call(app)
|
239
|
+
set_request_cookies(env, 'foo=bar')
|
240
|
+
|
241
|
+
subject.should_receive(:log).with(/unknown cookies: foo/)
|
242
|
+
subject.call(env)
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'does not log an error if the (unregistered) cookie was initially set by the application' do
|
246
|
+
# application sets cookie
|
247
|
+
stub_app_call(app, :application_cookies => 'foo=bar; path=/some/path; secure')
|
248
|
+
|
249
|
+
code, headers, response = subject.call(env)
|
250
|
+
|
251
|
+
received_cookies = extract_cookies(headers['Set-Cookie'])
|
252
|
+
received_cookies.should include('foo=bar') # sanity check
|
253
|
+
|
254
|
+
# client returns with the cookie, `app` and `subject` are different
|
255
|
+
# objects than in the previous request
|
256
|
+
other_app = stub('application')
|
257
|
+
other_subject = described_class.new(other_app)
|
258
|
+
|
259
|
+
stub_app_call(other_app)
|
260
|
+
set_request_cookies(env, *received_cookies)
|
261
|
+
|
262
|
+
other_subject.should_not_receive(:log)
|
263
|
+
other_subject.call(env)
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'does not log an error if the cookie is listed in the cookie configuration' do
|
267
|
+
SafeCookies.configure do |config|
|
268
|
+
config.register_cookie('foo', :expire_after => 3600)
|
269
|
+
end
|
270
|
+
|
271
|
+
stub_app_call(app)
|
272
|
+
set_request_cookies(env, 'foo=bar')
|
273
|
+
|
274
|
+
subject.should_not_receive(:log)
|
275
|
+
subject.call(env)
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'does not log an error if the cookie is ignored' do
|
279
|
+
SafeCookies.configure do |config|
|
280
|
+
config.ignore_cookie '__utma'
|
281
|
+
end
|
282
|
+
|
283
|
+
stub_app_call(app)
|
284
|
+
set_request_cookies(env, '__utma=tracking')
|
285
|
+
|
286
|
+
subject.should_not_receive(:log)
|
287
|
+
subject.call(env)
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
230
292
|
end
|
231
293
|
|
232
294
|
end
|
data/spec/util_spec.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SafeCookies::Util do
|
4
|
+
|
5
|
+
describe '.except!' do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@hash = { 'a' => 1, 'ab' => 2, 'b' => 3 }
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'deletes the given keys from the original hash' do
|
12
|
+
SafeCookies::Util.except!(@hash, 'a')
|
13
|
+
@hash.should == { 'ab' => 2, 'b' => 3 }
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'deletes all keys that match the regex' do
|
17
|
+
SafeCookies::Util.except!(@hash, /b/)
|
18
|
+
@hash.should == { 'a' => 1 }
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns the original hash' do
|
22
|
+
SafeCookies::Util.except!(@hash, /(?!)/).should == @hash
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
metadata
CHANGED
@@ -1,69 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safe_cookies
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominik Schöler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: timecop
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: debugger
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
description: Make all cookies `secure` and `HttpOnly`.
|
@@ -73,7 +73,7 @@ executables: []
|
|
73
73
|
extensions: []
|
74
74
|
extra_rdoc_files: []
|
75
75
|
files:
|
76
|
-
- .gitignore
|
76
|
+
- ".gitignore"
|
77
77
|
- Gemfile
|
78
78
|
- LICENSE
|
79
79
|
- README.md
|
@@ -89,8 +89,10 @@ files:
|
|
89
89
|
- spec/cookie_path_fix_spec.rb
|
90
90
|
- spec/safe_cookies_spec.rb
|
91
91
|
- spec/spec_helper.rb
|
92
|
+
- spec/util_spec.rb
|
92
93
|
homepage: http://www.makandra.de
|
93
|
-
licenses:
|
94
|
+
licenses:
|
95
|
+
- MIT
|
94
96
|
metadata: {}
|
95
97
|
post_install_message:
|
96
98
|
rdoc_options: []
|
@@ -98,17 +100,16 @@ require_paths:
|
|
98
100
|
- lib
|
99
101
|
required_ruby_version: !ruby/object:Gem::Requirement
|
100
102
|
requirements:
|
101
|
-
- -
|
103
|
+
- - ">="
|
102
104
|
- !ruby/object:Gem::Version
|
103
105
|
version: '0'
|
104
106
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
107
|
requirements:
|
106
|
-
- -
|
108
|
+
- - ">="
|
107
109
|
- !ruby/object:Gem::Version
|
108
110
|
version: '0'
|
109
111
|
requirements: []
|
110
|
-
|
111
|
-
rubygems_version: 2.1.2
|
112
|
+
rubygems_version: 3.2.15
|
112
113
|
signing_key:
|
113
114
|
specification_version: 4
|
114
115
|
summary: Make all cookies `secure` and `HttpOnly`.
|
@@ -117,3 +118,4 @@ test_files:
|
|
117
118
|
- spec/cookie_path_fix_spec.rb
|
118
119
|
- spec/safe_cookies_spec.rb
|
119
120
|
- spec/spec_helper.rb
|
121
|
+
- spec/util_spec.rb
|