Skip to content

Commit 7921ff8

Browse files
committed
Merge branch '4-1-sec' into 4-1-stable
* 4-1-sec: bumping version Remove unnecessary caching Eliminate instance level writers for class accessors allow :file to be outside rails root, but anything else must be inside the rails view directory Don't short-circuit reject_if proc stop caching mime types globally use secure string comparisons for basic auth username / password
2 parents a96c723 + 31ab3aa commit 7921ff8

File tree

29 files changed

+184
-39
lines changed

29 files changed

+184
-39
lines changed

RAILS_VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4.1.14
1+
4.1.14.1

actionmailer/lib/action_mailer/gem_version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module VERSION
88
MAJOR = 4
99
MINOR = 1
1010
TINY = 14
11-
PRE = nil
11+
PRE = "1"
1212

1313
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
1414
end

actionpack/lib/abstract_controller/rendering.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,13 @@ def view_assigns
7777
# render "foo/bar" to render :file => "foo/bar".
7878
# :api: plugin
7979
def _normalize_args(action=nil, options={})
80-
if action.is_a? Hash
80+
case action
81+
when ActionController::Parameters
82+
unless action.permitted?
83+
raise ArgumentError, "render parameters are not permitted"
84+
end
85+
action
86+
when Hash
8187
action
8288
else
8389
options

actionpack/lib/action_controller/metal/http_authentication.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require 'base64'
2+
require 'active_support/security_utils'
23

34
module ActionController
45
# Makes it dead easy to do HTTP Basic, Digest and Token authentication.
@@ -70,7 +71,11 @@ module ClassMethods
7071
def http_basic_authenticate_with(options = {})
7172
before_action(options.except(:name, :password, :realm)) do
7273
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
73-
name == options[:name] && password == options[:password]
74+
# This comparison uses & so that it doesn't short circuit and
75+
# uses `variable_size_secure_compare` so that length information
76+
# isn't leaked.
77+
ActiveSupport::SecurityUtils.variable_size_secure_compare(name, options[:name]) &
78+
ActiveSupport::SecurityUtils.variable_size_secure_compare(password, options[:password])
7479
end
7580
end
7681
end

actionpack/lib/action_dispatch/http/mime_type.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def #{method}(*)
2323

2424
SET = Mimes.new
2525
EXTENSION_LOOKUP = {}
26-
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
26+
LOOKUP = {}
2727

2828
class << self
2929
def [](type)
@@ -146,7 +146,7 @@ def register_callback(&block)
146146
end
147147

148148
def lookup(string)
149-
LOOKUP[string]
149+
LOOKUP[string] || Type.new(string)
150150
end
151151

152152
def lookup_by_extension(extension)
@@ -225,9 +225,12 @@ def unregister(symbol)
225225
end
226226
end
227227

228+
attr_reader :hash
229+
228230
def initialize(string, symbol = nil, synonyms = [])
229231
@symbol, @synonyms = symbol, synonyms
230232
@string = string
233+
@hash = [@string, @synonyms, @symbol].hash
231234
end
232235

233236
def to_s
@@ -261,6 +264,13 @@ def ==(mime_type)
261264
end
262265
end
263266

267+
def eql?(other)
268+
super || (self.class == other.class &&
269+
@string == other.string &&
270+
@synonyms == other.synonyms &&
271+
@symbol == other.symbol)
272+
end
273+
264274
def =~(mime_type)
265275
return false if mime_type.blank?
266276
regexp = Regexp.new(Regexp.quote(mime_type.to_s))
@@ -274,6 +284,10 @@ def html?
274284
end
275285

276286

287+
protected
288+
289+
attr_reader :string, :synonyms
290+
277291
private
278292

279293
def to_ary; end

actionpack/lib/action_dispatch/routing/route_set.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
require 'action_dispatch/journey'
22
require 'forwardable'
3-
require 'thread_safe'
43
require 'active_support/concern'
54
require 'active_support/core_ext/object/to_query'
65
require 'active_support/core_ext/hash/slice'
@@ -24,7 +23,6 @@ class Dispatcher #:nodoc:
2423
def initialize(options={})
2524
@defaults = options[:defaults]
2625
@glob_param = options.delete(:glob)
27-
@controller_class_names = ThreadSafe::Cache.new
2826
end
2927

3028
def call(env)
@@ -74,7 +72,7 @@ def controller(params, default_controller=true)
7472
private
7573

7674
def controller_reference(controller_param)
77-
const_name = @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
75+
const_name = "#{controller_param.camelize}Controller"
7876
ActiveSupport::Dependencies.constantize(const_name)
7977
end
8078

actionpack/lib/action_pack/gem_version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module VERSION
88
MAJOR = 4
99
MINOR = 1
1010
TINY = 14
11-
PRE = nil
11+
PRE = "1"
1212

1313
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
1414
end

actionpack/test/controller/render_test.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ def conditional_hello_with_record
5252
end
5353
end
5454

55+
def dynamic_render
56+
render params[:id] # => String, AC:Params
57+
end
58+
59+
def dynamic_render_with_file
60+
# This is extremely bad, but should be possible to do.
61+
file = params[:id] # => String, AC:Params
62+
render file: file
63+
end
64+
5565
def conditional_hello_with_public_header
5666
if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123], :public => true)
5767
render :action => 'hello_world'
@@ -251,6 +261,27 @@ def accessing_logger_in_template
251261
class ExpiresInRenderTest < ActionController::TestCase
252262
tests TestController
253263

264+
def test_dynamic_render_with_file
265+
# This is extremely bad, but should be possible to do.
266+
assert File.exist?(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb'))
267+
response = get :dynamic_render_with_file, { id: '../\\../test/abstract_unit.rb' }
268+
assert_equal File.read(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb')),
269+
response.body
270+
end
271+
272+
def test_dynamic_render
273+
assert File.exist?(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb'))
274+
assert_raises ActionView::MissingTemplate do
275+
get :dynamic_render, { id: '../\\../test/abstract_unit.rb' }
276+
end
277+
end
278+
279+
def test_dynamic_render_file_hash
280+
assert_raises ArgumentError do
281+
get :dynamic_render, { id: { file: '../\\../test/abstract_unit.rb' } }
282+
end
283+
end
284+
254285
def test_expires_in_header
255286
get :conditional_hello_with_expires_in
256287
assert_equal "max-age=60, private", @response.headers["Cache-Control"]

actionview/lib/action_view/gem_version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module VERSION
88
MAJOR = 4
99
MINOR = 1
1010
TINY = 14
11-
PRE = nil
11+
PRE = "1"
1212

1313
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
1414
end

actionview/lib/action_view/lookup_context.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ def find(name, prefixes = [], partial = false, keys = [], options = {})
125125
end
126126
alias :find_template :find
127127

128+
def find_file(name, prefixes = [], partial = false, keys = [], options = {})
129+
@view_paths.find_file(*args_for_lookup(name, prefixes, partial, keys, options))
130+
end
131+
128132
def find_all(name, prefixes = [], partial = false, keys = [], options = {})
129133
@view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
130134
end

actionview/lib/action_view/path_set.rb

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,35 @@ def find(*args)
4646
find_all(*args).first || raise(MissingTemplate.new(self, *args))
4747
end
4848

49+
def find_file(path, prefixes = [], *args)
50+
_find_all(path, prefixes, args, true).first || raise(MissingTemplate.new(self, path, prefixes, *args))
51+
end
52+
4953
def find_all(path, prefixes = [], *args)
54+
_find_all path, prefixes, args, false
55+
end
56+
57+
def exists?(path, prefixes, *args)
58+
find_all(path, prefixes, *args).any?
59+
end
60+
61+
private
62+
63+
def _find_all(path, prefixes, args, outside_app)
5064
prefixes = [prefixes] if String === prefixes
5165
prefixes.each do |prefix|
5266
paths.each do |resolver|
53-
templates = resolver.find_all(path, prefix, *args)
67+
if outside_app
68+
templates = resolver.find_all_anywhere(path, prefix, *args)
69+
else
70+
templates = resolver.find_all(path, prefix, *args)
71+
end
5472
return templates unless templates.empty?
5573
end
5674
end
5775
[]
5876
end
5977

60-
def exists?(path, prefixes, *args)
61-
find_all(path, prefixes, *args).any?
62-
end
63-
64-
private
65-
6678
def typecast(paths)
6779
paths.map do |path|
6880
case path

actionview/lib/action_view/renderer/abstract_renderer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module ActionView
1515
# that new object is called in turn. This abstracts the setup and rendering
1616
# into a separate classes for partials and templates.
1717
class AbstractRenderer #:nodoc:
18-
delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
18+
delegate :find_template, :find_file, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
1919

2020
def initialize(lookup_context)
2121
@lookup_context = lookup_context

actionview/lib/action_view/renderer/template_renderer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def determine_template(options) #:nodoc:
3030
elsif options.key?(:html)
3131
Template::HTML.new(options[:html], formats.first)
3232
elsif options.key?(:file)
33-
with_fallbacks { find_template(options[:file], nil, false, keys, @details) }
33+
with_fallbacks { find_file(options[:file], nil, false, keys, @details) }
3434
elsif options.key?(:inline)
3535
handler = Template.handler_for_extension(options[:type] || "erb")
3636
Template.new(options[:inline], "inline template", handler, :locals => keys)

actionview/lib/action_view/template/resolver.rb

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,13 @@ def clear_cache
112112
# Normalizes the arguments and passes it on to find_templates.
113113
def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
114114
cached(key, [name, prefix, partial], details, locals) do
115-
find_templates(name, prefix, partial, details)
115+
find_templates(name, prefix, partial, details, false)
116+
end
117+
end
118+
119+
def find_all_anywhere(name, prefix, partial=false, details={}, key=nil, locals=[])
120+
cached(key, [name, prefix, partial], details, locals) do
121+
find_templates(name, prefix, partial, details, true)
116122
end
117123
end
118124

@@ -173,15 +179,16 @@ def initialize(pattern=nil)
173179

174180
private
175181

176-
def find_templates(name, prefix, partial, details)
182+
def find_templates(name, prefix, partial, details, outside_app_allowed = false)
177183
path = Path.build(name, prefix, partial)
178-
query(path, details, details[:formats])
184+
query(path, details, details[:formats], outside_app_allowed)
179185
end
180186

181-
def query(path, details, formats)
187+
def query(path, details, formats, outside_app_allowed)
182188
query = build_query(path, details)
183189

184190
template_paths = find_template_paths query
191+
template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed
185192

186193
template_paths.map { |template|
187194
handler, format, variant = extract_handler_and_format_and_variant(template, formats)
@@ -196,6 +203,10 @@ def query(path, details, formats)
196203
}
197204
end
198205

206+
def reject_files_external_to_app(files)
207+
files.reject { |filename| !inside_path?(@path, filename) }
208+
end
209+
199210
if RUBY_VERSION >= '2.2.0'
200211
def find_template_paths(query)
201212
Dir[query].reject { |filename|
@@ -216,6 +227,12 @@ def find_template_paths(query)
216227
end
217228
end
218229

230+
def inside_path?(path, filename)
231+
filename = File.expand_path(filename)
232+
path = File.join(path, '')
233+
filename.start_with?(path)
234+
end
235+
219236
# Helper for building query glob string based on resolver's pattern.
220237
def build_query(path, details)
221238
query = @pattern.dup

actionview/lib/action_view/testing/resolvers.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def to_s
1919

2020
private
2121

22-
def query(path, exts, formats)
22+
def query(path, exts, formats, _)
2323
query = ""
2424
EXTENSIONS.each_key do |ext|
2525
query << '(' << exts[ext].map {|e| e && Regexp.escape(".#{e}") }.join('|') << '|)'
@@ -44,7 +44,7 @@ def query(path, exts, formats)
4444
end
4545

4646
class NullResolver < PathResolver
47-
def query(path, exts, formats)
47+
def query(path, exts, formats, _)
4848
handler, format, variant = extract_handler_and_format_and_variant(path, formats)
4949
[ActionView::Template.new("Template generated by Null Resolver", path, handler, :virtual_path => path, :format => format, :variant => variant)]
5050
end

actionview/test/template/render_test.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ def test_render_partial_from_default
142142
assert_equal "only partial", @view.render("test/partial_only")
143143
end
144144

145+
def test_render_outside_path
146+
assert File.exist?(File.join(File.dirname(__FILE__), '../../test/abstract_unit.rb'))
147+
assert_raises ActionView::MissingTemplate do
148+
@view.render(:template => "../\\../test/abstract_unit.rb")
149+
end
150+
end
151+
145152
def test_render_partial
146153
assert_equal "only partial", @view.render(:partial => "test/partial_only")
147154
end

activemodel/lib/active_model/gem_version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module VERSION
88
MAJOR = 4
99
MINOR = 1
1010
TINY = 14
11-
PRE = nil
11+
PRE = "1"
1212

1313
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
1414
end

activemodel/lib/active_model/serializers/json.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module JSON
1010
included do
1111
extend ActiveModel::Naming
1212

13-
class_attribute :include_root_in_json
13+
class_attribute :include_root_in_json, instance_writer: false
1414
self.include_root_in_json = false
1515
end
1616

activemodel/lib/active_model/validations.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ module Validations
4646
include HelperMethods
4747

4848
attr_accessor :validation_context
49+
private :validation_context=
4950
define_callbacks :validate, scope: :name
5051

51-
class_attribute :_validators
52+
class_attribute :_validators, instance_writer: false
5253
self._validators = Hash.new { |h,k| h[k] = [] }
5354
end
5455

activerecord/lib/active_record/enum.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ module ActiveRecord
6868
# Where conditions on an enum attribute must use the ordinal value of an enum.
6969
module Enum
7070
def self.extended(base) # :nodoc:
71-
base.class_attribute(:defined_enums)
71+
base.class_attribute(:defined_enums, instance_writer: false)
7272
base.defined_enums = {}
7373
end
7474

activerecord/lib/active_record/gem_version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module VERSION
88
MAJOR = 4
99
MINOR = 1
1010
TINY = 14
11-
PRE = nil
11+
PRE = "1"
1212

1313
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
1414
end

0 commit comments

Comments
 (0)