Adds Rubocop rule to detect calls to old cmd_exec API
This commit is contained in:
@@ -24,6 +24,7 @@ require:
|
||||
- ./lib/rubocop/cop/lint/module_enforce_notes.rb
|
||||
- ./lib/rubocop/cop/lint/detect_invalid_pack_directives.rb
|
||||
- ./lib/rubocop/cop/lint/detect_metadata_trailing_leading_whitespace.rb
|
||||
- ./lib/rubocop/cop/lint/detect_outdated_cmd_exec_api.rb
|
||||
|
||||
Layout/SpaceBeforeBrackets:
|
||||
Enabled: true
|
||||
@@ -676,3 +677,9 @@ Style/UnpackFirst:
|
||||
|
||||
Lint/DetectMetadataTrailingLeadingWhitespace:
|
||||
Enabled: true
|
||||
|
||||
Lint/DetectOutdatedCmdExecApi:
|
||||
Description: >-
|
||||
Detects outdated usage of cmd_exec with separate arguments.
|
||||
Use `create_process(executable, args: [], time_out: 15, opts: {})` API with an args array instead.
|
||||
Enabled: true
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Lint
|
||||
# Detects outdated usage of the `cmd_exec` API where arguments are passed as a second parameter.
|
||||
# The modern API is `create_process(executable, args: [])` which properly handles argument arrays.
|
||||
#
|
||||
# `cmd_exec` should only be used with a single static command string. When you need to pass
|
||||
# arguments or construct commands dynamically, use `create_process` instead.
|
||||
#
|
||||
# @example
|
||||
# # bad - outdated API with args parameter
|
||||
# cmd_exec('cmd.exe', '/c echo hello')
|
||||
# cmd_exec(binary, args, timeout)
|
||||
# cmd_exec("ls", "-la /tmp")
|
||||
#
|
||||
# # good - static command strings
|
||||
# cmd_exec('id -u')
|
||||
# cmd_exec('hostname')
|
||||
# cmd_exec("echo $PPID")
|
||||
#
|
||||
# # good - modern API with args array
|
||||
# create_process('cmd.exe', args: ['/c', 'echo', 'hello'])
|
||||
# create_process(binary, args: args_array, time_out: timeout)
|
||||
class DetectOutdatedCmdExecApi < Base
|
||||
MSG = 'Do not use cmd_exec with separate arguments. ' \
|
||||
"Use create_process with an args array instead use: `create_process(executable, args: [], time_out: 15, opts: {})`"
|
||||
|
||||
# Called for every method in the code
|
||||
# Checks if it's a cmd_exec call with separate arguments and registers an offense if so
|
||||
# @param node [RuboCop::AST::SendNode] The method call node being checked
|
||||
def on_send(node)
|
||||
return unless cmd_exec_with_args?(node)
|
||||
|
||||
add_offense(node, message: MSG)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Check if this is a cmd_exec call with a second argument (args parameter)
|
||||
# @param node [RuboCop::AST::SendNode]
|
||||
# @return [Boolean]
|
||||
def cmd_exec_with_args?(node)
|
||||
return false unless node.method_name == :cmd_exec
|
||||
|
||||
# cmd_exec with 2 or more arguments (cmd, args, ...) is outdated
|
||||
# cmd_exec with 1 argument (just the command) is acceptable
|
||||
node.arguments.length >= 2 && !nil_second_arg?(node)
|
||||
end
|
||||
|
||||
# Check if the second argument is explicitly nil
|
||||
# cmd_exec(cmd, nil, timeout) might be used to skip args but set timeout
|
||||
# @param node [RuboCop::AST::SendNode]
|
||||
# @return [Boolean]
|
||||
def nil_second_arg?(node)
|
||||
return false if node.arguments.length < 2
|
||||
|
||||
second_arg = node.arguments[1]
|
||||
# Use nil_type? to check if the node represents a nil literal in the code (e.g., `nil`)
|
||||
second_arg.nil_type?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,81 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop/cop/lint/detect_outdated_cmd_exec_api'
|
||||
require 'rubocop/rspec/support'
|
||||
|
||||
RSpec.describe RuboCop::Cop::Lint::DetectOutdatedCmdExecApi, :config do
|
||||
subject(:cop) { described_class.new(config) }
|
||||
|
||||
let(:config) { RuboCop::Config.new }
|
||||
|
||||
it 'registers an offense when cmd_exec is called with separate arguments' do
|
||||
expect_offense(<<~RUBY)
|
||||
cmd_exec('cmd.exe', '/c echo hello')
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Lint/DetectOutdatedCmdExecApi: Do not use cmd_exec with separate arguments. Use create_process with an args array instead use: `create_process(executable, args: [], time_out: 15, opts: {})`
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when cmd_exec is called with variable arguments' do
|
||||
expect_offense(<<~RUBY)
|
||||
cmd_exec(binary, args, timeout)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Lint/DetectOutdatedCmdExecApi: Do not use cmd_exec with separate arguments. Use create_process with an args array instead use: `create_process(executable, args: [], time_out: 15, opts: {})`
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when cmd_exec is called with command and args string' do
|
||||
expect_offense(<<~RUBY)
|
||||
cmd_exec("ls", "-la /tmp")
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ Lint/DetectOutdatedCmdExecApi: Do not use cmd_exec with separate arguments. Use create_process with an args array instead use: `create_process(executable, args: [], time_out: 15, opts: {})`
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when cmd_exec is called with command and timeout without explicit nil' do
|
||||
expect_offense(<<~RUBY)
|
||||
cmd_exec('cmd.exe', "/c \#{rasdial_cmd}", 60)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Lint/DetectOutdatedCmdExecApi: Do not use cmd_exec with separate arguments. Use create_process with an args array instead use: `create_process(executable, args: [], time_out: 15, opts: {})`
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense when cmd_exec is called with a single static command' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
cmd_exec('id -u')
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense when cmd_exec is called with a single command string' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
cmd_exec('hostname')
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense when cmd_exec is called with a single interpolated command' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
cmd_exec("echo $PPID")
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense when cmd_exec is called with nil as second argument' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
cmd_exec(cmd, nil, timeout)
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense when cmd_exec is called with explicit nil args and timeout' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
cmd_exec("./\#{exploit_name} \#{arg}", nil, timeout)
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense for create_process calls' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
create_process('cmd.exe', args: ['/c', 'echo', 'hello'])
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense for create_process with variable args' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
create_process(binary, args: args_array, time_out: timeout)
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user