diff --git a/NEWS.md b/NEWS.md index 44770f5bd..dd1538f17 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,7 @@ Unreleased * Remove `suspenders` system executable * Introduce `suspenders:accessibility` generator * Introduce `Suspenders::Generators::APIAppUnsupported` module and concern +* Introduce `suspenders:advisories` generator 20230113.0 (January, 13, 2023) diff --git a/README.md b/README.md index 27d95fa2d..2ff3a7f0a 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,18 @@ Installs [capybara_accessibility_audit] and [capybara_accessible_selectors] [capybara_accessibility_audit]: https://github.com/thoughtbot/capybara_accessibility_audit [capybara_accessible_selectors]: https://github.com/citizensadvice/capybara_accessible_selectors +### Advisories + +Show security advisories during development. + +Uses the [bundler-audit][] gem and rake task to update the local security +database and show any relevant issues with the app's dependencies. This happens +on every test run and interaction with `bin/rake` and `bin/rails`. + +`./bin/rails g suspenders:advisories` + + [bundler-audit]: https://github.com/rubysec/bundler-audit + ## Contributing See the [CONTRIBUTING] document. diff --git a/lib/generators/suspenders/advisories_generator.rb b/lib/generators/suspenders/advisories_generator.rb new file mode 100644 index 000000000..b269784fe --- /dev/null +++ b/lib/generators/suspenders/advisories_generator.rb @@ -0,0 +1,26 @@ +module Suspenders + module Generators + class AdvisoriesGenerator < Rails::Generators::Base + source_root File.expand_path("../../templates/advisories", __FILE__) + desc(<<~TEXT) + Show security advisories during development. + + Uses the `bundler-audit` gem and rake task to update the local security + database and show any relevant issues with the app's dependencies. This happens + on every test run and interaction with `bin/rake` and `bin/rails`. + TEXT + + def add_bundler_audit + gem_group :development, :test do + gem "bundler-audit", ">= 0.7.0", require: false + end + Bundler.with_unbundled_env { run "bundle install" } + end + + def configurea_rake_task + copy_file "bundler_audit.rake", "lib/tasks/bundler_audit.rake" + append_file "Rakefile", %(\ntask default: "bundle:audit"\n) + end + end + end +end diff --git a/lib/generators/templates/advisories/bundler_audit.rake b/lib/generators/templates/advisories/bundler_audit.rake new file mode 100644 index 000000000..72f6b5120 --- /dev/null +++ b/lib/generators/templates/advisories/bundler_audit.rake @@ -0,0 +1,4 @@ +if Rails.env.development? || Rails.env.test? + require "bundler/audit/task" + Bundler::Audit::Task.new +end diff --git a/test/generators/suspenders/advisories_generator_test.rb b/test/generators/suspenders/advisories_generator_test.rb new file mode 100644 index 000000000..2a6f04587 --- /dev/null +++ b/test/generators/suspenders/advisories_generator_test.rb @@ -0,0 +1,87 @@ +require "test_helper" +require "generators/suspenders/advisories_generator" + +module Suspenders + module Generators + class AdvisoriesGeneratorTest < Rails::Generators::TestCase + include Suspenders::TestHelpers + + tests Suspenders::Generators::AdvisoriesGenerator + destination Rails.root + setup :prepare_destination + teardown :restore_destination + + test "adds gems to Gemfile" do + expected_output = <<~RUBY + group :development, :test do + gem "bundler-audit", ">= 0.7.0", require: false + end + RUBY + + run_generator + + assert_file app_root("Gemfile") do |file| + assert_match(expected_output, file) + end + end + + test "installs gems with Bundler" do + Bundler.stubs(:with_unbundled_env).yields + generator.expects(:run).with("bundle install").once + + capture(:stdout) do + generator.add_bundler_audit + end + end + + test "generator has a description" do + description = <<~TEXT + Show security advisories during development. + + Uses the `bundler-audit` gem and rake task to update the local security + database and show any relevant issues with the app's dependencies. This happens + on every test run and interaction with `bin/rake` and `bin/rails`. + TEXT + + assert_equal description, Suspenders::Generators::AdvisoriesGenerator.desc + end + + test "creates custom Rake task" do + expected_rake_task = <<~RUBY + if Rails.env.development? || Rails.env.test? + require "bundler/audit/task" + Bundler::Audit::Task.new + end + RUBY + + run_generator + + assert_file app_root("lib/tasks/bundler_audit.rake") do |file| + assert_match expected_rake_task, file + end + end + + test "modidies existing Rakefile" do + run_generator + + assert_file app_root("Rakefile") do |file| + assert_match(/task default: "bundle:audit"/, file) + :w + end + end + + private + + def prepare_destination + touch "Gemfile" + backup_file "Rakefile" + end + + def restore_destination + remove_file_if_exists "Gemfile" + remove_file_if_exists "lib/tasks/bundler_audit.rake" + restore_file "Rakefile" + end + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 060d7dc22..3fb50d589 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -68,8 +68,6 @@ class Application < Rails::Application restore_file "config/application.rb" end - private - def backup_file(file) FileUtils.mv app_root(file), app_root("#{file}.bak") touch file