Chromium Code Reviews| 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 |