Feature #6409

public_send is easily bypassed

Added by postmodern (Hal Brodigan) over 8 years ago. Updated over 8 years ago.

Target version:


(({public_send})) can easily be bypassed, by using it to call (({send})). (({public_send})) should explicitly not allow calling (({send})).

class Test

  def secret
    "top secret"

t =

# => NoMethodError: private method `secret' called for #<Test:0x0000000159b950>

t.public_send(:send, :secret)
# => "top secret"

t.public_send(:send, :exec, "rm -rf ~")



Updated by marcandre (Marc-Andre Lafortune) over 8 years ago

  • Tracker changed from Bug to Feature

This is definitely not a bug, as send is public.

I don't understand the rationale behind your request. You are still using send. public_send does not and cannot guarantee that a private method won't be called at some point; only that it won't send the message in case it's a not a public method.

Updated by postmodern (Hal Brodigan) over 8 years ago

(({public_send})) should only allow calling public methods. By extension, it should not allow calling (({send})), since that would negate the purpose of (({public_send})). In the context of (({public_send})), the (({send})) method has special meaning.

Updated by jeremyevans0 (Jeremy Evans) over 8 years ago

I see no reason to special case this. send is a public method, therefore public_send should be allowed to call it. Attempting to deny access to send for safety reasons is pointless considering that instance_eval is public can be used to work around the issue in the same way:

t.public_send(:instance_eval, 'secret')
t.public_send(:instance_eval, 'exec("rm -rf ~")')

public_send doesn't imply safety, at all, and it was not designed for such a purpose.

Updated by matz (Yukihiro Matsumoto) over 8 years ago

  • Status changed from Open to Rejected

The whole purpose of public_send is to prohibit the invocation of non-public methods, probably to help detecting error earlier. In that sense, as Jeremy expressed, we see no reason to prohibit #send the public method. public_send (and method visibility in general) is not the way to ensure anything, e.g. security.


Updated by alexeymuranov (Alexey Muranov) over 8 years ago

postmodern (Hal Brodigan), send is a public method, why would public_send refuse to call it? Were you suggesting to remove the send method?

Updated by MartinBosslet (Martin Bosslet) over 8 years ago

Wouldn't something as proposed in help in the long run?

Updated by postmodern (Hal Brodigan) over 8 years ago

Now I know public_send should not be trusted with arbitrary method names/arguments. Is there even a safe version of send?

Updated by alexeymuranov (Alexey Muranov) over 8 years ago

Maybe something like:

class SafeClass
METHOD_SAFE = { :safe_method_1 => true, :safe_method_2 => true }

def safe_send(method, *arguments)
  send(method, *arguments) if METHOD_SAFE[method]


But this is not completely safe either, because anybody can reopen this class later and change the (({METHOD_SAFE})) constant or even the (({safe_send})) method.

Also available in: Atom PDF