From 8a5f5b7238f7390f3f8a1adf7091ab425f8e1069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20B=C3=A4lter?= Date: Wed, 23 Oct 2024 16:29:36 +0200 Subject: [PATCH] Recreate project using rubocop-extension-generator (#10) * Recreate project using rubocop-extension-generator Also adds `EnsureRedirect` * Bump checkout version Co-authored-by: Robin Wallin * Fix version nr Co-authored-by: Robin Wallin * Bundle --------- Co-authored-by: Robin Wallin --- .github/workflows/main.yml | 27 ++++ .gitignore | 12 ++ .rspec | 2 + .rubocop.yml | 31 +---- CHANGELOG.md | 5 + CONTRIBUTING.md | 3 - Gemfile | 11 +- Gemfile.lock | 83 ++++++------ LICENSE.md | 12 +- README.md | 74 ++--------- Rakefile | 48 +++---- bin/console | 15 +++ bin/setup | 8 ++ config/default.yml | 15 ++- lib/rubocop-eightyfourcodes.rb | 21 +--- .../command_literal_injection.rb | 6 +- lib/rubocop/cop/eighty_four_codes/cop.rb | 70 ----------- .../cop/eighty_four_codes/ensure_redirect.rb | 48 +++++++ .../eighty_four_codes/ruby_version_file.rb | 64 +++++----- lib/rubocop/cop/eightyfourcodes_cops.rb | 5 + lib/rubocop/eighty_four_codes/concept.rb | 34 ----- .../eighty_four_codes/config_formatter.rb | 33 ----- .../description_extractor.rb | 72 ----------- lib/rubocop/eighty_four_codes/example.rb | 32 ----- .../eighty_four_codes/example_group.rb | 95 -------------- lib/rubocop/eighty_four_codes/hook.rb | 49 -------- lib/rubocop/eighty_four_codes/language.rb | 118 ------------------ .../language/node_pattern.rb | 20 --- .../eighty_four_codes/top_level_describe.rb | 57 --------- lib/rubocop/eighty_four_codes/util.rb | 19 --- lib/rubocop/eighty_four_codes/version.rb | 10 -- lib/rubocop/eighty_four_codes/wording.rb | 81 ------------ ...ighty_four_codes.rb => eightyfourcodes.rb} | 7 +- .../inject.rb | 6 +- lib/rubocop/eightyfourcodes/version.rb | 7 ++ rubocop-eightyfourcodes.gemspec | 41 +++--- sig/rubocop/eightyfourcodes.rbs | 6 + .../command_literal_injection_spec.rb | 54 ++++---- .../eighty_four_codes/ensure_redirect_spec.rb | 70 +++++++++++ .../ruby_version_file_spec.rb | 16 ++- spec/rubocop/eighty_four_codes_spec.rb | 9 ++ spec/spec_helper.rb | 25 ++-- 42 files changed, 430 insertions(+), 991 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 CONTRIBUTING.md create mode 100755 bin/console create mode 100755 bin/setup delete mode 100644 lib/rubocop/cop/eighty_four_codes/cop.rb create mode 100644 lib/rubocop/cop/eighty_four_codes/ensure_redirect.rb create mode 100644 lib/rubocop/cop/eightyfourcodes_cops.rb delete mode 100644 lib/rubocop/eighty_four_codes/concept.rb delete mode 100644 lib/rubocop/eighty_four_codes/config_formatter.rb delete mode 100644 lib/rubocop/eighty_four_codes/description_extractor.rb delete mode 100644 lib/rubocop/eighty_four_codes/example.rb delete mode 100644 lib/rubocop/eighty_four_codes/example_group.rb delete mode 100644 lib/rubocop/eighty_four_codes/hook.rb delete mode 100644 lib/rubocop/eighty_four_codes/language.rb delete mode 100644 lib/rubocop/eighty_four_codes/language/node_pattern.rb delete mode 100644 lib/rubocop/eighty_four_codes/top_level_describe.rb delete mode 100644 lib/rubocop/eighty_four_codes/util.rb delete mode 100644 lib/rubocop/eighty_four_codes/version.rb delete mode 100644 lib/rubocop/eighty_four_codes/wording.rb rename lib/rubocop/{eighty_four_codes.rb => eightyfourcodes.rb} (67%) rename lib/rubocop/{eighty_four_codes => eightyfourcodes}/inject.rb (64%) create mode 100644 lib/rubocop/eightyfourcodes/version.rb create mode 100644 sig/rubocop/eightyfourcodes.rbs create mode 100644 spec/rubocop/cop/eighty_four_codes/ensure_redirect_spec.rb create mode 100644 spec/rubocop/eighty_four_codes_spec.rb diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..b131488 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,27 @@ +name: Ruby + +on: + push: + branches: + - main + + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + name: Ruby ${{ matrix.ruby }} + strategy: + matrix: + ruby: + - '3.3.5' + + steps: + - uses: actions/checkout@v4 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run the default task + run: bundle exec rake diff --git a/.gitignore b/.gitignore index e69de29..1cbe6b0 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,12 @@ +/.bundle/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ +.vscode + +# rspec failure tracking +.rspec_status diff --git a/.rspec b/.rspec index c99d2e7..34c5164 100644 --- a/.rspec +++ b/.rspec @@ -1 +1,3 @@ +--format documentation +--color --require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml index 96b7e31..9e1bb0c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,30 +1,9 @@ require: - rubocop-rspec + - rubocop-rake -AllCops: - TargetRubyVersion: 2.3 - DisplayCopNames: true - DisplayStyleGuide: false - -Metrics/BlockLength: - Enabled: false - -Metrics/LineLength: - Enabled: false - -RSpec/ExampleLength: - Enabled: false - -RSpec/FilePath: - Enabled: false - -Style/FileName: +Naming/FileName: Exclude: - - "lib/rubocop-eightyfourcodes.rb" - - "lib/rubocop/eightyfourcodes.rb" - -Style/FormatStringToken: - Enabled: false - -Style/FrozenStringLiteralComment: - Enabled: false + - lib/rubocop-eightyfourcodes.rb +RSpec/ExampleLength: + Max: 10 diff --git a/CHANGELOG.md b/CHANGELOG.md index e661d59..f052491 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.0.3 (2024-10-23) + +- Recreated entire project using +- Added `EnsureRedirect` + ## 0.0.2 (2020-09-24) - Added `RubyVersionFile`: Ensure we read Gemfile ruby version from `.ruby-version` file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index f0cc9a8..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,3 +0,0 @@ -# Contributing - - diff --git a/Gemfile b/Gemfile index 8ec6163..5332a42 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,14 @@ +# frozen_string_literal: true + source 'https://rubygems.org' +# Specify your gem's dependencies in rubocop-eightyfourcodes.gemspec gemspec group :development, :test do - gem 'pry' - gem 'rspec', '~> 3.6.0' - gem 'rubocop-rspec', '~> 1.21.0' + gem 'rake' + gem 'rspec' + gem 'rubocop' + gem 'rubocop-rake' + gem 'rubocop-rspec' end diff --git a/Gemfile.lock b/Gemfile.lock index 3524053..0ff2b98 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,59 +1,66 @@ PATH remote: . specs: - rubocop-eightyfourcodes (0.0.2) - rubocop (>= 0.51) + rubocop-eightyfourcodes (0.0.3) + rubocop GEM remote: https://rubygems.org/ specs: - ast (2.4.0) - coderay (1.1.2) - diff-lcs (1.3) - jaro_winkler (1.5.3) - method_source (0.9.2) - parallel (1.17.0) - parser (2.6.4.0) - ast (~> 2.4.0) - pry (0.12.2) - coderay (~> 1.1.0) - method_source (~> 0.9.0) - rainbow (3.0.0) - rake (12.3.3) - rspec (3.6.0) - rspec-core (~> 3.6.0) - rspec-expectations (~> 3.6.0) - rspec-mocks (~> 3.6.0) - rspec-core (3.6.0) - rspec-support (~> 3.6.0) - rspec-expectations (3.6.0) + ast (2.4.2) + diff-lcs (1.5.1) + json (2.7.2) + language_server-protocol (3.17.0.3) + parallel (1.26.3) + parser (3.3.5.0) + ast (~> 2.4.1) + racc + racc (1.8.1) + rainbow (3.1.1) + rake (13.2.1) + regexp_parser (2.9.2) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.6.0) - rspec-mocks (3.6.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.6.0) - rspec-support (3.6.0) - rubocop (0.74.0) - jaro_winkler (~> 1.5.1) + rspec-support (~> 3.13.0) + rspec-support (3.13.1) + rubocop (1.67.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 2.6) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.4, < 3.0) + rubocop-ast (>= 1.32.2, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 1.7) - rubocop-rspec (1.21.0) - rubocop (>= 0.52.0) - ruby-progressbar (1.10.1) - unicode-display_width (1.6.0) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.32.3) + parser (>= 3.3.1.0) + rubocop-rake (0.6.0) + rubocop (~> 1.0) + rubocop-rspec (3.1.0) + rubocop (~> 1.61) + ruby-progressbar (1.13.0) + unicode-display_width (2.6.0) PLATFORMS ruby DEPENDENCIES - pry rake - rspec (~> 3.6.0) + rspec + rubocop rubocop-eightyfourcodes! - rubocop-rspec (~> 1.21.0) + rubocop-rake + rubocop-rspec BUNDLED WITH - 2.1.4 + 2.5.22 diff --git a/LICENSE.md b/LICENSE.md index 15e49a7..d418c80 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,6 @@ -Copyright (c) 2019 eightyfourcodes AB +The MIT License (MIT) + +Copyright (c) 2024 84codes AB Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -7,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 36e7da9..fd462a8 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,16 @@ +# Rubocop::EightyFourCodes + This is a collection of cops developed and used by 84codes AB -This code is based heavily upon the [rubocop-gitlab-security](https://gitlab.com/gitlab-org/rubocop-gitlab-security) -code released under the MIT License. ## Installation -Just install the `rubocop-eightyfourcodes` gem +Install the gem and add to the application's Gemfile by executing: -```bash -gem install rubocop-eightyfourcodes -``` + bundle add rubocop-eightyfourcodes --require=false -or if you use bundler put this in your `Gemfile` +If bundler is not being used to manage dependencies, install the gem by executing: -```yaml -gem 'rubocop-eightyfourcodes' -``` + gem install rubocop-eightyfourcodes ## Usage @@ -32,64 +28,20 @@ require: rubocop-eightyfourcodes Now you can run `rubocop` and it will automatically load the RuboCop eightyfourcodes cops together with the standard cops. -### Command line - -```bash -rubocop --require rubocop-eightyfourcodes -``` - -### Rake task +## Development -```ruby -RuboCop::RakeTask.new do |task| - task.requires << 'rubocop-eightyfourcodes' -end -``` +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. -## Inspecting specific files +Use `bundle exec rake 'new_cop[EightyFourCodes/CommandLiteralInjection]'` to generate a new cop. -By default, `rubocop-eightyfourcodes` inspects all files. You can override this setting in your config file by specifying one or more patterns: +The [NodePattern Debugger](https://nodepattern.herokuapp.com/) is a very helpful resource when creating new AST matchers. -```yaml -# Inspect all files -AllCops: - EightyFourCodes: - Patterns: - - '.+' -``` - -```yaml -# Inspect only controller files. -AllCops: - EightyFourCodes: - Patterns: - - app/controllers/**/*.rb -``` - -## The Cops - -All cops are located under -[`lib/rubocop/cop/eighty_four_codes`](lib/rubocop/cop/eighty_four_codes), and contain -examples/documentation. - -In your `.rubocop.yml`, you may treat the eightyfourcodes cops just like any other -cop. For example: - -```yaml -EightyFourCodes/CommandLiteralInjection: - Exclude: - - 'spec/**/*' -``` +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing -1. Fork it -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Add some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create new Merge Request +Bug reports and pull requests are welcome on GitHub at . ## License -`rubocop-eightyfourcodes` is MIT licensed. [See the accompanying file](LICENSE.md) for -the full text. +The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). diff --git a/Rakefile b/Rakefile index 5b9424e..ec416e1 100644 --- a/Rakefile +++ b/Rakefile @@ -1,50 +1,32 @@ -require 'rake' -require 'rubocop' -require 'rubocop-eightyfourcodes' -require 'rubocop/rake_task' +# frozen_string_literal: true -module RuboCop - module Cop - class Generator - def bump_minor_version - versions = RuboCop::EightyFourCodes::Version::STRING.split('.') +require 'bundler/gem_tasks' +require 'rspec/core/rake_task' +require 'rubocop/rake_task' - "#{versions[0]}.#{versions[1].succ}.0" - end +RuboCop::RakeTask.new - class ConfigurationInjector - alias old_find_target_line find_target_line +task default: %i[spec rubocop] - def find_target_line - old_find_target_line + 1 - end - end - end - end +RSpec::Core::RakeTask.new(:spec) do |spec| + spec.pattern = FileList['spec/**/*_spec.rb'] end -desc 'Generate a new cop template' +desc 'Generate a new cop with a template' task :new_cop, [:cop] do |_task, args| + require 'rubocop' + cop_name = args.fetch(:cop) do - warn 'usage: bundle exec rake new_cop[Name]' + warn 'usage: bundle exec rake new_cop[Department/Name]' exit! end - github_user = `git config github.user`.chop - github_user = 'your_id' if github_user.empty? - - generator = RuboCop::Cop::Generator.new("EightyFourCodes/#{cop_name}", github_user) + generator = RuboCop::Cop::Generator.new(cop_name) generator.write_source generator.write_spec - # generator.inject_require - generator.inject_config + generator.inject_require(root_file_path: 'lib/rubocop/cop/eightyfourcodes_cops.rb') + generator.inject_config(config_file_path: 'config/default.yml') puts generator.todo end - -require 'rspec/core/rake_task' - -RSpec::Core::RakeTask.new(:spec) - -task default: :spec diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..88084a2 --- /dev/null +++ b/bin/console @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'bundler/setup' +require 'rubocop/eightyfourcodes' + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require 'irb' +IRB.start(__FILE__) diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..dce67d8 --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/config/default.yml b/config/default.yml index ece561d..0d0f7b5 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1,15 +1,14 @@ ---- -AllCops: - EightyFourCodes: - Patterns: - - ".+" +# Write it! EightyFourCodes/CommandLiteralInjection: - Description: "Check for Command Injection in `` and %x" + Description: "Do not include variables command literals" Enabled: true VersionAdded: "0.0.1" - EightyFourCodes/RubyVersionFile: - Description: "Ensure .ruby-version file use in Gemfile" + Description: "Control Ruby version via .ruby-version" Enabled: true VersionAdded: "0.0.2" +EightyFourCodes/EnsureRedirect: + Description: "Checks for `redirect` from an `ensure` block" + Enabled: true + VersionAdded: "0.0.3" diff --git a/lib/rubocop-eightyfourcodes.rb b/lib/rubocop-eightyfourcodes.rb index a098c98..53c1217 100644 --- a/lib/rubocop-eightyfourcodes.rb +++ b/lib/rubocop-eightyfourcodes.rb @@ -1,22 +1,11 @@ -require 'pathname' -require 'yaml' +# frozen_string_literal: true require 'rubocop' -require 'rubocop/eighty_four_codes' -require 'rubocop/eighty_four_codes/version' -require 'rubocop/eighty_four_codes/inject' -require 'rubocop/eighty_four_codes/top_level_describe' -require 'rubocop/eighty_four_codes/wording' -require 'rubocop/eighty_four_codes/util' -require 'rubocop/eighty_four_codes/language' -require 'rubocop/eighty_four_codes/language/node_pattern' -require 'rubocop/eighty_four_codes/concept' -require 'rubocop/eighty_four_codes/example_group' -require 'rubocop/eighty_four_codes/example' -require 'rubocop/eighty_four_codes/hook' -require 'rubocop/cop/eighty_four_codes/cop' +require_relative 'rubocop/eightyfourcodes' +require_relative 'rubocop/eightyfourcodes/version' +require_relative 'rubocop/eightyfourcodes/inject' RuboCop::EightyFourCodes::Inject.defaults! -Dir["#{__dir__}/rubocop/cop/eighty_four_codes/**/*.rb"].each { |cop| require cop } +require_relative 'rubocop/cop/eightyfourcodes_cops' diff --git a/lib/rubocop/cop/eighty_four_codes/command_literal_injection.rb b/lib/rubocop/cop/eighty_four_codes/command_literal_injection.rb index fc0d830..0d53ecd 100644 --- a/lib/rubocop/cop/eighty_four_codes/command_literal_injection.rb +++ b/lib/rubocop/cop/eighty_four_codes/command_literal_injection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module RuboCop module Cop module EightyFourCodes @@ -15,8 +17,8 @@ module EightyFourCodes # # even better # exec("/bin/ls", shell_escape(filename)) # - class CommandLiteralInjection < RuboCop::Cop::Cop - MSG = 'Do not include variables command literals. Use parameters "system(cmd, params)" or exec() instead'.freeze + class CommandLiteralInjection < Base + MSG = 'Do not include variables command literals. Use parameters "system(cmd, params)" or exec() instead' def_node_matcher :literal_var?, <<-PATTERN (begin ...) diff --git a/lib/rubocop/cop/eighty_four_codes/cop.rb b/lib/rubocop/cop/eighty_four_codes/cop.rb deleted file mode 100644 index 1a783bf..0000000 --- a/lib/rubocop/cop/eighty_four_codes/cop.rb +++ /dev/null @@ -1,70 +0,0 @@ -module RuboCop - module Cop # rubocop:disable Style/Documentation - WorkaroundCop84 = Cop.dup - - # Clone of the the normal RuboCop::Cop::Cop class so we can rewrite - # the inherited method without breaking functionality - class WorkaroundCop84 - # Overwrite the cop inherited method to be a noop. Our RSpec::Cop - # class will invoke the inherited hook instead - def self.inherited(*); end - - # Special case `Module#<` so that the rspec support rubocop exports - # is compatible with our subclass - def self.<(other) - other.equal?(RuboCop::Cop::Cop) || super - end - end - private_constant(:WorkaroundCop84) - - module EightyFourCodes - # @abstract parent class to rspec cops - # - # The criteria for whether rubocop-rspec analyzes a certain ruby file - # is configured via `AllCops/RSpec`. For example, if you want to - # customize your project to scan all files within a `test/` directory - # then you could add this to your configuration: - # - # @example configuring analyzed paths - # - # AllCops: - # RSpec: - # Patterns: - # - '_test.rb$' - # - '(?:^|/)test/' - class Cop < WorkaroundCop84 - include RuboCop::EightyFourCodes::Language - include RuboCop::EightyFourCodes::Language::NodePattern - - DEFAULT_CONFIGURATION = - RuboCop::EightyFourCodes::CONFIG.fetch('AllCops').fetch('EightyFourCodes') - - # Invoke the original inherited hook so our cops are recognized - def self.inherited(subclass) - RuboCop::Cop::Cop.inherited(subclass) - end - - def relevant_file?(file) - relevant_rubocop_rspec_file?(file) && super - end - - private - - def relevant_rubocop_rspec_file?(file) - rspec_pattern =~ file - end - - def rspec_pattern - Regexp.union(rspec_pattern_config.map(&Regexp.public_method(:new))) - end - - def rspec_pattern_config - config - .for_all_cops - .fetch('EightyFourCodes', DEFAULT_CONFIGURATION) - .fetch('Patterns') - end - end - end - end -end diff --git a/lib/rubocop/cop/eighty_four_codes/ensure_redirect.rb b/lib/rubocop/cop/eighty_four_codes/ensure_redirect.rb new file mode 100644 index 0000000..195ce50 --- /dev/null +++ b/lib/rubocop/cop/eighty_four_codes/ensure_redirect.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module EightyFourCodes + # Checks for `redirect` from an `ensure` block. + # `redirect` from an ensure block is a dangerous code smell as it + # will take precedence over any exception being raised, + # and the exception will be silently thrown away as if it were rescued. + # + # If you want to rescue some (or all) exceptions, best to do it explicitly + # + # @example + # + # # bad + # def foo + # do_something + # ensure + # cleanup + # redirect "/" + # end + # + # + # # good + # def foo + # begin + # do_something + # rescue SomeException + # # Let's ignore this exception + # end + # redirect "/" + # ensure + # cleanup + # end + class EnsureRedirect < Base + MSG = 'Do not redirect from an `ensure` block.' + + def on_ensure(node) + # `:send` nodes represent method calls, so we look for send nodes and then check if they are `redirect` + node.body&.each_node(:send) do |send_node| + # Check if the method name being called is `redirect` + add_offense(send_node) if send_node.method?(:redirect) + end + end + end + end + end +end diff --git a/lib/rubocop/cop/eighty_four_codes/ruby_version_file.rb b/lib/rubocop/cop/eighty_four_codes/ruby_version_file.rb index 8a83f80..761ad81 100644 --- a/lib/rubocop/cop/eighty_four_codes/ruby_version_file.rb +++ b/lib/rubocop/cop/eighty_four_codes/ruby_version_file.rb @@ -1,48 +1,44 @@ # frozen_string_literal: true module RuboCop - module Cop - module EightyFourCodes - # Read Ruby version from a .ruby-version file - # - # Instead of staticly defining the Ruby runtime version in Gemfile, load it from - # a .ruby-version file definition. As this Ruby version file is read by rbenv, chruby etc - # it's much easier for the developer to work with multiple projects with different versions. - # - # @example - # # bad - # ruby 2.6.6 - # - # # good - # ruby File.read('.ruby-version') - class RubyVersionFile < Base - extend AutoCorrector + module Cop + module EightyFourCodes + # Read Ruby version from a .ruby-version file + # + # Instead of staticly defining the Ruby runtime version in Gemfile, load it from + # a .ruby-version file definition. As this Ruby version file is read by rbenv, chruby etc + # it's much easier for the developer to work with multiple projects with different versions. + # + # @example + # # bad + # ruby 2.6.6 + # + # # good + # ruby File.read('.ruby-version') + class RubyVersionFile < Base + extend AutoCorrector - MSG = "Control Ruby version via .ruby-version, fix by replacing with File.read('.ruby-version')" + MSG = "Control Ruby version via .ruby-version, fix by replacing with File.read('.ruby-version')" - RESTRICT_ON_SEND = %i[ruby].freeze + RESTRICT_ON_SEND = %i[ruby].freeze - def_node_matcher :static_version_found?, <<~PATTERN - (send nil? :ruby - $(str _)) - PATTERN + def_node_matcher :static_version_found?, <<~PATTERN + (send nil? :ruby + $(str _)) + PATTERN - def on_send(node) - return unless File.basename(processed_source.file_path).eql?('Gemfile') - static_version_found?(node) do |source_node, source| - message = format(MSG, source: source) + def on_send(node) + return unless File.basename(processed_source.file_path).eql?('Gemfile') - add_offense( - source_node, - message: message - ) do |corrector| - corrector.replace( - source_node, "File.read('.ruby-version')" - ) - end + static_version_found?(node) do |source_node, source| + message = format(MSG, source: source) + + add_offense(source_node, message: message) do |corrector| + corrector.replace(source_node, "File.read('.ruby-version')") end end end end end end +end diff --git a/lib/rubocop/cop/eightyfourcodes_cops.rb b/lib/rubocop/cop/eightyfourcodes_cops.rb new file mode 100644 index 0000000..3f6a871 --- /dev/null +++ b/lib/rubocop/cop/eightyfourcodes_cops.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require_relative 'eighty_four_codes/command_literal_injection' +require_relative 'eighty_four_codes/ensure_redirect' +require_relative 'eighty_four_codes/ruby_version_file' diff --git a/lib/rubocop/eighty_four_codes/concept.rb b/lib/rubocop/eighty_four_codes/concept.rb deleted file mode 100644 index d350455..0000000 --- a/lib/rubocop/eighty_four_codes/concept.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module EightyFourCodes - # Wrapper for RSpec DSL methods - class Concept - include Language - include Language::NodePattern - extend NodePattern::Macros - - def initialize(node) - @node = node - end - - def eql?(other) - node == other.node - end - - alias == eql? - - def hash - [self.class, node].hash - end - - def to_node - node - end - - protected - - attr_reader :node - end - end -end diff --git a/lib/rubocop/eighty_four_codes/config_formatter.rb b/lib/rubocop/eighty_four_codes/config_formatter.rb deleted file mode 100644 index f369772..0000000 --- a/lib/rubocop/eighty_four_codes/config_formatter.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'yaml' - -module RuboCop - module EightyFourCodes - # Builds a YAML config file from two config hashes - class ConfigFormatter - NAMESPACES = /^(#{Regexp.union('eightyfourcodes')})/ - - def initialize(config, descriptions) - @config = config - @descriptions = descriptions - end - - def dump - YAML.dump(unified_config).gsub(NAMESPACES, "\n\\1") - end - - private - - def unified_config - cops.each_with_object(config.dup) do |cop, unified| - unified[cop] = config.fetch(cop).merge(descriptions.fetch(cop)) - end - end - - def cops - (descriptions.keys | config.keys).grep(NAMESPACES) - end - - attr_reader :config, :descriptions - end - end -end diff --git a/lib/rubocop/eighty_four_codes/description_extractor.rb b/lib/rubocop/eighty_four_codes/description_extractor.rb deleted file mode 100644 index af15bd4..0000000 --- a/lib/rubocop/eighty_four_codes/description_extractor.rb +++ /dev/null @@ -1,72 +0,0 @@ -module RuboCop - module EightyFourCodes - # Extracts cop descriptions from YARD docstrings - class DescriptionExtractor - def initialize(yardocs) - @code_objects = yardocs.map(&CodeObject.public_method(:new)) - end - - def to_h - code_objects - .select(&:rspec_cop?) - .map(&:configuration) - .reduce(:merge) - end - - private - - attr_reader :code_objects - - # Decorator of a YARD code object for working with documented rspec cops - class CodeObject - RSPEC_NAMESPACE = 'RuboCop::Cop::eightyfourcodes'.freeze - - def initialize(yardoc) - @yardoc = yardoc - end - - # Test if the YARD code object documents a concrete rspec cop class - # - # @return [Boolean] - def rspec_cop? - class_documentation? && rspec_cop_namespace? && !abstract? - end - - # Configuration for the documented cop that would live in default.yml - # - # @return [Hash] - def configuration - { cop_name => { 'Description' => description } } - end - - private - - def cop_name - Object.const_get(documented_constant).cop_name - end - - def description - yardoc.docstring.split("\n\n").first.to_s - end - - def class_documentation? - yardoc.type.equal?(:class) - end - - def rspec_cop_namespace? - documented_constant.start_with?(RSPEC_NAMESPACE) - end - - def documented_constant - yardoc.to_s - end - - def abstract? - yardoc.tags.any? { |tag| tag.tag_name.eql?('abstract') } - end - - attr_reader :yardoc - end - end - end -end diff --git a/lib/rubocop/eighty_four_codes/example.rb b/lib/rubocop/eighty_four_codes/example.rb deleted file mode 100644 index 966fdc1..0000000 --- a/lib/rubocop/eighty_four_codes/example.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module EightyFourCodes - # Wrapper for RSpec examples - class Example < Concept - def_node_matcher :extract_doc_string, '(send _ _ $str ...)' - def_node_matcher :extract_metadata, '(send _ _ _ $...)' - def_node_matcher :extract_implementation, '(block send args $_)' - - def doc_string - extract_doc_string(definition) - end - - def metadata - extract_metadata(definition) - end - - def implementation - extract_implementation(node) - end - - def definition - if node.send_type? - node - else - node.children.first - end - end - end - end -end diff --git a/lib/rubocop/eighty_four_codes/example_group.rb b/lib/rubocop/eighty_four_codes/example_group.rb deleted file mode 100644 index 8ec1dc6..0000000 --- a/lib/rubocop/eighty_four_codes/example_group.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module EightyFourCodes - # Wrapper for RSpec example groups - class ExampleGroup < Concept - # @!method scope_change?(node) - # - # Detect if the node is an example group or shared example - # - # Selectors which indicate that we should stop searching - # - def_node_matcher :scope_change?, - (ExampleGroups::ALL + SharedGroups::ALL).block_pattern - - # @!method hook(node) - # - # Detect if node is `before`, `after`, `around` - def_node_matcher :hook, <<-PATTERN - (block {$(send nil #{Hooks::ALL.node_pattern_union} ...)} ...) - PATTERN - - def_node_matcher :subject, Subject::ALL.block_pattern - - def subjects - subjects_in_scope(node) - end - - def examples - examples_in_scope(node).map(&Example.public_method(:new)) - end - - def hooks - hooks_in_scope(node).map(&Hook.public_method(:new)) - end - - private - - def subjects_in_scope(node) - node.each_child_node.flat_map do |child| - find_subjects(child) - end - end - - def find_subjects(node) - return [] if scope_change?(node) - - if subject(node) - [node] - else - subjects_in_scope(node) - end - end - - def hooks_in_scope(node) - node.each_child_node.flat_map do |child| - find_hooks(child) - end - end - - def find_hooks(node) - return [] if scope_change?(node) || example?(node) - - if hook(node) - [node] - else - hooks_in_scope(node) - end - end - - def examples_in_scope(node, &blk) - node.each_child_node.flat_map do |child| - find_examples(child, &blk) - end - end - - # Recursively search for examples within the current scope - # - # Searches node for examples and halts when a scope change is detected - # - # @param node [RuboCop::Node] node to recursively search for examples - # - # @return [Array] discovered example nodes - def find_examples(node) - return [] if scope_change?(node) - - if example?(node) - [node] - else - examples_in_scope(node) - end - end - end - end -end diff --git a/lib/rubocop/eighty_four_codes/hook.rb b/lib/rubocop/eighty_four_codes/hook.rb deleted file mode 100644 index 29d40e4..0000000 --- a/lib/rubocop/eighty_four_codes/hook.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module EightyFourCodes - # Wrapper for RSpec hook - class Hook < Concept - STANDARDIZED_SCOPES = %i[each context suite].freeze - private_constant(:STANDARDIZED_SCOPES) - - def name - node.method_name - end - - def knowable_scope? - return true unless scope_argument - - scope_argument.sym_type? - end - - def valid_scope? - STANDARDIZED_SCOPES.include?(scope) - end - - def example? - scope.equal?(:each) - end - - def scope - case scope_name - when nil, :each, :example then :each - when :context, :all then :context - when :suite then :suite - else - scope_name - end - end - - private - - def scope_name - scope_argument.to_a.first - end - - def scope_argument - node.first_argument - end - end - end -end diff --git a/lib/rubocop/eighty_four_codes/language.rb b/lib/rubocop/eighty_four_codes/language.rb deleted file mode 100644 index 2c5f191..0000000 --- a/lib/rubocop/eighty_four_codes/language.rb +++ /dev/null @@ -1,118 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module EightyFourCodes - # RSpec public API methods that are commonly used in cops - module Language - # Set of method selectors - class SelectorSet - def initialize(selectors) - @selectors = selectors - end - - def ==(other) - selectors.eql?(other.selectors) - end - - def +(other) - self.class.new(selectors + other.selectors) - end - - def include?(selector) - selectors.include?(selector) - end - - def block_pattern - "(block #{send_pattern} ...)" - end - - def send_pattern - "(send _ #{node_pattern_union} ...)" - end - - def node_pattern_union - "{#{node_pattern}}" - end - - def node_pattern - selectors.map(&:inspect).join(' ') - end - - protected - - attr_reader :selectors - end - - module Matchers - MESSAGE_CHAIN = SelectorSet.new(%i[receive_message_chain stub_chain]) - end - - module ExampleGroups - GROUPS = SelectorSet.new(%i[describe context feature example_group]) - SKIPPED = SelectorSet.new(%i[xdescribe xcontext xfeature]) - FOCUSED = SelectorSet.new(%i[fdescribe fcontext ffeature]) - - ALL = GROUPS + SKIPPED + FOCUSED - end - - module SharedGroups - EXAMPLES = SelectorSet.new(%i[shared_examples shared_examples_for]) - CONTEXT = SelectorSet.new(%i[shared_context]) - - ALL = EXAMPLES + CONTEXT - end - - module Includes - EXAMPLES = SelectorSet.new( - %i[ - it_behaves_like - it_should_behave_like - include_examples - ] - ) - CONTEXT = SelectorSet.new(%i[include_context]) - - ALL = EXAMPLES + CONTEXT - end - - module Examples - EXAMPLES = SelectorSet.new(%i[it specify example scenario its]) - FOCUSED = SelectorSet.new(%i[fit fspecify fexample fscenario focus]) - SKIPPED = SelectorSet.new(%i[xit xspecify xexample xscenario skip]) - PENDING = SelectorSet.new(%i[pending]) - - ALL = EXAMPLES + FOCUSED + SKIPPED + PENDING - end - - module Hooks - ALL = SelectorSet.new( - %i[ - prepend_before - before - append_before - around - prepend_after - after - append_after - ] - ) - end - - module Helpers - ALL = SelectorSet.new(%i[let let!]) - end - - module Subject - ALL = SelectorSet.new(%i[subject subject!]) - end - - ALL = - ExampleGroups::ALL + - SharedGroups::ALL + - Examples::ALL + - Hooks::ALL + - Helpers::ALL + - Subject::ALL - end - end -end diff --git a/lib/rubocop/eighty_four_codes/language/node_pattern.rb b/lib/rubocop/eighty_four_codes/language/node_pattern.rb deleted file mode 100644 index a0de75a..0000000 --- a/lib/rubocop/eighty_four_codes/language/node_pattern.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module EightyFourCodes - module Language - # Common node matchers used for matching against the rspec DSL - module NodePattern - extend RuboCop::NodePattern::Macros - - def_node_matcher :example_group?, ExampleGroups::ALL.block_pattern - - def_node_matcher :example_group_with_body?, <<-PATTERN - (block #{ExampleGroups::ALL.send_pattern} args [!nil]) - PATTERN - - def_node_matcher :example?, Examples::ALL.block_pattern - end - end - end -end diff --git a/lib/rubocop/eighty_four_codes/top_level_describe.rb b/lib/rubocop/eighty_four_codes/top_level_describe.rb deleted file mode 100644 index 9c0c0a7..0000000 --- a/lib/rubocop/eighty_four_codes/top_level_describe.rb +++ /dev/null @@ -1,57 +0,0 @@ -module RuboCop - module EightyFourCodes - # Helper methods for top level describe cops - module TopLevelDescribe - extend NodePattern::Macros - - def_node_matcher :described_constant, <<-PATTERN - (block $(send _ :describe $(const ...)) (args) $_) - PATTERN - - def on_send(node) - return unless respond_to?(:on_top_level_describe) - return unless top_level_describe?(node) - - _receiver, _method_name, *args = *node - - on_top_level_describe(node, args) - end - - private - - def top_level_describe?(node) - _receiver, method_name, *_args = *node - return false unless method_name == :describe - - top_level_nodes.include?(node) - end - - def top_level_nodes - nodes = describe_statement_children(root_node) - # If we have no top level describe statements, we need to check any - # blocks on the top level (e.g. after a require). - if nodes.empty? - nodes = root_node.each_child_node(:block).flat_map do |child| - describe_statement_children(child) - end - end - - nodes - end - - def root_node - processed_source.ast - end - - def single_top_level_describe? - top_level_nodes.one? - end - - def describe_statement_children(node) - node.each_child_node(:send).select do |element| - element.children[1] == :describe - end - end - end - end -end diff --git a/lib/rubocop/eighty_four_codes/util.rb b/lib/rubocop/eighty_four_codes/util.rb deleted file mode 100644 index 599c451..0000000 --- a/lib/rubocop/eighty_four_codes/util.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module EightyFourCodes - # Utility methods - module Util - # Error raised by `Util.one` if size is less than zero or greater than one - SizeError = Class.new(IndexError) - - # Return only element in array if it contains exactly one member - def one(array) - return array.first if array.one? - - raise SizeError, - "expected size to be exactly 1 but size was #{array.size}" - end - end - end -end diff --git a/lib/rubocop/eighty_four_codes/version.rb b/lib/rubocop/eighty_four_codes/version.rb deleted file mode 100644 index b29cdc9..0000000 --- a/lib/rubocop/eighty_four_codes/version.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module EightyFourCodes - # Version information for the eightyfourcodes Rubocop plugin. - module Version - STRING = '0.0.2' - end - end -end diff --git a/lib/rubocop/eighty_four_codes/wording.rb b/lib/rubocop/eighty_four_codes/wording.rb deleted file mode 100644 index 2210377..0000000 --- a/lib/rubocop/eighty_four_codes/wording.rb +++ /dev/null @@ -1,81 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module EightyFourCodes - # RSpec example wording rewriter - class Wording - SHOULDNT_PREFIX = /\Ashould(?:n't| not)\b/i - SHOULDNT_BE_PREFIX = /#{SHOULDNT_PREFIX} be\b/i - ES_SUFFIX_PATTERN = /(?:o|s|x|ch|sh|z)\z/i - IES_SUFFIX_PATTERN = /[^aeou]y\z/i - - def initialize(text, ignore:, replace:) - @text = text - @ignores = ignore - @replacements = replace - end - - def rewrite - case text - when SHOULDNT_BE_PREFIX - replace_prefix(SHOULDNT_BE_PREFIX, 'is not') - when SHOULDNT_PREFIX - replace_prefix(SHOULDNT_PREFIX, 'does not') - else - remove_should_and_pluralize - end - end - - private - - attr_reader :text, :ignores, :replacements - - def replace_prefix(pattern, replacement) - text.sub(pattern) do |shouldnt| - uppercase?(shouldnt) ? replacement.upcase : replacement - end - end - - def uppercase?(word) - word.upcase.eql?(word) - end - - def remove_should_and_pluralize - _should, *words = text.split - - words.each_with_index do |word, index| - next if ignored_word?(word) - - words[index] = substitute(word) - - break - end - - words.join(' ') - end - - def ignored_word?(word) - ignores.any? { |ignore| ignore.casecmp(word).zero? } - end - - def substitute(word) - # NOTE: Custom replacements are case sensitive. - return replacements.fetch(word) if replacements.key?(word) - - case word - when ES_SUFFIX_PATTERN then append_suffix(word, 'es') - when IES_SUFFIX_PATTERN then append_suffix(word[0..-2], 'ies') - else append_suffix(word, 's') - end - end - - def append_suffix(word, suffix) - suffix = suffix.upcase if uppercase?(word) - - "#{word}#{suffix}" - end - - private_constant(*constants(false)) - end - end -end diff --git a/lib/rubocop/eighty_four_codes.rb b/lib/rubocop/eightyfourcodes.rb similarity index 67% rename from lib/rubocop/eighty_four_codes.rb rename to lib/rubocop/eightyfourcodes.rb index c6f8361..657980d 100644 --- a/lib/rubocop/eighty_four_codes.rb +++ b/lib/rubocop/eightyfourcodes.rb @@ -1,6 +1,11 @@ +# frozen_string_literal: true + +require_relative 'eightyfourcodes/version' + module RuboCop - # RuboCop RSpec project namespace + # Namespace for EightyFourCodes cops module EightyFourCodes + class Error < StandardError; end PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze diff --git a/lib/rubocop/eighty_four_codes/inject.rb b/lib/rubocop/eightyfourcodes/inject.rb similarity index 64% rename from lib/rubocop/eighty_four_codes/inject.rb rename to lib/rubocop/eightyfourcodes/inject.rb index 968633e..2914a36 100644 --- a/lib/rubocop/eighty_four_codes/inject.rb +++ b/lib/rubocop/eightyfourcodes/inject.rb @@ -1,3 +1,7 @@ +# frozen_string_literal: true + +# The original code is from https://github.com/rubocop/rubocop-rspec/blob/master/lib/rubocop/rspec/inject.rb +# See https://github.com/rubocop/rubocop-rspec/blob/master/MIT-LICENSE.md module RuboCop module EightyFourCodes # Because RuboCop doesn't yet support plugins, we have to monkey patch in a @@ -6,7 +10,7 @@ module Inject def self.defaults! path = CONFIG_DEFAULT.to_s hash = ConfigLoader.send(:load_yaml_configuration, path) - config = Config.new(hash, path) + config = Config.new(hash, path).tap(&:make_excludes_absolute) puts "configuration from #{path}" if ConfigLoader.debug? config = ConfigLoader.merge_with_default(config, path) ConfigLoader.instance_variable_set(:@default_configuration, config) diff --git a/lib/rubocop/eightyfourcodes/version.rb b/lib/rubocop/eightyfourcodes/version.rb new file mode 100644 index 0000000..28eabcd --- /dev/null +++ b/lib/rubocop/eightyfourcodes/version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module RuboCop + module EightyFourCodes + VERSION = '0.0.3' + end +end diff --git a/rubocop-eightyfourcodes.gemspec b/rubocop-eightyfourcodes.gemspec index 3699925..03abeb1 100644 --- a/rubocop-eightyfourcodes.gemspec +++ b/rubocop-eightyfourcodes.gemspec @@ -1,35 +1,30 @@ -$LOAD_PATH.unshift File.expand_path('lib', __dir__) -require 'rubocop/eighty_four_codes/version' +# frozen_string_literal: true + +require_relative 'lib/rubocop/eightyfourcodes/version' Gem::Specification.new do |spec| spec.name = 'rubocop-eightyfourcodes' - spec.summary = 'Basic security checks for projects' + spec.version = RuboCop::EightyFourCodes::VERSION + spec.authors = ['Anders Bälter'] + spec.email = ['anders@84codes.com'] + + spec.summary = 'This is a collection of cops developed and used by 84codes AB.' spec.description = <<~DESCRIPTION - Basic security checking for Ruby files. A plugin for the RuboCop code style enforcing & linting tool. DESCRIPTION spec.homepage = 'https://github.com/84codes/rubocop-eightyfourcodes/' - spec.authors = ['Anders Bälter', 'Brian Neel'] - spec.email = [ - 'anders@eightyfourcodes.com', - 'brian@gitlab.com' - ] - spec.licenses = ['MIT'] + spec.license = 'MIT' + spec.required_ruby_version = '>= 2.6.0' - spec.version = RuboCop::EightyFourCodes::Version::STRING - spec.platform = Gem::Platform::RUBY - spec.required_ruby_version = '>= 2.3.0' + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + spec.files = Dir.chdir(__dir__) do + `git ls-files -z`.split("\x0").reject do |f| + (File.expand_path(f) == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)}) + end + end spec.require_paths = ['lib'] - spec.files = Dir[ - '{config,lib}/**/*', - '*.md', - '*.gemspec', - 'Gemfile' - ] + spec.add_runtime_dependency 'rubocop' spec.extra_rdoc_files = ['LICENSE.md', 'README.md'] - - spec.add_runtime_dependency 'rubocop', '>= 0.51' - - spec.add_development_dependency 'rake' end diff --git a/sig/rubocop/eightyfourcodes.rbs b/sig/rubocop/eightyfourcodes.rbs new file mode 100644 index 0000000..931c7ae --- /dev/null +++ b/sig/rubocop/eightyfourcodes.rbs @@ -0,0 +1,6 @@ +module Rubocop + module EightyFourCodes + VERSION: String + # See the writing guide of rbs: https://github.com/ruby/rbs#guides + end +end diff --git a/spec/rubocop/cop/eighty_four_codes/command_literal_injection_spec.rb b/spec/rubocop/cop/eighty_four_codes/command_literal_injection_spec.rb index 4374d71..cc09b1b 100644 --- a/spec/rubocop/cop/eighty_four_codes/command_literal_injection_spec.rb +++ b/spec/rubocop/cop/eighty_four_codes/command_literal_injection_spec.rb @@ -1,30 +1,36 @@ # frozen_string_literal: true -RSpec.describe RuboCop::Cop::EightyFourCodes::CommandLiteralInjection do - subject(:cop) { described_class.new } +require './spec/spec_helper' + +RSpec.describe RuboCop::Cop::EightyFourCodes::CommandLiteralInjection, :config do + let(:config) { RuboCop::Config.new } + let(:cop_name) { 'EightyFourCodes/CommandLiteralInjection' } + let(:cop_message) do + "#{cop_name}: Do not include variables command literals. Use parameters \"system(cmd, params)\" or exec() instead" + end describe '%x commands with interpolation' do it 'registers an offense' do - expect_offense(<<~'RUBY') + expect_offense(<<~RUBY) my_var = 1 - %x(cat #{my_var}) - ^^^^^^^^^^^^^^^^^ Do not include variables command literals. Use parameters "system(cmd, params)" or exec() instead + %x(cat \#{my_var}) + ^^^^^^^^^^^^^^^^^ #{cop_message} RUBY end it 'registers an offense if command conitnues' do - expect_offense(<<~'RUBY') + expect_offense(<<~RUBY) my_var = 1 - %x(cat #{my_var} | less) - ^^^^^^^^^^^^^^^^^^^^^^^^ Do not include variables command literals. Use parameters "system(cmd, params)" or exec() instead + %x(cat \#{my_var} | less) + ^^^^^^^^^^^^^^^^^^^^^^^^ #{cop_message} RUBY end it 'registers an offense if variable is at the beginning' do - expect_offense(<<~'RUBY') + expect_offense(<<~RUBY) my_var = 1 - %x(#{my_var} | less) - ^^^^^^^^^^^^^^^^^^^^ Do not include variables command literals. Use parameters "system(cmd, params)" or exec() instead + %x(\#{my_var} | less) + ^^^^^^^^^^^^^^^^^^^^ #{cop_message} RUBY end @@ -37,40 +43,40 @@ describe '`` commands with interpolation' do it 'registers an offense' do - expect_offense(<<~'RUBY') + expect_offense(<<~RUBY) my_var = 1 - `cat #{my_var}` - ^^^^^^^^^^^^^^^ Do not include variables command literals. Use parameters "system(cmd, params)" or exec() instead + `cat \#{my_var}` + ^^^^^^^^^^^^^^^ #{cop_message} RUBY end it 'registers an offense if command conitnues' do - expect_offense(<<~'RUBY') + expect_offense(<<~RUBY) my_var = 1 - `cat #{my_var} | less` - ^^^^^^^^^^^^^^^^^^^^^^ Do not include variables command literals. Use parameters "system(cmd, params)" or exec() instead + `cat \#{my_var} | less` + ^^^^^^^^^^^^^^^^^^^^^^ #{cop_message} RUBY end it 'registers an offense if variable is at the beginning' do - expect_offense(<<~'RUBY') + expect_offense(<<~RUBY) my_var = 1 - `#{my_var} | less` - ^^^^^^^^^^^^^^^^^^ Do not include variables command literals. Use parameters "system(cmd, params)" or exec() instead + `\#{my_var} | less` + ^^^^^^^^^^^^^^^^^^ #{cop_message} RUBY end it 'registers an offense on multiple variables' do - expect_offense(<<~'RUBY') + expect_offense(<<~RUBY) my_var = 1 - `cat #{my_var} | less #{my_var}` - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not include variables command literals. Use parameters "system(cmd, params)" or exec() instead + `cat \#{my_var} | less \#{my_var}` + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{cop_message} RUBY end it 'allows %x without arguments' do expect_no_offenses(<<-'RUBY') - `cat /var/log/syslog` + `cat /var/log/syslog` RUBY end end diff --git a/spec/rubocop/cop/eighty_four_codes/ensure_redirect_spec.rb b/spec/rubocop/cop/eighty_four_codes/ensure_redirect_spec.rb new file mode 100644 index 0000000..40ec512 --- /dev/null +++ b/spec/rubocop/cop/eighty_four_codes/ensure_redirect_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require './spec/spec_helper' + +RSpec.describe RuboCop::Cop::EightyFourCodes::EnsureRedirect, :config do + let(:config) { RuboCop::Config.new } + let(:cop_name) { 'EightyFourCodes/EnsureRedirect' } + let(:cop_message) { "#{cop_name}: Do not redirect from an `ensure` block." } + + it 'registers an offense and corrects for redirect in ensure' do + expect_offense(<<~RUBY) + begin + something + ensure + file.close + redirect "/" + ^^^^^^^^^^^^ #{cop_message} + end + RUBY + + expect_no_corrections + end + + it 'registers an offense and corrects for redirect with argument in ensure' do + expect_offense(<<~RUBY) + begin + foo + ensure + redirect "/" + ^^^^^^^^^^^^ #{cop_message} + end + RUBY + + expect_no_corrections + end + + it 'registers an offense when redirecting multiple values in `ensure`' do + expect_offense(<<~RUBY) + begin + something + ensure + do_something + redirect "/", 303 + ^^^^^^^^^^^^^^^^^ #{cop_message} + end + RUBY + + expect_no_corrections + end + + it 'does not register an offense for redirect outside ensure' do + expect_no_offenses(<<~RUBY) + begin + something + redirect "/" + ensure + file.close + end + RUBY + end + + it 'does not check when ensure block has no body' do + expect_no_offenses(<<~RUBY) + begin + something + ensure + end + RUBY + end +end diff --git a/spec/rubocop/cop/eighty_four_codes/ruby_version_file_spec.rb b/spec/rubocop/cop/eighty_four_codes/ruby_version_file_spec.rb index 01d58b2..dbd70e4 100644 --- a/spec/rubocop/cop/eighty_four_codes/ruby_version_file_spec.rb +++ b/spec/rubocop/cop/eighty_four_codes/ruby_version_file_spec.rb @@ -1,14 +1,18 @@ # frozen_string_literal: true -RSpec.describe RuboCop::Cop::EightyFourCodes::RubyVersionFile do - subject(:cop) { described_class.new(config) } +require './spec/spec_helper' +RSpec.describe RuboCop::Cop::EightyFourCodes::RubyVersionFile, :config do let(:config) { RuboCop::Config.new } + let(:cop_name) { 'EightyFourCodes/RubyVersionFile' } + let(:cop_message) do + "#{cop_name}: Control Ruby version via .ruby-version, fix by replacing with File.read('.ruby-version')" + end it 'registers an offense defined with double quotes' do - expect_offense(<<~'RUBY', 'Gemfile') + expect_offense(<<~RUBY, 'Gemfile') ruby "2.6.6" - ^^^^^^^ Control Ruby version via .ruby-version, fix by replacing with File.read('.ruby-version') + ^^^^^^^ #{cop_message} RUBY expect_correction(<<~RUBY) @@ -17,9 +21,9 @@ end it 'registers an offense defined with single quotes' do - expect_offense(<<~'RUBY', 'Gemfile') + expect_offense(<<~RUBY, 'Gemfile') ruby '2.1.0' - ^^^^^^^ Control Ruby version via .ruby-version, fix by replacing with File.read('.ruby-version') + ^^^^^^^ #{cop_message} RUBY expect_correction(<<~RUBY) diff --git a/spec/rubocop/eighty_four_codes_spec.rb b/spec/rubocop/eighty_four_codes_spec.rb new file mode 100644 index 0000000..1779f4f --- /dev/null +++ b/spec/rubocop/eighty_four_codes_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require './spec/spec_helper' + +RSpec.describe RuboCop::EightyFourCodes do + it 'has a version number' do + expect(RuboCop::EightyFourCodes::VERSION).not_to be_nil + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e05691c..a3c5d50 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,26 +1,17 @@ -require 'rubocop' +# frozen_string_literal: true + +require 'rubocop-eightyfourcodes' +require 'rspec' require 'rubocop/rspec/support' RSpec.configure do |config| - config.expect_with :rspec do |expectations| - expectations.include_chain_clauses_in_custom_matcher_descriptions = true - end - - config.mock_with :rspec do |mocks| - mocks.verify_partial_doubles = true - end + config.include RuboCop::RSpec::ExpectOffense config.disable_monkey_patching! - config.filter_run_when_matching :focus - config.shared_context_metadata_behavior = :apply_to_host_groups + config.raise_errors_for_deprecations! + config.raise_on_warning = true + config.fail_if_no_examples = true config.order = :random Kernel.srand config.seed - - config.include(RuboCop::RSpec::ExpectOffense) end - -$LOAD_PATH.unshift(File.join(__dir__, '..', 'lib')) -$LOAD_PATH.unshift(__dir__) - -require 'rubocop-eightyfourcodes'