Skip to content

Commit

Permalink
Make Security/Open aware of URI.open
Browse files Browse the repository at this point in the history
This PR makes `Security/Open` aware of `URI.open` and
tweaks the doc.
`URI.open` has the same security risk as `Kernel#open`.

## `Kernel#open`

```console
% ruby -ve "p open('| ls').read"
ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin19]
"CHANGELOG.md\nCODE_OF_CONDUCT.md\nCONTRIBUTING.md\n...
```

## `URI.open`

```console
% ruby -ropen-uri -ve "p URI.open('| ls').read"
ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin19]
"CHANGELOG.md\nCODE_OF_CONDUCT.md\nCONTRIBUTING.md\n...
```
  • Loading branch information
koic committed Oct 16, 2020
1 parent eeb00eb commit 7da3629
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -16,6 +16,7 @@
* [#7966](https://github.com/rubocop-hq/rubocop/issues/7966): **(Breaking)** Enable all pending cops for RuboCop 1.0. ([@koic][])
* [#8490](https://github.com/rubocop-hq/rubocop/pull/8490): **(Breaking)** Change logic for cop department name computation. Cops inside deep namespaces (5 or more levels deep) now belong to departments with names that are calculated by joining module names starting from the third one with slashes as separators. For example, cop `Rubocop::Cop::Foo::Bar::Baz` now belongs to `Foo/Bar` department (previously it was `Bar`). ([@dsavochkin][])
* [#8692](https://github.com/rubocop-hq/rubocop/pull/8692): Default changed to disallow `Layout/TrailingWhitespace` in heredoc. ([@marcandre][])
* [#8894](https://github.com/rubocop-hq/rubocop/issues/8894): Make `Security/Open` aware of `URI.open`. ([@koic][])

## 0.93.1 (2020-10-12)

Expand Down
3 changes: 2 additions & 1 deletion config/default.yml
Expand Up @@ -2415,9 +2415,10 @@ Security/MarshalLoad:
VersionAdded: '0.47'

Security/Open:
Description: 'The use of Kernel#open represents a serious security risk.'
Description: 'The use of `Kernel#open` and `URI.open` represent a serious security risk.'
Enabled: true
VersionAdded: '0.53'
VersionChanged: '0.94'
Safe: false

Security/YAMLLoad:
Expand Down
15 changes: 8 additions & 7 deletions docs/modules/ROOT/pages/cops_security.adoc
Expand Up @@ -117,23 +117,24 @@ Marshal.load(Marshal.dump({}))
| No
| No
| 0.53
| -
| 0.94
|===

This cop checks for the use of `Kernel#open`.
This cop checks for the use of `Kernel#open` and `URI.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`, `IO.popen` or
`URI#open` explicitly.
`Kernel#open` and `URI.open` enable 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` and `URI.open`. It would be better to use
`File.open`, `IO.popen` or `URI.parse#open` explicitly.

=== Examples

[source,ruby]
----
# bad
open(something)
URI.open(something)
# good
File.open(something)
Expand Down
22 changes: 12 additions & 10 deletions lib/rubocop/cop/security/open.rb
Expand Up @@ -3,35 +3,37 @@
module RuboCop
module Cop
module Security
# This cop checks for the use of `Kernel#open`.
# This cop checks for the use of `Kernel#open` and `URI.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`, `IO.popen` or
# `URI#open` explicitly.
# `Kernel#open` and `URI.open` enable 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` and `URI.open`. It would be better to use
# `File.open`, `IO.popen` or `URI.parse#open` explicitly.
#
# @example
# # bad
# open(something)
# URI.open(something)
#
# # good
# File.open(something)
# IO.popen(something)
# URI.parse(something).open
class Open < Base
MSG = 'The use of `Kernel#open` is a serious security risk.'
MSG = 'The use of `%<receiver>sopen` is a serious security risk.'
RESTRICT_ON_SEND = %i[open].freeze

def_node_matcher :open?, <<~PATTERN
(send nil? :open $!str ...)
(send ${nil? (const {nil? cbase} :URI)} :open $!str ...)
PATTERN

def on_send(node)
open?(node) do |code|
open?(node) do |receiver, code|
return if safe?(code)

add_offense(node.loc.selector)
message = format(MSG, receiver: receiver ? "#{receiver.source}." : 'Kernel#')
add_offense(node.loc.selector, message: message)
end
end

Expand Down
14 changes: 14 additions & 0 deletions spec/rubocop/cop/security/open_spec.rb
Expand Up @@ -31,6 +31,20 @@
RUBY
end

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

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

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

0 comments on commit 7da3629

Please sign in to comment.