开发者

HTML escaped in Rails 3

开发者 https://www.devze.com 2023-01-20 00:05 出处:网络
I have a method call in my view like this <%= Navigation.with(params) do |menu| if current_user && current_user.can_verify?

I have a method call in my view like this

<%= Navigation.with(params) do |menu| 
              if current_user && current_user.can_verify?
                menu.item("Listings", manage_listings_path())
                menu.item("Listing changes", needing_change_approval_manage_listings_path())
                menu.item("Flagged Items", flagged_manage_listings_path())
                menu.item("Transfers", manage_listing_transfers_path())
                menu.item("Reviews", 开发者_Python百科manage_listing_reviews_path())
              end
              if current_user && current_user.admin?
                menu.item("Log", manage_verifications_path())
                menu.item("Indexer Compensations", manage_compensations_path())
                menu.item("Users", manage_users_path())
              end
            end%>

that splits out the below string

"<li><a href="/manage/listings" class="active">Listings</a></li> <li><a href="/manage/listings/needing_change_approval">Listing changes</a></li> <li><a href="/manage/listings/flagged">Flagged Items</a></li> <li><a href="/manage/listing_transfers">Transfers</a></li> <li><a href="/manage/listing_reviews">Reviews</a></li> <li><a href="/manage/verifications">Log</a></li> <li><a href="/manage/compensations">Indexer Compensations</a></li> <li><a href="/manage/users">Users</a></li>"

I just get this string in my page. I wanted them to be menus nicely styled by CSS. I am just getting the above raw text in my page. How do I convert this string to be treated as HTML by the browser.

Please help

Here is the navigation class

class NavigationMenu < ActionView::Base
  def initialize(params)
    @params = params
  end

  def item(title, path, options={})
    @items ||= Array.new
    unless (route = Rails.application.routes.recognize_path(path,:method => options[:method]|| :get))
      raise "Unrecognised path #{path}, are you sure it's in routes.rb?"
    end

    @items << content_tag(:li, link_to(title,path, :class => (@params[:controller] == route[:controller] && @params[:action] == route[:action])? 'active' : nil))

  end

  def output
    return '' if @items.blank?
    content_tag(:ul, @items.join("\n"), :id => 'navigation')
  end
end

class Navigation
  def self.with(params, &block)
    menu = NavigationMenu.new(params)
    yield menu
    menu.output
  end
end


You have to add a call to the raw method:

<%= raw ... %>

This is necessary, because in Rails 3 every string is escaped by default, unless you use the raw method. It's like an inverse of the h method in Rails 2, where every string is unescaped by default, unless you use the h method.

Example:

This code in Rails 2...

<%= h "String which must be escaped" %>
<%= "String which must be output raw %>

... must be this in Rails 3:

<%= "String which must be escaped" %>
<%= raw "String which must be output raw %>

(Although an additional call to h doesn't do any harm in Rails 3)


You need to append .html_safe to the string - this will stop rails from escaping it when it's time to output text. Probably best to put it in the item method that you call repeatedly.


I recently wrote an article regarding XSS protection in Rails 3 when upgrading from Rails 2: http://developer.uservoice.com/entries/upgrading-to-rails-3-printing-escaped-strings

The idea is to hook code to printing HTML so that we can determine when we are actually printing something we don't want to:

module ActionView
  module Helpers
    module TextHelper

      def simple_format_with_double_escape_reporting(*args)
        HtmlDoubleEscapeReporter.assert_sane(simple_format_without_double_escape_reporting(*args))
      end 
      alias_method_chain :simple_format, :double_escape_reporting
    end 

    module TagHelper
      private
      def content_tag_string_with_double_escape_reporting(*args)
        HtmlDoubleEscapeReporter.assert_sane(content_tag_string_without_double_escape_reporting(*args))
      end 
      alias_method_chain :content_tag_string, :double_escape_reporting
    end 
    module UrlHelper
      def link_to_with_double_escape_reporting(*args, &block)
        HtmlDoubleEscapeReporter.assert_sane(link_to_without_double_escape_reporting(*args, &block))
      end 
      alias_method_chain :link_to, :double_escape_reporting
    end 
  end 
end

Method HtmlDoubleEscapeReporter.assert_sane can be written, for example, like this:

class HtmlDoubleEscapeReporter
  def self.assert_sane(str)
    if (str.match(/&lt;[a-z]/) || str.match(/&amp;(quot|rarr|larr|amp|#)/)) &&
        !str.match(/looks something you do not want to print/
      send_problem_report('#{str}' looks something you do not want to print")
    end
    return str
  end
end

Here, 'looks something you do not want to print' is used to prevent the possibility of infinite loops. The line send_problem_report('#{str}' looks something you do not want to print") can be replaced with a call to "debugger" (from ruby-debug gem) so that you are able to check the backtrace and see where the problem is coming from.


Here is the new class. At last... I got that bug.

class NavigationMenu < ActionView::Base
  def initialize(params)
    @params = params
  end

  def item(title, path, options={})
    @items ||= Array.new
    unless (route = Rails.application.routes.recognize_path(path,:method => options[:method]|| :get))
      raise "Unrecognised path #{path}, are you sure it's in routes.rb?"
    end

    @items << content_tag(:li, link_to(title,path, :class => (@params[:controller] == route[:controller] && @params[:action] == route[:action])? 'active' : nil))

  end

  def output
    @items = @items.join("\n").html_safe
    return '' if @items.blank?
    content_tag(:ul, @items, :id => 'navigation')
  end
end

class Navigation
  def self.with(params, &block)
    menu = NavigationMenu.new(params)
    yield menu
    menu.output
  end
end
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号