Class: RuboCop::Cop::DevDoc::Style::AvoidSend
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::DevDoc::Style::AvoidSend
- 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[column_name] 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}")
NOTE: A prefix narrows the callable surface but does not eliminate it —
an attacker-controlled suffix can still reach any method sharing the
prefix (e.g. "export_#{x}" could hit export_and_destroy). Use the
narrowest prefix that fits, and prefer an explicit whitelist when the
set of targets is small.
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
67 68 69 70 71 72 73 74 75 |
# File 'lib/rubocop/cop/dev_doc/style/avoid_send.rb', line 67 def on_send(node) return if node.receiver.nil? arg = node.first_argument return if arg&.sym_type? return if prefixed_dynamic_method?(arg) add_offense(node.loc.selector, message: format(MSG, method: node.method_name)) end |