escape_utils 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|