[FIX] website: escaping saved html content

Escape text nodes changed via the web editor before sending the content
it to the server controller.

It is done since the content is unescaped one time when being displayed,
and it is not done for inline style and script tags (which may be
injected by dropping a snippet) since that would break them.

replacing the solution in cdb900044.
This commit is contained in:
Nicolas Lempereur 2015-08-18 08:42:41 +02:00
parent 96be5b404a
commit 8c77c711ee
3 changed files with 32 additions and 14 deletions

View File

@ -7,7 +7,6 @@ from openerp import SUPERUSER_ID, api
from openerp.addons.website.models import website
from openerp.http import request
from openerp.osv import osv, fields
from openerp.tools import html_escape
class view(osv.osv):
_inherit = "ir.ui.view"
@ -120,14 +119,6 @@ class view(osv.osv):
# ensure there's only one match
[root] = arch.xpath(section_xpath)
# html text need to be escaped for xml storage
def escape_node(node):
node.text = node.text and html_escape(node.text)
node.tail = node.tail and html_escape(node.tail)
escape_node(replacement)
for descendant in replacement.iterdescendants():
escape_node(descendant)
root.text = replacement.text
root.tail = replacement.tail
# replace all children

View File

@ -423,7 +423,14 @@
* Saves an RTE content, which always corresponds to a view section (?).
*/
saveElement: function ($el) {
var markup = $el.prop('outerHTML');
// escape text nodes for xml saving
var escaped_el = $el.clone();
escaped_el.find('*').addBack().not('script,style').contents().each(function(){
if(this.nodeType == 3) {
this.nodeValue = _.escape(this.nodeValue);
}
});
var markup = escaped_el.prop('outerHTML');
return openerp.jsonRpc('/web/dataset/call', 'call', {
model: 'ir.ui.view',
method: 'save',

View File

@ -175,14 +175,34 @@ class TestViewSaving(common.TransactionCase):
)
def test_save_escaped_text(self):
""" Test saving html special chars in text nodes """
view_id = self.registry('ir.ui.view').create(self.cr, self.uid, {
'arch':'<t>hello world</t>',
'arch':'<t><p><h1>hello world</h1></p></t>',
'type':'qweb'
})
view = self.registry('ir.ui.view').browse(self.cr, self.uid, view_id)
replacement = 'hello world &amp; &lt;angle brackets&gt;!'
view.save(replacement, xpath='/t')
self.assertEqual(view.render(), replacement, 'html special characters wrongly escaped')
# script and style text nodes should not escaped client side
replacement = '<script>1 && "hello & world"</script>'
view.save(replacement, xpath='/t/p/h1')
self.assertIn(
replacement.replace('&', '&amp;'),
view.arch,
'inline script should be escaped server side'
)
self.assertIn(
replacement,
view.render(),
'inline script should not be escaped when rendering'
)
# common text nodes should be be escaped client side
replacement = 'world &amp;amp; &amp;lt;b&amp;gt;cie'
view.save(replacement, xpath='/t/p')
self.assertIn(replacement, view.arch, 'common text node should not be escaped server side')
self.assertIn(
replacement,
view.render().replace('&', '&amp;'),
'text node characters wrongly unescaped when rendering'
)
def test_save_only_embedded(self):
Company = self.registry('res.company')