Add rubocop rule
This commit is contained in:
@@ -26,6 +26,7 @@ require:
|
|||||||
- ./lib/rubocop/cop/lint/detect_metadata_trailing_leading_whitespace.rb
|
- ./lib/rubocop/cop/lint/detect_metadata_trailing_leading_whitespace.rb
|
||||||
- ./lib/rubocop/cop/lint/detect_outdated_cmd_exec_api.rb
|
- ./lib/rubocop/cop/lint/detect_outdated_cmd_exec_api.rb
|
||||||
- ./lib/rubocop/cop/lint/datastore_srvhost_usage.rb
|
- ./lib/rubocop/cop/lint/datastore_srvhost_usage.rb
|
||||||
|
- ./lib/rubocop/cop/lint/bare_check_code_in_non_exploit.rb
|
||||||
|
|
||||||
Layout/SpaceBeforeBrackets:
|
Layout/SpaceBeforeBrackets:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
@@ -684,3 +685,14 @@ Lint/DetectOutdatedCmdExecApi:
|
|||||||
Detects outdated usage of cmd_exec with separate arguments.
|
Detects outdated usage of cmd_exec with separate arguments.
|
||||||
Use `create_process(executable, args: [], time_out: 15, opts: {})` API with an args array instead.
|
Use `create_process(executable, args: [], time_out: 15, opts: {})` API with an args array instead.
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
|
Lint/BareCheckCodeInNonExploit:
|
||||||
|
Description: >-
|
||||||
|
Use Exploit::CheckCode instead of bare CheckCode in non-exploit modules.
|
||||||
|
Bare CheckCode will raise a NameError at runtime in auxiliary, post, and evasion modules
|
||||||
|
because CheckCode is defined inside Msf::Exploit which is not in their ancestor chain.
|
||||||
|
Enabled: true
|
||||||
|
Include:
|
||||||
|
- 'modules/auxiliary/**/*'
|
||||||
|
- 'modules/post/**/*'
|
||||||
|
- 'modules/evasion/**/*'
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Lint
|
||||||
|
# Detects usage of bare `CheckCode::*` without the `Exploit::` prefix in
|
||||||
|
# auxiliary, post, and evasion modules.
|
||||||
|
#
|
||||||
|
# These modules inherit from `Msf::Auxiliary`, `Msf::Post`, or `Msf::Evasion` — not
|
||||||
|
# `Msf::Exploit` — so bare `CheckCode::*` will raise a `NameError` at runtime because
|
||||||
|
# `CheckCode` is defined inside `Msf::Exploit` and is not in the ancestor chain.
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# # bad - raises NameError at runtime
|
||||||
|
# CheckCode::Safe
|
||||||
|
# CheckCode::Vulnerable
|
||||||
|
# CheckCode::Appears('message')
|
||||||
|
#
|
||||||
|
# # good
|
||||||
|
# Exploit::CheckCode::Safe
|
||||||
|
# Exploit::CheckCode::Vulnerable
|
||||||
|
# Exploit::CheckCode::Appears('message')
|
||||||
|
#
|
||||||
|
# # also acceptable
|
||||||
|
# Msf::Exploit::CheckCode::Safe
|
||||||
|
class BareCheckCodeInNonExploit < Base
|
||||||
|
extend AutoCorrector
|
||||||
|
|
||||||
|
MSG = 'Use `Exploit::CheckCode` instead of bare `CheckCode` in non-exploit modules. ' \
|
||||||
|
'Bare `CheckCode` will raise a NameError at runtime.'
|
||||||
|
|
||||||
|
# Matches bare `CheckCode::Something` (e.g. CheckCode::Safe)
|
||||||
|
# but NOT `Exploit::CheckCode::Something` or `Msf::Exploit::CheckCode::Something`
|
||||||
|
def_node_matcher :bare_check_code?, <<~PATTERN
|
||||||
|
(const
|
||||||
|
(const nil? :CheckCode) _)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
# Matches bare `CheckCode::Something(...)` method calls.
|
||||||
|
# e.g. CheckCode::Appears('msg') parses as:
|
||||||
|
# (send (const nil :CheckCode) :Appears (str "msg"))
|
||||||
|
def_node_matcher :bare_check_code_call?, <<~PATTERN
|
||||||
|
(send
|
||||||
|
(const nil? :CheckCode) ...)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def on_const(node)
|
||||||
|
return unless in_non_exploit_module?(node)
|
||||||
|
return unless bare_check_code?(node)
|
||||||
|
# Don't flag if the parent is already a higher const (avoid double-flagging)
|
||||||
|
return if node.parent&.const_type? && node.parent.children.first == node
|
||||||
|
|
||||||
|
add_offense(node) do |corrector|
|
||||||
|
check_code_const = find_root_const(node)
|
||||||
|
corrector.insert_before(check_code_const, 'Exploit::') if check_code_const
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_send(node)
|
||||||
|
return unless in_non_exploit_module?(node)
|
||||||
|
return unless bare_check_code_call?(node)
|
||||||
|
|
||||||
|
add_offense(node.receiver) do |corrector|
|
||||||
|
check_code_const = find_root_const(node.receiver)
|
||||||
|
corrector.insert_before(check_code_const, 'Exploit::') if check_code_const
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Walk up the AST to find if we're inside a class that inherits from
|
||||||
|
# Msf::Auxiliary, Msf::Post, or Msf::Evasion
|
||||||
|
def in_non_exploit_module?(node)
|
||||||
|
node.each_ancestor(:class).any? do |class_node|
|
||||||
|
superclass = class_node.parent_class
|
||||||
|
next false unless superclass
|
||||||
|
|
||||||
|
superclass_name = superclass.source
|
||||||
|
superclass_name.match?(/\bMsf::(Auxiliary|Post|Evasion)\b/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find the root const node in a const chain (the leftmost constant)
|
||||||
|
def find_root_const(node)
|
||||||
|
current = node
|
||||||
|
while current.const_type? && current.children.first&.const_type?
|
||||||
|
current = current.children.first
|
||||||
|
end
|
||||||
|
current
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rubocop/cop/lint/bare_check_code_in_non_exploit'
|
||||||
|
require 'rubocop/rspec/support'
|
||||||
|
|
||||||
|
RSpec.describe RuboCop::Cop::Lint::BareCheckCodeInNonExploit, :config do
|
||||||
|
subject(:cop) { described_class.new(config) }
|
||||||
|
|
||||||
|
let(:config) { RuboCop::Config.new }
|
||||||
|
|
||||||
|
context 'in an auxiliary module' do
|
||||||
|
it 'registers an offense for bare CheckCode::Safe' do
|
||||||
|
expect_offense(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
def check
|
||||||
|
CheckCode::Safe
|
||||||
|
^^^^^^^^^^^^^^^ Lint/BareCheckCodeInNonExploit: Use `Exploit::CheckCode` instead of bare `CheckCode` in non-exploit modules. Bare `CheckCode` will raise a NameError at runtime.
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
expect_correction(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
def check
|
||||||
|
Exploit::CheckCode::Safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'registers an offense for bare CheckCode::Unknown' do
|
||||||
|
expect_offense(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
def check
|
||||||
|
CheckCode::Unknown
|
||||||
|
^^^^^^^^^^^^^^^^^^ Lint/BareCheckCodeInNonExploit: Use `Exploit::CheckCode` instead of bare `CheckCode` in non-exploit modules. Bare `CheckCode` will raise a NameError at runtime.
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
expect_correction(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
def check
|
||||||
|
Exploit::CheckCode::Unknown
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'registers an offense for bare CheckCode::Appears with a message argument' do
|
||||||
|
expect_offense(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
def check
|
||||||
|
CheckCode::Appears('Version is vulnerable')
|
||||||
|
^^^^^^^^^ Lint/BareCheckCodeInNonExploit: Use `Exploit::CheckCode` instead of bare `CheckCode` in non-exploit modules. Bare `CheckCode` will raise a NameError at runtime.
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
expect_correction(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
def check
|
||||||
|
Exploit::CheckCode::Appears('Version is vulnerable')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'registers an offense for bare CheckCode::Vulnerable with details kwarg' do
|
||||||
|
expect_offense(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
def check
|
||||||
|
CheckCode::Vulnerable(details: { version: '1.0' })
|
||||||
|
^^^^^^^^^ Lint/BareCheckCodeInNonExploit: Use `Exploit::CheckCode` instead of bare `CheckCode` in non-exploit modules. Bare `CheckCode` will raise a NameError at runtime.
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
expect_correction(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
def check
|
||||||
|
Exploit::CheckCode::Vulnerable(details: { version: '1.0' })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not register an offense for Exploit::CheckCode::Safe' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
def check
|
||||||
|
Exploit::CheckCode::Safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not register an offense for Exploit::CheckCode::Safe' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
def check
|
||||||
|
Exploit::CheckCode::Safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not register an offense for Exploit::CheckCode::Appears with message' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Auxiliary
|
||||||
|
def check
|
||||||
|
Exploit::CheckCode::Appears('Version is vulnerable')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'in a post module' do
|
||||||
|
it 'registers an offense for bare CheckCode::Safe' do
|
||||||
|
expect_offense(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Post
|
||||||
|
def check
|
||||||
|
CheckCode::Safe
|
||||||
|
^^^^^^^^^^^^^^^ Lint/BareCheckCodeInNonExploit: Use `Exploit::CheckCode` instead of bare `CheckCode` in non-exploit modules. Bare `CheckCode` will raise a NameError at runtime.
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
expect_correction(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Post
|
||||||
|
def check
|
||||||
|
Exploit::CheckCode::Safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'in an evasion module' do
|
||||||
|
it 'registers an offense for bare CheckCode::Safe' do
|
||||||
|
expect_offense(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Evasion
|
||||||
|
def check
|
||||||
|
CheckCode::Safe
|
||||||
|
^^^^^^^^^^^^^^^ Lint/BareCheckCodeInNonExploit: Use `Exploit::CheckCode` instead of bare `CheckCode` in non-exploit modules. Bare `CheckCode` will raise a NameError at runtime.
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
expect_correction(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Evasion
|
||||||
|
def check
|
||||||
|
Exploit::CheckCode::Safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'in an exploit module' do
|
||||||
|
it 'does not register an offense for bare CheckCode::Safe' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
class MetasploitModule < Msf::Exploit
|
||||||
|
def check
|
||||||
|
CheckCode::Safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'outside a module class' do
|
||||||
|
it 'does not register an offense for bare CheckCode::Safe' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
CheckCode::Safe
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user