Agile Web Development

Build it. Launch it. Love it.

Acts as sanitiled

This plugin, based on Chris Wanstrath’s venerable acts_as_textiled, extends the automatic textiling functionality to sanitization as well using as its basis Ryan Grove’s powerful yet simple Sanitize gem.

The reasoning behind this approach is simple. Filtering input before it is saved to the database (as xss_terminate and many other popular plugins do) often fails to preserve user intent. On the other hand, filtering output at the template level is error prone, and you are begging to get pwned. Short of some sort of taint mode (which Rails 3 will have!), I believe the method employed by acts_as_textiled is the next best thing: you get safe output by default, but input is never corrupted.

Requirements

  • Sanitize >1.1.0 (prior versions had a whitespace issue)
  • Nokogiri >1.3.3
  • RedCloth (for Textile support)
  • ActiveRecord (tested on 2.3.4)

Installation

acts_as_sanitiled is distributed via Gemcutter. If you are enlightened you can simply do:

  gem install acts_as_sanitiled

If you haven’t checkout out Gemcutter yet:

  gem install --source http://gemcutter.org acts_as_sanitiled

Then in your Rails environment.rb:

  config.gem 'acts_as_sanitiled'

Changes from acts_as_textiled

acts_as_sanitiled mostly maintains the API, but one noticeable difference is that it needs to expose the Sanitize config. Therefore acts_as_textiled use of a hash to provide per-column RedCloth configuration had to be replaced with Sanitize config. RedCloth options can still be passed as an array that applies to all fields listed.

The other big change is that acts_as_sanitiled uses Sanitize which outputs utf8 rather than HTML entities. For my own purposes this is preferable anyway, but it might give someone a few headaches getting encoding issues. My advice: take your lumps now and figure out your encoding pipelines.

Usage

  class Story < ActiveRecord::Base
    acts_as_sanitiled :body_text, :description
  end

  >> story = Story.find(3)
  => #<Story:0x245fed8 ... >

  >> story.description
  => "<p>This is <strong>cool</strong>.</p>"

  >> story.description(:source)
  => "This is *cool*."

  >> story.description(:plain)
  => "This is cool."

  >> story.description = "I _know_!"
  => "I _know_!"

  >> story.save
  => true

  >> story.description
  => "<p>I <em>know</em>!</p>"

  >> story.textiled = false
  => false

  >> story.description
  => "I _know_!"

  >> story.textiled = true
  => true

  >> story.description
  => "<p>I <em>know</em>!</p>"

Different Modes

Sanitize supports a detailed configuration hash describing what HTML is allowed (among other things). This can be passed at the end of the declaration. See the Sanitize docs for more information.

  class Story < ActiveRecord::Base
    acts_as_sanitiled :body_text, :elements => ['em','strong','div'], :attributes => {'div' => ['class','id']}
  end

RedCloth supports different modes, such as :lite_mode. To use a mode on a specific attribute simply pass one or more options in an array after the field names. Like so:

  class Story < ActiveRecord::Base
    acts_as_sanitiled :body_text, :description, [ :lite_mode ]
  end

Of course you can combine them as well:

  class Story < ActiveRecord::Base
    acts_as_sanitiled :body_text, :description, [ :lite_mode ], :elements => ['a'], :add_attributes => {'a' => {'rel' => 'nofollow'}}
  end

Suppose you want to sanitize but not textilize:

  class Story < ActiveRecord::Base
    acts_as_sanitized :body_text, :elements => ['br', 'p']
  end

Or vice-versa:

  class Story < ActiveRecord::Base
    acts_as_textilized :body_text, [ :lite_mode ]
  end

Get it? Now let’s say you have an admin tool and you want the text to be displayed in the text boxes / fields as plaintext. Do you have to change all your views?

Hell no.

form_for

Are you using form_for? If you are, you don’t have to change any code at all.

  <% form_for :story, @story do |f| %>
    Description: <br/> <%= f.text_field :description %>
  <% end %>

You’ll see the Textile plaintext in the text field. It Just Works.

form tags

If you’re being a bit unconvential, no worries. You can still get at your raw Textile like so:

  Description: <br/> <%= text_field_tag :description, @story.description(:source) %>

And there’s always object.textiled = false, as demo’d above.

Pre-fetching

acts_as_sanitiled locally caches rendered HTML once the attribute in question has been requested. Obviously this doesn’t bode well for marshalling or caching.

If you need to force your object to build and cache HTML for all textiled attributes, call the textilize method on your object.

If you’re real crazy you can even do something like this:

  class Story < ActiveRecord::Base
    acts_as_sanitiled :body_text, :description

    def after_find
      textilize
    end
  end

All your Textile will now be ready to go in spiffy HTML format. But you probably won’t need to do this.

Enjoy.

  • By Chris Wanstrath [ chris[at]ozmm[dot]org ]
  • Butchered and Sanitized by Gabe da Silveira [ gabe[at]websaviour[dot]com ]

Vitals

Home http://github.com/dasil003/acts_as_sanitiled
Repository git://github.com/dasil003/acts_as_sanitiled.git
License Rails' (MIT)
Tags Tag_red
Rating (0 votes)
Owner Gabe da Silveira
Created 15 October 2009

Comments

Add a comment