← Home

An ERB Safemode handler for ActionView

You can try things out like this:

  1. create a new Rails application
  2. install the plugin: http://svn.artweb-design.de/stuff/ruby/safemode/
  3. add a controller, model and a view, like UserController, User and index.html.erb

For example, these would work:


# controllers/users_controller.rb
class UserController < ApplicationController
  def index
    @user = User.new('Sven')
  end
end

# models/user.rb
class User
  attr_reader :name
  def initialize(name)
    @name = name
  end
end

# views/user/index.rb
<p><%= @user.name %></p>

At this point, this is just plain ERB rendered like in any other Rails application. You should see the user name output when you render /user.

Now, with the ERB safemode handler we can restrict what users can access in their ERB templates in a whitelisted manner. You can turn it on by registering the handler to ActionView:


# config/initializers/erb_safemode.rb
ActionView::Base.register_default_template_handler :erb,
  ActionView::TemplateHandlers::SafeErb

When you now try to render /user you'll see an empty page and an exception thrown on the console that says: undefined method 'name' for Safemode::Jail (User).

From that you can see that the user object has been wrapped into a Safemode::Jail proxy which restricts the access to the methods on the object. To allow the user to access the name method you can add this to the User model:


class User
  class Jail < Safemode::Jail
    allow :name
  end
end

You should also be able to wrap that into a layout and use Rails helper methods:


# layouts/user.html.erb
<%= stylesheet_link_tag 'default' %>
<%= yield %>

What's missing

The one issue with this that I just could not get my head wrapped around yet is the borked error handling.

If you've followed the steps above you've already seen that right now the template handler rescues exceptions itself and just prints them to the console (see the handler code here).

That, of course, is not too nice. If I remove that rescue block from there, though, and an exception gets raised (e.g. remove the Jail from the User model) then I'm getting no response from the server at all. Not a blank page, just no response.


curl -I http://localhost:3000/user

... just hangs and says "(7) couldn't connect to host" after a while. Safari tells me the same while the log only states that is has started "Rendering user/index", but ths usual execution time summary ("Completed in ...") is missing.

Hu?

My first idea was that Rails tried to use the safemode handler to render the error page and ran into another error in that stage. So I tried to register the handler for *.serb.html and renamed the views ... same result.

If you happen to have an idea how to fix this that would be very appreciated! :)