<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -26,15 +26,17 @@ X   Can we restrict admin cookies to /admin ? No--need /accounts, too.
 /     filtered_column_code_macro
 /     filtered_column_flikr_macro - lots of issues
 /   Do we have trackback support to check? No.
-  Password change
-    Verify token required to change e-mail and password
+/ Password change
+/   Verify token required to change e-mail and password
   Everything else
 /   Don't ship :session_key in environment.rb!
 /   Do we need to override verifiable_request_format? No.
-    Check redirection in lib/authenticated_system.rb
+/   Check redirection in lib/authenticated_system.rb
     Detect mass assignment failures in unit tests
-    Review mass assignment in public controllers
-    Check regexes for ^ and $
+/   Review mass assignment in public controllers - comments
+/   Check regexes for ^ and $
+    Filter IMG tags
+    Block database updates on POST requests
     Review http://guides.rubyonrails.org/security.html again
 
   Admin only</diff>
      <filename>RAILS-2.2-TODO.txt</filename>
    </modified>
    <modified>
      <diff>@@ -138,7 +138,7 @@ class Admin::ArticlesController &lt; Admin::BaseController
       with_site_timezone do
         date = Time.parse_from_attributes(params[:article], :published_at, :local)
         next unless date
-        params[:article].delete_if { |k, v| k.to_s =~ /^#{:published_at}/ }
+        params[:article].delete_if { |k, v| k.to_s =~ /\A#{:published_at}/ }
         params[:article][:published_at] = local_to_utc(date)
       end
     end</diff>
      <filename>app/controllers/admin/articles_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -22,7 +22,7 @@ class Admin::BaseController &lt; ApplicationController
     end
 
     def find_and_sort_templates
-      @layouts, @templates = site.templates.partition { |t| t.dirname.to_s =~ /layouts$/ }
+      @layouts, @templates = site.templates.partition { |t| t.dirname.to_s =~ /layouts\z/ }
     end
     
     def self.clear_empty_templates_for(model, *attributes)</diff>
      <filename>app/controllers/admin/base_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,7 +7,7 @@ class Admin::DesignController &lt; Admin::BaseController
       return
     end
 
-    if params[:filename] =~ /\.(css|js)$/i
+    if params[:filename] =~ /\.(css|js)\z/i
       @resource = @theme.resources.write params[:filename], params[:data]
       redirect_to url_for_theme(:controller =&gt; 'resources', :action =&gt; 'edit', :filename =&gt; @resource.basename.to_s)
     else</diff>
      <filename>app/controllers/admin/design_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,7 @@ class FeedController &lt; ApplicationController
   def feed
     sections = params[:sections].clone
     last = sections.last
-    sections.delete(last) if last =~ /\.xml$/
+    sections.delete(last) if last =~ /\.xml\z/
     @section_path = sections.blank? ? '' : sections.join('/')
     case last
       when 'all_comments.xml'</diff>
      <filename>app/controllers/feed_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -11,7 +11,7 @@ class CommentDrop &lt; BaseDrop
   
   def author_url
     return nil if source.author_url.blank?
-    @source.author_url =~ /^https?:\/\// ? @source.author_url : &quot;http://&quot; + @source.author_url
+    @source.author_url =~ /\Ahttps?:\/\// ? @source.author_url : &quot;http://&quot; + @source.author_url
   end
 
   def url</diff>
      <filename>app/drops/comment_drop.rb</filename>
    </modified>
    <modified>
      <diff>@@ -28,7 +28,7 @@ module CoreFilters
 
   def parse_date(date)
     return Time.now.utc if date.blank?
-    date = &quot;#{date}-1&quot; if date.to_s =~ /^\d{4}-\d{1,2}$/ unless [Time, Date].include?(date.class)
+    date = &quot;#{date}-1&quot; if date.to_s =~ /\A\d{4}-\d{1,2}\z/ unless [Time, Date].include?(date.class)
     date = date.to_time
   end
 </diff>
      <filename>app/filters/core_filters.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,7 +3,7 @@ module ApplicationHelper
 
   def author_link_for(comment)
     return h(comment.author) if comment.author_url.blank?
-    link_to h(comment.author), &quot;#{'http://' unless comment.author_url =~ /^https?:\/\//}#{comment.author_url}&quot;
+    link_to h(comment.author), &quot;#{'http://' unless comment.author_url =~ /\Ahttps?:\/\//}#{comment.author_url}&quot;
   end
 
   def avatar_for(user, options = {})</diff>
      <filename>app/helpers/application_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,11 +14,11 @@ class Asset &lt; ActiveRecord::Base
 
   class &lt;&lt; self
     def movie?(content_type)
-      content_type.to_s =~ /^video/ || extra_content_types[:movie].include?(content_type)
+      content_type.to_s =~ /\Avideo/ || extra_content_types[:movie].include?(content_type)
     end
         
     def audio?(content_type)
-      content_type.to_s =~ /^audio/ || extra_content_types[:audio].include?(content_type)
+      content_type.to_s =~ /\Aaudio/ || extra_content_types[:audio].include?(content_type)
     end
     
     def other?(content_type)
@@ -72,7 +72,7 @@ class Asset &lt; ActiveRecord::Base
 
   def public_filename_with_host(thumbnail = nil)
     returning public_filename_without_host(thumbnail) do |s|
-      s.gsub! /^\/assets\/[^\/]+\//, &quot;/assets/#{$1}&quot; if Site.multi_sites_enabled
+      s.gsub! /\A\/assets\/[^\/]+\//, &quot;/assets/#{$1}&quot; if Site.multi_sites_enabled
     end
   end
   alias_method_chain :public_filename, :host</diff>
      <filename>app/models/asset.rb</filename>
    </modified>
    <modified>
      <diff>@@ -20,8 +20,8 @@ class Resources &lt; Attachments
   def [](filename)
     path = 
       case filename
-        when /\.js$/i  then 'javascripts'
-        when /\.css$/i then 'stylesheets'
+        when /\.js\z/i  then 'javascripts'
+        when /\.css\z/i then 'stylesheets'
         else                'images'
       end
       </diff>
      <filename>app/models/resources.rb</filename>
    </modified>
    <modified>
      <diff>@@ -260,8 +260,8 @@ class Site &lt; ActiveRecord::Base
     end
 
     def check_permalink_style
-      permalink_style.sub! /^\//, ''
-      permalink_style.sub! /\/$/, ''
+      permalink_style.sub! /\A\//, ''
+      permalink_style.sub! /\/\z/, ''
       pieces = permalink_style.split('/')
       errors.add :permalink_style, 'cannot have blank paths' if pieces.any?(&amp;:blank?)
       pieces.each do |p|</diff>
      <filename>app/models/site.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,8 +7,8 @@ class Templates &lt; Attachments
   end
   
   def [](template_name)
-    template_name = File.basename(template_name.to_s).sub /#{theme.extension}$/, ''
-    theme.path + &quot;#{template_name =~ /layout$/ ? 'layouts' : 'templates'}/#{template_name}#{theme.extension}&quot;
+    template_name = File.basename(template_name.to_s).sub /#{theme.extension}\z/, ''
+    theme.path + &quot;#{template_name =~ /layout\z/ ? 'layouts' : 'templates'}/#{template_name}#{theme.extension}&quot;
   end
 
   def collect_templates(template_type, *custom_templates)</diff>
      <filename>app/models/templates.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,7 +9,7 @@ class Theme
     dest        = options[:to].is_a?(Pathname) ? options[:to] : Pathname.new(options[:to] || '.')
     basename    = dest.basename.to_s
     if dest.exist? || basename == 'current'
-      basename  = basename =~ /(.*)_(\d+)$/ ? $1 : basename
+      basename  = basename =~ /(.*)_(\d+)\z/ ? $1 : basename
       number    = $2 ? $2.to_i + 1 : 2
       dirname   = dest.dirname
       dest      = dirname + &quot;#{basename}_#{number}&quot;
@@ -27,7 +27,7 @@ class Theme
         dir_path = Pathname.new(dest + dir)
         FileUtils.mkdir_p dir_path unless dir_path.exist?
         z.dir.entries(dir).each do |entry|
-          next unless entry =~ /(\.\w+)$/ &amp;&amp; allowed_extensions.include?($1)
+          next unless entry =~ /(\.\w+)\z/ &amp;&amp; allowed_extensions.include?($1)
           z.file.open(File.join(dir, entry)) { |zf| File.open(dir_path + entry, 'wb') { |f| f &lt;&lt; zf.read } }
         end
       end
@@ -47,7 +47,7 @@ class Theme
       @base_path = base
       @path      = Pathname.new(@base_path)
     end
-    layout = (@path + &quot;layouts&quot;).children(false).select {|v| v.to_s =~ /^layout/}[0] if (@path + &quot;layouts&quot;).directory?
+    layout = (@path + &quot;layouts&quot;).children(false).select {|v| v.to_s =~ /\Alayout/}[0] if (@path + &quot;layouts&quot;).directory?
     @extension = layout.extname if layout
   end
 </diff>
      <filename>app/models/theme.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 module Format
   # yes this is valid ruby, even if textmate's highlighter can't grok it
-  DOMAIN = /^([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+((a[cdefgilmnoqrstuwxz]|aero|arpa)|(b[abdefghijmnorstvwyz]|biz)|(c[acdfghiklmnorsuvxyz]|cat|com|coop)|d[ejkmoz]|(e[ceghrstu]|edu)|f[ijkmor]|(g[abdefghilmnpqrstuwy]|gov)|(h[kmnrtu]#{RAILS_ENV=='test'?'|host':''})|(i[delmnoqrst]|info|int)|(j[emop]|jobs)|k[eghimnprwyz]|l[abcikrstuvy]|(m[acdghklmnopqrstuvwxyz]|mil|mobi|museum)|(n[acefgilopruz]|name|net)|(om|org)|(p[aefghklmnrstwy]|pro)|qa|r[eouw]|s[abcdeghijklmnortvyz]|(t[cdfghjklmnoprtvwz]|travel)|u[agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw])$/ unless const_defined?(:DOMAIN)
-  STRING = /^[a-z0-9-]+$/
+  DOMAIN = /\A([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+((a[cdefgilmnoqrstuwxz]|aero|arpa)|(b[abdefghijmnorstvwyz]|biz)|(c[acdfghiklmnorsuvxyz]|cat|com|coop)|d[ejkmoz]|(e[ceghrstu]|edu)|f[ijkmor]|(g[abdefghilmnpqrstuwy]|gov)|(h[kmnrtu]#{RAILS_ENV=='test'?'|host':''})|(i[delmnoqrst]|info|int)|(j[emop]|jobs)|k[eghimnprwyz]|l[abcikrstuvy]|(m[acdghklmnopqrstuvwxyz]|mil|mobi|museum)|(n[acefgilopruz]|name|net)|(om|org)|(p[aefghklmnrstwy]|pro)|qa|r[eouw]|s[abcdeghijklmnortvyz]|(t[cdfghjklmnoprtvwz]|travel)|u[agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw])\z/ unless const_defined?(:DOMAIN)
+  STRING = /\A[a-z0-9-]+\z/
   EMAIL  = /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
 end
\ No newline at end of file</diff>
      <filename>lib/format.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 # Object model of a plugin in plugins/vendor. May or may not have an internal Mephisto::Plugins::Plugin (an AR).
 module Mephisto
   class DirectoryPlugin
-    @@filter = /^mephisto_(\w+)$/
+    @@filter = /\Amephisto_(\w+)\z/
     attr_accessor :plugin, :path
     
     def self.scan</diff>
      <filename>lib/mephisto/directory_plugin.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 module Mephisto
   class Dispatcher
     PERMALINK_OPTIONS = { :year =&gt; '\d{4}', :month =&gt; '\d{1,2}', :day =&gt; '\d{1,2}', :permalink =&gt; '[\w\-]+', :id =&gt; '\d+' }
-    PERMALINK_VAR     = /^:([a-z]+)$/
+    PERMALINK_VAR     = /\A:([a-z]+)\z/
 
     def self.run(site, path)
       # check for any bad urls like /foo//bar
@@ -80,7 +80,7 @@ module Mephisto
             result.first[var] = match[i+1]
           end
           result &lt;&lt; match[variables.size + 2] # comments | comments.xml | changes.xml
-          result.last.gsub!(/\/(.*)$/, '') if result.last
+          result.last.gsub!(/\/(.*)\z/, '') if result.last
           result &lt;&lt; match[variables.size + 4] # comment id
         end
       end</diff>
      <filename>lib/mephisto/dispatcher.rb</filename>
    </modified>
    <modified>
      <diff>@@ -29,7 +29,7 @@ module Mephisto
     end
     
     def mephisto_plugin?
-      name =~ /^mephisto_(\w+)$/
+      name =~ /\Amephisto_(\w+)\z/
     end
     
     def configurable?
@@ -37,7 +37,7 @@ module Mephisto
     end
     
     def mephisto_name
-      name.sub /^mephisto_/, ''
+      name.sub /\Amephisto_/, ''
     end
     alias :conf_name :mephisto_name
   end</diff>
      <filename>lib/mephisto/plugin.rb</filename>
    </modified>
    <modified>
      <diff>@@ -87,7 +87,7 @@ module Mephisto
     end
     
     protected
-      @@sanitize_path_regex = /^(\/)|(https?:\/\/)/
+      @@sanitize_path_regex = /\A(\/)|(https?:\/\/)/
       def self.sanitize_path(path)
         path =~ @@sanitize_path_regex ? path : &quot;/#{path.split(&quot;://&quot;).last}&quot;
       end</diff>
      <filename>lib/mephisto/routing.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@ module XMLRPC
   module Convert
     def self.dateTime(str)
       case str
-      when /^(-?\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(?:Z|([+-])(\d\d):?(\d\d))?$/
+      when /\A(-?\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(?:Z|([+-])(\d\d):?(\d\d))?\z/
         a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
         if $7
           ofs = $8.to_i*3600 + $9.to_i*60
@@ -16,7 +16,7 @@ module XMLRPC
           a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ]
         end
         XMLRPC::DateTime.new(*a)
-      when /^(-?\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(Z|([+-]\d\d):(\d\d))?$/
+      when /\A(-?\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(Z|([+-]\d\d):(\d\d))?\z/
         a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
         if a[0] &lt; 70
           a[0] += 2000</diff>
      <filename>lib/xmlrpc_patch.rb</filename>
    </modified>
    <modified>
      <diff>@@ -40,6 +40,11 @@ class CommentDropTest &lt; Test::Unit::TestCase
     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
   end
   
+  def test_should_not_be_fooled_by_newlines_in_author_url
+    @comment.source.author_url = &quot;javascript:alert('Oops')\nhttp://&quot;
+    assert_equal &quot;http://javascript:alert('Oops')\nhttp://&quot;, @comment.author_url
+  end
+
   def test_should_show_filtered_text
     comment  = contents(:welcome).comments.create :body =&gt; '*test* comment', :author =&gt; 'bob', :author_ip =&gt; '127.0.0.1'
     assert_valid comment
@@ -74,4 +79,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>173c3b82e7dae0f7d616c5522104e48f76ef94ad</id>
    </parent>
  </parents>
  <author>
    <name>Eric Kidd</name>
    <login>emk</login>
    <email>git@randomhacks.net</email>
  </author>
  <url>http://github.com/emk/mephisto/commit/c8c4bcc772301abcee6735466df93b60291878aa</url>
  <id>c8c4bcc772301abcee6735466df93b60291878aa</id>
  <committed-date>2008-12-19T19:25:22-08:00</committed-date>
  <authored-date>2008-12-19T19:25:22-08:00</authored-date>
  <message>Security: Fix many broken filter regexps

In Ruby, &quot;foo\nbar&quot; =~ /^bar/ will result in a match, because ^ matches
at the start of any line, not at the start of the string.  In general,
we want to use \A and \z in place of ^ and $, respectively.

We rely heavily on regular expressions to filter untrusted data.  And
many of these regular expressions can be fooled easily because they rely
on ^ and $ when they shouldn't.  See comment_drop_test for a
user-exploitable example.

This patch does a bulk search-and-replace of the offending patterns.  It
may easily have missed something somewhere, but it's a good start.</message>
  <tree>7c76b84b39cf0d8fbaa0ec49c7c13251deb1f1d6</tree>
  <committer>
    <name>Eric Kidd</name>
    <login>emk</login>
    <email>git@randomhacks.net</email>
  </committer>
</commit>
