diff options
31 files changed, 155 insertions, 42 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index b23d0668d3..97bd2bf97f 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,15 @@ ## Rails 4.0.0 (unreleased) ## +* Ensure that digest authentication responds with a 401 status when a basic + header is received. + + *Brad Dunbar* + +* Include I18n locale fallbacks in view lookup. + Fixes GH#3512. + + *Juan Barreneche* + * Integration and functional tests allow headers and rack env variables to be passed when performing requests. Fixes #6513. diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index e295002b16..c7bb2dd147 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -299,6 +299,7 @@ module ActionController # allow a user to use new nonce without prompting user again for their # username and password. def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60) + return false if value.nil? t = ::Base64.decode64(value).split(":").first.to_i nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout end diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index 4e4816d983..d61cc0f304 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -43,7 +43,13 @@ module ActionView module Accessors #:nodoc: end - register_detail(:locale) { [I18n.locale, I18n.default_locale].uniq } + register_detail(:locale) do + locales = [I18n.locale] + locales.concat(I18n.fallbacks[I18n.locale]) if I18n.respond_to? :fallbacks + locales << I18n.default_locale + locales.uniq! + locales + end register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] } register_detail(:handlers){ Template::Handlers.extensions } diff --git a/actionpack/lib/action_view/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_view/vendor/html-scanner/html/sanitizer.rb index 6b4ececda2..30b6b8b141 100644 --- a/actionpack/lib/action_view/vendor/html-scanner/html/sanitizer.rb +++ b/actionpack/lib/action_view/vendor/html-scanner/html/sanitizer.rb @@ -77,7 +77,7 @@ module HTML # A regular expression of the valid characters used to separate protocols like # the ':' in 'http://foo.com' - self.protocol_separator = /:|(�*58)|(p)|(%|%)3A/ + self.protocol_separator = /:|(�*58)|(p)|(�*3a)|(%|%)3A/i # Specifies a Set of HTML attributes that can have URIs. self.uri_attributes = Set.new(%w(href src cite action longdesc xlink:href lowsrc)) @@ -121,8 +121,8 @@ module HTML style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ') # gauntlet - if style !~ /^([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*$/ || - style !~ /^(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*$/ + if style !~ /\A([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*\z/ || + style !~ /\A(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*\z/ return '' end @@ -133,7 +133,7 @@ module HTML elsif shorthand_css_properties.include?(prop.split('-')[0].downcase) unless val.split().any? do |keyword| !allowed_css_keywords.include?(keyword) && - keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/ + keyword !~ /\A(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)\z/ end clean << prop + ': ' + val + ';' end @@ -182,7 +182,7 @@ module HTML def contains_bad_protocols?(attr_name, value) uri_attributes.include?(attr_name) && - (value =~ /(^[^\/:]*):|(�*58)|(p)|(%|%)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase.strip)) + (value =~ /(^[^\/:]*):|(�*58)|(p)|(�*3a)|(%|%)3A/i && !allowed_protocols.include?(value.split(protocol_separator).first.downcase.strip)) end end end diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index 537de7a2dd..4287856550 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -249,6 +249,14 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal 'Definitely Maybe', @response.body end + test "when sent a basic auth header, returns Unauthorized" do + @request.env['HTTP_AUTHORIZATION'] = 'Basic Gwf2aXq8ZLF3Hxq=' + + get :display + + assert_response :unauthorized + end + private def encode_credentials(options) diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb index bac1d02977..6b02eedaed 100644 --- a/actionpack/test/controller/localized_templates_test.rb +++ b/actionpack/test/controller/localized_templates_test.rb @@ -25,4 +25,13 @@ class LocalizedTemplatesTest < ActionController::TestCase ensure I18n.locale = old_locale end + + def test_use_fallback_locales + I18n.locale = :"de-AT" + I18n.backend.class.send(:include, I18n::Backend::Fallbacks) + I18n.fallbacks[:"de-AT"] = [:de] + + get :hello_world + assert_equal "Gutten Tag", @response.body + end end diff --git a/actionpack/test/template/html-scanner/sanitizer_test.rb b/actionpack/test/template/html-scanner/sanitizer_test.rb index d9b57776c9..b1c1b83807 100644 --- a/actionpack/test/template/html-scanner/sanitizer_test.rb +++ b/actionpack/test/template/html-scanner/sanitizer_test.rb @@ -200,6 +200,7 @@ class SanitizerTest < ActionController::TestCase %(<IMG SRC="jav
ascript:alert('XSS');">), %(<IMG SRC="jav
ascript:alert('XSS');">), %(<IMG SRC="  javascript:alert('XSS');">), + %(<IMG SRC="javascript:alert('XSS');">), %(<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>)].each_with_index do |img_hack, i| define_method "test_should_not_fall_for_xss_image_hack_#{i+1}" do assert_sanitized img_hack, "<img>" @@ -279,6 +280,11 @@ class SanitizerTest < ActionController::TestCase assert_equal '', sanitize_css(raw) end + def test_should_sanitize_across_newlines + raw = %(\nwidth:\nexpression(alert('XSS'));\n) + assert_equal '', sanitize_css(raw) + end + def test_should_sanitize_img_vbscript assert_sanitized %(<img src='vbscript:msgbox("XSS")' />), '<img />' end @@ -299,6 +305,15 @@ class SanitizerTest < ActionController::TestCase assert_sanitized "<span class=\"\\", "<span class=\"\\\">" end + def test_x03a + assert_sanitized %(<a href="javascript:alert('XSS');">), "<a>" + assert_sanitized %(<a href="javascript:alert('XSS');">), "<a>" + assert_sanitized %(<a href="http://legit">), %(<a href="http://legit">) + assert_sanitized %(<a href="javascript:alert('XSS');">), "<a>" + assert_sanitized %(<a href="javascript:alert('XSS');">), "<a>" + assert_sanitized %(<a href="http://legit">), %(<a href="http://legit">) + end + protected def assert_sanitized(input, expected = nil) @sanitizer ||= HTML::WhiteListSanitizer.new diff --git a/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb index 8e8925f0a9..9a3fada380 100644 --- a/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb @@ -35,7 +35,7 @@ module ActiveRecord # record def associated_records_by_owner records = {} - super.each do |owner_key, rows| + super.each_value do |rows| rows.map! { |row| records[row[klass.primary_key]] ||= klass.instantiate(row) } end end diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index e0bfdb8f3e..aa92343f76 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -21,7 +21,7 @@ module ActiveRecord # Generates all the attribute related methods for columns in the database # accessors, mutators and query methods. def define_attribute_methods # :nodoc: - # Use a mutex; we don't want two thread simaltaneously trying to define + # Use a mutex; we don't want two thread simultaneously trying to define # attribute methods. @attribute_methods_mutex.synchronize do return if attribute_methods_generated? diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb index 2859fb31e8..c0a2111571 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb @@ -21,7 +21,7 @@ module ActiveRecord # limit is enforced by rails and Is less than or equal to # <tt>index_name_length</tt>. The gap between # <tt>index_name_length</tt> is to allow internal rails - # opreations to use prefixes in temporary opreations. + # operations to use prefixes in temporary operations. def allowed_index_name_length index_name_length end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 73c80a3220..2b6685499a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -78,7 +78,7 @@ module ActiveRecord @joinable = options.fetch(:joinable, true) end - # This state is necesarry so that we correctly handle stuff that might + # This state is necessary so that we correctly handle stuff that might # happen in a commit/rollback. But it's kinda distasteful. Maybe we can # find a better way to structure it in the future. def finishing? diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb index 3135465dfe..15736575a2 100644 --- a/activerecord/lib/active_record/explain.rb +++ b/activerecord/lib/active_record/explain.rb @@ -9,7 +9,7 @@ module ActiveRecord yield return current[:available_queries_for_explain] ensure - # Note that the return value above does not depend on this assigment. + # Note that the return value above does not depend on this assignment. current[:available_queries_for_explain] = original end diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index bd783a94cf..f44d46d15b 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -48,7 +48,7 @@ module ActiveRecord column = reflection.foreign_key end - queries << build(table[column.to_sym], value) + queries << build(table[column], value) queries end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 1de7ee0846..3a5dea6f13 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -193,7 +193,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end end - def test_finding_with_includes_on_has_one_assocation_with_same_include_includes_only_once + def test_finding_with_includes_on_has_one_association_with_same_include_includes_only_once author = authors(:david) post = author.post_about_thinking_with_last_comment last_comment = post.last_comment @@ -467,7 +467,7 @@ class EagerAssociationTest < ActiveRecord::TestCase posts_with_comments = people(:michael).posts.merge(:includes => :comments, :order => 'posts.id').to_a posts_with_author = people(:michael).posts.merge(:includes => :author, :order => 'posts.id').to_a posts_with_comments_and_author = people(:michael).posts.merge(:includes => [ :comments, :author ], :order => 'posts.id').to_a - assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size } + assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum + post.comments.size } assert_equal authors(:david), assert_no_queries { posts_with_author.first.author } assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author } end @@ -523,7 +523,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_with_has_many_and_limit posts = Post.all.merge!(:order => 'posts.id asc', :includes => [ :author, :comments ], :limit => 2).to_a assert_equal 2, posts.size - assert_equal 3, posts.inject(0) { |sum, post| sum += post.comments.size } + assert_equal 3, posts.inject(0) { |sum, post| sum + post.comments.size } end def test_eager_with_has_many_and_limit_and_conditions diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index c503c21e27..648596b828 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -159,8 +159,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase end def test_read_attributes_before_type_cast - category = Category.new({:name=>"Test categoty", :type => nil}) - category_attrs = {"name"=>"Test categoty", "id" => nil, "type" => nil, "categorizations_count" => nil} + category = Category.new({:name=>"Test category", :type => nil}) + category_attrs = {"name"=>"Test category", "id" => nil, "type" => nil, "categorizations_count" => nil} assert_equal category_attrs , category.attributes_before_type_cast end diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index acb8b5f562..87559925e7 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -11,7 +11,7 @@ class EachTest < ActiveRecord::TestCase Post.count('id') # preheat arel's table cache end - def test_each_should_excecute_one_query_per_batch + def test_each_should_execute_one_query_per_batch assert_queries(Post.count + 1) do Post.find_each(:batch_size => 1) do |post| assert_kind_of Post, post diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb index fe105b9d22..dbf6005a67 100644 --- a/activerecord/test/cases/dup_test.rb +++ b/activerecord/test/cases/dup_test.rb @@ -110,7 +110,7 @@ module ActiveRecord def test_dup_validity_is_independent repair_validations(Topic) do Topic.validates_presence_of :title - topic = Topic.new("title" => "Litterature") + topic = Topic.new("title" => "Literature") topic.valid? duped = topic.dup diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 94837341fc..7042d7f4b6 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -167,7 +167,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase def test_first_and_array_index_zero_methods_return_the_same_value_when_nested_attributes_are_set_to_update_existing_record Man.accepts_nested_attributes_for(:interests) man = Man.create(:name => "John") - interest = man.interests.create :topic => 'gardning' + interest = man.interests.create :topic => 'gardening' man = Man.find man.id man.interests_attributes = [{:id => interest.id, :topic => 'gardening'}] assert_equal man.interests.first.topic, man.interests[0].topic diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb index 8ce44636b4..92d1e013e8 100644 --- a/activerecord/test/cases/relation/where_chain_test.rb +++ b/activerecord/test/cases/relation/where_chain_test.rb @@ -6,26 +6,31 @@ module ActiveRecord class WhereChainTest < ActiveRecord::TestCase fixtures :posts + def setup + super + @name = 'title' + end + def test_not_eq - expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'hello') + expected = Arel::Nodes::NotEqual.new(Post.arel_table[@name], 'hello') relation = Post.where.not(title: 'hello') assert_equal([expected], relation.where_values) end def test_not_null - expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], nil) + expected = Arel::Nodes::NotEqual.new(Post.arel_table[@name], nil) relation = Post.where.not(title: nil) assert_equal([expected], relation.where_values) end def test_not_in - expected = Arel::Nodes::NotIn.new(Post.arel_table[:title], %w[hello goodbye]) + expected = Arel::Nodes::NotIn.new(Post.arel_table[@name], %w[hello goodbye]) relation = Post.where.not(title: %w[hello goodbye]) assert_equal([expected], relation.where_values) end def test_association_not_eq - expected = Arel::Nodes::NotEqual.new(Comment.arel_table[:title], 'hello') + expected = Arel::Nodes::NotEqual.new(Comment.arel_table[@name], 'hello') relation = Post.joins(:comments).where.not(comments: {title: 'hello'}) assert_equal(expected.to_sql, relation.where_values.first.to_sql) end @@ -33,20 +38,20 @@ module ActiveRecord def test_not_eq_with_preceding_where relation = Post.where(title: 'hello').where.not(title: 'world') - expected = Arel::Nodes::Equality.new(Post.arel_table[:title], 'hello') + expected = Arel::Nodes::Equality.new(Post.arel_table[@name], 'hello') assert_equal(expected, relation.where_values.first) - expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'world') + expected = Arel::Nodes::NotEqual.new(Post.arel_table[@name], 'world') assert_equal(expected, relation.where_values.last) end def test_not_eq_with_succeeding_where relation = Post.where.not(title: 'hello').where(title: 'world') - expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'hello') + expected = Arel::Nodes::NotEqual.new(Post.arel_table[@name], 'hello') assert_equal(expected, relation.where_values.first) - expected = Arel::Nodes::Equality.new(Post.arel_table[:title], 'world') + expected = Arel::Nodes::Equality.new(Post.arel_table[@name], 'world') assert_equal(expected, relation.where_values.last) end @@ -65,10 +70,10 @@ module ActiveRecord def test_chaining_multiple relation = Post.where.not(author_id: [1, 2]).where.not(title: 'ruby on rails') - expected = Arel::Nodes::NotIn.new(Post.arel_table[:author_id], [1, 2]) + expected = Arel::Nodes::NotIn.new(Post.arel_table['author_id'], [1, 2]) assert_equal(expected, relation.where_values[0]) - expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'ruby on rails') + expected = Arel::Nodes::NotEqual.new(Post.arel_table[@name], 'ruby on rails') assert_equal(expected, relation.where_values[1]) end end diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index 2b4aadc7ed..239004a223 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -390,20 +390,20 @@ class DefaultScopingTest < ActiveRecord::TestCase def test_default_scope_with_inheritance wheres = InheritedPoorDeveloperCalledJamis.all.where_values_hash - assert_equal "Jamis", wheres[:name] - assert_equal 50000, wheres[:salary] + assert_equal "Jamis", wheres['name'] + assert_equal 50000, wheres['salary'] end def test_default_scope_with_module_includes wheres = ModuleIncludedPoorDeveloperCalledJamis.all.where_values_hash - assert_equal "Jamis", wheres[:name] - assert_equal 50000, wheres[:salary] + assert_equal "Jamis", wheres['name'] + assert_equal 50000, wheres['salary'] end def test_default_scope_with_multiple_calls wheres = MultiplePoorDeveloperCalledJamis.all.where_values_hash - assert_equal "Jamis", wheres[:name] - assert_equal 50000, wheres[:salary] + assert_equal "Jamis", wheres['name'] + assert_equal 50000, wheres['salary'] end def test_scope_overwrites_default @@ -563,7 +563,7 @@ class DefaultScopingTest < ActiveRecord::TestCase Developer.select("id").unscope("select") end - assert_raises(ArgumentError) do + assert_raises(ArgumentError) do Developer.select("id").unscope(5) end end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 9e6b77e2f3..b629688591 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,5 +1,10 @@ ## Rails 4.0.0 (unreleased) ## +* `ActiveSupport::TimeWithZone` raises `NoMethodError` in proper context. + Fixes #9772. + + *Yves Senn* + * Fix deletion of empty directories in `ActiveSupport::Cache::FileStore`. *Charles Jones* diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 98c866ac43..4a032b0ad0 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -366,6 +366,8 @@ module ActiveSupport # TimeWithZone with the existing +time_zone+. def method_missing(sym, *args, &block) wrap_with_time_zone time.__send__(sym, *args, &block) + rescue NoMethodError => e + raise e, e.message.sub(time.inspect, self.inspect), e.backtrace end private diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb index 4551dd2f2d..27c64c4dca 100644 --- a/activesupport/lib/active_support/xml_mini/jdom.rb +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -37,6 +37,12 @@ module ActiveSupport {} else @dbf = DocumentBuilderFactory.new_instance + # secure processing of java xml + # http://www.ibm.com/developerworks/xml/library/x-tipcfsx/index.html + @dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + @dbf.setFeature("http://xml.org/sax/features/external-general-entities", false) + @dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false) + @dbf.setFeature(javax.xml.XMLConstants::FEATURE_SECURE_PROCESSING, true) xml_string_reader = StringReader.new(data) xml_input_source = InputSource.new(xml_string_reader) doc = @dbf.new_document_builder.parse(xml_input_source) diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 0f5699fd63..98a87ab9e6 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -779,6 +779,14 @@ class TimeWithZoneTest < ActiveSupport::TestCase assert_equal "Sun, 15 Jul 2007 10:30:00 EDT -04:00", (twz - 1.year).inspect end + def test_no_method_error_has_proper_context + e = assert_raises(NoMethodError) { + @twz.this_method_does_not_exist + } + assert_equal "undefined method `this_method_does_not_exist' for Fri, 31 Dec 1999 19:00:00 EST -05:00:Time", e.message + assert_no_match "rescue", e.backtrace.first + end + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz diff --git a/activesupport/test/fixtures/xml/jdom_doctype.dtd b/activesupport/test/fixtures/xml/jdom_doctype.dtd new file mode 100644 index 0000000000..89480496ef --- /dev/null +++ b/activesupport/test/fixtures/xml/jdom_doctype.dtd @@ -0,0 +1 @@ +<!ENTITY a "external entity"> diff --git a/activesupport/test/fixtures/xml/jdom_entities.txt b/activesupport/test/fixtures/xml/jdom_entities.txt new file mode 100644 index 0000000000..0337fdaa08 --- /dev/null +++ b/activesupport/test/fixtures/xml/jdom_entities.txt @@ -0,0 +1 @@ +<!ENTITY a "hello"> diff --git a/activesupport/test/fixtures/xml/jdom_include.txt b/activesupport/test/fixtures/xml/jdom_include.txt new file mode 100644 index 0000000000..239ca3afaf --- /dev/null +++ b/activesupport/test/fixtures/xml/jdom_include.txt @@ -0,0 +1 @@ +include me diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb index f77d78d42c..4d44b72df6 100644 --- a/activesupport/test/xml_mini/jdom_engine_test.rb +++ b/activesupport/test/xml_mini/jdom_engine_test.rb @@ -3,9 +3,12 @@ if RUBY_PLATFORM =~ /java/ require 'active_support/xml_mini' require 'active_support/core_ext/hash/conversions' + class JDOMEngineTest < ActiveSupport::TestCase include ActiveSupport + FILES_DIR = File.dirname(__FILE__) + '/../fixtures/xml' + def setup @default_backend = XmlMini.backend XmlMini.backend = 'JDOM' @@ -30,10 +33,41 @@ if RUBY_PLATFORM =~ /java/ assert_equal 'image/png', file.content_type end + def test_not_allowed_to_expand_entities_to_files + attack_xml = <<-EOT + <!DOCTYPE member [ + <!ENTITY a SYSTEM "file://#{FILES_DIR}/jdom_include.txt"> + ]> + <member>x&a;</member> + EOT + assert_equal 'x', Hash.from_xml(attack_xml)["member"] + end + + def test_not_allowed_to_expand_parameter_entities_to_files + attack_xml = <<-EOT + <!DOCTYPE member [ + <!ENTITY % b SYSTEM "file://#{FILES_DIR}/jdom_entities.txt"> + %b; + ]> + <member>x&a;</member> + EOT + assert_raise Java::OrgXmlSax::SAXParseException do + assert_equal 'x', Hash.from_xml(attack_xml)["member"] + end + end + + + def test_not_allowed_to_load_external_doctypes + attack_xml = <<-EOT + <!DOCTYPE member SYSTEM "file://#{FILES_DIR}/jdom_doctype.dtd"> + <member>x&a;</member> + EOT + assert_equal 'x', Hash.from_xml(attack_xml)["member"] + end + def test_exception_thrown_on_expansion_attack - assert_raise NativeException do + assert_raise Java::OrgXmlSax::SAXParseException do attack_xml = <<-EOT - <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE member [ <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;"> <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;"> diff --git a/guides/source/initialization.md b/guides/source/initialization.md index 8ba5fa4601..412f2faaaa 100644 --- a/guides/source/initialization.md +++ b/guides/source/initialization.md @@ -266,7 +266,7 @@ def start url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}" - puts "=> Call with -d to detach" unless options[:daemonize] + puts "=> Run `rails server -h` for more startup options" trap(:INT) { exit } puts "=> Ctrl-C to shutdown server" unless options[:daemonize] diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index ddf45d196a..e3119ecf22 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -42,6 +42,7 @@ module Rails set_environment end + # TODO: this is no longer required but we keep it for the moment to support older config.ru files. def app @app ||= begin app = super @@ -61,7 +62,7 @@ module Rails url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}" - puts "=> Call with -d to detach" unless options[:daemonize] + puts "=> Run `rails server -h` for more startup options" trap(:INT) { exit } puts "=> Ctrl-C to shutdown server" unless options[:daemonize] diff --git a/railties/lib/rails/generators/rails/app/templates/config.ru b/railties/lib/rails/generators/rails/app/templates/config.ru index fcfbc6b07a..5bc2a619e8 100644 --- a/railties/lib/rails/generators/rails/app/templates/config.ru +++ b/railties/lib/rails/generators/rails/app/templates/config.ru @@ -1,4 +1,4 @@ # This file is used by Rack-based servers to start the application. require ::File.expand_path('../config/environment', __FILE__) -run <%= app_const %> +run Rails.application |