OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of dart.convert; | 5 part of dart.convert; |
6 | 6 |
7 // TODO(floitsch) - Document - Issue 13097 | 7 /** |
8 * A `String` converter that converts characters to HTML entities. | |
9 * | |
10 * This is intended to sanitice text before inserting the text into an HTML | |
11 * document. Characters that are meaningful in HTML are converted to | |
12 * HTML entities (like `&` for `&`). | |
13 * | |
14 * The general converter escapes all characters that are meaningful in HTML | |
15 * attributes or normal element context. Elements with special content types | |
16 * (like CSS or JavaScript) may need a more specialized escaping that | |
17 * understands that content type. | |
18 * | |
19 * If the context where the text will be inserted is known in more detail, | |
20 * it's possible to omit escaping some characters (like quotes when not | |
21 * inside an attribute value). | |
22 * | |
23 * The escaped text should only be used inside quoted HTML attributes values | |
24 * or as text content of a normal element. Using the escaped text inside a | |
25 * tag, but not inside a quoted attribute value, is still dangerous. | |
26 */ | |
8 const HtmlEscape HTML_ESCAPE = const HtmlEscape(); | 27 const HtmlEscape HTML_ESCAPE = const HtmlEscape(); |
9 | 28 |
10 /** | 29 /** |
11 * HTML escape modes. | 30 * HTML escape modes. |
12 * | 31 * |
13 * Allows specifying a mode for HTML escaping that depend on the context | 32 * Allows specifying a mode for HTML escaping that depend on the context |
14 * where the escaped result is going to be used. | 33 * where the escaped result is going to be used. |
15 * The relevant contexts are: | 34 * The relevant contexts are: |
16 * | 35 * |
17 * * as text content of an HTML element. | 36 * * as text content of an HTML element. |
18 * * as value of a (single- or double-) quoted attribute value. | 37 * * as value of a (single- or double-) quoted attribute value. |
19 * | 38 * |
20 * All modes require escaping of `&` (ampersand) characters, and may | 39 * All modes require escaping of `&` (ampersand) characters, and may |
21 * enable escaping of more characters. | 40 * enable escaping of more characters. |
22 */ | 41 */ |
23 class HtmlEscapeMode { | 42 class HtmlEscapeMode { |
24 final String _name; | 43 final String _name; |
25 /** Whether to escape '<' and '>'. */ | 44 /** Whether to escape '<' and '>'. */ |
26 final bool escapeLtGt; | 45 final bool escapeLtGt; |
27 /** Whether to escape '"' (quote). */ | 46 /** Whether to escape '"' (quote). */ |
28 final bool escapeQuot; | 47 final bool escapeQuot; |
29 /** Whether to escape "'" (apostrophe). */ | 48 /** Whether to escape "'" (apostrophe). */ |
30 final bool escapeApos; | 49 final bool escapeApos; |
50 /** | |
51 * Whether to escape "/" (forward slash, solidus). | |
52 * | |
53 * Escaping a slash is recommended to avoid cross-site scripting attacks by | |
54 * [the Open Web Application Security Project](https://www.owasp.org/index.php /XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Befor e_Inserting_Untrusted_Data_into_HTML_Element_Content) | |
55 */ | |
56 final bool escapeSlash; | |
31 | 57 |
32 /** | 58 /** |
33 * Default escaping mode which escape all characters. | 59 * Default escaping mode which escape all characters. |
34 * | 60 * |
35 * The result of such an escaping is usable both in element content and | 61 * The result of such an escaping is usable both in element content and |
36 * in any attribute value. | 62 * in any attribute value. |
37 * | 63 * |
38 * The escaping only works for elements with normal HTML content, | 64 * The escaping only works for elements with normal HTML content, |
39 * and not for, for example, script or style element content, | 65 * and not for, for example, script or style element content, |
40 * which require escapes matching their particular content syntax. | 66 * which require escapes matching their particular content syntax. |
41 */ | 67 */ |
42 static const HtmlEscapeMode UNKNOWN = | 68 static const HtmlEscapeMode UNKNOWN = |
43 const HtmlEscapeMode._('unknown', true, true, true); | 69 const HtmlEscapeMode._('unknown', true, true, true, true); |
44 | 70 |
45 /** | 71 /** |
46 * Escaping mode for text going into double-quoted HTML attribute values. | 72 * Escaping mode for text going into double-quoted HTML attribute values. |
47 * | 73 * |
48 * The result should not be used as the content of an unquoted | 74 * The result should not be used as the content of an unquoted |
49 * or single-quoted attribute value. | 75 * or single-quoted attribute value. |
50 * | 76 * |
51 * Escapes only double quotes (`"`) but not single quotes (`'`). | 77 * Escapes only double quotes (`"`) but not single quotes (`'`). |
52 */ | 78 */ |
53 static const HtmlEscapeMode ATTRIBUTE = | 79 static const HtmlEscapeMode ATTRIBUTE = |
54 const HtmlEscapeMode._('attribute', false, true, false); | 80 const HtmlEscapeMode._('attribute', false, true, false, false); |
55 | 81 |
56 /** | 82 /** |
57 * Escaping mode for text going into single-quoted HTML attribute values. | 83 * Escaping mode for text going into single-quoted HTML attribute values. |
58 * | 84 * |
59 * The result should not be used as the content of an unquoted | 85 * The result should not be used as the content of an unquoted |
60 * or double-quoted attribute value. | 86 * or double-quoted attribute value. |
61 * | 87 * |
62 * Escapes only single quotes (`'`) but not double quotes (`"`). | 88 * Escapes only single quotes (`'`) but not double quotes (`"`). |
63 */ | 89 */ |
64 static const HtmlEscapeMode SQ_ATTRIBUTE = | 90 static const HtmlEscapeMode SQ_ATTRIBUTE = |
65 const HtmlEscapeMode._('attribute', false, false, true); | 91 const HtmlEscapeMode._('attribute', false, false, true, false); |
66 | 92 |
67 /** | 93 /** |
68 * Escaping mode for text going into HTML element content. | 94 * Escaping mode for text going into HTML element content. |
69 * | 95 * |
70 * The escaping only works for elements with normal HTML content, | 96 * The escaping only works for elements with normal HTML content, |
71 * and not for, for example, script or style element content, | 97 * and not for, for example, script or style element content, |
72 * which require escapes matching their particular content syntax. | 98 * which require escapes matching their particular content syntax. |
73 * | 99 * |
74 * Escapes `<` and `>` characters. | 100 * Escapes `<` and `>` characters. |
75 */ | 101 */ |
76 static const HtmlEscapeMode ELEMENT = | 102 static const HtmlEscapeMode ELEMENT = |
77 const HtmlEscapeMode._('element', true, false, false); | 103 const HtmlEscapeMode._('element', true, false, false, false); |
78 | 104 |
79 const HtmlEscapeMode._( | 105 const HtmlEscapeMode._(this._name, |
80 this._name, this.escapeLtGt, this.escapeQuot, this.escapeApos); | 106 this.escapeLtGt, |
107 this.escapeQuot, | |
108 this.escapeApos, | |
109 this.escapeSlash); | |
81 | 110 |
82 /** | 111 /** |
83 * Create a custom escaping mode. | 112 * Create a custom escaping mode. |
84 * | 113 * |
85 * All modes escape `&`. | 114 * All modes escape `&`. |
86 * The mode can further be set to escape `<` and `>` ([escapeLtGt]), | 115 * The mode can further be set to escape `<` and `>` ([escapeLtGt]), |
87 * `"` ([escapeQuot]) and/or `'` ([escapeApos]). | 116 * `"` ([escapeQuot]), `'` ([escapeApos]), and/or `/` ([escapeSlash]). |
88 */ | 117 */ |
89 const HtmlEscapeMode({String name: "custom", | 118 const HtmlEscapeMode({String name: "custom", |
90 this.escapeLtGt: false, | 119 this.escapeLtGt: false, |
91 this.escapeQuot: false, | 120 this.escapeQuot: false, |
92 this.escapeApos: false}) : _name = name; | 121 this.escapeApos: false, |
122 this.escapeSlash: false}) : _name = name; | |
93 | 123 |
94 String toString() => _name; | 124 String toString() => _name; |
95 } | 125 } |
96 | 126 |
97 /** | 127 /** |
98 * Converter which escapes characters with special meaning in HTML. | 128 * Converter which escapes characters with special meaning in HTML. |
99 * | 129 * |
100 * The converter finds characters that are siginificant in HTML source and | 130 * The converter finds characters that are siginificant in HTML source and |
101 * replaces them with corresponding HTML entities. | 131 * replaces them with corresponding HTML entities. |
102 * | 132 * |
103 * The characters that need escaping in HTML are: | 133 * The characters that need escaping in HTML are: |
104 * | 134 * |
105 * * `&` (ampersand) always need to be escaped. | 135 * * `&` (ampersand) always need to be escaped. |
106 * * `<` (less than) and '>' (greater than) when inside an element. | 136 * * `<` (less than) and '>' (greater than) when inside an element. |
107 * * `"` (quote) when inside a double-quoted attribute value. | 137 * * `"` (quote) when inside a double-quoted attribute value. |
108 * * `'` (apostrophe) when inside a single-quoted attribute value. | 138 * * `'` (apostrophe) when inside a single-quoted attribute value. |
109 * Apostrophe is escaped as `'` instead of `'` since | 139 * Apostrophe is escaped as `'` instead of `'` since |
110 * not all browsers understand `'`. | 140 * not all browsers understand `'`. |
141 * * `/` (slash) is recommended to be escaped because it may be used | |
142 * to terminate an element in some HTML dialects. | |
111 * | 143 * |
112 * Escaping `>` (greater than) isn't necessary, but the result is often | 144 * Escaping `>` (greater than) isn't necessary, but the result is often |
113 * found to be easier to read if greater-than is also escaped whenever | 145 * found to be easier to read if greater-than is also escaped whenever |
114 * less-than is. | 146 * less-than is. |
115 */ | 147 */ |
116 class HtmlEscape extends Converter<String, String> { | 148 class HtmlEscape extends Converter<String, String> { |
117 | 149 |
118 /** The [HtmlEscapeMode] used by the converter. */ | 150 /** The [HtmlEscapeMode] used by the converter. */ |
119 final HtmlEscapeMode mode; | 151 final HtmlEscapeMode mode; |
120 | 152 |
(...skipping 22 matching lines...) Expand all Loading... | |
143 StringBuffer result = null; | 175 StringBuffer result = null; |
144 for (int i = start; i < end; i++) { | 176 for (int i = start; i < end; i++) { |
145 var ch = text[i]; | 177 var ch = text[i]; |
146 String replacement = null; | 178 String replacement = null; |
147 switch (ch) { | 179 switch (ch) { |
148 case '&': replacement = '&'; break; | 180 case '&': replacement = '&'; break; |
149 case '"': if (mode.escapeQuot) replacement = '"'; break; | 181 case '"': if (mode.escapeQuot) replacement = '"'; break; |
150 case "'": if (mode.escapeApos) replacement = '''; break; | 182 case "'": if (mode.escapeApos) replacement = '''; break; |
151 case '<': if (mode.escapeLtGt) replacement = '<'; break; | 183 case '<': if (mode.escapeLtGt) replacement = '<'; break; |
152 case '>': if (mode.escapeLtGt) replacement = '>'; break; | 184 case '>': if (mode.escapeLtGt) replacement = '>'; break; |
185 case '/': if (mode.escapeSlash) replacement = '.'; break; | |
Siggi Cherem (dart-lang)
2015/04/15 22:09:52
should this be / (or like it was before /
Lasse Reichstein Nielsen
2015/04/16 06:42:21
Argh. Yes, ofcourse.
| |
153 } | 186 } |
154 if (replacement != null) { | 187 if (replacement != null) { |
155 if (result == null) result = new StringBuffer(); | 188 if (result == null) result = new StringBuffer(); |
156 if (i > start) result.write(text.substring(start, i)); | 189 if (i > start) result.write(text.substring(start, i)); |
157 result.write(replacement); | 190 result.write(replacement); |
158 start = i + 1; | 191 start = i + 1; |
159 } | 192 } |
160 } | 193 } |
161 if (result == null) return null; | 194 if (result == null) return null; |
162 if (end > start) result.write(text.substring(start, end)); | 195 if (end > start) result.write(text.substring(start, end)); |
(...skipping 19 matching lines...) Expand all Loading... | |
182 if(val == null) { | 215 if(val == null) { |
183 _sink.addSlice(chunk, start, end, isLast); | 216 _sink.addSlice(chunk, start, end, isLast); |
184 } else { | 217 } else { |
185 _sink.add(val); | 218 _sink.add(val); |
186 if (isLast) _sink.close(); | 219 if (isLast) _sink.close(); |
187 } | 220 } |
188 } | 221 } |
189 | 222 |
190 void close() => _sink.close(); | 223 void close() => _sink.close(); |
191 } | 224 } |
OLD | NEW |