rack-cache 1.1 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,12 @@
1
+ ## 1.2 / March 2012
2
+
3
+ * Fix a cookie leak vulnerability effecting large numbers of Rails 3.x installs:
4
+ https://github.com/rtomayko/rack-cache/pull/52
5
+
6
+ * Never 304 on PUT or POST requests.
7
+
8
+ * Misc bundler and test tooling fixes.
9
+
1
10
  ## 1.1 / September 2011
2
11
 
3
12
  * Allow (INM/IMS) validation requests through to backend on miss. Makes it
data/Rakefile CHANGED
@@ -15,13 +15,13 @@ end
15
15
  desc 'Run specs with unit test style output'
16
16
  task :test => FileList['test/*_test.rb'] do |t|
17
17
  suite = t.prerequisites
18
- sh "bacon -q -I.:lib:test #{suite.join(' ')}", :verbose => false
18
+ sh "bundle exec bacon -q -I.:lib:test #{suite.join(' ')}", :verbose => false
19
19
  end
20
20
 
21
21
  desc 'Run specs with story style output'
22
22
  task :spec => FileList['test/*_test.rb'] do |t|
23
23
  suite = t.prerequisites
24
- sh "bacon -I.:lib:test #{suite.join(' ')}", :verbose => false
24
+ sh "bundle exec bacon -I.:lib:test #{suite.join(' ')}", :verbose => false
25
25
  end
26
26
 
27
27
  desc 'Generate test coverage report'
@@ -18,6 +18,7 @@ module Rack::Cache
18
18
  def initialize(backend, options={})
19
19
  @backend = backend
20
20
  @trace = []
21
+ @env = nil
21
22
 
22
23
  initialize_options options
23
24
  yield self if block_given?
@@ -82,7 +83,10 @@ module Rack::Cache
82
83
  end
83
84
 
84
85
  # tidy up response a bit
85
- response.not_modified! if not_modified?(response)
86
+ if (@request.get? || @request.head?) && not_modified?(response)
87
+ response.not_modified!
88
+ end
89
+
86
90
  if @request.head?
87
91
  response.body.close if response.body.respond_to?(:close)
88
92
  response.body = []
@@ -259,6 +263,7 @@ module Rack::Cache
259
263
 
260
264
  # Write the response to the cache.
261
265
  def store(response)
266
+ strip_ignore_headers(response)
262
267
  metastore.store(@request, response, entitystore)
263
268
  response.headers['Age'] = response.age.to_s
264
269
  rescue Exception => e
@@ -268,6 +273,12 @@ module Rack::Cache
268
273
  record :store
269
274
  end
270
275
 
276
+ # Remove all ignored response headers before writing to the cache.
277
+ def strip_ignore_headers(response)
278
+ stripped_values = ignore_headers.map { |name| response.headers.delete(name) }
279
+ record :ignore if stripped_values.any?
280
+ end
281
+
271
282
  def log_error(exception)
272
283
  @env['rack.errors'].write("cache error: #{exception.message}\n#{exception.backtrace.join("\n")}\n")
273
284
  end
@@ -37,7 +37,7 @@ module Rack::Cache
37
37
  match = entries.detect{|req,res| requests_match?(res['Vary'], env, req)}
38
38
  return nil if match.nil?
39
39
 
40
- req, res = match
40
+ _, res = match
41
41
  if body = entity_store.open(res['X-Content-Digest'])
42
42
  restore_response(res, body)
43
43
  else
@@ -78,6 +78,14 @@ module Rack::Cache
78
78
  # Default: 0
79
79
  option_accessor :default_ttl
80
80
 
81
+ # Set of response headers that are removed before storing them in the
82
+ # cache. These headers are only removed for cacheable responses. For
83
+ # example, in most cases, it makes sense to prevent cookies from being
84
+ # stored in the cache.
85
+ #
86
+ # Default: ['Set-Cookie']
87
+ option_accessor :ignore_headers
88
+
81
89
  # Set of request headers that trigger "private" cache-control behavior
82
90
  # on responses that don't explicitly state whether the response is
83
91
  # public or private via a Cache-Control directive. Applications that use
@@ -138,6 +146,7 @@ module Rack::Cache
138
146
  'rack-cache.metastore' => 'heap:/',
139
147
  'rack-cache.entitystore' => 'heap:/',
140
148
  'rack-cache.default_ttl' => 0,
149
+ 'rack-cache.ignore_headers' => ['Set-Cookie'],
141
150
  'rack-cache.private_headers' => ['Authorization', 'Cookie'],
142
151
  'rack-cache.allow_reload' => false,
143
152
  'rack-cache.allow_revalidate' => false,
@@ -3,11 +3,11 @@ Gem::Specification.new do |s|
3
3
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
4
 
5
5
  s.name = 'rack-cache'
6
- s.version = '1.1'
7
- s.date = '2011-09-18'
6
+ s.version = '1.2'
7
+ s.date = '2012-03-05'
8
8
 
9
- s.description = "HTTP Caching for Rack"
10
9
  s.summary = "HTTP Caching for Rack"
10
+ s.description = "Rack::Cache is suitable as a quick drop-in component to enable HTTP caching for Rack-based applications that produce freshness (Expires, Cache-Control) and/or validation (Last-Modified, ETag) information."
11
11
 
12
12
  s.authors = ["Ryan Tomayko"]
13
13
  s.email = "r@tomayko.com"
@@ -17,7 +17,6 @@ Gem::Specification.new do |s|
17
17
  CHANGES
18
18
  COPYING
19
19
  Gemfile
20
- Gemfile.lock
21
20
  README
22
21
  Rakefile
23
22
  TODO
@@ -57,6 +57,7 @@ describe 'Rack::Cache::Context' do
57
57
  response.should.be.ok
58
58
  cache.trace.should.include :miss
59
59
  cache.trace.should.include :store
60
+ cache.trace.should.not.include :ignore
60
61
  response.headers.should.include 'Age'
61
62
  response.headers['Cache-Control'].should.equal 'public'
62
63
  end
@@ -85,6 +86,40 @@ describe 'Rack::Cache::Context' do
85
86
  response.headers['Cache-Control'].should.equal 'private'
86
87
  end
87
88
 
89
+ it 'does remove Set-Cookie response header from a cacheable response' do
90
+ respond_with 200, 'Cache-Control' => 'public', 'ETag' => '"FOO"', 'Set-Cookie' => 'TestCookie=OK'
91
+ get '/'
92
+
93
+ app.should.be.called
94
+ response.should.be.ok
95
+ cache.trace.should.include :store
96
+ cache.trace.should.include :ignore
97
+ response.headers['Set-Cookie'].should.be.nil
98
+ end
99
+
100
+ it 'does remove all configured ignore_headers from a cacheable response' do
101
+ respond_with 200, 'Cache-Control' => 'public', 'ETag' => '"FOO"', 'SET-COOKIE' => 'TestCookie=OK', 'X-Strip-Me' => 'Secret'
102
+ get '/', 'rack-cache.ignore_headers' => ['set-cookie', 'x-strip-me']
103
+
104
+ app.should.be.called
105
+ response.should.be.ok
106
+ cache.trace.should.include :store
107
+ cache.trace.should.include :ignore
108
+ response.headers['Set-Cookie'].should.be.nil
109
+ response.headers['x-strip-me'].should.be.nil
110
+ end
111
+
112
+ it 'does not remove Set-Cookie response header from a private response' do
113
+ respond_with 200, 'Cache-Control' => 'private', 'Set-Cookie' => 'TestCookie=OK'
114
+ get '/'
115
+
116
+ app.should.be.called
117
+ response.should.be.ok
118
+ cache.trace.should.not.include :store
119
+ cache.trace.should.not.include :ignore
120
+ response.headers['Set-Cookie'].should.equal 'TestCookie=OK'
121
+ end
122
+
88
123
  it 'responds with 304 when If-Modified-Since matches Last-Modified' do
89
124
  timestamp = Time.now.httpdate
90
125
  respond_with do |req,res|
metadata CHANGED
@@ -1,94 +1,75 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: rack-cache
3
- version: !ruby/object:Gem::Version
4
- hash: 13
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.2'
5
5
  prerelease:
6
- segments:
7
- - 1
8
- - 1
9
- version: "1.1"
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Ryan Tomayko
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2011-09-18 00:00:00 Z
18
- dependencies:
19
- - !ruby/object:Gem::Dependency
12
+ date: 2012-03-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
20
15
  name: rack
21
- prerelease: false
22
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &70248834703480 !ruby/object:Gem::Requirement
23
17
  none: false
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- hash: 3
28
- segments:
29
- - 0
30
- - 4
31
- version: "0.4"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0.4'
32
22
  type: :runtime
33
- version_requirements: *id001
34
- - !ruby/object:Gem::Dependency
35
- name: bacon
36
23
  prerelease: false
37
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *70248834703480
25
+ - !ruby/object:Gem::Dependency
26
+ name: bacon
27
+ requirement: &70248834703100 !ruby/object:Gem::Requirement
38
28
  none: false
39
- requirements:
40
- - - ">="
41
- - !ruby/object:Gem::Version
42
- hash: 3
43
- segments:
44
- - 0
45
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
46
33
  type: :development
47
- version_requirements: *id002
48
- - !ruby/object:Gem::Dependency
49
- name: memcached
50
34
  prerelease: false
51
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *70248834703100
36
+ - !ruby/object:Gem::Dependency
37
+ name: memcached
38
+ requirement: &70248834702640 !ruby/object:Gem::Requirement
52
39
  none: false
53
- requirements:
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- hash: 3
57
- segments:
58
- - 0
59
- version: "0"
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
60
44
  type: :development
61
- version_requirements: *id003
62
- - !ruby/object:Gem::Dependency
63
- name: dalli
64
45
  prerelease: false
65
- requirement: &id004 !ruby/object:Gem::Requirement
46
+ version_requirements: *70248834702640
47
+ - !ruby/object:Gem::Dependency
48
+ name: dalli
49
+ requirement: &70248834702220 !ruby/object:Gem::Requirement
66
50
  none: false
67
- requirements:
68
- - - ">="
69
- - !ruby/object:Gem::Version
70
- hash: 3
71
- segments:
72
- - 0
73
- version: "0"
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
74
55
  type: :development
75
- version_requirements: *id004
76
- description: HTTP Caching for Rack
56
+ prerelease: false
57
+ version_requirements: *70248834702220
58
+ description: Rack::Cache is suitable as a quick drop-in component to enable HTTP caching
59
+ for Rack-based applications that produce freshness (Expires, Cache-Control) and/or
60
+ validation (Last-Modified, ETag) information.
77
61
  email: r@tomayko.com
78
62
  executables: []
79
-
80
63
  extensions: []
81
-
82
- extra_rdoc_files:
64
+ extra_rdoc_files:
83
65
  - README
84
66
  - COPYING
85
67
  - TODO
86
68
  - CHANGES
87
- files:
69
+ files:
88
70
  - CHANGES
89
71
  - COPYING
90
72
  - Gemfile
91
- - Gemfile.lock
92
73
  - README
93
74
  - Rakefile
94
75
  - TODO
@@ -129,43 +110,35 @@ files:
129
110
  - test/storage_test.rb
130
111
  homepage: http://tomayko.com/src/rack-cache/
131
112
  licenses: []
132
-
133
113
  post_install_message:
134
- rdoc_options:
114
+ rdoc_options:
135
115
  - --line-numbers
136
116
  - --inline-source
137
117
  - --title
138
118
  - Rack::Cache
139
119
  - --main
140
120
  - Rack::Cache
141
- require_paths:
121
+ require_paths:
142
122
  - lib
143
- required_ruby_version: !ruby/object:Gem::Requirement
123
+ required_ruby_version: !ruby/object:Gem::Requirement
144
124
  none: false
145
- requirements:
146
- - - ">="
147
- - !ruby/object:Gem::Version
148
- hash: 3
149
- segments:
150
- - 0
151
- version: "0"
152
- required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
130
  none: false
154
- requirements:
155
- - - ">="
156
- - !ruby/object:Gem::Version
157
- hash: 3
158
- segments:
159
- - 0
160
- version: "0"
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
161
135
  requirements: []
162
-
163
136
  rubyforge_project:
164
- rubygems_version: 1.8.6
137
+ rubygems_version: 1.8.11
165
138
  signing_key:
166
139
  specification_version: 2
167
140
  summary: HTTP Caching for Rack
168
- test_files:
141
+ test_files:
169
142
  - test/cache_test.rb
170
143
  - test/cachecontrol_test.rb
171
144
  - test/context_test.rb
@@ -1,22 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- rack-cache (1.0.3)
5
- rack (>= 0.4)
6
-
7
- GEM
8
- remote: http://rubygems.org/
9
- specs:
10
- bacon (1.1.0)
11
- dalli (1.0.5)
12
- memcached (1.3)
13
- rack (1.3.2)
14
-
15
- PLATFORMS
16
- ruby
17
-
18
- DEPENDENCIES
19
- bacon
20
- dalli
21
- memcached
22
- rack-cache!