forked from rails/rails
-
Notifications
You must be signed in to change notification settings - Fork 0
/
output_safety.rb
146 lines (125 loc) · 3.59 KB
/
output_safety.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
require 'erb'
require 'active_support/core_ext/kernel/singleton_class'
class ERB
module Util
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' }
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
# A utility method for escaping HTML tag characters.
# This method is also aliased as <tt>h</tt>.
#
# In your ERb templates, use this method to escape any unsafe content. For example:
# <%=h @person.name %>
#
# ==== Example:
# puts html_escape("is a > 0 & a < 10?")
# # => is a > 0 & a < 10?
def html_escape(s)
s = s.to_s
if s.html_safe?
s
else
s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }.html_safe
end
end
remove_method(:h)
alias h html_escape
module_function :h
singleton_class.send(:remove_method, :html_escape)
module_function :html_escape
# A utility method for escaping HTML entities in JSON strings
# using \uXXXX JavaScript escape sequences for string literals:
#
# json_escape("is a > 0 & a < 10?")
# # => is a \u003E 0 \u0026 a \u003C 10?
#
# Note that after this operation is performed the output is not
# valid JSON. In particular double quotes are removed:
#
# json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
# # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
#
# This method is also aliased as +j+, and available as a helper
# in Rails templates:
#
# <%=j @person.to_json %>
#
def json_escape(s)
s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
end
alias j json_escape
module_function :j
module_function :json_escape
end
end
class Object
def html_safe?
false
end
end
class Fixnum
def html_safe?
true
end
end
module ActiveSupport #:nodoc:
class SafeBuffer < String
UNSAFE_STRING_METHODS = ["capitalize", "chomp", "chop", "delete", "downcase", "gsub", "lstrip", "next", "reverse", "rstrip", "slice", "squeeze", "strip", "sub", "succ", "swapcase", "tr", "tr_s", "upcase"].freeze
alias safe_concat concat
def concat(value)
if value.html_safe?
super(value)
else
super(ERB::Util.h(value))
end
end
alias << concat
def +(other)
dup.concat(other)
end
def html_safe?
true
end
def html_safe
self
end
def to_s
self
end
def to_yaml(*args)
to_str.to_yaml(*args)
end
for unsafe_method in UNSAFE_STRING_METHODS
class_eval <<-EOT, __FILE__, __LINE__
def #{unsafe_method}(*args)
super.to_str
end
def #{unsafe_method}!(*args)
raise TypeError, "Cannot modify SafeBuffer in place"
end
EOT
end
# Provides a special-case override for String#gsub
# to preserve some of the standard behaviour with respect
# to magic matching global variables
#
# Unlike standard gsub, magic matching globs are _not_
# available in code following a SafeBuffer#gsub call,
# but they are available within a block passed to gsub, eg:
# SafeBuffer.new('m').gsub(/(m)/){ $1 }
# In this case $1 == 'm' within the block, but not afterwards
#
# If you really need the magic matching variables after the gsub call
# you will need to convert SafeBuffer to a String first
def gsub(*args, &block)
to_str.gsub(*args, &block)
end
end
end
class String
def html_safe!
raise "You can't call html_safe! on a String"
end
def html_safe
ActiveSupport::SafeBuffer.new(self)
end
end