diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Gemfile | 7 | ||||
-rw-r--r-- | RELEASING_RAILS.rdoc | 31 | ||||
-rw-r--r-- | actionpack/CHANGELOG.md | 14 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/translation_helper.rb | 13 | ||||
-rw-r--r-- | actionpack/test/template/translation_helper_test.rb | 6 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb | 29 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb | 18 | ||||
-rw-r--r-- | activerecord/lib/active_record/session_store.rb | 7 | ||||
-rw-r--r-- | activerecord/test/cases/migration_test.rb | 174 | ||||
-rw-r--r-- | activesupport/lib/active_support/dependencies.rb | 2 | ||||
-rw-r--r-- | railties/guides/source/migrations.textile | 2 |
12 files changed, 85 insertions, 219 deletions
diff --git a/.gitignore b/.gitignore index aa14cee911..52a431867c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # Check out http://help.github.com/ignore-files/ for how to set that up. debug.log +.Gemfile /.bundle /.rbenv-version /.rvmrc @@ -36,13 +36,11 @@ gem "memcache-client", ">= 1.8.5" platforms :mri_18 do gem "system_timer" - gem "ruby-debug", ">= 0.10.3" unless ENV['TRAVIS'] gem "json" end -platforms :mri_19 do - gem "ruby-debug19", :require => "ruby-debug" unless ENV['TRAVIS'] || RUBY_VERSION >= "2.0" -end +# Add your own local bundler stuff +instance_eval File.read ".Gemfile" if File.exists? ".Gemfile" platforms :mri do group :test do @@ -69,7 +67,6 @@ platforms :ruby do end platforms :jruby do - gem "ruby-debug", ">= 0.10.3" gem "json" gem "activerecord-jdbcsqlite3-adapter", ">= 1.2.0" diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc index 1dfcfe2488..3f05e97b91 100644 --- a/RELEASING_RAILS.rdoc +++ b/RELEASING_RAILS.rdoc @@ -205,3 +205,34 @@ There are two simple steps for fixing the CI: 2. Fix it Repeat these steps until the CI is green. + +=== Manually trigger docs generation + +We have a post-receive hook in GitHub that calls the docs server on pushes. +Triggers generation and publication of edge docs, updates the contrib app, +and generates and publishes stable docs if a new stable tag is detected. + +The hook unfortunately is not invoked by tag pushing, so once the new stable +tag has been pushed to origin, please run + + curl -X POST -d '' http://rails-hooks.hashref.com/rails-master-hook + +You should see something like this: + + Rails master hook tasks scheduled: + + * updates the local checkout + * updates Rails Contributors + * generates and publishes edge docs + + If a new stable tag is detected it also + + * generates and publishes stable docs + + This needs typically a few minutes. + +Note you do not need to specify the tag, the docs server figures it out. + +Also, don't worry if you call that multiple times or the hook is triggered +again by some immediate regular push, if the scripts are running new calls +are just queued (in a queue of size 1). diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 4c265c41d8..9d847c763b 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -64,6 +64,20 @@ ## Rails 3.1.2 (unreleased) ## +* Fix XSS security vulnerability in the `translate` helper method. When using interpolation + in combination with HTML-safe translations, the interpolated input would not get HTML + escaped. *GH 3664* + + Before: + + translate('foo_html', :something => '<script>') # => "...<script>..." + + After: + + translate('foo_html', :something => '<script>') # => "...<script>..." + + *Sergey Nartimov* + * Upgrade sprockets dependency to ~> 2.1.0 * Ensure that the format isn't applied twice to the cache key, else it becomes impossible diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index be64dc823e..42726f888c 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -45,11 +45,16 @@ module ActionView # you know what kind of output to expect when you call translate in a template. def translate(key, options = {}) options.merge!(:rescue_format => :html) unless options.key?(:rescue_format) - translation = I18n.translate(scope_key_by_partial(key), options) - if html_safe_translation_key?(key) && translation.respond_to?(:html_safe) - translation.html_safe + if html_safe_translation_key?(key) + html_safe_options = options.dup + options.except(*I18n::RESERVED_KEYS).each do |name, value| + html_safe_options[name] = ERB::Util.html_escape(value.to_s) + end + translation = I18n.translate(scope_key_by_partial(key), html_safe_options) + + translation.respond_to?(:html_safe) ? translation.html_safe : translation else - translation + I18n.translate(scope_key_by_partial(key), options) end end alias :t :translate diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index cd9f54e04c..cabb29cfad 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -17,6 +17,7 @@ class TranslationHelperTest < ActiveSupport::TestCase :hello => '<a>Hello World</a>', :html => '<a>Hello World</a>', :hello_html => '<a>Hello World</a>', + :interpolated_html => '<a>Hello %{word}</a>', :array_html => %w(foo bar), :array => %w(foo bar) } @@ -83,6 +84,11 @@ class TranslationHelperTest < ActiveSupport::TestCase assert translate(:'translations.hello_html').html_safe? end + def test_translate_escapes_interpolations_in_translations_with_a_html_suffix + assert_equal '<a>Hello <World></a>', translate(:'translations.interpolated_html', :word => '<World>') + assert_equal '<a>Hello <World></a>', translate(:'translations.interpolated_html', :word => stub(:to_s => "<World>")) + end + def test_translation_returning_an_array_ignores_html_suffix assert_equal ["foo", "bar"], translate(:'translations.array_html') end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 7742f32213..faa42e2d19 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -155,17 +155,11 @@ module ActiveRecord # ) # # See also TableDefinition#column for details on how to create columns. - def create_table(table_name, options = {}, &blk) + def create_table(table_name, options = {}) td = table_definition td.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false - if block_given? - if blk.arity == 1 - yield td - else - td.instance_eval(&blk) - end - end + yield td if block_given? if options[:force] && table_exists?(table_name) drop_table(table_name) @@ -242,19 +236,14 @@ module ActiveRecord # # See also Table for details on # all of the various column transformation - def change_table(table_name, options = {}, &blk) - bulk_change = supports_bulk_alter? && options[:bulk] - recorder = bulk_change ? ActiveRecord::Migration::CommandRecorder.new(self) : self - table = Table.new(table_name, recorder) - - if block_given? - if blk.arity == 1 - yield table - else - table.instance_eval(&blk) - end + def change_table(table_name, options = {}) + if supports_bulk_alter? && options[:bulk] + recorder = ActiveRecord::Migration::CommandRecorder.new(self) + yield Table.new(table_name, recorder) + bulk_change_table(table_name, recorder.commands) + else + yield Table.new(table_name, self) end - bulk_change_table(table_name, recorder.commands) if bulk_change end # Renames a table. diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 35df0a1542..bc3804b3d9 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -480,30 +480,28 @@ module ActiveRecord drop_table(from) end - def copy_table(from, to, options = {}, &block) #:nodoc: - from_columns, from_primary_key = columns(from), primary_key(from) - options = options.merge(:id => (!from_columns.detect {|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s)) - table_definition = nil + def copy_table(from, to, options = {}) #:nodoc: + options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s)) create_table(to, options) do |definition| - table_definition = definition - from_columns.each do |column| + @definition = definition + columns(from).each do |column| column_name = options[:rename] ? (options[:rename][column.name] || options[:rename][column.name.to_sym] || column.name) : column.name - table_definition.column(column_name, column.type, + @definition.column(column_name, column.type, :limit => column.limit, :default => column.default, :precision => column.precision, :scale => column.scale, :null => column.null) end - table_definition.primary_key from_primary_key if from_primary_key - table_definition.instance_eval(&block) if block + @definition.primary_key(primary_key(from)) if primary_key(from) + yield @definition if block_given? end copy_table_indexes(from, to, options[:rename] || {}) copy_table_contents(from, to, - table_definition.columns.map {|column| column.name}, + @definition.columns.map {|column| column.name}, options[:rename] || {}) end diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 92550c7efc..76c37cc367 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -64,13 +64,12 @@ module ActiveRecord end def create_table! - id_col_name, data_col_name = session_id_column, data_column_name connection_pool.clear_table_cache!(table_name) connection.create_table(table_name) do |t| - t.string id_col_name, :limit => 255 - t.text data_col_name + t.string session_id_column, :limit => 255 + t.text data_column_name end - connection.add_index table_name, id_col_name, :unique => true + connection.add_index table_name, session_id_column, :unique => true end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 75eb9c2bce..3e219f2a49 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1639,180 +1639,6 @@ if ActiveRecord::Base.connection.supports_migrations? end - class SexyMigrationsTest < ActiveRecord::TestCase - def test_references_column_type_adds_id - with_new_table do |t| - t.expects(:column).with('customer_id', :integer, {}) - t.references :customer - end - end - - def test_references_column_type_with_polymorphic_adds_type - with_new_table do |t| - t.expects(:column).with('taggable_type', :string, {}) - t.expects(:column).with('taggable_id', :integer, {}) - t.references :taggable, :polymorphic => true - end - end - - def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag - with_new_table do |t| - t.expects(:column).with('taggable_type', :string, {:null => false}) - t.expects(:column).with('taggable_id', :integer, {:null => false}) - t.references :taggable, :polymorphic => true, :null => false - end - end - - def test_belongs_to_works_like_references - with_new_table do |t| - t.expects(:column).with('customer_id', :integer, {}) - t.belongs_to :customer - end - end - - def test_timestamps_creates_updated_at_and_created_at - with_new_table do |t| - t.expects(:column).with(:created_at, :datetime, kind_of(Hash)) - t.expects(:column).with(:updated_at, :datetime, kind_of(Hash)) - t.timestamps - end - end - - def test_integer_creates_integer_column - with_new_table do |t| - t.expects(:column).with(:foo, 'integer', {}) - t.expects(:column).with(:bar, 'integer', {}) - t.integer :foo, :bar - end - end - - def test_string_creates_string_column - with_new_table do |t| - t.expects(:column).with(:foo, 'string', {}) - t.expects(:column).with(:bar, 'string', {}) - t.string :foo, :bar - end - end - - if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLite3Adapter) || current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter) - def test_xml_creates_xml_column - type = current_adapter?(:PostgreSQLAdapter) ? 'xml' : :text - - with_new_table do |t| - t.expects(:column).with(:data, type, {}) - t.xml :data - end - end - else - def test_xml_creates_xml_column - with_new_table do |t| - assert_raises(NotImplementedError) do - t.xml :data - end - end - end - end - - protected - def with_new_table - Person.connection.create_table :delete_me, :force => true do |t| - yield t - end - ensure - Person.connection.drop_table :delete_me rescue nil - end - - end # SexyMigrationsTest - - class SexierMigrationsTest < ActiveRecord::TestCase - def test_create_table_with_column_without_block_parameter - Person.connection.create_table :testings, :force => true do - column :foo, :string - end - assert Person.connection.column_exists?(:testings, :foo, :string) - ensure - Person.connection.drop_table :testings rescue nil - end - - def test_create_table_with_sexy_column_without_block_parameter - Person.connection.create_table :testings, :force => true do - integer :bar - end - assert Person.connection.column_exists?(:testings, :bar, :integer) - ensure - Person.connection.drop_table :testings rescue nil - end - - def test_create_table_should_not_have_mixed_syntax - assert_raise(NoMethodError) do - Person.connection.create_table :testings, :force => true do |t| - t.string :foo - integer :bar - end - end - assert_raise(NameError) do - Person.connection.create_table :testings, :force => true do - t.string :foo - integer :bar - end - end - end - - def test_change_table_without_block_parameter_no_bulk - Person.connection.create_table :testings, :force => true do - string :foo - end - assert Person.connection.column_exists?(:testings, :foo, :string) - - Person.connection.change_table :testings do - remove :foo - integer :bar - end - - assert_equal %w(bar id), Person.connection.columns(:testings).map { |c| c.name }.sort - ensure - Person.connection.drop_table :testings rescue nil - end - - if ActiveRecord::Base.connection.supports_bulk_alter? - def test_change_table_without_block_parameter_with_bulk - Person.connection.create_table :testings, :force => true do - string :foo - end - assert Person.connection.column_exists?(:testings, :foo, :string) - - assert_queries(1) do - Person.connection.change_table(:testings, :bulk => true) do - integer :bar - string :foo_bar - end - end - - assert_equal %w(bar foo foo_bar id), Person.connection.columns(:testings).map { |c| c.name }.sort - ensure - Person.connection.drop_table :testings rescue nil - end - end - - def test_change_table_should_not_have_mixed_syntax - Person.connection.create_table :testings, :force => true do - string :foo - end - assert_raise(NoMethodError) do - Person.connection.change_table :testings do |t| - t.remove :foo - integer :bar - end - end - assert_raise(NameError) do - Person.connection.change_table :testings do - t.remove :foo - integer :bar - end - end - end - end # SexierMigrationsTest - class MigrationLoggerTest < ActiveRecord::TestCase def test_migration_should_be_run_without_logger previous_logger = ActiveRecord::Base.logger diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index b3ac271778..db90de4682 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -428,7 +428,7 @@ module ActiveSupport #:nodoc: end # Attempt to autoload the provided module name by searching for a directory - # matching the expect path suffix. If found, the module is created and assigned + # matching the expected path suffix. If found, the module is created and assigned # to +into+'s constants with the name +const_name+. Provided that the directory # was loaded from a reloadable base path, it is added to the set of constants # that are to be unloaded. diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile index 23e36b39f9..5b52a93853 100644 --- a/railties/guides/source/migrations.textile +++ b/railties/guides/source/migrations.textile @@ -658,7 +658,7 @@ In many ways this is exactly what it is. This file is created by inspecting the There is however a trade-off: +db/schema.rb+ cannot express database specific items such as foreign key constraints, triggers, or stored procedures. While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this, then you should set the schema format to +:sql+. -Instead of using Active Record's schema dumper, the database's structure will be dumped using a tool specific to the database (via the +db:structure:dump+ Rake task) into +db/#{Rails.env}_structure.sql+. For example, for the PostgreSQL RDBMS, the +pg_dump+ utility is used. For MySQL, this file will contain the output of +SHOW CREATE TABLE+ for the various tables. Loading these schemas is simply a question of executing the SQL statements they contain. By definition, this will create a perfect copy of the database's structure. Using the +:sql+ schema format will, however, prevent loading the schema into a RDBMS other than the one used to create it. +Instead of using Active Record's schema dumper, the database's structure will be dumped using a tool specific to the database (via the +db:structure:dump+ Rake task) into +db/structure.sql+. For example, for the PostgreSQL RDBMS, the +pg_dump+ utility is used. For MySQL, this file will contain the output of +SHOW CREATE TABLE+ for the various tables. Loading these schemas is simply a question of executing the SQL statements they contain. By definition, this will create a perfect copy of the database's structure. Using the +:sql+ schema format will, however, prevent loading the schema into a RDBMS other than the one used to create it. h4. Schema Dumps and Source Control |