diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 2fcd6359a65d1..250677233754c 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,7 @@ +* Add brakeman gem by default for static analysis of security vulnerabilities. Allow skipping with --skip-brakeman option. + + *vipulnsward* + * Add RuboCop with rules from rubocop-rails-omakase by default. Skip with --skip-rubocop. *DHH* and *zzak* diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index e4e14406d6fe1..ef2ca10aacbed 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -103,6 +103,9 @@ def self.add_shared_options_for(name) class_option :skip_rubocop, type: :boolean, default: nil, desc: "Skip RuboCop setup" + class_option :skip_brakeman, type: :boolean, default: nil, + desc: "Skip brakeman setup" + class_option :dev, type: :boolean, default: nil, desc: "Set up the #{name} with Gemfile pointing to your Rails checkout" @@ -386,6 +389,9 @@ def skip_rubocop? options[:skip_rubocop] end + def skip_brakeman? + options[:skip_brakeman] + end class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out) def initialize(name, version, comment, options = {}, commented_out = false) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 977a7e8d5db0a..4f1428dadec46 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -102,8 +102,8 @@ def app end def bin - options = skip_rubocop? ? { exclude_pattern: /rubocop/ } : {} - directory "bin", **options do |content| + exclude_pattern = Regexp.union([(/rubocop/ if skip_rubocop?), (/brakeman/ if skip_brakeman?)].compact) + directory "bin", { exclude_pattern: exclude_pattern } do |content| "#{shebang}\n" + content end chmod "bin", 0755 & ~File.umask, verbose: false diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt index fe5bc553c5519..f3e195d7bea9b 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt @@ -40,6 +40,11 @@ end <% end -%> group :development do +<%- unless options.skip_brakeman? -%> + # Static analysis for security vulnerabilities [https://brakemanscanner.org/] + gem "brakeman", require: false + +<%- end -%> <%- unless options.skip_rubocop? -%> # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/] gem "rubocop-rails-omakase", require: false diff --git a/railties/lib/rails/generators/rails/app/templates/bin/brakeman.tt b/railties/lib/rails/generators/rails/app/templates/bin/brakeman.tt new file mode 100644 index 0000000000000..a993625e04e15 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/bin/brakeman.tt @@ -0,0 +1,4 @@ +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("brakeman", "brakeman") diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index ebaeccd7aa75a..5967611b5095f 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -38,6 +38,7 @@ app/views/layouts/mailer.html.erb app/views/layouts/mailer.text.erb bin/docker-entrypoint + bin/brakeman bin/rails bin/rake bin/rubocop @@ -638,6 +639,27 @@ def test_rubocop_is_skipped_if_required assert_no_file ".rubocop.yml" end + def test_inclusion_of_brakeman + run_generator + assert_gem "brakeman" + end + + def test_brakeman_is_skipped_if_required + puts destination_root + run_generator [destination_root, "--skip-brakeman"] + + assert_no_gem "brakeman" + assert_no_file "bin/brakeman" + end + + def test_both_brakeman_and_rubocop_binstubs_are_skipped_if_required + puts destination_root + run_generator [destination_root, "--skip-brakeman", "--skip-rubocop"] + + assert_no_file "bin/rubocop" + assert_no_file "bin/brakeman" + end + def test_usage_read_from_file assert_called(File, :read, returns: "USAGE FROM FILE") do assert_equal "USAGE FROM FILE", Rails::Generators::AppGenerator.desc