Put HTML tags and apostrophes in fixtures and tests or a meanie will hack you.

Here’s a good way to protect against cross-site scripting attacks and SQL injection attacks. This will help catch mistakes where you (well actually your teammate, since you’re perfect) forgot to call “h” in a <%= %> block, or accidentally passed a SQL statement to the database without escaping the values:

Sprinkle unclosed HTML tags and apostrophes all over your fixture data and test code.

Then use assert_select liberally, which will barf on the console if it sees unclosed HTML tags–even if you were selecting some other part of the document.

I Like Stuff that’s Safe

Here is what a posts.yml file might look like:

test_post:
id: 1
  subject: <script> attack!
  detail: "sql injection: '; drop table posts;"

(If you use an apostrophe in YAML you have to quote the whole string.)

So assert_select has this handy side-effect I mentioned where it tells you about your malformed HTML. Since Rails tests don’t actually run in a browser, you need some other way to know that you’ve forgotten to escape data. Unclosed HTML tags in your fixtures, yeah, that’s the ticket.

And remember, you don’t need to call assert_select on the element that contains the bad data. Just call assert_select on anything and it will parse the output to make sure it’s well-formed.

  def test_show
    post = posts(:test_post)
    get :show, post.id
    assert_select "body"
  end

The idea is that by sprinkling XSS attacks through your fixtures and using assert_select whenever you’re testing other stuff, the XSS attacks will become apparent.

If you do need to assert that the output is correct, you can call CGI::escapeHTML:

  def test_show
    post = posts(:test_post)
    get :show, post.id
    assert_select "span", :count => 1,
      :text => CGI::escapeHTML(post.detail)
  end

I can’t haz SQL injection attacks

I admit that putting SQL injection attacks in the fixtures is a bit contrived and may not help. A better way to catch SQL injection attacks is to pass apostrophes into the app from your test code, so go ahead and sprinkle your test code with beauties like this:

  def test_update
    post :update, posts(:test_post).id,
      :detail => "sql injection: '; drop table posts;"
  end

The secret to making this work is:

  1. apostrophe
  2. semicolon
  3. SQL statement
  4. another semicolon

You want to use a SQL statement that will cause a test to fail. It would be coolio if there were some way to make the current test succeed and subsequent tests fail, but I’m not sure I know a way to do that consistently. But at least if you use a “drop table” statement, you’re going to cause subsequent tests to fail (if there are any subsequent tests that use that table) because a schema change does not happen in a transaction. So even if you’re using transactional fixtures, the next test will fail anyway cuz the dang table is gone.

4 thoughts on “Put HTML tags and apostrophes in fixtures and tests or a meanie will hack you.”

  1. If you put several layers of protection in, you’re less likely to suffer from XSS attacks. Use the whitelist plugin, and use Erubis with the default escape text. There’s also a couple of plugins that validate models for HTML.

  2. @Will: You no longer need to use the WhiteList plugin as it’s functionality has been integrated into Rails’ own sanitize method. The days of avoiding sanitize are over if you are on Rails 2.2+

  3. “schema change does not happen in a transaction”

    True except when dealing with postgresql which supports DDL transactions

  4. True anon, but my point was in this case it’s *good* to not have schema changes run in a transaction. And I’m sure it’s easy enough in postgresql to have your schema changes not run in a transaction.

Leave a Reply

Your email address will not be published. Required fields are marked *

Feel free to use <a>, <b>, <i>, <strong>, <em>, <strike>, <code>.

Code blocks:
[code language="ruby/javascript/html/css/sass/bash"]
[/code]