Class: RuboCop::Cop::DevDoc::Style::AvoidSend

Inherits:
Base
  • Object
show all
Defined in:
lib/rubocop/cop/dev_doc/style/avoid_send.rb

Overview

Avoid dynamic ‘send` and `public_send` with an explicit receiver.

## Rationale ‘send()` can call any method, including destructive ones like `destroy`. The risk is specifically with dynamic method names —when the argument is a variable or interpolated string, a crafted value could invoke methods the developer never intended to expose.

**Literal symbol arguments are exempt** — the method name is fixed at code-write time and visible to reviewers, equivalent to a direct call.

## Safer alternatives

**a) For model attributes — use bracket notation instead.** ‘@model` only accesses database columns, so it cannot accidentally invoke methods like `destroy`.

 Dangerous  method_name could be :destroy or any other method
@user.send(method_name)

✔️ Safe  only accesses database columns
@user[method_name]

**b) For non-model objects — use a prefix to restrict callable methods.** By interpolating the dynamic part into a fixed prefix, only methods with that prefix (e.g. ‘export_csv`, `export_pdf`) can be invoked, preventing accidental calls to unintended methods.

 Unrestricted  any method can be called
obj.send(method_name)

✔️ Restricted  only methods with the prefix can be called
obj.send("export_#{method_name}")

Examples:

# bad
@user.send(method_name)
obj.public_send(action)
obj.send("export_#{x}")

# good — literal symbol: method name is statically visible
instance.send(:private_helper, arg)

# good
@user[attribute_name]
obj.send("export_#{method_name}")

Constant Summary collapse

MSG =
"Avoid dynamic `%<method>s` — use bracket notation for model attributes, " \
"or a prefix (`obj.send(\"export_\#{x}\")`) to restrict callable methods.".freeze
RESTRICT_ON_SEND =
%i[send public_send].freeze

Instance Method Summary collapse

Instance Method Details

#on_send(node) ⇒ Object



56
57
58
59
60
61
# File 'lib/rubocop/cop/dev_doc/style/avoid_send.rb', line 56

def on_send(node)
  return if node.receiver.nil?
  return if node.first_argument&.sym_type?

  add_offense(node.loc.selector, message: format(MSG, method: node.method_name))
end