An ERB Safemode handler for ActionView
posted: April 22nd, 2008 · by: Sven
Just some quick notes about the safemode library I’ve been working on with the help of Peter Cooper recently. Rather than starting out with a Haml specific library Peter suggested turning this into a more widely usable tool and hacked his way to make it eat plain Ruby code as well as ERB.
Since I’ve cleaned up things a bit and started working on a Rails ActionView ERB handler so one could transparently use this library when rendering ERB templates with ActionView. Yesterday I’ve managed to render a blog index page (which I used as a sample app) through this handler for the first time.
You can try things out like this:
- create a new Rails application
- install the plugin: http://svn.artweb-design.de/stuff/ruby/safemode/
- 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! :)
max said December 5th, 2008 at 10:35 AM ¶
I had the same problem and I couldn’t fix it :(
goodwill said November 11th, 2009 at 05:58 PM ¶
This thing is kinda interesting- are you still working on this? I am using liquid… you know… that sucks…