better_html 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/better_html.rb +7 -35
- data/lib/better_html/better_erb.rb +1 -1
- data/lib/better_html/better_erb/runtime_checks.rb +9 -8
- data/lib/better_html/config.rb +16 -0
- data/lib/better_html/node_iterator/location.rb +6 -3
- data/lib/better_html/test_helper/safe_erb_tester.rb +13 -11
- data/lib/better_html/test_helper/safe_lodash_tester.rb +6 -5
- data/lib/better_html/version.rb +1 -1
- data/test/better_html/better_erb/implementation_test.rb +52 -48
- data/test/better_html/test_helper/safe_erb_tester_test.rb +13 -7
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b6246bfb4cf4ae99f87a12370d99b362b9b2737b
|
4
|
+
data.tar.gz: 67a8747a32552d237bacafe3cc782dc7b1815ffc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07c548e1027d5f582ee796df6b536bd6f895edcd42c3c2898d0eb1da61c9adffaff6683bf83cbfee3e59d0f2acb2ed70c3371606cb772b39b3a8c4df30a75d8a
|
7
|
+
data.tar.gz: d5168282725b6e5ba210eae30eefcfa88b8270063422f0127be7f53cc888dfa1470c9a82db09971d2b9ec540024a28f57337cf9d4e092afb5aa86c092f9aef0a
|
data/lib/better_html.rb
CHANGED
@@ -3,49 +3,21 @@ require 'active_support/core_ext/module/delegation'
|
|
3
3
|
require 'active_support/core_ext/class/attribute_accessors'
|
4
4
|
|
5
5
|
module BetterHtml
|
6
|
-
|
7
|
-
|
8
|
-
cattr_accessor :partial_tag_name_pattern
|
9
|
-
self.partial_tag_name_pattern = /\A[a-z0-9\-\:]+\z/
|
10
|
-
|
11
|
-
# regex to validate "bar" in "<foo bar=1>"
|
12
|
-
cattr_accessor :partial_attribute_name_pattern
|
13
|
-
self.partial_attribute_name_pattern = /\A[a-zA-Z0-9\-\:]+\z/
|
14
|
-
|
15
|
-
# true if "<foo bar='1'>" is valid syntax
|
16
|
-
cattr_accessor :allow_single_quoted_attributes
|
17
|
-
self.allow_single_quoted_attributes = false
|
18
|
-
|
19
|
-
# true if "<foo bar=1>" is valid syntax
|
20
|
-
cattr_accessor :allow_unquoted_attributes
|
21
|
-
self.allow_unquoted_attributes = false
|
22
|
-
|
23
|
-
# all methods that return "javascript-safe" strings
|
24
|
-
cattr_accessor :javascript_safe_methods
|
25
|
-
self.javascript_safe_methods = ['to_json']
|
26
|
-
|
27
|
-
# name of all html attributes that may contain javascript
|
28
|
-
cattr_accessor :javascript_attribute_names
|
29
|
-
self.javascript_attribute_names = [/\Aon/i]
|
30
|
-
|
31
|
-
cattr_accessor :template_exclusion_filter_block
|
32
|
-
|
33
|
-
def self.template_exclusion_filter(&block)
|
34
|
-
self.template_exclusion_filter_block = block
|
35
|
-
end
|
36
|
-
|
37
|
-
cattr_accessor :lodash_safe_javascript_expression
|
38
|
-
self.lodash_safe_javascript_expression = [/\AJSON\.stringify\(/]
|
6
|
+
def self.configure
|
7
|
+
yield config if block_given?
|
39
8
|
end
|
40
9
|
|
41
10
|
def self.config
|
42
11
|
@config ||= Config.new
|
43
|
-
|
44
|
-
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.config=(new_config)
|
15
|
+
@config = new_config
|
45
16
|
end
|
46
17
|
end
|
47
18
|
|
48
19
|
require 'better_html/version'
|
20
|
+
require 'better_html/config'
|
49
21
|
require 'better_html/helpers'
|
50
22
|
require 'better_html/errors'
|
51
23
|
require 'better_html/html_attributes'
|
@@ -52,7 +52,7 @@ class BetterHtml::BetterErb
|
|
52
52
|
# Always make sure we return a String in the default_internal
|
53
53
|
erb.encode!
|
54
54
|
|
55
|
-
excluded_template = !!BetterHtml
|
55
|
+
excluded_template = !!BetterHtml.config.template_exclusion_filter_block&.call(template.identifier)
|
56
56
|
klass = BetterHtml::BetterErb.content_types[exts] unless excluded_template
|
57
57
|
klass ||= self.class.erb_implementation
|
58
58
|
|
@@ -3,9 +3,10 @@ require 'action_view'
|
|
3
3
|
|
4
4
|
class BetterHtml::BetterErb
|
5
5
|
module RuntimeChecks
|
6
|
-
def initialize(
|
6
|
+
def initialize(erb, config: BetterHtml.config, **options)
|
7
7
|
@parser = HtmlTokenizer::Parser.new
|
8
|
-
|
8
|
+
@config = config
|
9
|
+
super(erb, **options)
|
9
10
|
end
|
10
11
|
|
11
12
|
def validate!
|
@@ -112,26 +113,26 @@ class BetterHtml::BetterErb
|
|
112
113
|
def check_tag_name(type, start, stop, line, column)
|
113
114
|
text = @parser.extract(start, stop)
|
114
115
|
return if text.upcase == "!DOCTYPE"
|
115
|
-
return if
|
116
|
+
return if @config.partial_tag_name_pattern === text
|
116
117
|
|
117
118
|
s = "Invalid tag name #{text.inspect} does not match "\
|
118
|
-
"regular expression #{
|
119
|
+
"regular expression #{@config.partial_tag_name_pattern.inspect}\n"
|
119
120
|
s << build_location(line, column, text.size)
|
120
121
|
raise BetterHtml::HtmlError, s
|
121
122
|
end
|
122
123
|
|
123
124
|
def check_attribute_name(type, start, stop, line, column)
|
124
125
|
text = @parser.extract(start, stop)
|
125
|
-
return if
|
126
|
+
return if @config.partial_attribute_name_pattern === text
|
126
127
|
|
127
128
|
s = "Invalid attribute name #{text.inspect} does not match "\
|
128
|
-
"regular expression #{
|
129
|
+
"regular expression #{@config.partial_attribute_name_pattern.inspect}\n"
|
129
130
|
s << build_location(line, column, text.size)
|
130
131
|
raise BetterHtml::HtmlError, s
|
131
132
|
end
|
132
133
|
|
133
134
|
def check_quoted_value(type, start, stop, line, column)
|
134
|
-
return if
|
135
|
+
return if @config.allow_single_quoted_attributes
|
135
136
|
text = @parser.extract(start, stop)
|
136
137
|
return if text == '"'
|
137
138
|
|
@@ -141,7 +142,7 @@ class BetterHtml::BetterErb
|
|
141
142
|
end
|
142
143
|
|
143
144
|
def check_unquoted_value(type, start, stop, line, column)
|
144
|
-
return if
|
145
|
+
return if @config.allow_unquoted_attributes
|
145
146
|
s = "Unquoted attribute values are not allowed\n"
|
146
147
|
s << build_location(line, column, stop-start)
|
147
148
|
raise BetterHtml::HtmlError, s
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'smart_properties'
|
2
|
+
|
3
|
+
module BetterHtml
|
4
|
+
class Config
|
5
|
+
include SmartProperties
|
6
|
+
|
7
|
+
property :partial_tag_name_pattern, default: /\A[a-z0-9\-\:]+\z/
|
8
|
+
property :partial_attribute_name_pattern, default: /\A[a-zA-Z0-9\-\:]+\z/
|
9
|
+
property :allow_single_quoted_attributes, default: true
|
10
|
+
property :allow_unquoted_attributes, default: false
|
11
|
+
property :javascript_safe_methods, default: ['to_json']
|
12
|
+
property :javascript_attribute_names, default: [/\Aon/i]
|
13
|
+
property :template_exclusion_filter
|
14
|
+
property :lodash_safe_javascript_expression, default: [/\AJSON\.stringify\(/]
|
15
|
+
end
|
16
|
+
end
|
@@ -28,10 +28,9 @@ module BetterHtml
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def line_source_with_underline
|
31
|
-
line_content =
|
32
|
-
line_content = line_content.nil? ? "" : line_content.gsub(/\n$/, '')
|
31
|
+
line_content = extract_line(line: line)
|
33
32
|
spaces = line_content.scan(/\A\s*/).first
|
34
|
-
column_without_spaces = column - spaces.length
|
33
|
+
column_without_spaces = [column - spaces.length, 0].max
|
35
34
|
underscore_length = [[stop - start, line_content.length - column_without_spaces].min, 1].max
|
36
35
|
"#{line_content.gsub(/\A\s*/, '')}\n#{' ' * column_without_spaces}#{'^' * underscore_length}"
|
37
36
|
end
|
@@ -45,6 +44,10 @@ module BetterHtml
|
|
45
44
|
def calculate_column
|
46
45
|
@document[0..start-1]&.split("\n", -1)&.last&.length || 0
|
47
46
|
end
|
47
|
+
|
48
|
+
def extract_line(line:)
|
49
|
+
@document.split("\n", -1)[line - 1]&.gsub(/\n$/, '') || ""
|
50
|
+
end
|
48
51
|
end
|
49
52
|
end
|
50
53
|
end
|
@@ -52,8 +52,9 @@ EOF
|
|
52
52
|
|
53
53
|
VALID_JAVASCRIPT_TAG_TYPES = ['text/javascript', 'text/template', 'text/html']
|
54
54
|
|
55
|
-
def initialize(data, **options)
|
55
|
+
def initialize(data, config: BetterHtml.config, **options)
|
56
56
|
@data = data
|
57
|
+
@config = config
|
57
58
|
@errors = Errors.new
|
58
59
|
@options = options.present? ? options.dup : {}
|
59
60
|
@options[:template_language] ||= :html
|
@@ -228,11 +229,11 @@ EOF
|
|
228
229
|
end
|
229
230
|
|
230
231
|
def javascript_attribute_name?(name)
|
231
|
-
|
232
|
+
@config.javascript_attribute_names.any?{ |other| other === name.to_s }
|
232
233
|
end
|
233
234
|
|
234
235
|
def javascript_safe_method?(name)
|
235
|
-
|
236
|
+
@config.javascript_safe_methods.include?(name.to_s)
|
236
237
|
end
|
237
238
|
|
238
239
|
def validate_script_tag_content(node)
|
@@ -240,19 +241,20 @@ EOF
|
|
240
241
|
case token.type
|
241
242
|
when :expr_literal, :expr_escaped
|
242
243
|
expr = RubyExpr.parse(token.code)
|
243
|
-
|
244
|
-
add_error(
|
245
|
-
"erb interpolation in javascript tag must call '(...).to_json'",
|
246
|
-
location: token.location,
|
247
|
-
)
|
248
|
-
else
|
249
|
-
validate_script_expression(node, token, expr)
|
250
|
-
end
|
244
|
+
validate_script_expression(node, token, expr)
|
251
245
|
end
|
252
246
|
end
|
253
247
|
end
|
254
248
|
|
255
249
|
def validate_script_expression(node, token, expr)
|
250
|
+
if expr.calls.empty?
|
251
|
+
add_error(
|
252
|
+
"erb interpolation in javascript tag must call '(...).to_json'",
|
253
|
+
location: token.location,
|
254
|
+
)
|
255
|
+
return
|
256
|
+
end
|
257
|
+
|
256
258
|
expr.calls.each do |call|
|
257
259
|
if call.method == :raw
|
258
260
|
call.arguments.each do |argument_node|
|
@@ -25,8 +25,8 @@ Never use <script> tags inside lodash template.
|
|
25
25
|
-----------
|
26
26
|
EOF
|
27
27
|
|
28
|
-
def assert_lodash_safety(data)
|
29
|
-
tester = Tester.new(data)
|
28
|
+
def assert_lodash_safety(data, **options)
|
29
|
+
tester = Tester.new(data, **options)
|
30
30
|
|
31
31
|
message = ""
|
32
32
|
tester.errors.each do |error|
|
@@ -47,8 +47,9 @@ EOF
|
|
47
47
|
class Tester
|
48
48
|
attr_reader :errors
|
49
49
|
|
50
|
-
def initialize(data)
|
50
|
+
def initialize(data, config: BetterHtml.config)
|
51
51
|
@data = data
|
52
|
+
@config = config
|
52
53
|
@errors = Errors.new
|
53
54
|
@nodes = BetterHtml::NodeIterator.new(data, template_language: :lodash)
|
54
55
|
validate!
|
@@ -109,11 +110,11 @@ EOF
|
|
109
110
|
end
|
110
111
|
|
111
112
|
def javascript_attribute_name?(name)
|
112
|
-
|
113
|
+
@config.javascript_attribute_names.any?{ |other| other === name }
|
113
114
|
end
|
114
115
|
|
115
116
|
def lodash_safe_javascript_expression?(code)
|
116
|
-
|
117
|
+
@config.lodash_safe_javascript_expression.any?{ |other| other === code }
|
117
118
|
end
|
118
119
|
|
119
120
|
def validate_no_statements(node)
|
data/lib/better_html/version.rb
CHANGED
@@ -6,36 +6,36 @@ require 'json'
|
|
6
6
|
class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
7
7
|
test "simple template rendering" do
|
8
8
|
assert_equal "<foo>some value<foo>",
|
9
|
-
render("<foo><%= bar %><foo>", { bar: 'some value' })
|
9
|
+
render("<foo><%= bar %><foo>", locals: { bar: 'some value' })
|
10
10
|
end
|
11
11
|
|
12
12
|
test "html_safe interpolation" do
|
13
13
|
assert_equal "<foo><bar /><foo>",
|
14
|
-
render("<foo><%= bar %><foo>", { bar: '<bar />'.html_safe })
|
14
|
+
render("<foo><%= bar %><foo>", locals: { bar: '<bar />'.html_safe })
|
15
15
|
end
|
16
16
|
|
17
17
|
test "non html_safe interpolation" do
|
18
18
|
assert_equal "<foo><bar /><foo>",
|
19
|
-
render("<foo><%= bar %><foo>", { bar: '<bar />' })
|
19
|
+
render("<foo><%= bar %><foo>", locals: { bar: '<bar />' })
|
20
20
|
end
|
21
21
|
|
22
22
|
test "interpolate non-html_safe inside attribute is escaped" do
|
23
23
|
assert_equal "<a href=\" '">x \">",
|
24
|
-
render("<a href=\"<%= value %>\">", { value: ' \'">x ' })
|
24
|
+
render("<a href=\"<%= value %>\">", locals: { value: ' \'">x ' })
|
25
25
|
end
|
26
26
|
|
27
27
|
test "interpolate html_safe inside attribute is magically force-escaped" do
|
28
28
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
29
|
-
render("<a href=\"<%= value %>\">", { value: ' \'">x '.html_safe })
|
29
|
+
render("<a href=\"<%= value %>\">", locals: { value: ' \'">x '.html_safe })
|
30
30
|
end
|
31
31
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
32
32
|
"into a quoted attribute value. The value cannot contain the character \".", e.message
|
33
33
|
end
|
34
34
|
|
35
35
|
test "interpolate html_safe inside single quoted attribute" do
|
36
|
-
|
36
|
+
config = build_config(allow_single_quoted_attributes: true)
|
37
37
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
38
|
-
render("<a href=\'<%= value %>\'>", { value: ' \'">x '.html_safe })
|
38
|
+
render("<a href=\'<%= value %>\'>", config: config, locals: { value: ' \'">x '.html_safe })
|
39
39
|
end
|
40
40
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
41
41
|
"into a quoted attribute value. The value cannot contain the character '.", e.message
|
@@ -43,12 +43,12 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
43
43
|
|
44
44
|
test "interpolate in attribute name" do
|
45
45
|
assert_equal "<a data-safe-foo>",
|
46
|
-
render("<a data-<%= value %>-foo>", { value: "safe" })
|
46
|
+
render("<a data-<%= value %>-foo>", locals: { value: "safe" })
|
47
47
|
end
|
48
48
|
|
49
49
|
test "interpolate in attribute name with unsafe value with spaces" do
|
50
50
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
51
|
-
render("<a data-<%= value %>-foo>", { value: "un safe" })
|
51
|
+
render("<a data-<%= value %>-foo>", locals: { value: "un safe" })
|
52
52
|
end
|
53
53
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
54
54
|
"into a attribute name around 'data-<%= value %>'.", e.message
|
@@ -56,7 +56,7 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
56
56
|
|
57
57
|
test "interpolate in attribute name with unsafe value with equal sign" do
|
58
58
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
59
|
-
render("<a data-<%= value %>-foo>", { value: "un=safe" })
|
59
|
+
render("<a data-<%= value %>-foo>", locals: { value: "un=safe" })
|
60
60
|
end
|
61
61
|
assert_equal "Detected invalid characters as part of the "\
|
62
62
|
"interpolation into a attribute name around 'data-<%= value %>'.", e.message
|
@@ -64,7 +64,7 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
64
64
|
|
65
65
|
test "interpolate in attribute name with unsafe value with quote" do
|
66
66
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
67
|
-
render("<a data-<%= value %>-foo>", { value: "un\"safe" })
|
67
|
+
render("<a data-<%= value %>-foo>", locals: { value: "un\"safe" })
|
68
68
|
end
|
69
69
|
assert_equal "Detected invalid characters as part of the "\
|
70
70
|
"interpolation into a attribute name around 'data-<%= value %>'.", e.message
|
@@ -76,9 +76,9 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
76
76
|
end
|
77
77
|
|
78
78
|
test "interpolate after an attribute name with equal sign" do
|
79
|
-
|
79
|
+
config = build_config(allow_unquoted_attributes: true)
|
80
80
|
e = assert_raises(BetterHtml::DontInterpolateHere) do
|
81
|
-
render("<a data-foo= <%= html_attributes(foo: 'bar') %>>")
|
81
|
+
render("<a data-foo= <%= html_attributes(foo: 'bar') %>>", config: config)
|
82
82
|
end
|
83
83
|
assert_equal "Do not interpolate without quotes after "\
|
84
84
|
"attribute around 'data-foo=<%= html_attributes(foo: 'bar') %>'.", e.message
|
@@ -94,18 +94,18 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
94
94
|
end
|
95
95
|
|
96
96
|
test "interpolate in attribute without quotes" do
|
97
|
-
|
97
|
+
config = build_config(allow_unquoted_attributes: true)
|
98
98
|
e = assert_raises(BetterHtml::DontInterpolateHere) do
|
99
|
-
render("<a href=<%= value %>>", { value: "un safe" })
|
99
|
+
render("<a href=<%= value %>>", config: config, locals: { value: "un safe" })
|
100
100
|
end
|
101
101
|
assert_equal "Do not interpolate without quotes after "\
|
102
102
|
"attribute around 'href=<%= value %>'.", e.message
|
103
103
|
end
|
104
104
|
|
105
105
|
test "interpolate in attribute after value" do
|
106
|
-
|
106
|
+
config = build_config(allow_unquoted_attributes: true)
|
107
107
|
e = assert_raises(BetterHtml::DontInterpolateHere) do
|
108
|
-
render("<a href=something<%= value %>>", { value: "" })
|
108
|
+
render("<a href=something<%= value %>>", config: config, locals: { value: "" })
|
109
109
|
end
|
110
110
|
assert_equal "Do not interpolate without quotes around this "\
|
111
111
|
"attribute value. Instead of <a href=something<%= value %>> "\
|
@@ -114,12 +114,12 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
114
114
|
|
115
115
|
test "interpolate in tag name" do
|
116
116
|
assert_equal "<tag-safe-foo>",
|
117
|
-
render("<tag-<%= value %>-foo>", { value: "safe" })
|
117
|
+
render("<tag-<%= value %>-foo>", locals: { value: "safe" })
|
118
118
|
end
|
119
119
|
|
120
120
|
test "interpolate in tag name with space" do
|
121
121
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
122
|
-
render("<tag-<%= value %>-foo>", { value: "un safe" })
|
122
|
+
render("<tag-<%= value %>-foo>", locals: { value: "un safe" })
|
123
123
|
end
|
124
124
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
125
125
|
"into a tag name around: <tag-<%= value %>>.", e.message
|
@@ -127,7 +127,7 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
127
127
|
|
128
128
|
test "interpolate in tag name with slash" do
|
129
129
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
130
|
-
render("<tag-<%= value %>-foo>", { value: "un/safe" })
|
130
|
+
render("<tag-<%= value %>-foo>", locals: { value: "un/safe" })
|
131
131
|
end
|
132
132
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
133
133
|
"into a tag name around: <tag-<%= value %>>.", e.message
|
@@ -135,7 +135,7 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
135
135
|
|
136
136
|
test "interpolate in tag name with end of tag" do
|
137
137
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
138
|
-
render("<tag-<%= value %>-foo>", { value: "><script>" })
|
138
|
+
render("<tag-<%= value %>-foo>", locals: { value: "><script>" })
|
139
139
|
end
|
140
140
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
141
141
|
"into a tag name around: <tag-<%= value %>>.", e.message
|
@@ -143,12 +143,12 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
143
143
|
|
144
144
|
test "interpolate in comment" do
|
145
145
|
assert_equal "<!-- safe -->",
|
146
|
-
render("<!-- <%= value %> -->", { value: "safe" })
|
146
|
+
render("<!-- <%= value %> -->", locals: { value: "safe" })
|
147
147
|
end
|
148
148
|
|
149
149
|
test "interpolate in comment with end-of-comment" do
|
150
150
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
151
|
-
render("<!-- <%= value %> -->", { value: "-->".html_safe })
|
151
|
+
render("<!-- <%= value %> -->", locals: { value: "-->".html_safe })
|
152
152
|
end
|
153
153
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
154
154
|
"into a html comment around: <!-- <%= value %>.", e.message
|
@@ -156,18 +156,18 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
156
156
|
|
157
157
|
test "non html_safe interpolation into comment tag" do
|
158
158
|
assert_equal "<!-- --> -->",
|
159
|
-
render("<!-- <%= value %> -->", value: '-->')
|
159
|
+
render("<!-- <%= value %> -->", locals: { value: '-->' })
|
160
160
|
end
|
161
161
|
|
162
162
|
test "interpolate in script tag" do
|
163
163
|
assert_equal "<script> foo safe bar<script>",
|
164
|
-
render("<script> foo <%= value %> bar<script>", { value: "safe" })
|
164
|
+
render("<script> foo <%= value %> bar<script>", locals: { value: "safe" })
|
165
165
|
end
|
166
166
|
|
167
167
|
test "interpolate in script tag with start of comment" do
|
168
168
|
skip "skip for now; causing problems"
|
169
169
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
170
|
-
render("<script> foo <%= value %> bar<script>", { value: "<!--".html_safe })
|
170
|
+
render("<script> foo <%= value %> bar<script>", locals: { value: "<!--".html_safe })
|
171
171
|
end
|
172
172
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
173
173
|
"into a script tag around: <script> foo <%= value %>. "\
|
@@ -176,7 +176,7 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
176
176
|
|
177
177
|
test "interpolate in script tag with start of script" do
|
178
178
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
179
|
-
render("<script> foo <%= value %> bar<script>", { value: "<script".html_safe })
|
179
|
+
render("<script> foo <%= value %> bar<script>", locals: { value: "<script".html_safe })
|
180
180
|
end
|
181
181
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
182
182
|
"into a script tag around: <script> foo <%= value %>. "\
|
@@ -185,12 +185,12 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
185
185
|
|
186
186
|
test "interpolate in script tag with raw interpolation" do
|
187
187
|
assert_equal "<script> x = \"foo\" </script>",
|
188
|
-
render("<script> x = <%== value %> </script>", { value: JSON.dump("foo") })
|
188
|
+
render("<script> x = <%== value %> </script>", locals: { value: JSON.dump("foo") })
|
189
189
|
end
|
190
190
|
|
191
191
|
test "interpolate in script tag with start of script case insensitive" do
|
192
192
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
193
|
-
render("<script> foo <%= value %> bar<script>", { value: "<ScRIpT".html_safe })
|
193
|
+
render("<script> foo <%= value %> bar<script>", locals: { value: "<ScRIpT".html_safe })
|
194
194
|
end
|
195
195
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
196
196
|
"into a script tag around: <script> foo <%= value %>. "\
|
@@ -199,7 +199,7 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
199
199
|
|
200
200
|
test "interpolate in script tag with end of script" do
|
201
201
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
202
|
-
render("<script> foo <%= value %> bar<script>", { value: "</script".html_safe })
|
202
|
+
render("<script> foo <%= value %> bar<script>", locals: { value: "</script".html_safe })
|
203
203
|
end
|
204
204
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
205
205
|
"into a script tag around: <script> foo <%= value %>. "\
|
@@ -221,17 +221,17 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
221
221
|
|
222
222
|
test "non html_safe interpolation into rawtext tag" do
|
223
223
|
assert_equal "<title></title></title>",
|
224
|
-
render("<title><%= value %></title>", value: '</title>')
|
224
|
+
render("<title><%= value %></title>", locals: { value: '</title>' })
|
225
225
|
end
|
226
226
|
|
227
227
|
test "html_safe interpolation into rawtext tag" do
|
228
228
|
assert_equal "<title><safe></title>",
|
229
|
-
render("<title><%= value %></title>", value: '<safe>'.html_safe)
|
229
|
+
render("<title><%= value %></title>", locals: { value: '<safe>'.html_safe })
|
230
230
|
end
|
231
231
|
|
232
232
|
test "html_safe interpolation terminating the current tag" do
|
233
233
|
e = assert_raises(BetterHtml::UnsafeHtmlError) do
|
234
|
-
render("<title><%= value %></title>", value: '</title>'.html_safe)
|
234
|
+
render("<title><%= value %></title>", locals: { value: '</title>'.html_safe })
|
235
235
|
end
|
236
236
|
assert_equal "Detected invalid characters as part of the interpolation "\
|
237
237
|
"into a title tag around: <title><%= value %>.", e.message
|
@@ -273,7 +273,7 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
273
273
|
|
274
274
|
test "can interpolate method calls without parenthesis" do
|
275
275
|
assert_equal "<div>foo</div>",
|
276
|
-
render("<div><%= send 'value' %></div>", value: 'foo')
|
276
|
+
render("<div><%= send 'value' %></div>", locals: { value: 'foo' })
|
277
277
|
end
|
278
278
|
|
279
279
|
test "tag names are validated against tag_name_pattern regexp" do
|
@@ -290,16 +290,16 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
290
290
|
e = assert_raises(BetterHtml::HtmlError) do
|
291
291
|
render("<foo bar_baz=\"1\">")
|
292
292
|
end
|
293
|
-
assert_equal "Invalid attribute name \"bar_baz\" does not match regular expression #{
|
293
|
+
assert_equal "Invalid attribute name \"bar_baz\" does not match regular expression #{build_config.partial_attribute_name_pattern.inspect}\n"\
|
294
294
|
"On line 1 column 5:\n"\
|
295
295
|
"<foo bar_baz=\"1\">\n"\
|
296
296
|
" ^^^^^^^", e.message
|
297
297
|
end
|
298
298
|
|
299
299
|
test "single quotes are disallowed when allow_single_quoted_attributes=false" do
|
300
|
-
|
300
|
+
config = build_config(allow_single_quoted_attributes: false)
|
301
301
|
e = assert_raises(BetterHtml::HtmlError) do
|
302
|
-
render("<foo bar='1'>")
|
302
|
+
render("<foo bar='1'>", config: config)
|
303
303
|
end
|
304
304
|
assert_equal "Single-quoted attributes are not allowed\n"\
|
305
305
|
"On line 1 column 9:\n"\
|
@@ -308,16 +308,16 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
308
308
|
end
|
309
309
|
|
310
310
|
test "single quotes are allowed when allow_single_quoted_attributes=true" do
|
311
|
-
|
311
|
+
config = build_config(allow_single_quoted_attributes: true)
|
312
312
|
assert_nothing_raised do
|
313
|
-
render("<foo bar='1'>")
|
313
|
+
render("<foo bar='1'>", config: config)
|
314
314
|
end
|
315
315
|
end
|
316
316
|
|
317
317
|
test "unquoted values are disallowed when allow_unquoted_attributes=false" do
|
318
|
-
|
318
|
+
config = build_config(allow_unquoted_attributes: false)
|
319
319
|
e = assert_raises(BetterHtml::HtmlError) do
|
320
|
-
render("<foo bar=1>")
|
320
|
+
render("<foo bar=1>", config: config)
|
321
321
|
end
|
322
322
|
assert_equal "Unquoted attribute values are not allowed\n"\
|
323
323
|
"On line 1 column 9:\n"\
|
@@ -326,9 +326,9 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
326
326
|
end
|
327
327
|
|
328
328
|
test "unquoted values are allowed when allow_unquoted_attributes=true" do
|
329
|
-
|
329
|
+
config = build_config(allow_unquoted_attributes: true)
|
330
330
|
assert_nothing_raised do
|
331
|
-
render("<foo bar=1>")
|
331
|
+
render("<foo bar=1>", config: config)
|
332
332
|
end
|
333
333
|
end
|
334
334
|
|
@@ -382,9 +382,13 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
382
382
|
end
|
383
383
|
end
|
384
384
|
|
385
|
-
def
|
385
|
+
def build_config(**options)
|
386
|
+
BetterHtml::Config.new(**options)
|
387
|
+
end
|
388
|
+
|
389
|
+
def render(source, config: build_config, locals: {})
|
386
390
|
context = ViewContext.new(locals)
|
387
|
-
impl = compile(source)
|
391
|
+
impl = compile(source, config: config)
|
388
392
|
if ActionView.version < Gem::Version.new("5.1")
|
389
393
|
impl.result(context.get_binding)
|
390
394
|
else
|
@@ -392,11 +396,11 @@ class BetterHtml::BetterErb::ImplementationTest < ActiveSupport::TestCase
|
|
392
396
|
end
|
393
397
|
end
|
394
398
|
|
395
|
-
def compile(source)
|
399
|
+
def compile(source, config: build_config)
|
396
400
|
if ActionView.version < Gem::Version.new("5.1")
|
397
|
-
BetterHtml::BetterErb::ErubisImplementation.new(source)
|
401
|
+
BetterHtml::BetterErb::ErubisImplementation.new(source, config: config)
|
398
402
|
else
|
399
|
-
BetterHtml::BetterErb::ErubiImplementation.new(source)
|
403
|
+
BetterHtml::BetterErb::ErubiImplementation.new(source, config: config)
|
400
404
|
end
|
401
405
|
end
|
402
406
|
end
|
@@ -5,12 +5,10 @@ module BetterHtml
|
|
5
5
|
module TestHelper
|
6
6
|
class SafeErbTesterTest < ActiveSupport::TestCase
|
7
7
|
setup do
|
8
|
-
BetterHtml.
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
.stubs(:javascript_attribute_names)
|
13
|
-
.returns([/\Aon/i, 'data-eval'])
|
8
|
+
@config = BetterHtml::Config.new(
|
9
|
+
javascript_safe_methods: ['j', 'escape_javascript', 'to_json'],
|
10
|
+
javascript_attribute_names: [/\Aon/i, 'data-eval'],
|
11
|
+
)
|
14
12
|
end
|
15
13
|
|
16
14
|
test "string without interpolation is safe" do
|
@@ -390,9 +388,17 @@ module BetterHtml
|
|
390
388
|
assert_equal "erb interpolation in javascript attribute must call '(...).to_json'", errors.first.message
|
391
389
|
end
|
392
390
|
|
391
|
+
test "ivar missing .to_json is unsafe" do
|
392
|
+
errors = parse('<script><%= @feature.html_safe %></script>').errors
|
393
|
+
|
394
|
+
assert_equal 1, errors.size
|
395
|
+
assert_equal "<%= @feature.html_safe %>", errors.first.location.source
|
396
|
+
assert_equal "erb interpolation in javascript tag must call '(...).to_json'", errors.first.message
|
397
|
+
end
|
398
|
+
|
393
399
|
private
|
394
400
|
def parse(data, template_language: :html)
|
395
|
-
SafeErbTester::Tester.new(data, template_language: template_language)
|
401
|
+
SafeErbTester::Tester.new(data, config: @config, template_language: template_language)
|
396
402
|
end
|
397
403
|
end
|
398
404
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: better_html
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Francois Chagnon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: erubi
|
@@ -66,6 +66,34 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '2.4'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: smart_properties
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: html_tokenizer
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
name: rake
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,6 +124,7 @@ files:
|
|
96
124
|
- lib/better_html/better_erb/erubis_implementation.rb
|
97
125
|
- lib/better_html/better_erb/runtime_checks.rb
|
98
126
|
- lib/better_html/better_erb/validated_output_buffer.rb
|
127
|
+
- lib/better_html/config.rb
|
99
128
|
- lib/better_html/errors.rb
|
100
129
|
- lib/better_html/helpers.rb
|
101
130
|
- lib/better_html/html_attributes.rb
|