ruby-on-railsrubyaasm

Is this use of a Ruby eval method really dangerous, and if so what's the alternative? (Rails)


Okay so I'm using AASM in my Rails app to manage workflows around my User model. In the view layer, I want to make a little dropdown that displays all the available transitions for a given user and perform that transition when clicked, which adjusts dynamically depending on what state the user is in. It works okay. Here's the basic implementation:

views/admin/users/index.html.erb:

<% user.available_transitions.each do |t| %>
  <%= link_to(t.to_s.humanize, eval("#{t}_admin_user_path(user)"), class: 'dropdown-item', :method => :post) %>
<% end %>

And in the routes file:

namespace :admin do
    ...
    resources :users do
      member do
        post 'apply'
        post 'invite_to_interview'
        post 'approve_for_training'
        ...
      end
    end
  end

Each one has a corresponding action in the controller. It isn't really worth listing each one, they just call the appropriate transition, such as @user.invite_to_interview! with some begin/rescues to catch invalid transitions due to guards, etc.

Brakeman is freaking out about the eval() method in the view. I'm not 100% sure why it's bothered by this - is it just treating all eval() methods as evil? The object that's being passed into the method (t) isn't a user input, it's based on the transitions made available by the state machine. I don't know how this is something that could be a vulnerability? Maybe this is my lack of understanding about some underlying concept though...

So two questions really:

Thank you! I appreciate the help.


Solution

  • Is there a better method for implementing this? Maybe some kind of generic transition action in the controller that has the transition passed in? Seems cleaner, interested to hear if anyone else has taken this approach.

    How about something along the lines of:

    <% user.available_transitions.each do |t| %>
      <%= link_to t.to_s.humanize, admin_user_path(user, transition: t), class: 'dropdown-item', method: :patch %>
    <% end %>
    

    Then, in the update method of your AdminUsersController (or wherever admin_user_path using the patch HTTP verb resolves to), you can test for the presence of the transition param and act accordingly. You can include, as you say, some begin/rescues to catch invalid transitions due to guards, etc.

    That way, you have only one action for all of your links.