safe_cookies 0.1.0 → 0.1.1

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.
@@ -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