使用eval或public_send执行动态方法

时间:2019-06-04 17:59:22

标签: ruby-on-rails ruby

我有执行动态方法的这些代码。我在这里使用eval来执行它,但是我想做的就是将其更改为public_send,因为这样告诉我,它更安全。

当前代码:

  # update workstep logic here.
        incoming_status = params[params[:name]]

        # grab workflow, this is current data, use this to compare status to in comming status
        workflow = get_workorder_product_workstep(params[:workflow_id])

        # check current status if its pending allow to update
        # security concern EVAL!

        if eval("workflow.can_#{incoming_status}?")
          # update status
          eval("workflow.#{incoming_status}")
          # updated attribute handled_by
          workflow.update_attributes(handled_by_id: @curr_user.id)
          workflow.save
        else
          flash[:notice] = 'Action not allowed'
        end

这里是eval。如何将其更改为public_send

这是我的工作。

public_send("workflow.can_#{incoming_status}?")

public_send("#{workflow}.can_#{incoming_status}?")

它们都不起作用。给我没有方法的错误。第一个公共错误返回此undefined method workflow.can_queue? for #<Spree::Admin::WorkordersController:0x00007ff71c8e6f00>

但是它应该工作,因为我有方法workflow.can_queue?

公开的第二个错误是 undefined method #<Spree::WorkorderProductWorkstep:0x00007ff765663550>.can_queue? for #<Spree::Admin::WorkordersController:0x00007ff76597f798>

我认为第二个workflow正在单独评估?我不确定。

1 个答案:

答案 0 :(得分:4)

使用public_send,您可以将相关行更改为:

if workflow.public_send("can_#{incoming_status}?")
  # update status
  workflow.public_send(incoming_status.to_s)
    # ...

有关安全性和风险的说明

workflow.public_send("can_#{xyz}?")只能在workflow上调用public且以前缀can_开头并以?结尾的方法。那可能只是少数方法,您可以轻松决定是否要允许所有这些方法。

workflow.public_send("#{incoming_status'})有所不同,因为它允许workflow甚至destroy上的所有公共方法。这意味着在没有"can_#{incoming_status}?"的情况下使用它可能是一个坏主意。或者,您至少应该首先检查incoming_status是否在允许的方法的白名单中。

eval是最糟糕的,因为它将在没有任何上下文的情况下评估整个字符串(例如workflow之类的对象)。对您拥有eval("workflow.#{incoming_status}")的映像进行检查,无需先检查是否实际允许incoming_status。如果有人随后像这样发送incoming_status "to_s; system('xyz')",那么xyz可能就是一切-例如通过电子邮件发送隐藏文件,安装后门或删除某些文件的命令。