From ae9de7aa92aee3286298f09e9397f0f9cd6764a4 Mon Sep 17 00:00:00 2001 From: Dmitriy Simushev Date: Fri, 12 Sep 2014 10:59:56 +0000 Subject: [PATCH] Sanitize messages at server side --- .../js/source/default/handlebars_helpers.js | 18 --------- .../js/source/default/model_views/message.js | 40 ------------------- src/mibew/libs/chat.php | 20 ++++++++++ .../Mibew/Controller/HistoryController.php | 5 ++- .../RequestProcessor/ThreadProcessor.php | 8 ++-- .../client_side/chat/message.handlebars | 2 +- .../client_side/message.handlebars | 2 +- .../client_side/default/message.handlebars | 2 +- .../test_cases/handlebars_helpers_tests.js | 37 ----------------- 9 files changed, 31 insertions(+), 103 deletions(-) diff --git a/src/mibew/js/source/default/handlebars_helpers.js b/src/mibew/js/source/default/handlebars_helpers.js index c4308df6..045e9ee6 100644 --- a/src/mibew/js/source/default/handlebars_helpers.js +++ b/src/mibew/js/source/default/handlebars_helpers.js @@ -17,24 +17,6 @@ */ (function(Mibew, Handlebars){ - /** - * Register 'allowTags' Handlebars helper. - * - * This helper unescape HTML entities for allowed (span and strong) tags. - */ - Handlebars.registerHelper('allowTags', function(text) { - var result = text.toString(); - result = result.replace( - /<(span|strong)>(.*?)<\/\1>/g, - '<$1>$2' - ); - result = result.replace( - /<span class="(.*?)">(.*?)<\/span>/g, - '$2' - ); - return new Handlebars.SafeString(result); - }); - /** * Register 'formatTime' Handlebars helper. * diff --git a/src/mibew/js/source/default/model_views/message.js b/src/mibew/js/source/default/model_views/message.js index 957b1c9e..7f099085 100644 --- a/src/mibew/js/source/default/model_views/message.js +++ b/src/mibew/js/source/default/model_views/message.js @@ -18,27 +18,6 @@ (function(Mibew, Backbone, Handlebars) { - /** - * List of replacements pairs - * @type Object - * @private - */ - var badCharList = { - "<": "<", - ">": ">", - "&": "&", - '"': """, - "'": "'", - "`": "`" - } - - /** - * Regular expression for characters that must be replaced by HTML entities - * @type RegExp - * @private - */ - var badCharRegEx = /[&<>'"`]/g; - /** * @class Represents default message view */ @@ -75,10 +54,7 @@ var messageKind = this.model.get('kind'); // Add message fields - msg.allowFormatting = (messageKind != this.model.KIND_USER - && messageKind != this.model.KIND_AGENT); msg.kindName = this.kindToString(messageKind); - msg.message = this.escapeString(msg.message); return msg; }, @@ -111,22 +87,6 @@ return "plugin"; } return ""; - }, - - /** - * Replace HTML special characters('<', '>', '&', "'", '"', '`') by - * corresponding HTML entities. - * - * @param {String} str Unescaped string - * @returns {String} Escaped string - */ - escapeString: function(str) { - return str.replace( - badCharRegEx, - function(chr) { - return badCharList[chr] || "&"; - } - ); } } ); diff --git a/src/mibew/libs/chat.php b/src/mibew/libs/chat.php index eba0b2fb..4cdb4c3e 100644 --- a/src/mibew/libs/chat.php +++ b/src/mibew/libs/chat.php @@ -50,6 +50,26 @@ function message_to_text($msg) } } +/** + * Sanitize message body and make it a safe HTML string. + * + * @param array $msg Message object + * @return array Message object with sanitized body. + */ +function sanitize_message($msg) +{ + $message_body = $msg['message']; + + // Messages entered by user or operator cannot contain any markup + if ($msg['kind'] == Thread::KIND_USER || $msg['kind'] == Thread::KIND_AGENT) { + $message_body = safe_htmlspecialchars($message_body); + } + + $msg['message'] = sanitize_string($message_body, 'low', 'moderate'); + + return $msg; +} + /** * Format username * diff --git a/src/mibew/libs/classes/Mibew/Controller/HistoryController.php b/src/mibew/libs/classes/Mibew/Controller/HistoryController.php index a5709de3..2a1cd7a3 100644 --- a/src/mibew/libs/classes/Mibew/Controller/HistoryController.php +++ b/src/mibew/libs/classes/Mibew/Controller/HistoryController.php @@ -197,7 +197,10 @@ class HistoryController extends AbstractController // Build messages list $last_id = -1; - $messages = $thread->getMessages(false, $last_id); + $messages = array_map( + 'sanitize_message', + $thread->getMessages(false, $last_id) + ); $page['threadMessages'] = json_encode($messages); $page['title'] = getlocal("Chat log"); diff --git a/src/mibew/libs/classes/Mibew/RequestProcessor/ThreadProcessor.php b/src/mibew/libs/classes/Mibew/RequestProcessor/ThreadProcessor.php index 6a110f05..21f3813e 100644 --- a/src/mibew/libs/classes/Mibew/RequestProcessor/ThreadProcessor.php +++ b/src/mibew/libs/classes/Mibew/RequestProcessor/ThreadProcessor.php @@ -340,10 +340,10 @@ class ThreadProcessor extends ClientSideProcessor implements RouterAwareInterfac // Send new messages $last_message_id = $args['lastId']; - $messages = $thread->getMessages($args['user'], $last_message_id); - if (empty($messages)) { - $messages = array(); - } + $messages = array_map( + 'sanitize_message', + $thread->getMessages($args['user'], $last_message_id) + ); return array( 'messages' => $messages, diff --git a/src/mibew/styles/dialogs/default/templates_src/client_side/chat/message.handlebars b/src/mibew/styles/dialogs/default/templates_src/client_side/chat/message.handlebars index 3137ac76..8016359f 100644 --- a/src/mibew/styles/dialogs/default/templates_src/client_side/chat/message.handlebars +++ b/src/mibew/styles/dialogs/default/templates_src/client_side/chat/message.handlebars @@ -1,3 +1,3 @@ {{formatTime created}} {{#if name}}{{name}}: {{/if}} -{{#if allowFormatting}}{{allowTags (nl2br (urlReplace message))}}{{else}}{{nl2br (urlReplace message)}}{{/if}}
\ No newline at end of file +{{nl2br (urlReplace message)}}
\ No newline at end of file diff --git a/src/mibew/styles/dialogs/default/templates_src/client_side/message.handlebars b/src/mibew/styles/dialogs/default/templates_src/client_side/message.handlebars index 6f751bd7..5cdf0714 100644 --- a/src/mibew/styles/dialogs/default/templates_src/client_side/message.handlebars +++ b/src/mibew/styles/dialogs/default/templates_src/client_side/message.handlebars @@ -1,3 +1,3 @@ {{formatTime created}} {{#if name}}{{name}}: {{/if}} -{{#if allowFormatting}}{{allowTags (nl2br (urlReplace message))}}{{else}}{{nl2br (urlReplace message)}}{{/if}}
\ No newline at end of file +{{nl2br (urlReplace message)}}
\ No newline at end of file diff --git a/src/mibew/styles/pages/default/templates_src/client_side/default/message.handlebars b/src/mibew/styles/pages/default/templates_src/client_side/default/message.handlebars index 6f751bd7..5cdf0714 100644 --- a/src/mibew/styles/pages/default/templates_src/client_side/default/message.handlebars +++ b/src/mibew/styles/pages/default/templates_src/client_side/default/message.handlebars @@ -1,3 +1,3 @@ {{formatTime created}} {{#if name}}{{name}}: {{/if}} -{{#if allowFormatting}}{{allowTags (nl2br (urlReplace message))}}{{else}}{{nl2br (urlReplace message)}}{{/if}}
\ No newline at end of file +{{nl2br (urlReplace message)}}
\ No newline at end of file diff --git a/src/tests/client_side/qunit/test_cases/handlebars_helpers_tests.js b/src/tests/client_side/qunit/test_cases/handlebars_helpers_tests.js index 521ffecb..0fcd040e 100644 --- a/src/tests/client_side/qunit/test_cases/handlebars_helpers_tests.js +++ b/src/tests/client_side/qunit/test_cases/handlebars_helpers_tests.js @@ -37,43 +37,6 @@ test('urlReplace', function() { ); }); -// Test "allowTags" Handlebars helper -test('allowTags', function() { - var template = '{{allowTags foo}}'; - var compiledTemplate = Handlebars.compile(template); - var escape = Handlebars.Utils.escapeExpression; - - equal( - compiledTemplate({foo: escape('The content')}), - 'The content', - 'Test a tag without attributes' - ); - - equal( - compiledTemplate({foo: escape('The content')}), - 'The content', - 'Test a tag with class attribute' - ); - - equal( - compiledTemplate({foo: escape('The content')}), - escape('The content'), - 'Test a tag with arbitrary attributes' - ); - - equal( - compiledTemplate({foo: 'content'}), - 'content', - 'Test not a tag' - ); - - equal( - compiledTemplate({foo: 456}), - '456', - 'Test not a string argument' - ); -}); - // Test "nl2br" Handlebars helper test('nl2br', function() { var template = '{{nl2br foo}}';