| @@ -3,8 +3,64 @@ | ||
| 3 | 3 | // eslint-disable-next-line no-unused-vars |
| 4 | 4 | const CharitableUtils = window.CharitableUtils || ( function( document, window, $ ) { |
| 5 | 5 | |
| 6 | 6 | /** |
| 7 | + * Characters removed from user-entered headlines, labels and titles. | |
| 8 | + * | |
| 9 | + * This is a denylist on purpose. The previous allowlist only permitted ASCII | |
| 10 | + * word characters plus the Latin Extended blocks, so every non-Latin script | |
| 11 | + * (Japanese, Chinese, Korean, Cyrillic, Greek, Arabic, Hebrew, Thai, | |
| 12 | + * Devanagari) was silently deleted as the user typed. An allowlist widened to | |
| 13 | + * \p{L}\p{N}\p{M}\p{Zs} still drops the punctuation those languages actually | |
| 14 | + * use, so bare kanji would survive while "今すぐ寄付!" quietly lost its | |
| 15 | + * fullwidth "!" and "寄付する。" lost its kuten. | |
| 16 | + * | |
| 17 | + * Escaping is handled server side -- generate_text() strips quotes and runs | |
| 18 | + * esc_html() before echoing into a value attribute, and every output path | |
| 19 | + * uses esc_html() -- so this is belt and braces. It only needs to remove the | |
| 20 | + * characters that could break an HTML context, plus control characters. | |
| 21 | + * | |
| 22 | + * Deliberately NOT stripped, despite looking like candidates: | |
| 23 | + * | |
| 24 | + * - Bidi marks and isolates (U+200E/U+200F, U+202A-U+202E, U+2066-U+2069) and | |
| 25 | + * zero-width joiners (U+200B-U+200D, U+FEFF). Removing these would break the | |
| 26 | + * very languages this fix exists to support: RLM/LRM are load-bearing in | |
| 27 | + * Arabic and Hebrew, and ZWNJ/ZWJ are required in Persian, Arabic and Indic | |
| 28 | + * scripts and in emoji sequences. They carry a theoretical display-spoofing | |
| 29 | + * risk, but the author already needs campaign-edit capability, so spoofing | |
| 30 | + * their own button label grants them nothing. | |
| 31 | + * - HTML entity text such as "<script>". innerHTML decodes entities into | |
| 32 | + * text nodes rather than re-parsing them as markup, so they cannot create an | |
| 33 | + * element. Verified. | |
| 34 | + * | |
| 35 | + * @since 1.8.12.3 | |
| 36 | + * | |
| 37 | + * @type {RegExp} | |
| 38 | + */ | |
| 39 | + const UNSAFE_TEXT_CHARS = /[<>`]|[\u0000-\u001F\u007F-\u009F]/g; // eslint-disable-line no-control-regex | |
| 40 | + | |
| 41 | + /** | |
| 42 | + * Strip the unsafe characters from a value, tolerating non-string input. | |
| 43 | + * | |
| 44 | + * Non-strings return '' rather than being coerced. The previous implementation | |
| 45 | + * called .replace() directly and so threw a TypeError on null, undefined and | |
| 46 | + * numbers alike; coercing with String() instead would put the literal text | |
| 47 | + * "null" or "[object Object]" into a campaign field, which is worse than | |
| 48 | + * either. Every real caller passes a jQuery .val(), which is always a string. | |
| 49 | + * | |
| 50 | + * @since 1.8.12.3 | |
| 51 | + * | |
| 52 | + * @param {string} stringValue The text, usually coming from user input. | |
| 53 | + * | |
| 54 | + * @returns {string} The cleaned string. | |
| 55 | + */ | |
| 56 | + function stripUnsafeText( stringValue ) { | |
| 57 | + | |
| 58 | + return 'string' === typeof stringValue ? stringValue.replace( UNSAFE_TEXT_CHARS, '' ) : ''; | |
| 59 | + | |
| 60 | + } | |
| 61 | + | |
| 62 | + /** | |
| 7 | 63 | * Public functions and properties. |
| 8 | 64 | * |
| 9 | 65 | * @since 1.8.0 |
| 10 | 66 | * |
| @@ -15,8 +71,9 @@ | ||
| 15 | 71 | /** |
| 16 | 72 | * function that prevents certain HTML/JS/etc characters from being inputted into headlines, campaign titles, etc. |
| 17 | 73 | * |
| 18 | 74 | * @since 1.8.0 |
| 75 | + * @since 1.8.12.3 Switched to a denylist so non-Latin scripts survive. | |
| 19 | 76 | * |
| 20 | 77 | * @param {string} stringValue The text, usually coming from user input. |
| 21 | 78 | * |
| 22 | 79 | * @returns {Event} Event object. |
| @@ -22,9 +79,9 @@ | ||
| 22 | 79 | * @returns {Event} Event object. |
| 23 | 80 | */ |
| 24 | 81 | santitizeTitle: function ( stringValue ) { |
| 25 | 82 | |
| 26 | - return stringValue.replace(/[^\w\s\u00C0-\u024F\u1E00-\u1EFF\u2C60-\u2C7F\uA720-\uA7FF _.,!"()'\/$[\]:@#%-]/gi, ''); | |
| 83 | + return stripUnsafeText( stringValue ); | |
| 27 | 84 | |
| 28 | 85 | }, |
| 29 | 86 | |
| 30 | 87 | |
| @@ -31,8 +88,9 @@ | ||
| 31 | 88 | /** |
| 32 | 89 | * function that prevents certain HTML/JS/etc characters from being inputted into generic text input areas/boxes. |
| 33 | 90 | * |
| 34 | 91 | * @since 1.8.0 |
| 92 | + * @since 1.8.12.3 Switched to a denylist so non-Latin scripts survive. | |
| 35 | 93 | * |
| 36 | 94 | * @param {string} stringValue The text, usually coming from user input. |
| 37 | 95 | * |
| 38 | 96 | * @returns {Event} Event object. |
| @@ -38,9 +96,9 @@ | ||
| 38 | 96 | * @returns {Event} Event object. |
| 39 | 97 | */ |
| 40 | 98 | santitizeTextInput: function ( stringValue ) { |
| 41 | 99 | |
| 42 | - return stringValue.replace(/[^\w\s\u00C0-\u024F\u1E00-\u1EFF\u2C60-\u2C7F\uA720-\uA7FF _.,!"()'\/$[\]:@#%-]/gi, ''); | |
| 100 | + return stripUnsafeText( stringValue ); | |
| 43 | 101 | |
| 44 | 102 | }, |
| 45 | 103 | |
| 46 | 104 | /** |