Add cop for IO.read usage
This commit is contained in:
		
							parent
							
								
									6c9ef43e72
								
							
						
					
					
						commit
						e5285b5ed8
					
				@ -54,6 +54,10 @@ FormulaAudit:
 | 
			
		||||
FormulaAuditStrict:
 | 
			
		||||
  Enabled: true
 | 
			
		||||
 | 
			
		||||
# enable all Homebrew custom cops
 | 
			
		||||
Homebrew:
 | 
			
		||||
  Enabled: true
 | 
			
		||||
 | 
			
		||||
# makes DSL usage ugly.
 | 
			
		||||
Layout/SpaceBeforeBrackets:
 | 
			
		||||
  Exclude:
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ require "rubocop-rails"
 | 
			
		||||
require "rubocop-rspec"
 | 
			
		||||
require "rubocop-sorbet"
 | 
			
		||||
 | 
			
		||||
require "rubocops/io_read"
 | 
			
		||||
require "rubocops/shell_commands"
 | 
			
		||||
 | 
			
		||||
require "rubocops/formula_desc"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								Library/Homebrew/rubocops/io_read.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								Library/Homebrew/rubocops/io_read.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
# typed: true
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    module Homebrew
 | 
			
		||||
      # This cop restricts usage of IO.read functions for security reasons.
 | 
			
		||||
      #
 | 
			
		||||
      # @api private
 | 
			
		||||
      class IORead < Base
 | 
			
		||||
        MSG = "The use of `IO.%<method>s` is a security risk."
 | 
			
		||||
        RESTRICT_ON_SEND = [:read, :readlines].freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return if node.receiver != s(:const, nil, :IO)
 | 
			
		||||
          return if safe?(node.arguments.first)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, message: format(MSG, method: node.method_name))
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def safe?(node)
 | 
			
		||||
          if node.str_type?
 | 
			
		||||
            !node.str_content.empty? && !node.str_content.start_with?("|")
 | 
			
		||||
          elsif node.dstr_type? || (node.send_type? && node.method?(:+))
 | 
			
		||||
            safe?(node.children.first)
 | 
			
		||||
          else
 | 
			
		||||
            false
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -6,7 +6,7 @@ require "rubocops/shared/helper_functions"
 | 
			
		||||
 | 
			
		||||
module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    module Style
 | 
			
		||||
    module Homebrew
 | 
			
		||||
      # https://github.com/ruby/ruby/blob/v2_6_3/process.c#L2430-L2460
 | 
			
		||||
      SHELL_BUILTINS = %w[
 | 
			
		||||
        !
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										75
									
								
								Library/Homebrew/test/rubocops/io_read_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								Library/Homebrew/test/rubocops/io_read_spec.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,75 @@
 | 
			
		||||
# typed: false
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "rubocops/io_read"
 | 
			
		||||
 | 
			
		||||
describe RuboCop::Cop::Homebrew::IORead do
 | 
			
		||||
  subject(:cop) { described_class.new }
 | 
			
		||||
 | 
			
		||||
  it "reports an offense when `IO.read` is used with a pipe character" do
 | 
			
		||||
    expect_offense(<<~RUBY)
 | 
			
		||||
      IO.read("|echo test")
 | 
			
		||||
      ^^^^^^^^^^^^^^^^^^^^^ The use of `IO.read` is a security risk.
 | 
			
		||||
    RUBY
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "does not report an offense when `IO.read` is used without a pipe character" do
 | 
			
		||||
    expect_no_offenses(<<~RUBY)
 | 
			
		||||
      IO.read("file.txt")
 | 
			
		||||
    RUBY
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "reports an offense when `IO.read` is used with untrustworthy input" do
 | 
			
		||||
    expect_offense(<<~RUBY)
 | 
			
		||||
      input = "input value from an unknown source"
 | 
			
		||||
      IO.read(input)
 | 
			
		||||
      ^^^^^^^^^^^^^^ The use of `IO.read` is a security risk.
 | 
			
		||||
    RUBY
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "reports an offense when `IO.read` is used with a dynamic string starting with a pipe character" do
 | 
			
		||||
    expect_offense(<<~'RUBY')
 | 
			
		||||
      input = "test"
 | 
			
		||||
      IO.read("|echo #{input}")
 | 
			
		||||
      ^^^^^^^^^^^^^^^^^^^^^^^^^ The use of `IO.read` is a security risk.
 | 
			
		||||
    RUBY
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "reports an offense when `IO.read` is used with a dynamic string at the start" do
 | 
			
		||||
    expect_offense(<<~'RUBY')
 | 
			
		||||
      input = "|echo test"
 | 
			
		||||
      IO.read("#{input}.txt")
 | 
			
		||||
      ^^^^^^^^^^^^^^^^^^^^^^^ The use of `IO.read` is a security risk.
 | 
			
		||||
    RUBY
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "does not report an offense when `IO.read` is used with a dynamic string safely" do
 | 
			
		||||
    expect_no_offenses(<<~'RUBY')
 | 
			
		||||
      input = "test"
 | 
			
		||||
      IO.read("somefile#{input}.txt")
 | 
			
		||||
    RUBY
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "reports an offense when `IO.read` is used with a concatenated string starting with a pipe character" do
 | 
			
		||||
    expect_offense(<<~'RUBY')
 | 
			
		||||
      input = "|echo test"
 | 
			
		||||
      IO.read("|echo " + input)
 | 
			
		||||
      ^^^^^^^^^^^^^^^^^^^^^^^^^ The use of `IO.read` is a security risk.
 | 
			
		||||
    RUBY
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "reports an offense when `IO.read` is used with a concatenated string starting with untrustworthy input" do
 | 
			
		||||
    expect_offense(<<~'RUBY')
 | 
			
		||||
      input = "|echo test"
 | 
			
		||||
      IO.read(input + ".txt")
 | 
			
		||||
      ^^^^^^^^^^^^^^^^^^^^^^^ The use of `IO.read` is a security risk.
 | 
			
		||||
    RUBY
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "does not report an offense when `IO.read` is used with a concatenated string safely" do
 | 
			
		||||
    expect_no_offenses(<<~'RUBY')
 | 
			
		||||
      input = "test"
 | 
			
		||||
      IO.read("somefile" + input + ".txt")
 | 
			
		||||
    RUBY
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -5,7 +5,7 @@ require "rubocops/shell_commands"
 | 
			
		||||
 | 
			
		||||
module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    module Style
 | 
			
		||||
    module Homebrew
 | 
			
		||||
      describe ShellCommands do
 | 
			
		||||
        subject(:cop) { described_class.new }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user