Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new Security/Open cop #5319

Merged
merged 1 commit into from Dec 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,7 @@

* [#3394](https://github.com/bbatsov/rubocop/issues/3394): Add new `Style/TrailingCommmaInArrayLiteral` cop. ([@garettarrowood][])
* [#3394](https://github.com/bbatsov/rubocop/issues/3394): Add new `Style/TrailingCommmaInHashLiteral` cop. ([@garettarrowood][])
* [#5319](https://github.com/bbatsov/rubocop/pull/5319): Add new `Security/Open` cop. ([@mame][])

### Changes

Expand Down Expand Up @@ -3130,3 +3131,4 @@
[@melch]: https://github.com/melch
[@flyerhzm]: https://github.com/flyerhzm
[@ybiquitous]: https://github.com/ybiquitous
[@mame]: https://github.com/mame
4 changes: 4 additions & 0 deletions config/enabled.yml
Expand Up @@ -1253,6 +1253,10 @@ Security/MarshalLoad:
Reference: 'http://ruby-doc.org/core-2.3.3/Marshal.html#module-Marshal-label-Security+considerations'
Enabled: true

Security/Open:
Description: 'The use of Kernel#open represents a serious security risk.'
Enabled: true

Security/YAMLLoad:
Description: >-
Prefer usage of `YAML.safe_load` over `YAML.load` due to potential
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -557,6 +557,7 @@
require_relative 'rubocop/cop/security/eval'
require_relative 'rubocop/cop/security/json_load'
require_relative 'rubocop/cop/security/marshal_load'
require_relative 'rubocop/cop/security/open'
require_relative 'rubocop/cop/security/yaml_load'

require_relative 'rubocop/cop/team'
Expand Down
48 changes: 48 additions & 0 deletions lib/rubocop/cop/security/open.rb
@@ -0,0 +1,48 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Security
# This cop checks for the use of `Kernel#open`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add more explanations here, as to why this is a security risk. After all this comment is going to become the cop's description in the manual.

# `Kernel#open` enables not only file access but also process invocation
# by prefixing a pipe symbol (e.g., `open("| ls")`). So, it may lead to
# a serious security risk by using variable input to the argument of
# `Kernel#open`. It would be better to use `File.open` or `IO.popen`
# explicitly.
#
# @example
# # bad
# open(something)
#
# # good
# File.open(something)
# IO.popen(something)
class Open < Cop
MSG = 'The use of `Kernel#open` is a serious security risk.'.freeze

def_node_matcher :open?, <<-PATTERN
(send nil? :open $!str ...)
PATTERN

def safe?(node)
if node.str_type?
!node.str_content.empty? && !node.str_content.start_with?('|')
elsif node.dstr_type?
safe?(node.child_nodes.first)
elsif node.send_type? && node.method_name == :+
safe?(node.child_nodes.first)
else
false
end
end

def on_send(node)
open?(node) do |code|
return if safe?(code)
add_offense(node, location: :selector)
end
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/rubocop/remote_config.rb
Expand Up @@ -21,7 +21,7 @@ def file
request do |response|
next if response.is_a?(Net::HTTPNotModified)
next if response.is_a?(SocketError)
open cache_path, 'w' do |io|
File.open cache_path, 'w' do |io|
io.write response.body
end
end
Expand Down
1 change: 1 addition & 0 deletions manual/cops.md
Expand Up @@ -359,6 +359,7 @@ In the following section you find all available cops:
* [Security/Eval](cops_security.md#securityeval)
* [Security/JSONLoad](cops_security.md#securityjsonload)
* [Security/MarshalLoad](cops_security.md#securitymarshalload)
* [Security/Open](cops_security.md#securityopen)
* [Security/YAMLLoad](cops_security.md#securityyamlload)

#### Department [Style](cops_style.md)
Expand Down
24 changes: 24 additions & 0 deletions manual/cops_security.md
Expand Up @@ -83,6 +83,30 @@ Marshal.load(Marshal.dump({}))

* [http://ruby-doc.org/core-2.3.3/Marshal.html#module-Marshal-label-Security+considerations](http://ruby-doc.org/core-2.3.3/Marshal.html#module-Marshal-label-Security+considerations)

## Security/Open

Enabled by default | Supports autocorrection
--- | ---
Enabled | No

This cop checks for the use of `Kernel#open`.
`Kernel#open` enables not only file access but also process invocation
by prefixing a pipe symbol (e.g., `open("| ls")`). So, it may lead to
a serious security risk by using variable input to the argument of
`Kernel#open`. It would be better to use `File.open` or `IO.popen`
explicitly.

### Examples

```ruby
# bad
open(something)

# good
File.open(something)
IO.popen(something)
```

## Security/YAMLLoad

Enabled by default | Supports autocorrection
Expand Down
61 changes: 61 additions & 0 deletions spec/rubocop/cop/security/open_spec.rb
@@ -0,0 +1,61 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Security::Open do
subject(:cop) { described_class.new }

it 'registers an offense for open' do
expect_offense(<<-RUBY.strip_indent)
open(something)
^^^^ The use of `Kernel#open` is a serious security risk.
RUBY
end

it 'registers an offense for open with mode argument' do
expect_offense(<<-RUBY.strip_indent)
open(something, "r")
^^^^ The use of `Kernel#open` is a serious security risk.
RUBY
end

it 'registers an offense for open with dynamic string that is not prefixed' do
expect_offense(<<-'RUBY'.strip_indent)
open("#{foo}.txt")
^^^^ The use of `Kernel#open` is a serious security risk.
RUBY
end

it 'registers an offense for open with string that starts with a pipe' do
expect_offense(<<-'RUBY'.strip_indent)
open("| #{foo}")
^^^^ The use of `Kernel#open` is a serious security risk.
RUBY
end

it 'accepts open as variable' do
expect_no_offenses('open = something')
end

it 'accepts File.open as method' do
expect_no_offenses('File.open(something)')
end

it 'accepts open on a literal string' do
expect_no_offenses('open("foo.txt")')
end

it 'accepts open with no arguments' do
expect_no_offenses('open')
end

it 'accepts open with string that has a prefixed interpolation' do
expect_no_offenses('open "prefix_#{foo}"')
end

it 'accepts open with prefix string literal plus something' do
expect_no_offenses('open "prefix" + foo')
end

it 'accepts open with a string that interpolates a literal' do
expect_no_offenses('open "foo#{2}.txt"')
end
end