Skip to content

Commit

Permalink
Copy helpers and tests from hanami-helpers
Browse files Browse the repository at this point in the history
These are copied verbatim from the “Hanami 2” PR from hanami-helpers: hanami/helpers#199.

These helpers will not work in this commit, but it will allow for subsequent commits to show the changes required to bring these helpers into a working state.
  • Loading branch information
timriley committed Apr 28, 2023
1 parent a66d972 commit f109701
Show file tree
Hide file tree
Showing 22 changed files with 2,374 additions and 0 deletions.
15 changes: 15 additions & 0 deletions lib/hanami/view/helpers/errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Hanami
module Helpers
# @api public
# @since 2.0.0
class Error < ::StandardError
end

# @api public
# @since 2.0.0
class CoercionError < Error
end
end
end
23 changes: 23 additions & 0 deletions lib/hanami/view/helpers/escape.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

require "temple/utils"
require "temple/html/safe"
require "escape_utils"

module Hanami
module Helpers
module Escape
def self.call(string)
Temple::Utils.escape_html_safe(string)
end

def self.safe_string(string)
Temple::HTML::SafeString.new(string.to_s)
end

def self.uri(string)
::EscapeUtils.escape_uri(string)
end
end
end
end
275 changes: 275 additions & 0 deletions lib/hanami/view/helpers/escape_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
# frozen_string_literal: true

require "hanami/helpers/escape"
require "hanami/utils/escape"

module Hanami
module Helpers
# Escape helpers
#
# You can include this module inside your view and
# the view will have access all methods.
#
# By including <tt>Hanami::Helpers::EscapeHelper</tt> it will inject private
# methods as markup escape utilities.
#
# @since 0.1.0
module EscapeHelper
private

# Escape the given HTML tag content.
#
# This should be used only for untrusted contents: user input.
#
# This should be used only for tag contents.
# To escape tag attributes please use <tt>Hanami::Helpers::EscapeHelper#escape_html_attribute</tt>.
#
# @param input [String] the input
#
# @return [String] the escaped string
#
# @since 0.1.0
#
# @see Hanami::Helpers::EscapeHelper#escape_html_attribute
#
# @example Basic usage
# require 'hanami/helpers/escape_helper'
#
# class MyView
# include Hanami::Helpers::EscapeHelper
#
# def good_content
# h "hello"
# end
#
# def evil_content
# h "<script>alert('xss')</script>"
# end
# end
#
# view = MyView.new
#
# view.good_content
# # => "hello"
#
# view.evil_content
# # => "&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;"
#
# @example With HTML builder
# #
# # CONTENTS ARE AUTOMATICALLY ESCAPED
# #
# require 'hanami/helpers'
#
# class MyView
# include Hanami::Helpers
#
# def evil_content
# html.div do
# "<script>alert('xss')</script>"
# end
# end
# end
#
# view = MyView.new
# view.evil_content
# # => "<div>\n&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</div>"
def escape_html(input)
Helpers::Escape.(input)
end

# @since 0.1.0
alias_method :h, :escape_html

# Escape the given HTML tag attribute.
#
# This MUST be used for escaping HTML tag attributes.
#
# This should be used only for untrusted contents: user input.
#
# This can also be used to escape tag contents, but it's slower.
# For this purpose use <tt>Hanami::Helpers::EscapeHelper#escape_html</tt>.
#
# @param input [String] the input
#
# @return [String] the escaped string
#
# @since 0.1.0
#
# @see Hanami::Helpers::EscapeHelper#escape_html
#
# @example Basic usage
# require 'hanami/helpers/escape_helper'
#
# class MyView
# include Hanami::Helpers::EscapeHelper
#
# def good_attribute
# attribute = "small"
#
# %(<span class="#{ ha(attribute) }">hello</span>
# end
#
# def evil_attribute
# attribute = %(" onclick="javascript:alert('xss')" id=")
#
# %(<span class="#{ ha(attribute) }">hello</span>
# end
# end
#
# view = MyView.new
#
# view.good_attribute
# # => %(<span class="small">hello</span>)
#
# view.evil_attribute
# # => %(<span class="&quot;&#x20;onclick&#x3d;&quot;javascript&#x3a;alert&#x28;&#x27;xss&#x27;&#x29;&quot;&#x20;id&#x3d;&quot;">hello</span>
#
# @example With HTML builder
# #
# # ATTRIBUTES AREN'T AUTOMATICALLY ESCAPED
# #
# require 'hanami/helpers'
#
# class MyView
# include Hanami::Helpers
#
# def evil_attribute
# user_input_attribute = %(" onclick="javascript:alert('xss')" id=")
#
# html.span id: 'greet', class: ha(user_input_attribute) do
# "hello"
# end
# end
# end
#
# view = MyView.new
# view.evil_attribute
# # => %(<span class="&quot;&#x20;onclick&#x3d;&quot;javascript&#x3a;alert&#x28;&#x27;xss&#x27;&#x29;&quot;&#x20;id&#x3d;&quot;">hello</span>
def escape_html_attribute(input)
Utils::Escape.html_attribute(input)
end

# @since 0.1.0
alias_method :ha, :escape_html_attribute

# Escape an URL to be used in HTML attributes
#
# This allows only URLs with whitelisted schemes to pass the filter.
# Everything else is stripped.
#
# Default schemes are:
#
# * http
# * https
# * mailto
#
# If you want to allow a different set of schemes, you should pass it as
# second argument.
#
# This should be used only for untrusted contents: user input.
#
# @param input [String] the input
# @param schemes [Array<String>] an optional array of whitelisted schemes
#
# @return [String] the escaped string
#
# @since 0.1.0
#
# @see Hanami::Utils::Escape.url
# @see Hanami::Utils::Escape::DEFAULT_URL_SCHEMES
#
# @example Basic usage
# require 'hanami/helpers/escape_helper'
#
# class MyView
# include Hanami::Helpers::EscapeHelper
#
# def good_url
# url = "http://hanamirb.org"
#
# %(<a href="#{ hu(url) }">Hanami</a>
# end
#
# def evil_url
# url = "javascript:alert('xss')"
#
# %(<a href="#{ hu(url) }">Evil</a>
# end
# end
#
# view = MyView.new
#
# view.good_url
# # => %(<a href="http://hanamirb.org">Hanami</a>)
#
# view.evil_url
# # => %(<a href="">Evil</a>)
#
# @example Custom schemes
# require 'hanami/helpers/escape_helper'
#
# class MyView
# include Hanami::Helpers::EscapeHelper
#
# def ftp_link
# schemes = ['ftp', 'ftps']
# url = 'ftps://ftp.example.org'
#
# %(<a href="#{ hu(url, schemes) }">FTP</a>
# end
# end
#
# view = MyView.new
#
# view.ftp_link
# # => %(<a href="ftps://ftp.example.org">FTP</a>)
def escape_url(input, schemes = Utils::Escape::DEFAULT_URL_SCHEMES)
Utils::Escape.url(input, schemes)
end

# @since 0.1.0
alias_method :hu, :escape_url

# Bypass escape.
#
# Please notice that this can be really dangerous.
# Use at your own peril.
#
# @param input [String] the input
#
# @return [Hanami::Utils::Escape::SafeString] the string marked as safe string
#
# @since 0.1.0
#
# @example
# require 'hanami/helpers/escape_helper'
#
# class MyView
# include Hanami::Helpers::EscapeHelper
#
# def good_content
# raw "<p>hello</p>"
# end
#
# def evil_content
# raw "<script>alert('xss')</script>"
# end
# end
#
# view = MyView.new
#
# view.good_content
# # => "<p>hello</p>"
#
# #
# # !!! WE HAVE OPENED OUR APPLICATION TO AN XSS ATTACK !!!
# #
# view.evil_content
# # => "<script>alert('xss')</script>"
def raw(input)
Helpers::Escape.safe_string(input)
end
end
end
end
Loading

0 comments on commit f109701

Please sign in to comment.