escape_utils 1.2.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +47 -88
- data/benchmark/html_escape_once.rb +25 -0
- data/benchmark/javascript_escape.rb +1 -1
- data/benchmark/javascript_unescape.rb +1 -1
- data/benchmark/url_decode.rb +28 -0
- data/benchmark/url_encode.rb +37 -0
- data/benchmark/xml_escape.rb +7 -11
- data/ext/escape_utils/escape_utils.c +7 -115
- data/ext/escape_utils/houdini.h +3 -5
- data/ext/escape_utils/houdini_html_e.c +52 -24
- data/ext/escape_utils/houdini_uri_e.c +6 -17
- data/ext/escape_utils/houdini_uri_u.c +5 -15
- data/ext/escape_utils/houdini_xml_e.c +15 -1
- data/lib/escape_utils/html/cgi.rb +10 -8
- data/lib/escape_utils/html/erb.rb +1 -10
- data/lib/escape_utils/html/haml.rb +1 -7
- data/lib/escape_utils/html/rack.rb +3 -3
- data/lib/escape_utils/html_safety.rb +13 -0
- data/lib/escape_utils/url/cgi.rb +0 -8
- data/lib/escape_utils/url/erb.rb +1 -1
- data/lib/escape_utils/url/uri.rb +11 -7
- data/lib/escape_utils/version.rb +1 -1
- data/lib/escape_utils.rb +61 -9
- data/test/helper.rb +16 -3
- data/test/html/escape_test.rb +41 -39
- data/test/html/unescape_test.rb +0 -16
- data/test/html_safety_test.rb +1 -27
- data/test/javascript/escape_test.rb +0 -5
- data/test/query/escape_test.rb +0 -16
- data/test/query/unescape_test.rb +2 -18
- data/test/uri/unescape_test.rb +2 -2
- data/test/uri_component/unescape_test.rb +2 -2
- data/test/url/escape_test.rb +0 -16
- data/test/url/unescape_test.rb +2 -18
- metadata +6 -8
- data/benchmark/html_escape.rb +0 -68
- data/benchmark/html_unescape.rb +0 -35
- data/benchmark/url_escape.rb +0 -56
- data/benchmark/url_unescape.rb +0 -50
- data/ext/escape_utils/houdini_html_u.c +0 -122
@@ -18,8 +18,8 @@
|
|
18
18
|
static const char HTML_ESCAPE_TABLE[] = {
|
19
19
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
20
20
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
21
|
-
0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0,
|
22
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
21
|
+
0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0,
|
22
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 5, 0,
|
23
23
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
24
24
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
25
25
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
@@ -35,24 +35,64 @@ static const char HTML_ESCAPE_TABLE[] = {
|
|
35
35
|
};
|
36
36
|
|
37
37
|
static const char *HTML_ESCAPES[] = {
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
">"
|
38
|
+
"",
|
39
|
+
""",
|
40
|
+
"&",
|
41
|
+
"'",
|
42
|
+
"<",
|
43
|
+
">"
|
45
44
|
};
|
46
45
|
|
46
|
+
static const int HTML_ESCAPES_LENGTHS[] = {
|
47
|
+
0,
|
48
|
+
6,
|
49
|
+
5,
|
50
|
+
5,
|
51
|
+
4,
|
52
|
+
4
|
53
|
+
};
|
54
|
+
|
55
|
+
static int
|
56
|
+
is_entity(const uint8_t *src, size_t size)
|
57
|
+
{
|
58
|
+
size_t i = 0;
|
59
|
+
|
60
|
+
if (size == 0 || src[0] != '&')
|
61
|
+
return false;
|
62
|
+
|
63
|
+
if (size > 16)
|
64
|
+
size = 16;
|
65
|
+
|
66
|
+
if (size >= 4 && src[1] == '#') {
|
67
|
+
if (_isdigit(src[2])) {
|
68
|
+
for (i = 3; i < size && _isdigit(src[i]); ++i);
|
69
|
+
}
|
70
|
+
else if ((src[2] == 'x' || src[2] == 'X') && _isxdigit(src[3])) {
|
71
|
+
for (i = 4; i < size && _isxdigit(src[i]); ++i);
|
72
|
+
}
|
73
|
+
else return false;
|
74
|
+
}
|
75
|
+
else {
|
76
|
+
for (i = 1; i < size && _isasciialpha(src[i]); ++i);
|
77
|
+
if (i == 1) return false;
|
78
|
+
}
|
79
|
+
|
80
|
+
return i < size && src[i] == ';';
|
81
|
+
}
|
82
|
+
|
47
83
|
int
|
48
|
-
|
84
|
+
houdini_escape_html_once(gh_buf *ob, const uint8_t *src, size_t size)
|
49
85
|
{
|
50
86
|
size_t i = 0, org, esc = 0;
|
51
87
|
|
52
88
|
while (i < size) {
|
53
89
|
org = i;
|
54
|
-
while (i < size
|
90
|
+
while (i < size) {
|
91
|
+
esc = HTML_ESCAPE_TABLE[src[i]];
|
92
|
+
if (unlikely(esc != 0) && !is_entity(src + i, size - i))
|
93
|
+
break;
|
55
94
|
i++;
|
95
|
+
}
|
56
96
|
|
57
97
|
if (i > org) {
|
58
98
|
if (unlikely(org == 0)) {
|
@@ -69,22 +109,10 @@ houdini_escape_html0(gh_buf *ob, const uint8_t *src, size_t size, int secure)
|
|
69
109
|
if (unlikely(i >= size))
|
70
110
|
break;
|
71
111
|
|
72
|
-
|
73
|
-
if (src[i] == '/' && !secure) {
|
74
|
-
gh_buf_putc(ob, '/');
|
75
|
-
} else {
|
76
|
-
gh_buf_puts(ob, HTML_ESCAPES[esc]);
|
77
|
-
}
|
112
|
+
gh_buf_put(ob, HTML_ESCAPES[esc], HTML_ESCAPES_LENGTHS[esc]);
|
78
113
|
|
79
114
|
i++;
|
80
115
|
}
|
81
116
|
|
82
117
|
return 1;
|
83
118
|
}
|
84
|
-
|
85
|
-
int
|
86
|
-
houdini_escape_html(gh_buf *ob, const uint8_t *src, size_t size)
|
87
|
-
{
|
88
|
-
return houdini_escape_html0(ob, src, size, 1);
|
89
|
-
}
|
90
|
-
|
@@ -44,7 +44,7 @@ static const char URI_SAFE[] = {
|
|
44
44
|
|
45
45
|
static int
|
46
46
|
escape(gh_buf *ob, const uint8_t *src, size_t size,
|
47
|
-
const char *safe_table
|
47
|
+
const char *safe_table)
|
48
48
|
{
|
49
49
|
static const uint8_t hex_chars[] = "0123456789ABCDEF";
|
50
50
|
|
@@ -73,13 +73,9 @@ escape(gh_buf *ob, const uint8_t *src, size_t size,
|
|
73
73
|
if (i >= size)
|
74
74
|
break;
|
75
75
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];
|
80
|
-
hex_str[2] = hex_chars[src[i] & 0xF];
|
81
|
-
gh_buf_put(ob, hex_str, 3);
|
82
|
-
}
|
76
|
+
hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];
|
77
|
+
hex_str[2] = hex_chars[src[i] & 0xF];
|
78
|
+
gh_buf_put(ob, hex_str, 3);
|
83
79
|
|
84
80
|
i++;
|
85
81
|
}
|
@@ -90,18 +86,11 @@ escape(gh_buf *ob, const uint8_t *src, size_t size,
|
|
90
86
|
int
|
91
87
|
houdini_escape_uri(gh_buf *ob, const uint8_t *src, size_t size)
|
92
88
|
{
|
93
|
-
return escape(ob, src, size, URI_SAFE
|
89
|
+
return escape(ob, src, size, URI_SAFE);
|
94
90
|
}
|
95
91
|
|
96
92
|
int
|
97
93
|
houdini_escape_uri_component(gh_buf *ob, const uint8_t *src, size_t size)
|
98
94
|
{
|
99
|
-
return escape(ob, src, size, URL_SAFE
|
100
|
-
}
|
101
|
-
|
102
|
-
int
|
103
|
-
houdini_escape_url(gh_buf *ob, const uint8_t *src, size_t size)
|
104
|
-
{
|
105
|
-
return escape(ob, src, size, URL_SAFE, true);
|
95
|
+
return escape(ob, src, size, URL_SAFE);
|
106
96
|
}
|
107
|
-
|
@@ -7,13 +7,13 @@
|
|
7
7
|
#define hex2c(c) ((c | 32) % 39 - 9)
|
8
8
|
|
9
9
|
static int
|
10
|
-
unescape(gh_buf *ob, const uint8_t *src, size_t size
|
10
|
+
unescape(gh_buf *ob, const uint8_t *src, size_t size)
|
11
11
|
{
|
12
12
|
size_t i = 0, org;
|
13
13
|
|
14
14
|
while (i < size) {
|
15
15
|
org = i;
|
16
|
-
while (i < size && src[i] != '%'
|
16
|
+
while (i < size && src[i] != '%')
|
17
17
|
i++;
|
18
18
|
|
19
19
|
if (likely(i > org)) {
|
@@ -31,11 +31,7 @@ unescape(gh_buf *ob, const uint8_t *src, size_t size, bool unescape_plus)
|
|
31
31
|
if (i >= size)
|
32
32
|
break;
|
33
33
|
|
34
|
-
|
35
|
-
gh_buf_putc(ob, unescape_plus ? ' ' : '+');
|
36
|
-
continue;
|
37
|
-
}
|
38
|
-
|
34
|
+
i++;
|
39
35
|
if (i + 1 < size && _isxdigit(src[i]) && _isxdigit(src[i + 1])) {
|
40
36
|
unsigned char new_char = (hex2c(src[i]) << 4) + hex2c(src[i + 1]);
|
41
37
|
gh_buf_putc(ob, new_char);
|
@@ -51,18 +47,12 @@ unescape(gh_buf *ob, const uint8_t *src, size_t size, bool unescape_plus)
|
|
51
47
|
int
|
52
48
|
houdini_unescape_uri(gh_buf *ob, const uint8_t *src, size_t size)
|
53
49
|
{
|
54
|
-
return unescape(ob, src, size
|
50
|
+
return unescape(ob, src, size);
|
55
51
|
}
|
56
52
|
|
57
53
|
int
|
58
54
|
houdini_unescape_uri_component(gh_buf *ob, const uint8_t *src, size_t size)
|
59
55
|
{
|
60
|
-
return unescape(ob, src, size
|
61
|
-
}
|
62
|
-
|
63
|
-
int
|
64
|
-
houdini_unescape_url(gh_buf *ob, const uint8_t *src, size_t size)
|
65
|
-
{
|
66
|
-
return unescape(ob, src, size, true);
|
56
|
+
return unescape(ob, src, size);
|
67
57
|
}
|
68
58
|
|
@@ -25,6 +25,20 @@ static const char *LOOKUP_CODES[] = {
|
|
25
25
|
">"
|
26
26
|
};
|
27
27
|
|
28
|
+
static const int LOOKUP_CODES_LENGTHS[] = {
|
29
|
+
0,
|
30
|
+
0,
|
31
|
+
0,
|
32
|
+
0,
|
33
|
+
0,
|
34
|
+
1,
|
35
|
+
6,
|
36
|
+
5,
|
37
|
+
6,
|
38
|
+
4,
|
39
|
+
4
|
40
|
+
};
|
41
|
+
|
28
42
|
static const char CODE_INVALID = 5;
|
29
43
|
|
30
44
|
static const char XML_LOOKUP_TABLE[] = {
|
@@ -129,7 +143,7 @@ houdini_escape_xml(gh_buf *ob, const uint8_t *src, size_t size)
|
|
129
143
|
if (end >= size)
|
130
144
|
break;
|
131
145
|
|
132
|
-
|
146
|
+
gh_buf_put(ob, LOOKUP_CODES[code], LOOKUP_CODES_LENGTHS[code]);
|
133
147
|
}
|
134
148
|
|
135
149
|
return 1;
|
@@ -1,11 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module EscapeUtils
|
2
|
+
module CGIHtmlSafety
|
3
|
+
def escapeHTML(html)
|
4
|
+
::EscapeUtils::HtmlSafety.escape_once(html) { |s| super(s) }
|
5
|
+
end
|
6
6
|
|
7
|
-
def unescapeHTML(
|
8
|
-
|
7
|
+
def unescapeHTML(html)
|
8
|
+
super(html.to_s)
|
9
9
|
end
|
10
10
|
end
|
11
|
-
end
|
11
|
+
end
|
12
|
+
|
13
|
+
CGI.singleton_class.prepend(EscapeUtils::CGIHtmlSafety)
|
@@ -1,6 +1,15 @@
|
|
1
1
|
module EscapeUtils
|
2
2
|
module HtmlSafety
|
3
3
|
if "".respond_to? :html_safe?
|
4
|
+
def self.escape_once(s)
|
5
|
+
s = s.to_s
|
6
|
+
if s.html_safe?
|
7
|
+
s.html_safe
|
8
|
+
else
|
9
|
+
yield(s).html_safe
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
4
13
|
def _escape_html(s)
|
5
14
|
if s.html_safe?
|
6
15
|
s.to_s.html_safe
|
@@ -9,6 +18,10 @@ module EscapeUtils
|
|
9
18
|
end
|
10
19
|
end
|
11
20
|
else
|
21
|
+
def self.escape_once(s)
|
22
|
+
yield s.to_s
|
23
|
+
end
|
24
|
+
|
12
25
|
def _escape_html(s)
|
13
26
|
EscapeUtils.escape_html(s.to_s)
|
14
27
|
end
|
data/lib/escape_utils/url/cgi.rb
CHANGED
data/lib/escape_utils/url/erb.rb
CHANGED
data/lib/escape_utils/url/uri.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
if URI.respond_to?(:escape) # Was removed in Ruby 3.0. Let's not bring it back
|
4
|
+
module URI
|
5
|
+
def self.escape(s, unsafe=nil)
|
6
|
+
EscapeUtils.escape_uri(s.to_s)
|
7
|
+
end
|
8
|
+
def self.unescape(s)
|
9
|
+
EscapeUtils.unescape_uri(s.to_s)
|
10
|
+
end
|
4
11
|
end
|
5
|
-
|
6
|
-
EscapeUtils.unescape_uri(s.to_s)
|
7
|
-
end
|
8
|
-
end
|
12
|
+
end
|
data/lib/escape_utils/version.rb
CHANGED
data/lib/escape_utils.rb
CHANGED
@@ -1,22 +1,74 @@
|
|
1
|
+
require 'cgi'
|
1
2
|
require 'escape_utils/escape_utils'
|
2
3
|
require 'escape_utils/version' unless defined? EscapeUtils::VERSION
|
3
4
|
|
4
5
|
module EscapeUtils
|
5
6
|
extend self
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def html_secure
|
9
|
+
warn "EscapeUtils.html_secure is deprecated"
|
10
|
+
false
|
11
|
+
end
|
12
|
+
|
13
|
+
def html_secure=(val)
|
14
|
+
warn "EscapeUtils.html_secure is deprecated"
|
12
15
|
end
|
13
|
-
self.html_secure = true
|
14
16
|
|
15
17
|
# Default String class to return from HTML escaping
|
16
|
-
|
17
|
-
|
18
|
+
attr_reader :html_safe_string_class
|
19
|
+
|
20
|
+
def html_safe_string_class=(klass)
|
21
|
+
unless String >= klass
|
22
|
+
raise ArgumentError, "EscapeUtils.html_safe_string_class must inherit from ::String"
|
23
|
+
end
|
24
|
+
@html_safe_string_class = klass
|
18
25
|
end
|
26
|
+
|
19
27
|
self.html_safe_string_class = String
|
20
28
|
|
21
29
|
autoload :HtmlSafety, 'escape_utils/html_safety'
|
22
|
-
|
30
|
+
|
31
|
+
def self.escape_html_once_as_html_safe(html)
|
32
|
+
escaped = escape_html_once(html)
|
33
|
+
if String == @html_safe_string_class
|
34
|
+
escaped
|
35
|
+
else
|
36
|
+
escaped = @html_safe_string_class.new(escaped)
|
37
|
+
escaped.instance_variable_set(:@html_safe, true)
|
38
|
+
escaped
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.escape_html(html, secure = false)
|
43
|
+
warn "EscapeUtils.escape_html is deprecated. Use GCI.escapeHTML instead, it's faster"
|
44
|
+
CGI.escapeHTML(html)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.escape_html_as_html_safe(html)
|
48
|
+
warn "EscapeUtils.escape_html_as_html_safe is deprecated. Use GCI.escapeHTML(str).html_safe instead, it's faster"
|
49
|
+
|
50
|
+
escaped = CGI.escapeHTML(html)
|
51
|
+
if String == @html_safe_string_class
|
52
|
+
escaped
|
53
|
+
else
|
54
|
+
escaped = @html_safe_string_class.new(escaped)
|
55
|
+
escaped.instance_variable_set(:@html_safe, true)
|
56
|
+
escaped
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.unescape_html(html)
|
61
|
+
warn "EscapeUtils.unescape_html is deprecated. Use GCI.unescapeHTML instead, performance is similar"
|
62
|
+
CGI.unescapeHTML(html)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.escape_url(string)
|
66
|
+
warn "EscapeUtils.escape_url is deprecated. Use CGI.escape instead, performance is similar"
|
67
|
+
CGI.escape(string)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.unescape_url(string)
|
71
|
+
warn "EscapeUtils.unescape_url is deprecated. Use CGI.unescape instead, performance is similar"
|
72
|
+
CGI.unescape(string)
|
73
|
+
end
|
74
|
+
end
|
data/test/helper.rb
CHANGED
@@ -1,11 +1,24 @@
|
|
1
1
|
# Basic test environment.
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
module HideOwnWarnings
|
4
|
+
def warn(message)
|
5
|
+
unless message.include?("EscapeUtils")
|
6
|
+
super
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
Warning.prepend(HideOwnWarnings)
|
6
11
|
|
12
|
+
require 'bundler/setup'
|
7
13
|
require 'escape_utils'
|
8
14
|
|
15
|
+
require 'active_support'
|
16
|
+
require 'active_support/json'
|
17
|
+
require "active_support/core_ext/string/output_safety"
|
18
|
+
|
19
|
+
require 'action_view'
|
20
|
+
require 'action_view/helpers'
|
21
|
+
|
9
22
|
# bring in minitest
|
10
23
|
require 'minitest/autorun'
|
11
24
|
|
data/test/html/escape_test.rb
CHANGED
@@ -1,9 +1,18 @@
|
|
1
1
|
require File.expand_path("../../helper", __FILE__)
|
2
2
|
|
3
|
-
class MyCustomHtmlSafeString < String
|
4
|
-
end
|
5
|
-
|
6
3
|
class HtmlEscapeTest < Minitest::Test
|
4
|
+
MyCustomHtmlSafeString = Class.new(String)
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@_previous_safe = EscapeUtils.html_secure
|
8
|
+
@_previous_class = EscapeUtils.html_safe_string_class
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
EscapeUtils.html_secure = @_previous_safe
|
13
|
+
EscapeUtils.html_safe_string_class = @_previous_class
|
14
|
+
end
|
15
|
+
|
7
16
|
def test_escape_source_encoding_is_maintained
|
8
17
|
source = 'foobar'
|
9
18
|
str = EscapeUtils.escape_html_as_html_safe(source)
|
@@ -29,38 +38,53 @@ class HtmlEscapeTest < Minitest::Test
|
|
29
38
|
end
|
30
39
|
|
31
40
|
def test_escape_basic_html_with_secure
|
32
|
-
assert_equal "<some_tag
|
41
|
+
assert_equal "<some_tag/>", EscapeUtils.escape_html("<some_tag/>")
|
33
42
|
|
34
|
-
secure_before = EscapeUtils.html_secure
|
35
43
|
EscapeUtils.html_secure = true
|
36
|
-
assert_equal "<some_tag
|
37
|
-
EscapeUtils.html_secure = secure_before
|
44
|
+
assert_equal "<some_tag/>", EscapeUtils.escape_html("<some_tag/>")
|
38
45
|
end
|
39
46
|
|
40
47
|
def test_escape_basic_html_without_secure
|
41
48
|
assert_equal "<some_tag/>", EscapeUtils.escape_html("<some_tag/>", false)
|
42
49
|
|
43
|
-
secure_before = EscapeUtils.html_secure
|
44
50
|
EscapeUtils.html_secure = false
|
45
51
|
assert_equal "<some_tag/>", EscapeUtils.escape_html("<some_tag/>")
|
46
|
-
EscapeUtils.html_secure = secure_before
|
47
52
|
end
|
48
53
|
|
49
54
|
def test_escape_double_quotes
|
50
|
-
assert_equal "<some_tag some_attr="some value"
|
55
|
+
assert_equal "<some_tag some_attr="some value"/>", EscapeUtils.escape_html("<some_tag some_attr=\"some value\"/>")
|
51
56
|
end
|
52
57
|
|
53
58
|
def test_escape_single_quotes
|
54
|
-
assert_equal "<some_tag some_attr='some value'
|
59
|
+
assert_equal "<some_tag some_attr='some value'/>", EscapeUtils.escape_html("<some_tag some_attr='some value'/>")
|
55
60
|
end
|
56
61
|
|
57
62
|
def test_escape_ampersand
|
58
|
-
assert_equal "<b>Bourbon & Branch<
|
59
|
-
end
|
60
|
-
|
61
|
-
def
|
62
|
-
|
63
|
-
|
63
|
+
assert_equal "<b>Bourbon & Branch</b>", EscapeUtils.escape_html("<b>Bourbon & Branch</b>")
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_escape_html_once
|
67
|
+
{
|
68
|
+
'&<' => '&<',
|
69
|
+
'&<&x;' => '&<&x;',
|
70
|
+
'&' => '&amp',
|
71
|
+
'&!;' => '&!;',
|
72
|
+
'�' => '�',
|
73
|
+
' ' => ' ',
|
74
|
+
'
' => '&#10',
|
75
|
+
'�' => '�',
|
76
|
+
'�' => '�',
|
77
|
+
'ð' => 'ð',
|
78
|
+
'ð' => '&#xf0',
|
79
|
+
'&#x;' => '&#x;',
|
80
|
+
'oo;' => '&#xfoo;',
|
81
|
+
'&#;' => '&#;',
|
82
|
+
'&#foo;' => '&#foo;',
|
83
|
+
'foo&bar' => 'foo&bar',
|
84
|
+
}.each do |(input, output)|
|
85
|
+
assert_equal output, EscapeUtils.escape_html_once(input)
|
86
|
+
assert_equal output, EscapeUtils.escape_html_once_as_html_safe(input)
|
87
|
+
end
|
64
88
|
end
|
65
89
|
|
66
90
|
def test_html_safe_escape_default_works
|
@@ -69,27 +93,21 @@ class HtmlEscapeTest < Minitest::Test
|
|
69
93
|
end
|
70
94
|
|
71
95
|
def test_returns_custom_string_class
|
72
|
-
klass_before = EscapeUtils.html_safe_string_class
|
73
96
|
EscapeUtils.html_safe_string_class = MyCustomHtmlSafeString
|
74
97
|
|
75
98
|
str = EscapeUtils.escape_html_as_html_safe('foobar')
|
76
99
|
assert_equal 'foobar', str
|
77
100
|
assert_equal MyCustomHtmlSafeString, str.class
|
78
101
|
assert_equal true, str.instance_variable_get(:@html_safe)
|
79
|
-
ensure
|
80
|
-
EscapeUtils.html_safe_string_class = klass_before
|
81
102
|
end
|
82
103
|
|
83
104
|
def test_returns_custom_string_class_when_string_requires_escaping
|
84
|
-
klass_before = EscapeUtils.html_safe_string_class
|
85
105
|
EscapeUtils.html_safe_string_class = MyCustomHtmlSafeString
|
86
106
|
|
87
107
|
str = EscapeUtils.escape_html_as_html_safe("<script>")
|
88
108
|
assert_equal "<script>", str
|
89
109
|
assert_equal MyCustomHtmlSafeString, str.class
|
90
110
|
assert_equal true, str.instance_variable_get(:@html_safe)
|
91
|
-
ensure
|
92
|
-
EscapeUtils.html_safe_string_class = klass_before
|
93
111
|
end
|
94
112
|
|
95
113
|
def test_html_safe_string_class_descends_string
|
@@ -105,22 +123,6 @@ class HtmlEscapeTest < Minitest::Test
|
|
105
123
|
end
|
106
124
|
end
|
107
125
|
|
108
|
-
def test_utf8_or_ascii_input_only
|
109
|
-
str = "<b>Bourbon & Branch</b>"
|
110
|
-
|
111
|
-
str.force_encoding 'ISO-8859-1'
|
112
|
-
assert_raises Encoding::CompatibilityError do
|
113
|
-
EscapeUtils.escape_html(str)
|
114
|
-
end
|
115
|
-
|
116
|
-
str.force_encoding 'UTF-8'
|
117
|
-
begin
|
118
|
-
EscapeUtils.escape_html(str)
|
119
|
-
rescue Encoding::CompatibilityError => e
|
120
|
-
assert_nil e, "#{e.class.name} raised, expected not to"
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
126
|
def test_return_value_is_tagged_as_utf8
|
125
127
|
str = "<b>Bourbon & Branch</b>".encode('utf-8')
|
126
128
|
assert_equal Encoding.find('UTF-8'), EscapeUtils.escape_html(str).encoding
|
data/test/html/unescape_test.rb
CHANGED
@@ -23,22 +23,6 @@ class HtmlUnescapeTest < Minitest::Test
|
|
23
23
|
assert_equal "<", EscapeUtils.unescape_html("<")
|
24
24
|
end
|
25
25
|
|
26
|
-
def test_input_must_be_utf8_or_ascii
|
27
|
-
escaped = EscapeUtils.escape_html("<b>Bourbon & Branch</b>")
|
28
|
-
|
29
|
-
escaped.force_encoding 'ISO-8859-1'
|
30
|
-
assert_raises Encoding::CompatibilityError do
|
31
|
-
EscapeUtils.unescape_html(escaped)
|
32
|
-
end
|
33
|
-
|
34
|
-
escaped.force_encoding 'UTF-8'
|
35
|
-
begin
|
36
|
-
EscapeUtils.unescape_html(escaped)
|
37
|
-
rescue Encoding::CompatibilityError => e
|
38
|
-
assert_nil e, "#{e.class.name} raised, expected not to"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
26
|
def test_return_value_is_tagged_as_utf8
|
43
27
|
escaped = EscapeUtils.escape_html("<b>Bourbon & Branch</b>")
|
44
28
|
assert_equal Encoding.find('UTF-8'), EscapeUtils.unescape_html(escaped).encoding
|
data/test/html_safety_test.rb
CHANGED
@@ -1,37 +1,11 @@
|
|
1
1
|
require File.expand_path("../helper", __FILE__)
|
2
2
|
|
3
|
-
class Object
|
4
|
-
def html_safe?
|
5
|
-
false
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
class TestSafeBuffer < String
|
10
|
-
def html_safe?
|
11
|
-
true
|
12
|
-
end
|
13
|
-
|
14
|
-
def html_safe
|
15
|
-
self
|
16
|
-
end
|
17
|
-
|
18
|
-
def to_s
|
19
|
-
self
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class String
|
24
|
-
def html_safe
|
25
|
-
TestSafeBuffer.new(self)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
3
|
class HtmlEscapeTest < Minitest::Test
|
30
4
|
include EscapeUtils::HtmlSafety
|
31
5
|
|
32
6
|
def test_marks_escaped_strings_safe
|
33
7
|
escaped = _escape_html("<strong>unsafe</strong>")
|
34
|
-
assert_equal "<strong>unsafe<
|
8
|
+
assert_equal "<strong>unsafe</strong>", escaped
|
35
9
|
assert escaped.html_safe?
|
36
10
|
end
|
37
11
|
|