safe_cookies 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module SafeCookies
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
data/lib/safe_cookies.rb CHANGED
@@ -100,7 +100,7 @@ module SafeCookies
100
100
  # It contains more information than the "HTTP_COOKIE" header from the
101
101
  # browser's request contained, so a `Rack::Request` can't parse it for
102
102
  # us. A `Rack::Response` doesn't offer a way either.
103
- headers['Set-Cookie'] = cookies.join(",") # cookies are comma-separated
103
+ headers['Set-Cookie'] = cookies.join("\n")
104
104
  end
105
105
  end
106
106
 
@@ -0,0 +1,113 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ # Explanation:
5
+ # app#call(env) is how the middleware calls the app
6
+ # returns the app's response
7
+ # subject#call(env) is how the middleware is called "from below"
8
+ # returns the response that is passed through the web server to the client
9
+
10
+ describe SafeCookies::Middleware do
11
+
12
+ let(:app) { stub 'application' }
13
+ let(:env) { { 'HTTPS' => 'on' } }
14
+ subject { described_class.new(app) }
15
+
16
+ it 'should rewrite specified existing cookies as "secure" and "HttpOnly", but only once' do
17
+ Timecop.freeze do
18
+ # first request: rewrite cookie
19
+ subject = described_class.new(app, :foo => 24 * 60 * 60)
20
+ app.should_receive(:call).and_return([ stub, {}, stub ])
21
+ env['HTTP_COOKIE'] = 'foo=bar'
22
+
23
+ code, headers, response = subject.call(env)
24
+ expected_expiry = Rack::Utils.rfc2822((Time.now + 24 * 60 * 60).gmtime) # a special date format needed here
25
+ headers['Set-Cookie'].should =~ /foo=bar;[^\n]* HttpOnly/
26
+ headers['Set-Cookie'].should =~ /foo=bar;[^\n]* secure/
27
+ headers['Set-Cookie'].should =~ /expires=#{expected_expiry}/
28
+ headers['Set-Cookie'].should =~ /secured_old_cookies=/ # the indication cookie
29
+
30
+ # second request: do not rewrite cookie again
31
+ subject = described_class.new(app, :foo => 24 * 60 * 60)
32
+ app.should_receive(:call).and_return([ stub, {}, stub ])
33
+ received_cookies = headers['Set-Cookie'].scan(/[^,;]+=[^,;]+(?=;\s)/i) # extract cookies
34
+ env['HTTP_COOKIE'] = received_cookies.join(',')
35
+
36
+ code, headers, response = subject.call(env)
37
+ headers['Set-Cookie'].to_s.should == ""
38
+ end
39
+ end
40
+
41
+ it "should make new cookies secure" do
42
+ app.should_receive(:call).and_return([ stub, { 'Set-Cookie' => 'neuer_cookie=neuer_cookie_wert'}, stub ])
43
+
44
+ code, headers, response = subject.call(env)
45
+ headers['Set-Cookie'].should =~ /neuer_cookie=neuer_cookie_wert;[^\n]* secure/
46
+ end
47
+
48
+ it "should make new cookies http_only" do
49
+ app.should_receive(:call).and_return([ stub, { 'Set-Cookie' => 'neuer_cookie=neuer_cookie_wert'}, stub ])
50
+
51
+ code, headers, response = subject.call(env)
52
+ headers['Set-Cookie'].should =~ /neuer_cookie=neuer_cookie_wert;[^\n]* HttpOnly/
53
+ end
54
+
55
+ it "should not make new cookies secure that are specified as 'non_secure'" do
56
+ subject = described_class.new(app, :non_secure => %w[filter-settings])
57
+ app.should_receive(:call).and_return([ stub, { 'Set-Cookie' => 'filter-settings=sort_by_date'}, stub ])
58
+
59
+ code, headers, response = subject.call(env)
60
+ headers['Set-Cookie'].should include("filter-settings=sort_by_date")
61
+ headers['Set-Cookie'].should_not match(/secure/i)
62
+ end
63
+
64
+ it "should not make new cookies http_only that are specified as 'non_http_only'" do
65
+ subject = described_class.new(app, :non_http_only => %w[javascript-cookie])
66
+ app.should_receive(:call).and_return([ stub, { 'Set-Cookie' => 'javascript-cookie=xss'}, stub ])
67
+
68
+ code, headers, response = subject.call(env)
69
+ headers['Set-Cookie'].should include("javascript-cookie=xss")
70
+ headers['Set-Cookie'].should_not match(/HttpOnly/i)
71
+ end
72
+
73
+ it "should prefer the application's cookie if both client and app are sending one" do
74
+ app.should_receive(:call).and_return([ stub, { 'Set-Cookie' => 'cookie=überschrieben'}, stub ])
75
+ env['HTTP_COOKIE'] = 'cookie=wert'
76
+
77
+ code, headers, response = subject.call(env)
78
+ headers['Set-Cookie'].should include("cookie=überschrieben")
79
+ end
80
+
81
+ it "should not make existing cookies secure that are specified as 'non_secure'" do
82
+ subject = described_class.new(app, :filter => 24 * 60 * 60, :non_secure => %w[filter])
83
+ app.should_receive(:call).and_return([ stub, {}, stub ])
84
+ env['HTTP_COOKIE'] = 'filter=cars_only'
85
+
86
+ code, headers, response = subject.call(env)
87
+ set_cookie = headers['Set-Cookie'].gsub(/,(?=\s\d)/, '') # remove commas in expiry dates to simplify matching below
88
+ set_cookie.should =~ /filter=cars_only;[^,]* HttpOnly/
89
+ set_cookie.should_not match(/filter=cars_only;[^,]* secure/)
90
+ end
91
+
92
+ it "should not make existing cookies http_only that are specified as 'non_http_only'" do
93
+ subject = described_class.new(app, :js_data => 24 * 60 * 60, :non_http_only => %w[js_data])
94
+ app.should_receive(:call).and_return([ stub, {}, stub ])
95
+ env['HTTP_COOKIE'] = 'js_data=json'
96
+
97
+ code, headers, response = subject.call(env)
98
+ set_cookie = headers['Set-Cookie'].gsub(/,(?=\s\d)/, '') # remove commas in expiry dates to simplify matching below
99
+ set_cookie.should =~ /js_data=json;[^,]* secure/
100
+ set_cookie.should_not match(/js_data=json;[^,]* HttpOnly/)
101
+ end
102
+
103
+ it "should not make cookies secure if the request was not secure" do
104
+ subject = described_class.new(app)
105
+ app.should_receive(:call).and_return([ stub, { 'Set-Cookie' => 'filter-settings=sort_by_date'}, stub ])
106
+ env['HTTPS'] = 'off'
107
+
108
+ code, headers, response = subject.call(env)
109
+ headers['Set-Cookie'].should include("filter-settings=sort_by_date")
110
+ headers['Set-Cookie'].should_not match(/secure/i)
111
+ end
112
+
113
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path('../../lib/safe_cookies', __FILE__)
2
+ require 'timecop'
3
+
4
+ RSpec.configure do |config|
5
+ config.treat_symbols_as_metadata_keys_with_true_values = true
6
+ config.run_all_when_everything_filtered = true
7
+ config.filter_run :focus
8
+
9
+ # Run specs in random order to surface order dependencies. If you find an
10
+ # order dependency and want to debug it, you can fix the order by providing
11
+ # the seed, which is printed after each run.
12
+ # --seed 1234
13
+ config.order = 'random'
14
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safe_cookies
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-21 00:00:00.000000000 Z
12
+ date: 2013-06-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -74,6 +74,8 @@ files:
74
74
  - lib/safe_cookies.rb
75
75
  - lib/safe_cookies/version.rb
76
76
  - safe_cookies.gemspec
77
+ - spec/safe_cookies_spec.rb
78
+ - spec/spec_helper.rb
77
79
  homepage: http://www.makandra.de
78
80
  licenses: []
79
81
  post_install_message:
@@ -98,4 +100,6 @@ rubygems_version: 1.8.24
98
100
  signing_key:
99
101
  specification_version: 3
100
102
  summary: Make cookies as `secure` and `HttpOnly` as possible.
101
- test_files: []
103
+ test_files:
104
+ - spec/safe_cookies_spec.rb
105
+ - spec/spec_helper.rb