Class: RuboCop::Cop::DevDoc::Style::AvoidInsecureSend
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::DevDoc::Style::AvoidInsecureSend
- Defined in:
- lib/rubocop/cop/dev_doc/style/avoid_insecure_send.rb
Overview
Avoid insecure 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.
public_send respects method visibility, but still allows calling
any public method. When the method name is dynamic, a prefix restricts
the callable surface to methods sharing that prefix.
Rules
send() with a dynamic argument is always flagged. There is no
safe use case for dynamic send — use public_send instead, and
disable with a justification when bypassing visibility is intentional.
public_send() with a dynamic argument is allowed when:
- The argument is a literal symbol or string (method name is fixed at code-write time).
- The call uses a prefix pattern (
"prefix_#{method_name}") that restricts callable methods. - The call is in a validator file (
app/validators/) — method names come from model DSL, not user input. - The call is a mailer dispatch — method names are trusted internal symbols.
public_send() with a dynamic argument is flagged when:
- The argument is an unrestricted variable or interpolation with no static prefix.
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.public_send(method_name)
✔️ Restricted — only methods with the prefix can be called
obj.public_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_SEND =
"Avoid dynamic `send` — use `public_send` instead, or " \ "bracket notation for model attributes. Disable with a " \ "justification when bypassing visibility is intentional.".freeze
- MSG_PUBLIC_SEND =
"Avoid unrestricted dynamic `public_send` — use a " \ "prefix (`obj.public_send(\"export_\#{x}\")`) to " \ "restrict callable methods, or bracket notation for " \ "model attributes.".freeze
- RESTRICT_ON_SEND =
__send__is the canonical alias — omitting it would leave a zero-cost dodge for the exact dynamic dispatch this cop restricts. %i[send public_send __send__].freeze
Instance Method Summary collapse
Instance Method Details
#on_send(node) ⇒ Object
98 99 100 101 102 103 104 |
# File 'lib/rubocop/cop/dev_doc/style/avoid_insecure_send.rb', line 98 def on_send(node) return if node.receiver.nil? return if literal_argument?(node) return if prefixed_dynamic_method?(node) check_send(node) end |