Feature #6409

public_send is easily bypassed

Added by postmodern (Hal Brodigan) about 6 years ago. Updated about 6 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 ~")



#1 Updated by marcandre (Marc-Andre Lafortune) about 6 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.

#2 [ruby-core:44934] Updated by postmodern (Hal Brodigan) about 6 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.

#3 [ruby-core:44935] Updated by jeremyevans0 (Jeremy Evans) about 6 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.

#4 [ruby-core:44936] Updated by matz (Yukihiro Matsumoto) about 6 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.


#5 [ruby-core:44937] Updated by alexeymuranov (Alexey Muranov) about 6 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?

#6 [ruby-core:44938] Updated by MartinBosslet (Martin Bosslet) about 6 years ago

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

#7 [ruby-core:44939] Updated by postmodern (Hal Brodigan) about 6 years ago

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

#8 [ruby-core:44940] Updated by alexeymuranov (Alexey Muranov) about 6 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