require 'lotus/utils/escape'
require 'lotus/presenter'
module Lotus
module View
# Auto escape logic for views and presenters.
#
# @since 0.4.0
module Escape
module InstanceMethods
private
# Mark the given string as safe to render.
#
# !!! ATTENTION !!! This may open your application to XSS attacks.
#
# @param string [String] the input string
#
# @return [Lotus::Utils::Escape::SafeString] the string marked as safe
#
# @since 0.4.0
# @api public
#
# @example View usage
# require 'lotus/view'
#
# User = Struct.new(:name)
#
# module Users
# class Show
# include Lotus::View
#
# def user_name
# _raw user.name
# end
# end
# end
#
# # ERB template
# #
<%= user_name %>
#
# user = User.new("")
# html = Users::Show.render(format: :html, user: user)
#
# html # =>
#
# @example Presenter usage
# require 'lotus/view'
#
# User = Struct.new(:name)
#
# class UserPresenter
# include Lotus::Presenter
#
# def name
# _raw @object.name
# end
# end
#
# user = User.new("")
# presenter = UserPresenter.new(user)
#
# presenter.name # => ""
def _raw(string)
::Lotus::Utils::Escape::SafeString.new(string)
end
# Force the output escape for the given object
#
# @param object [Object] the input object
#
# @return [Lotus::View::Escape::Presenter] a presenter with output
# autoescape
#
# @since 0.4.0
# @api public
#
# @see Lotus::View::Escape::Presenter
#
# @example View usage
# require 'lotus/view'
#
# User = Struct.new(:first_name, :last_name)
#
# module Users
# class Show
# include Lotus::View
#
# def user
# _escape locals[:user]
# end
# end
# end
#
# # ERB template:
# #
# #
# # <%= user.first_name %>
# #
# #
# # <%= user.last_name %>
# #
#
# first_name = ""
# last_name = ""
#
# user = User.new(first_name, last_name)
# html = Users::Show.render(format: :html, user: user)
#
# html
# # =>
# #
# # <script>alert('first_name')</script>
# #
# #
# # <script>alert('last_name')</script>
# #
def _escape(object)
::Lotus::View::Escape::Presenter.new(object)
end
end
# Auto escape presenter
#
# @since 0.4.0
# @api private
#
# @see Lotus::View::Escape::InstanceMethods#_escape
class Presenter
include ::Lotus::Presenter
end
# Escape the given input if it's a string, otherwise return the oject as it is.
#
# @param input [Object] the input
#
# @return [Object,String] the escaped string or the given object
#
# @since 0.4.0
# @api private
def self.html(input)
case input
when String
Utils::Escape.html(input)
else
input
end
end
# Module extended override
#
# @since 0.4.0
# @api private
def self.extended(base)
base.class_eval do
include ::Lotus::Utils::ClassAttribute
include ::Lotus::View::Escape::InstanceMethods
class_attribute :autoescape_methods
self.autoescape_methods = {}
end
end
# Wraps concrete view methods with escape logic.
#
# @since 0.4.0
# @api private
def method_added(method_name)
unless autoescape_methods[method_name]
prepend Module.new {
module_eval %{
def #{ method_name }(*args, &blk); ::Lotus::View::Escape.html super; end
}
}
autoescape_methods[method_name] = true
end
end
end
end
end