<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>db/migrate/20081219130711_unescape_comment_fields.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -2,16 +2,25 @@ class CommentDrop &lt; BaseDrop
   include Mephisto::Liquid::UrlMethods
   
   timezone_dates :published_at, :created_at
-  liquid_attributes.push(*[:author, :author_email, :author_ip, :title])
+  liquid_attributes.push(:title) # Not sure who uses this.
 
   def initialize(source)
     super
-    @liquid.update 'is_approved' =&gt; @source.approved?, 'body' =&gt; ActionView::Base.white_list_sanitizer.sanitize(@source.body_html)
+    @liquid.update('is_approved' =&gt; @source.approved?,
+                   'body' =&gt; ActionView::Base.white_list_sanitizer.sanitize(@source.body_html))
+
+    # We used to escape these fields when we saved them to the database.
+    # Now we've unescaped everything in the database, but we still need to
+    # preserve backwards compatibility with old themes, which expect these
+    # values to be escaped.  So we escape these fields manually here.
+    [:author, :author_email, :author_ip].each do |a|
+      @liquid.update(a.to_s =&gt; CGI.escapeHTML(@source.send(a) || ''))
+    end
   end
-  
+
   def author_url
     return nil if source.author_url.blank?
-    @source.author_url =~ /^https?:\/\// ? @source.author_url : &quot;http://&quot; + @source.author_url
+    CGI.escapeHTML(@source.author_url =~ /^https?:\/\// ? @source.author_url : &quot;http://&quot; + @source.author_url)
   end
 
   def url
@@ -23,7 +32,7 @@ class CommentDrop &lt; BaseDrop
   end
 
   def author_link
-    @source.author_url.blank? ? &quot;&lt;span&gt;#{@source.author}&lt;/span&gt;&quot; : %Q{&lt;a href=&quot;#{author_url}&quot;&gt;#{@source.author}&lt;/a&gt;}
+    @source.author_url.blank? ? &quot;&lt;span&gt;#{@liquid['author']}&lt;/span&gt;&quot; : %Q{&lt;a href=&quot;#{author_url}&quot;&gt;#{@liquid['author']}&lt;/a&gt;}
   end
   
   def presentation_class</diff>
      <filename>app/drops/comment_drop.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,7 +7,6 @@ class Comment &lt; Content
   before_validation :clean_up_author_url
   after_validation_on_create  :snag_article_attributes
   before_create  :check_comment_expiration
-  before_create  :sanitize_attributes
   before_save    :update_counter_cache
   before_destroy :decrement_counter_cache
   belongs_to :article
@@ -78,12 +77,6 @@ class Comment &lt; Content
   end
 
   protected
-    def sanitize_attributes
-      [:author, :author_url, :author_email, :author_ip, :user_agent, :referrer].each do |a|
-        self.send(&quot;#{a}=&quot;, CGI::escapeHTML(self.send(a).to_s))
-      end
-    end
-
     def snag_article_attributes
       self.filter ||= article.site.filter
       [:site, :title, :published_at, :permalink].each { |a| self.send(&quot;#{a}=&quot;, article.send(a)) }</diff>
      <filename>app/models/comment.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 # This file is auto-generated from the current state of the database. Instead of editing this file, 
-# please use the migrations feature of ActiveRecord to incrementally modify your database, and
+# please use the migrations feature of Active Record to incrementally modify your database, and
 # then regenerate this schema definition.
 #
 # Note that this schema.rb definition is the authoritative source for your database schema. If you need
@@ -9,7 +9,7 @@
 #
 # It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version =&gt; 76) do
+ActiveRecord::Schema.define(:version =&gt; 20081219130711) do
 
   create_table &quot;assets&quot;, :force =&gt; true do |t|
     t.string   &quot;content_type&quot;
@@ -110,8 +110,8 @@ ActiveRecord::Schema.define(:version =&gt; 76) do
     t.integer  &quot;assets_count&quot;,                  :default =&gt; 0
   end
 
-  add_index &quot;contents&quot;, [&quot;published_at&quot;], :name =&gt; &quot;idx_articles_published&quot;
   add_index &quot;contents&quot;, [&quot;article_id&quot;, &quot;approved&quot;, &quot;type&quot;], :name =&gt; &quot;idx_comments&quot;
+  add_index &quot;contents&quot;, [&quot;published_at&quot;], :name =&gt; &quot;idx_articles_published&quot;
 
   create_table &quot;events&quot;, :force =&gt; true do |t|
     t.string   &quot;mode&quot;</diff>
      <filename>db/schema.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,7 +5,7 @@ class CommentDropTest &lt; Test::Unit::TestCase
   
   def setup
     @comment = contents(:welcome_comment).to_liquid
-    @mock_comment = [:published_at, :created_at, :author, :author_email, :author_ip, :title, :approved?].inject({:body_html =&gt; 'foo'}) { |h, i| h.update i =&gt; true }
+    @mock_comment = [:published_at, :created_at, :title, :approved?].inject({:body_html =&gt; 'foo', :author =&gt; 'Bob', :author_email =&gt; 'bob@example.com', :author_ip =&gt; '127.0.0.1' }) { |h, i| h.update i =&gt; true }
   end
   
   def test_should_convert_comment_to_drop
@@ -36,8 +36,7 @@ class CommentDropTest &lt; Test::Unit::TestCase
     assert_equal %Q{&lt;a href=&quot;https://abc&quot;&gt;rico&lt;/a&gt;}, @comment.author_link
     @comment.source.author     = '&lt;strong&gt;rico&lt;/strong&gt;'
     @comment.source.author_url = '&lt;strong&gt;https://abc&lt;/strong&gt;'
-    @comment.source.send(:sanitize_attributes)
-    assert_equal %Q{&lt;a href=&quot;http://&amp;lt;strong&amp;gt;https://abc&amp;lt;/strong&amp;gt;&quot;&gt;&amp;lt;strong&amp;gt;rico&amp;lt;/strong&amp;gt;&lt;/a&gt;}, @comment.author_link
+    assert_equal %Q{&lt;a href=&quot;http://&amp;lt;strong&amp;gt;https://abc&amp;lt;/strong&amp;gt;&quot;&gt;&amp;lt;strong&amp;gt;rico&amp;lt;/strong&amp;gt;&lt;/a&gt;}, @comment.source.to_liquid.author_link
   end
   
   def test_should_show_filtered_text
@@ -74,4 +73,4 @@ class CommentDropTest &lt; Test::Unit::TestCase
         def stub.id() 55; end
       end
     end
-end
\ No newline at end of file
+end</diff>
      <filename>test/unit/comment_drop_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>bf1a5deb79007d56901984d70eb3ad0f8122ef7a</id>
    </parent>
  </parents>
  <author>
    <name>Eric Kidd</name>
    <login>emk</login>
    <email>git@randomhacks.net</email>
  </author>
  <url>http://github.com/emk/mephisto/commit/b7cb8221e066cb55884bf941e1b421ccf6082404</url>
  <id>b7cb8221e066cb55884bf941e1b421ccf6082404</id>
  <committed-date>2008-12-19T06:26:23-08:00</committed-date>
  <authored-date>2008-12-19T06:26:23-08:00</authored-date>
  <message>Security: Fix XSS attack against new comment form

WARNING: If you're one of the first people testing this commit, please
use a backup database.

How to reproduce: Create a new comment, and set all fields to
&lt;script&gt;alert(&quot;Pwned&quot;)&lt;/script&gt;.  Submit it.  You will see a JavaScript
alert dialog, which is bad.

What's happening: Untrusted fields in Comment objects are sanitized
immediately before they're written to the database for the first time.
But if validation fails, it leaves the application with an unsanitized
comment object.  When the &quot;can't submit comment&quot; error is displayed,
this unsanitized comment object can be passed straight throught to
Liquid, which assumes that all HTML tags have been escaped.

(This may look like &quot;self XSS&quot; attack only, but hostile pages can
trigger it by tricking you into submitting a comment form back to your
own site, preloaded with malicious data.)

How we fix it: We make HTML escaping the responsibility of CommentDrop,
not the Comment model.  This means that we need to unescape several
existing fields in the database.

Possible issues: This means that we're storing dangerous, untrusted data
in our database, and that we need to rely on the proper use of 'h' and
'CGI.escapeHTML'.  In the case of 'h', we're already using SafeERB, so
insecure admin templates will be caught automatically, and dangerous
data should never be sent to the user.  In the case of Liquid, we need
to carefully examine our CommentDrop class to make sure that we're not
passing any unescaped data through to the Liquid templates.  But this is
a pretty manageable &quot;proof obligation&quot;--and remember that the old
&quot;sanitize on create&quot; code actually suffered from XSS attacks, because it
was too easy to do the sanitization in the wrong place.</message>
  <tree>0703ba7b1fb18d1eae97eb5c7a0537cffa8fb2eb</tree>
  <committer>
    <name>Eric Kidd</name>
    <login>emk</login>
    <email>git@randomhacks.net</email>
  </committer>
</commit>
