Esempio n. 1
0
    def test_style_parsing(self):
        test_data = [
            ('<span style="position: fixed; top: 0px; left: 50px; width: 40%; height: 50%; background-color: red;">Coin coin </span>',
             ['background-color: red',
              'Coin coin'], ['position', 'top', 'left']),
            ("""<div style='before: "Email Address; coincoin cheval: lapin";  
   font-size: 30px; max-width: 100%; after: "Not sure
    
          this; means: anything ?#ùµ"
    ; some-property: 2px; top: 3'>youplaboum</div>""",
             ['font-size: 30px',
              'youplaboum'], ['some-property', 'top', 'cheval']),
            ('<span style="width">Coincoin</span>', [], ['width'])
        ]

        for test, in_lst, out_lst in test_data:
            new_html = html_sanitize(test,
                                     strict=False,
                                     strip_style=False,
                                     strip_classes=False)
            for text in in_lst:
                self.assertIn(text, new_html)
            for text in out_lst:
                self.assertNotIn(text, new_html)

        # style should not be sanitized if removed
        new_html = html_sanitize(test_data[0][0],
                                 strict=False,
                                 strip_style=True,
                                 strip_classes=False)
        self.assertEqual(new_html, u'<span>Coin coin </span>')
Esempio n. 2
0
 def _get_desc(self, cr, uid, ids, field_name=None, arg=None, context=None):
     res = dict.fromkeys(ids, "")
     for module in self.browse(cr, uid, ids, context=context):
         path = get_module_resource(module.name, "static/description/index.html")
         if path:
             with tools.file_open(path, "rb") as desc_file:
                 doc = desc_file.read()
                 html = lxml.html.document_fromstring(doc)
                 for element, attribute, link, pos in html.iterlinks():
                     if (
                         element.get("src")
                         and not "//" in element.get("src")
                         and not "static/" in element.get("src")
                     ):
                         element.set("src", "/%s/static/description/%s" % (module.name, element.get("src")))
                 res[module.id] = html_sanitize(lxml.html.tostring(html))
         else:
             overrides = {
                 "embed_stylesheet": False,
                 "doctitle_xform": False,
                 "output_encoding": "unicode",
                 "xml_declaration": False,
             }
             output = publish_string(
                 source=module.description or "", settings_overrides=overrides, writer=MyWriter()
             )
             res[module.id] = html_sanitize(output)
     return res
Esempio n. 3
0
    def test_style_parsing(self):
        test_data = [
            (
                '<span style="position: fixed; top: 0px; left: 50px; width: 40%; height: 50%; background-color: red;">Coin coin </span>',
                ['background-color: red', 'Coin coin'],
                ['position', 'top', 'left']
            ), (
                """<div style='before: "Email Address; coincoin cheval: lapin";  
   font-size: 30px; max-width: 100%; after: "Not sure
    
          this; means: anything ?#ùµ"
    ; some-property: 2px; top: 3'>youplaboum</div>""",
                ['font-size: 30px', 'youplaboum'],
                ['some-property', 'top', 'cheval']
            ), (
                '<span style="width">Coincoin</span>',
                [],
                ['width']
            )
        ]

        for test, in_lst, out_lst in test_data:
            new_html = html_sanitize(test, strict=False, strip_style=False, strip_classes=False)
            for text in in_lst:
                self.assertIn(text, new_html)
            for text in out_lst:
                self.assertNotIn(text, new_html)

        # style should not be sanitized if removed
        new_html = html_sanitize(test_data[0][0], strict=False, strip_style=True, strip_classes=False)
        self.assertEqual(new_html, u'<span>Coin coin </span>')
Esempio n. 4
0
 def _get_desc(self, cr, uid, ids, field_name=None, arg=None, context=None):
     res = dict.fromkeys(ids, '')
     for module in self.browse(cr, uid, ids, context=context):
         path = get_module_resource(module.name,
                                    'static/description/index.html')
         if path:
             with tools.file_open(path, 'rb') as desc_file:
                 doc = desc_file.read()
                 html = lxml.html.document_fromstring(doc)
                 for element, attribute, link, pos in html.iterlinks():
                     if element.get('src') and not '//' in element.get(
                             'src') and not 'static/' in element.get('src'):
                         element.set(
                             'src', "/%s/static/description/%s" %
                             (module.name, element.get('src')))
                 res[module.id] = html_sanitize(lxml.html.tostring(html))
         else:
             overrides = {
                 'embed_stylesheet': False,
                 'doctitle_xform': False,
                 'output_encoding': 'unicode',
                 'xml_declaration': False,
             }
             output = publish_string(source=module.description or '',
                                     settings_overrides=overrides,
                                     writer=MyWriter())
             res[module.id] = html_sanitize(output)
     return res
Esempio n. 5
0
    def test_evil_malicious_code(self):
        # taken from https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Tests
        cases = [
            ("<IMG SRC=javascript:alert('XSS')>"),  # no quotes and semicolons
            (
                "<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>"
            ),  # UTF-8 Unicode encoding
            (
                "<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>"
            ),  # hex encoding
            ("<IMG SRC=\"jav&#x0D;ascript:alert('XSS');\">"),  # embedded carriage return
            ("<IMG SRC=\"jav&#x0A;ascript:alert('XSS');\">"),  # embedded newline
            ("<IMG SRC=\"jav   ascript:alert('XSS');\">"),  # embedded tab
            ("<IMG SRC=\"jav&#x09;ascript:alert('XSS');\">"),  # embedded encoded tab
            ("<IMG SRC=\" &#14;  javascript:alert('XSS');\">"),  # spaces and meta-characters
            ("<IMG SRC=\"javascript:alert('XSS')\""),  # half-open html
            ('<IMG """><SCRIPT>alert("XSS")</SCRIPT>">'),  # malformed tag
            ('<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>'),  # non-alpha-non-digits
            ('<SCRIPT/SRC="http://ha.ckers.org/xss.js"></SCRIPT>'),  # non-alpha-non-digits
            ('<<SCRIPT>alert("XSS");//<</SCRIPT>'),  # extraneous open brackets
            ("<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >"),  # non-closing script tags
            ('<INPUT TYPE="IMAGE" SRC="javascript:alert(\'XSS\');">'),  # input image
            ("<BODY BACKGROUND=\"javascript:alert('XSS')\">"),  # body image
            ("<IMG DYNSRC=\"javascript:alert('XSS')\">"),  # img dynsrc
            ("<IMG LOWSRC=\"javascript:alert('XSS')\">"),  # img lowsrc
            ("<TABLE BACKGROUND=\"javascript:alert('XSS')\">"),  # table
            ("<TABLE><TD BACKGROUND=\"javascript:alert('XSS')\">"),  # td
            ("<DIV STYLE=\"background-image: url(javascript:alert('XSS'))\">"),  # div background
            (
                "<DIV STYLE=\"background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029\">"
            ),  # div background with unicoded exploit
            ("<DIV STYLE=\"background-image: url(&#1;javascript:alert('XSS'))\">"),  # div background + extra characters
            ("<IMG SRC='vbscript:msgbox(\"XSS\")'>"),  # VBscrip in an image
            ("<BODY ONLOAD=alert('XSS')>"),  # event handler
            ("<BR SIZE=\"&{alert('XSS')}\>"),  # & javascript includes
            ('<LINK REL="stylesheet" HREF="javascript:alert(\'XSS\');">'),  # style sheet
            ('<LINK REL="stylesheet" HREF="http://ha.ckers.org/xss.css">'),  # remote style sheet
            ("<STYLE>@import'http://ha.ckers.org/xss.css';</STYLE>"),  # remote style sheet 2
            (
                '<META HTTP-EQUIV="Link" Content="<http://ha.ckers.org/xss.css>; REL=stylesheet">'
            ),  # remote style sheet 3
            ('<STYLE>BODY{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}</STYLE>'),  # remote style sheet 4
            (
                "<IMG STYLE=\"xss:expr/*XSS*/ession(alert('XSS'))\">"
            ),  # style attribute using a comment to break up expression
        ]
        for content in cases:
            html = html_sanitize(content)
            self.assertNotIn("javascript", html, "html_sanitize did not remove a malicious javascript")
            self.assertTrue(
                "ha.ckers.org" not in html or "http://ha.ckers.org/xss.css" in html,
                "html_sanitize did not remove a malicious code in %s (%s)" % (content, html),
            )

        content = "<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->"  # down-level hidden block
        self.assertEquals(html_sanitize(content, silent=False), "")
Esempio n. 6
0
    def test_quote_hotmail_html(self):
        html = html_sanitize(test_mail_examples.QUOTE_HOTMAIL_HTML)
        for ext in test_mail_examples.QUOTE_HOTMAIL_HTML_IN:
            self.assertIn(ext, html)
        for ext in test_mail_examples.QUOTE_HOTMAIL_HTML_OUT:
            self.assertIn(ext, html)

        html = html_sanitize(test_mail_examples.HOTMAIL_1)
        for ext in test_mail_examples.HOTMAIL_1_IN:
            self.assertIn(ext, html)
        for ext in test_mail_examples.HOTMAIL_1_OUT:
            self.assertIn(ext, html)
Esempio n. 7
0
    def test_quote_hotmail_html(self):
        html = html_sanitize(test_mail_examples.QUOTE_HOTMAIL_HTML)
        for ext in test_mail_examples.QUOTE_HOTMAIL_HTML_IN:
            self.assertIn(ext, html)
        for ext in test_mail_examples.QUOTE_HOTMAIL_HTML_OUT:
            self.assertIn(ext, html)

        html = html_sanitize(test_mail_examples.HOTMAIL_1)
        for ext in test_mail_examples.HOTMAIL_1_IN:
            self.assertIn(ext, html)
        for ext in test_mail_examples.HOTMAIL_1_OUT:
            self.assertIn(ext, html)
Esempio n. 8
0
    def test_quote_text(self):
        html = html_sanitize(test_mail_examples.TEXT_1)
        for ext in test_mail_examples.TEXT_1_IN:
            self.assertIn(ext, html)
        for ext in test_mail_examples.TEXT_1_OUT:
            self.assertIn('<span data-o-mail-quote="1">%s</span>' % cgi.escape(ext), html)

        html = html_sanitize(test_mail_examples.TEXT_2)
        for ext in test_mail_examples.TEXT_2_IN:
            self.assertIn(ext, html)
        for ext in test_mail_examples.TEXT_2_OUT:
            self.assertIn('<span data-o-mail-quote="1">%s</span>' % cgi.escape(ext), html)
Esempio n. 9
0
    def test_misc(self):
        # False / void should not crash
        html = html_sanitize('')
        self.assertEqual(html, '')
        html = html_sanitize(False)
        self.assertEqual(html, False)

        # Message with xml and doctype tags don't crash
        html = html_sanitize(u'<?xml version="1.0" encoding="iso-8859-1"?>\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n <head>\n  <title>404 - Not Found</title>\n </head>\n <body>\n  <h1>404 - Not Found</h1>\n </body>\n</html>\n')
        self.assertNotIn('encoding', html)
        self.assertNotIn('<title>404 - Not Found</title>', html)
        self.assertIn('<h1>404 - Not Found</h1>', html)
Esempio n. 10
0
    def test_html(self):
        sanitized_html = html_sanitize(test_mail_examples.MISC_HTML_SOURCE)
        for tag in ['<div', '<b', '<i', '<u', '<strike', '<li', '<blockquote', '<a href']:
            self.assertIn(tag, sanitized_html, 'html_sanitize stripped too much of original html')
        for attr in ['javascript']:
            self.assertNotIn(attr, sanitized_html, 'html_sanitize did not remove enough unwanted attributes')

        emails = [("Charles <*****@*****.**>", "Charles &lt;[email protected]&gt;"),
                ("Dupuis <'tr/-: ${dupuis#$'@truc.baz.fr>", "Dupuis &lt;'tr/-: ${dupuis#$'@truc.baz.fr&gt;"),
                ("Technical <service/[email protected]>", "Technical &lt;service/[email protected]&gt;"),
                ("Div nico <*****@*****.**>", "Div nico &lt;[email protected]&gt;")]
        for email in emails:
            self.assertIn(email[1], html_sanitize(email[0]), 'html_sanitize stripped emails of original html')
Esempio n. 11
0
    def test_html(self):
        sanitized_html = html_sanitize(test_mail_examples.MISC_HTML_SOURCE)
        for tag in ['<div', '<b', '<i', '<u', '<strike', '<li', '<blockquote', '<a href']:
            self.assertIn(tag, sanitized_html, 'html_sanitize stripped too much of original html')
        for attr in ['javascript']:
            self.assertNotIn(attr, sanitized_html, 'html_sanitize did not remove enough unwanted attributes')

        emails = [("Charles <*****@*****.**>", "Charles &lt;[email protected]&gt;"),
                ("Dupuis <'tr/-: ${dupuis#$'@truc.baz.fr>", "Dupuis &lt;'tr/-: ${dupuis#$'@truc.baz.fr&gt;"),
                ("Technical <service/[email protected]>", "Technical &lt;service/[email protected]&gt;"),
                ("Div nico <*****@*****.**>", "Div nico &lt;[email protected]&gt;")]
        for email in emails:
            self.assertIn(email[1], html_sanitize(email[0]), 'html_sanitize stripped emails of original html')
Esempio n. 12
0
    def test_misc(self):
        # False / void should not crash
        html = html_sanitize('')
        self.assertEqual(html, '')
        html = html_sanitize(False)
        self.assertEqual(html, False)

        # Message with xml and doctype tags don't crash
        html = html_sanitize(
            u'<?xml version="1.0" encoding="iso-8859-1"?>\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n <head>\n  <title>404 - Not Found</title>\n </head>\n <body>\n  <h1>404 - Not Found</h1>\n </body>\n</html>\n'
        )
        self.assertNotIn('encoding', html)
        self.assertNotIn('<title>404 - Not Found</title>', html)
        self.assertIn('<h1>404 - Not Found</h1>', html)
    def message_update(self, msg_dict, update_vals=None):
        """ Override to update the support ticket according to the email. """

        if self.close_lock:
            # Send lock email
            setting_ticket_lock_email_template_id = self.env['ir.default'].get('website.support.settings', 'ticket_lock_email_template_id')
            if setting_ticket_lock_email_template_id:
                mail_template = self.env['mail.template'].browse(setting_ticket_lock_email_template_id)
            else:
                # BACK COMPATABLITY FAIL SAFE
                mail_template = self.env['ir.model'].get_object('website_support', 'support_ticket_close_lock')

            mail_template.send_mail(self.id, True)
            
            return False

        body_short = tools.html_sanitize(msg_dict['body'])
        #body_short = tools.html_email_clean(msg_dict['body'], shorten=True, remove=True)

        #Add to message history to keep HTML clean
        self.conversation_history.create({'ticket_id': self.id, 'by': 'customer', 'content': body_short })

        #If the to email address is to the customer then it must be a staff member
        if msg_dict.get('to') == self.email:
            change_state = self.env['ir.model.data'].get_object('website_support','website_ticket_state_staff_replied')
        else:
            change_state = self.env['ir.model.data'].get_object('website_support','website_ticket_state_customer_replied')

        self.state = change_state.id

        return super(WebsiteSupportTicket, self).message_update(msg_dict, update_vals=update_vals)
Esempio n. 14
0
    def convert_answer_to_comment(self):
        """ Tools to convert an answer (forum.post) to a comment (mail.message).
        The original post is unlinked and a new comment is posted on the question
        using the post create_uid as the comment's author. """
        if not self.parent_id:
            return False

        # karma-based action check: use the post field that computed own/all value
        if not self.can_comment_convert:
            raise KarmaError('Not enough karma to convert an answer to a comment')

        # post the message
        question = self.parent_id
        values = {
            'author_id': self.sudo().create_uid.partner_id.id,  # use sudo here because of access to res.users model
            'body': tools.html_sanitize(self.content, strict=True, strip_style=True, strip_classes=True),
            'message_type': 'comment',
            'subtype': 'mail.mt_comment',
            'date': self.create_date,
        }
        new_message = self.browse(question.id).with_context(mail_create_nosubscribe=True).message_post(**values)

        # unlink the original answer, using SUPERUSER_ID to avoid karma issues
        self.sudo().unlink()

        return new_message
Esempio n. 15
0
    def test_quote_text(self):
        html = html_sanitize(test_mail_examples.TEXT_1)
        for ext in test_mail_examples.TEXT_1_IN:
            self.assertIn(ext, html)
        for ext in test_mail_examples.TEXT_1_OUT:
            self.assertIn(
                '<span data-o-mail-quote="1">%s</span>' % cgi.escape(ext),
                html)

        html = html_sanitize(test_mail_examples.TEXT_2)
        for ext in test_mail_examples.TEXT_2_IN:
            self.assertIn(ext, html)
        for ext in test_mail_examples.TEXT_2_OUT:
            self.assertIn(
                '<span data-o-mail-quote="1">%s</span>' % cgi.escape(ext),
                html)
    def generate_email(self, cr, uid, template_id, res_id, context=None):
        """Generates an email from the template for given (model, res_id) pair.

           :param template_id: id of the template to render.
           :param res_id: id of the record to use for rendering the template (model
                          is taken from template definition)
           :returns: a dict containing all relevant fields for creating a new
                     mail.mail entry, with one extra key ``attachments``, in the
                     format [(report_name, data)] where data is base64 encoded.
        """
        if context is None:
            context = {}
        report_xml_pool = self.pool.get('ir.actions.report.xml')
        template = self.get_email_template(cr, uid, template_id, res_id, context)
        values = {}
        for field in ['subject', 'body_html', 'email_from',
                      'email_to', 'email_recipients', 'email_cc', 'reply_to']:
            values[field] = self.render_template(cr, uid, getattr(template, field),
                                                 template.model, res_id, context=context) \
                                                 or False
        if template.user_signature:
            signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
            values['body_html'] = tools.append_content_to_html(values['body_html'], signature)

        if values['body_html']:
            values['body'] = tools.html_sanitize(values['body_html'])

        values.update(mail_server_id=template.mail_server_id.id or False,
                      auto_delete=template.auto_delete,
                      model=template.model,
                      res_id=res_id or False)

        attachments = []
        # Add report in attachments
        if template.report_template:
            report_name = self.render_template(cr, uid, template.report_name, template.model, res_id, context=context)
            report_service = 'report.' + report_xml_pool.browse(cr, uid, template.report_template.id, context).report_name
            # Ensure report is rendered using template's language
            ctx = context.copy()
            if template.lang:
                ctx['lang'] = self.render_template(cr, uid, template.lang, template.model, res_id, context)
            service = netsvc.LocalService(report_service)
            (result, format) = service.create(cr, uid, [res_id], {'model': template.model}, ctx)
            # TODO in trunk, change return format to binary to match message_post expected format
            result = base64.b64encode(result)
            if not report_name:
                report_name = report_service
            ext = "." + format
            if not report_name.endswith(ext):
                report_name += ext
            attachments.append((report_name, result))

        attachment_ids = []
        # Add template attachments
        for attach in template.attachment_ids:
            attachment_ids.append(attach.id)

        values['attachments'] = attachments
        values['attachment_ids'] = attachment_ids
        return values
Esempio n. 17
0
    def message_new(self, msg, custom_values=None):
        """ Create new support ticket upon receiving new email"""

        defaults = {'support_email': msg.get('to'), 'subject': msg.get('subject')}

        #Extract the name from the from email if you can
        if "<" in msg.get('from') and ">" in msg.get('from'):
            start = msg.get('from').rindex( "<" ) + 1
            end = msg.get('from').rindex( ">", start )
            from_email = msg.get('from')[start:end]
            from_name = msg.get('from').split("<")[0].strip()
            defaults['person_name'] = from_name
        else:
            from_email = msg.get('from')

        defaults['email'] = from_email
        defaults['channel'] = "Email"

        #Try to find the partner using the from email
        search_partner = self.env['res.partner'].sudo().search([('email','=', from_email)])
        if len(search_partner) > 0:
            defaults['partner_id'] = search_partner[0].id
            defaults['person_name'] = search_partner[0].name

        defaults['description'] = tools.html_sanitize(msg.get('body'))

        #Assign to default category
        setting_email_default_category_id = self.env['ir.default'].get('website.support.settings', 'email_default_category_id')

        if setting_email_default_category_id:
            defaults['category'] = setting_email_default_category_id

        return super(WebsiteSupportTicket, self).message_new(msg, custom_values=defaults)
Esempio n. 18
0
    def send_get_email_dict(self, cr, uid, mail, partner=None, context=None):
        """Return a dictionary for specific email values, depending on a
        partner, or generic to the whole recipients given by mail.email_to.

            :param browse_record mail: mail.mail browse_record
            :param browse_record partner: specific recipient partner
        """
        body = self.send_get_mail_body(cr,
                                       uid,
                                       mail,
                                       partner=partner,
                                       context=context)
        body_alternative = tools.html2plaintext(tools.html_sanitize(body))
        res = {
            'body':
            body,
            'body_alternative':
            body_alternative,
            'subject':
            self.send_get_mail_subject(cr,
                                       uid,
                                       mail,
                                       partner=partner,
                                       context=context),
            'email_to':
            self.send_get_mail_to(cr,
                                  uid,
                                  mail,
                                  partner=partner,
                                  context=context),
        }
        return res
Esempio n. 19
0
    def message_new(self, msg, custom_values=None):
        """ Create new support ticket upon receiving new email"""

        from_email = msg.get('from')
        from_name = msg.get('from')
        if "<" in msg.get('from') and ">" in msg.get('from'):
            start = msg.get('from').rindex( "<" ) + 1
            end = msg.get('from').rindex( ">", start )
            from_email = msg.get('from')[start:end]
            from_name = msg.get('from').split("<")[0].strip()

        search_partner = self.env['res.partner'].sudo().search([('email','=', from_email )])

        partner_id = False
        if len(search_partner) > 0:
            partner_id = search_partner[0].id
            from_name = search_partner[0].name

        body_short = tools.html_sanitize(msg.get('body'))

        #body_short = tools.html_email_clean(msg.get('body'), shorten=True, remove=True)
        portal_access_key = randint(1000000000,2000000000)
        defaults = {'partner_id': partner_id, 'person_name': from_name, 'email': msg.get('from'), 'subject': msg.get('subject'), 'description': body_short, 'portal_access_key': portal_access_key}
        
        return super(WebsiteSupportTicket, self).message_new(msg, custom_values=defaults)
Esempio n. 20
0
    def message_new(self, msg, custom_values=None):
        """ Create new support ticket upon receiving new email"""

        defaults = {'support_email': msg.get('to'), 'subject': msg.get('subject')}

        #Extract the name from the from email if you can
        if "<" in msg.get('from') and ">" in msg.get('from'):
            start = msg.get('from').rindex( "<" ) + 1
            end = msg.get('from').rindex( ">", start )
            from_email = msg.get('from')[start:end]
            from_name = msg.get('from').split("<")[0].strip()
            defaults['person_name'] = from_name
        else:
            from_email = msg.get('from')

        defaults['email'] = from_email
        defaults['channel'] = "Email"

        #Try to find the partner using the from email
        search_partner = self.env['res.partner'].sudo().search([('email','=', from_email)])
        if len(search_partner) > 0:
            defaults['partner_id'] = search_partner[0].id
            defaults['person_name'] = search_partner[0].name

        defaults['description'] = tools.html_sanitize(msg.get('body'))

        #Assign to default category
        setting_email_default_category_id = self.env['ir.default'].get('website.support.settings', 'email_default_category_id')

        if setting_email_default_category_id:
            defaults['category'] = setting_email_default_category_id

        return super(WebsiteSupportTicket, self).message_new(msg, custom_values=defaults)
    def message_new(self, msg, custom_values=None):
        """ Create new support ticket upon receiving new email"""

        defaults = {
            'support_email': msg.get('to'),
            'subject': msg.get('subject')
        }

        #Extract the name from the from email if you can
        if "<" in msg.get('from') and ">" in msg.get('from'):
            start = msg.get('from').rindex("<") + 1
            end = msg.get('from').rindex(">", start)
            from_email = msg.get('from')[start:end]
            from_name = msg.get('from').split("<")[0].strip()
            defaults['first_name'] = from_name
        else:
            from_email = msg.get('from')

        defaults['email'] = from_email

        #Try to find the partner using the from email
        search_partner = self.env['res.partner'].sudo().search([('email', '=',
                                                                 from_email)])
        if len(search_partner) > 0:
            defaults['partner_id'] = search_partner[0].id
            defaults['first_name'] = search_partner[0].name

        defaults['description'] = tools.html_sanitize(msg.get('body'))

        portal_access_key = randint(1000000000, 2000000000)
        defaults['portal_access_key'] = portal_access_key

        return super(WebsiteSupportTicket,
                     self).message_new(msg, custom_values=defaults)
Esempio n. 22
0
    def convert_answer_to_comment(self):
        """ Tools to convert an answer (forum.post) to a comment (mail.message).
        The original post is unlinked and a new comment is posted on the question
        using the post create_uid as the comment's author. """
        if not self.parent_id:
            return False

        # karma-based action check: use the post field that computed own/all value
        if not self.can_comment_convert:
            raise KarmaError('Not enough karma to convert an answer to a comment')

        # post the message
        question = self.parent_id
        values = {
            'author_id': self.sudo().create_uid.partner_id.id,  # use sudo here because of access to res.users model
            'body': tools.html_sanitize(self.content, strict=True, strip_style=True, strip_classes=True),
            'type': 'comment',
            'subtype': 'mail.mt_comment',
            'date': self.create_date,
        }
        new_message = self.browse(question.id).with_context(mail_create_nosubscribe=True).message_post(**values)

        # unlink the original answer, using SUPERUSER_ID to avoid karma issues
        self.sudo().unlink()

        return new_message
Esempio n. 23
0
 def generate_email(self, res_ids, fields=None):
     multi_mode = True
     if isinstance(res_ids, (int, long)):
         res_ids = [res_ids]
         multi_mode = False
     result = super(MailTemplate, self).generate_email(res_ids,
                                                       fields=fields)
     for record_id, this in self.get_email_template(res_ids).iteritems():
         if this.body_type == 'qweb' and\
                 (not fields or 'body_html' in fields):
             for record in self.env[this.model].browse(record_id):
                 body_html = this.body_view_id.render({
                     'object':
                     record,
                     'email_template':
                     this,
                 })
                 # Some wizards, like when sending a sales order, need this
                 # fix to display accents correctly
                 if not isinstance(body_html, unicode):
                     body_html = body_html.decode('utf-8')
                 result[record_id]['body_html'] = self.render_post_process(
                     body_html)
                 result[record_id]['body'] = tools.html_sanitize(
                     result[record_id]['body_html'])
     return multi_mode and result or result[res_ids[0]]
    def message_update(self, msg_dict, update_vals=None):
        """ Override to update the support ticket according to the email. """

        body_short = tools.html_sanitize(msg_dict['body'])
        #body_short = tools.html_email_clean(msg_dict['body'], shorten=True, remove=True)

        #s = MLStripper()
        #s.feed(body_short)
        #body_short = s.get_data()

        #Add to message history field for back compatablity
        self.conversation_history.create({
            'ticket_id': self.id,
            'by': 'customer',
            'content': body_short
        })

        #If the to email address is to the customer then it must be a staff member...
        if msg_dict.get('to') == self.email:
            change_state = self.env['ir.model.data'].get_object(
                'website_support', 'website_ticket_state_staff_replied')
        else:
            change_state = self.env['ir.model.data'].get_object(
                'website_support', 'website_ticket_state_customer_replied')

        self.state = change_state.id

        return super(WebsiteSupportTicket,
                     self).message_update(msg_dict, update_vals=update_vals)
Esempio n. 25
0
 def generate_email_batch(self, template_id, res_ids, fields=None):
     result = super(EmailTemplate, self).generate_email_batch(template_id,
                                                              res_ids,
                                                              fields=fields)
     this = self.browse(template_id)
     for record_id, this in self.get_email_template_batch(
             template_id, res_ids).iteritems():
         if this.model_id.model == 'newsletter.newsletter' and\
            self.env.context.get('newsletter_res_id') and\
            this.body_type == 'qweb' and\
            (not fields or 'body_html' in fields):
             for record in self.env[this.model].browse(record_id):
                 result[record_id]['body_html'] = self.env['ir.qweb']\
                     ._model.render_node(
                         etree.fromstring(
                             result[record_id]['body_html'],
                             etree.HTMLParser()
                         ),
                         self
                         ._generate_email_batch_get_newsletter_qcontext({
                             'object': self.env[record.type_id.model.model]
                             .browse(self.env.context['newsletter_res_id']),
                             'newsletter': record,
                         }),
                     )
                 result[record_id]['body'] = tools.html_sanitize(
                     result[record_id]['body_html'])
     return result
Esempio n. 26
0
 def test_quote_basic_text(self):
     test_data = [
         (
             """This is Sparta!\n--\nAdministrator\n+9988776655""",
             ['This is Sparta!'],
             ['\n--\nAdministrator\n+9988776655']
         ), (
             """<p>This is Sparta!\n--\nAdministrator</p>""",
             [],
             ['\n--\nAdministrator']
         ), (
             """<p>This is Sparta!<br/>--<br>Administrator</p>""",
             ['This is Sparta!'],
             []
         ), (
             """This is Sparta!\n>Ah bon ?\nCertes\n> Chouette !\nClair""",
             ['This is Sparta!', 'Certes', 'Clair'],
             ['\n>Ah bon ?', '\n> Chouette !']
         )
     ]
     for test, in_lst, out_lst in test_data:
         new_html = html_sanitize(test)
         for text in in_lst:
             self.assertIn(text, new_html)
         for text in out_lst:
             self.assertIn('<span data-o-mail-quote="1">%s</span>' % cgi.escape(text), new_html)
Esempio n. 27
0
def migrate_phonecalls(env):
    activity = env.ref('crm.crm_activity_data_call')
    env.cr.execute(
        'select id, opportunity_id, partner_id, '
        'coalesce(user_id, write_uid, create_uid) as user_id, '
        'name, description, partner_phone, partner_mobile '
        'from crm_phonecall '
        "where active and state not in ('cancel')")
    env = env(context={
        'mail_post_autofollow': False,
        'mail_create_nosubscribe': True,
    })
    for phonecall in env.cr.dictfetchall():
        record = env['res.users'].browse(phonecall['user_id'])
        if phonecall['opportunity_id']:  # pragma: no cover
            record = env['crm.lead'].browse(phonecall['opportunity_id'])
        if phonecall['partner_id']:
            record = env['res.partner'].browse(phonecall['partner_id'])
        body = (
            '<div>%(description)s</div><div>%(partner_phone)s</div>'
            '<div>%(partner_mobile)s</div><div>Phone call %(id)s</div>'
        ) % {
            key: html_sanitize(value)
            for key, value in phonecall.iteritems()
        }
        record.message_post(
            body=body, subject=phonecall['name'] or 'Phone call',
            subtype_id=activity.subtype_id.id)
Esempio n. 28
0
    def generate_email(self, cr, uid, template_id, res_id, context=None):
        """Generates an email from the template for given (model, res_id) pair.

           :param template_id: id of the template to render.
           :param res_id: id of the record to use for rendering the template (model
                          is taken from template definition)
           :returns: a dict containing all relevant fields for creating a new
                     mail.mail entry, with one extra key ``attachments``, in the
                     format expected by :py:meth:`mail_thread.message_post`.
        """
        if context is None:
            context = {}
        report_xml_pool = self.pool.get('ir.actions.report.xml')
        template = self.get_email_template(cr, uid, template_id, res_id, context)
        values = {}
        for field in ['subject', 'body_html', 'email_from',
                      'email_to', 'email_recipients', 'email_cc', 'reply_to']:
            values[field] = self.render_template(cr, uid, getattr(template, field),
                                                 template.model, res_id, context=context) \
                                                 or False
        if template.user_signature:
            signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
            values['body_html'] = tools.append_content_to_html(values['body_html'], signature)

        if values['body_html']:
            values['body'] = tools.html_sanitize(values['body_html'])

        values.update(mail_server_id=template.mail_server_id.id or False,
                      auto_delete=template.auto_delete,
                      model=template.model,
                      res_id=res_id or False)

        attachments = []
        # Add report in attachments
        if template.report_template:
            report_name = self.render_template(cr, uid, template.report_name, template.model, res_id, context=context)
            report_service = 'report.' + report_xml_pool.browse(cr, uid, template.report_template.id, context).report_name
            # Ensure report is rendered using template's language
            ctx = context.copy()
            if template.lang:
                ctx['lang'] = self.render_template(cr, uid, template.lang, template.model, res_id, context)
            service = netsvc.LocalService(report_service)
            (result, format) = service.create(cr, uid, [res_id], {'model': template.model}, ctx)
            result = base64.b64encode(result)
            if not report_name:
                report_name = report_service
            ext = "." + format
            if not report_name.endswith(ext):
                report_name += ext
            attachments.append((report_name, result))

        attachment_ids = []
        # Add template attachments
        for attach in template.attachment_ids:
            attachment_ids.append(attach.id)

        values['attachments'] = attachments
        values['attachment_ids'] = attachment_ids
        return values
Esempio n. 29
0
 def _get_desc(self, cr, uid, ids, field_name=None, arg=None, context=None):
     res = dict.fromkeys(ids, '')
     for module in self.browse(cr, uid, ids, context=context):
         overrides = dict(embed_stylesheet=False, doctitle_xform=False,
                          output_encoding='unicode', xml_declaration=False)
         output = publish_string(source=module.description, settings_overrides=overrides, writer=MyWriter())
         res[module.id] = html_sanitize(output)
     return res
Esempio n. 30
0
 def test_quote_blockquote(self):
     html = html_sanitize(test_mail_examples.QUOTE_BLOCKQUOTE)
     for ext in test_mail_examples.QUOTE_BLOCKQUOTE_IN:
         self.assertIn(ext, html)
     for ext in test_mail_examples.QUOTE_BLOCKQUOTE_OUT:
         self.assertIn(
             '<span data-o-mail-quote="1">%s' %
             cgi.escape(ext.decode('utf-8')), html)
Esempio n. 31
0
 def test_quote_thunderbird(self):
     html = html_sanitize(test_mail_examples.QUOTE_THUNDERBIRD_1)
     for ext in test_mail_examples.QUOTE_THUNDERBIRD_1_IN:
         self.assertIn(ext, html)
     for ext in test_mail_examples.QUOTE_THUNDERBIRD_1_OUT:
         self.assertIn(
             '<span data-o-mail-quote="1">%s</span>' %
             cgi.escape(ext.decode('utf-8')), html)
Esempio n. 32
0
 def _compute_description_rst_html(self):
     for version in self:
         if version.description_rst:
             try:
                 output = publish_string(
                     source=version.description_rst,
                     settings_overrides=self._SETTING_OVERRIDES,
                     writer=MyWriter())
             except:
                 output =\
                     "<h1 style='color:red;'>" +\
                     _("Incorrect RST Description") +\
                     "</h1>"
         else:
             output = html_sanitize("<h1 style='color:gray;'>" +
                                    _("No Version Found") + "</h1>")
         version.description_rst_html = html_sanitize(output)
Esempio n. 33
0
 def test_quote_bugs(self):
     html = html_sanitize(test_mail_examples.BUG1)
     for ext in test_mail_examples.BUG_1_IN:
         self.assertIn(ext, html)
     for ext in test_mail_examples.BUG_1_OUT:
         self.assertIn(
             '<span data-o-mail-quote="1">%s</span>' %
             cgi.escape(ext.decode('utf-8')), html)
Esempio n. 34
0
 def test_sanitize_unescape_emails(self):
     not_emails = [
         '<blockquote cite="mid:CAEJSRZvWvud8c6Qp=wfNG6O1+wK3i_jb33qVrF7XyrgPNjnyUA@mail.gmail.com" type="cite">cat</blockquote>',
         '<img alt="@github-login" class="avatar" src="/web/image/pi" height="36" width="36">']
     for email in not_emails:
         sanitized = html_sanitize(email)
         left_part = email.split('>')[0]  # take only left part, as the sanitizer could add data information on node
         self.assertNotIn(cgi.escape(email), sanitized, 'html_sanitize stripped emails of original html')
         self.assertIn(left_part, sanitized)
Esempio n. 35
0
 def test_edi_source(self):
     html = html_sanitize(test_mail_examples.EDI_LIKE_HTML_SOURCE)
     self.assertIn('div style="font-family: \'Lucica Grande\', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF;', html,
         'html_sanitize removed valid style attribute')
     self.assertIn('<span style="color: #222; margin-bottom: 5px; display: block; ">', html,
         'html_sanitize removed valid style attribute')
     self.assertIn('img class="oe_edi_paypal_button" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"', html,
         'html_sanitize removed valid img')
     self.assertNotIn('</body></html>', html, 'html_sanitize did not remove extra closing tags')
    def generate_sms_batch(self, cr, uid, sms_template_id, res_ids, context=None, fields=None):
        """Generates an sms from the template for given the given model based on
        records given by res_ids.

        :param sms_template_id: id of the template to render.
        :param res_id: id of the record to use for rendering the template (model
                       is taken from template definition)
        :returns: a dict containing all relevant fields for creating a new
                  sms.sms entry(smsclient), with one extra key ``attachments``, in the
                  format [(report_name, data)] where data is base64 encoded.
        """
        if context is None:
            context = {}
        if fields is None:
            fields = ['body_html', 'sms_from', 'sms_to']

        report_xml_pool = self.pool.get('ir.actions.report.xml')
        res_ids_to_templates = self.get_sms_template_batch(cr, uid, sms_template_id, res_ids, context)

        # templates: res_id -> template; template -> res_ids
        templates_to_res_ids = {}
        for res_id, template in res_ids_to_templates.iteritems():
            templates_to_res_ids.setdefault(template, []).append(res_id)

        results = dict()
        for template, template_res_ids in templates_to_res_ids.iteritems():
            # generate fields value for all res_ids linked to the current template
            for field in fields:
                generated_field_values = self.render_template_batch(
                    cr, uid, getattr(template, field), template.model, template_res_ids,
                    post_process=(field == 'body_html'),
                    context=context)
                for res_id, field_value in generated_field_values.iteritems():
                    results.setdefault(res_id, dict())[field] = field_value
            # compute recipients
            #results = self.generate_recipients_batch(cr, uid, results, template.id, template_res_ids, context=context)
            # update values for all res_ids
            for res_id in template_res_ids:
                values = results[res_id]
                # body: add user signature, sanitize
                if 'body_html' in fields and template.user_signature:
                    signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
                    values['body_html'] = tools.append_content_to_html(values['body_html'], signature)
                if values.get('body_html'):
                    values['body'] = tools.html_sanitize(values['body_html'])
                # technical settings
                values.update(
                    sms_server_id=template.sms_server_id.id or False,
                    auto_delete=template.auto_delete,
                    model=template.model,
                    res_id=res_id or False,
                    attachment_ids=[attach.id for attach in template.attachment_ids],
                )

            # Add report in attachments: generate once for all template_res_ids

        return results
Esempio n. 37
0
 def test_sanitize_escape_emails(self):
     emails = [
         "Charles <*****@*****.**>",
         "Dupuis <'tr/-: ${dupuis#$'@truc.baz.fr>",
         "Technical <service/[email protected]>",
         "Div nico <*****@*****.**>"
     ]
     for email in emails:
         self.assertIn(cgi.escape(email), html_sanitize(email), 'html_sanitize stripped emails of original html')
Esempio n. 38
0
 def test_edi_source(self):
     html = html_sanitize(test_mail_examples.EDI_LIKE_HTML_SOURCE)
     self.assertIn(
         'font-family: \'Lucida Grande\', Ubuntu, Arial, Verdana, sans-serif;', html,
         'html_sanitize removed valid styling')
     self.assertIn(
         'src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"', html,
         'html_sanitize removed valid img')
     self.assertNotIn('</body></html>', html, 'html_sanitize did not remove extra closing tags')
Esempio n. 39
0
 def test_edi_source(self):
     html = html_sanitize(test_mail_examples.EDI_LIKE_HTML_SOURCE)
     self.assertIn('div style="font-family: \'Lucica Grande\', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF;', html,
         'html_sanitize removed valid style attribute')
     self.assertIn('<span style="color: #222; margin-bottom: 5px; display: block; ">', html,
         'html_sanitize removed valid style attribute')
     self.assertIn('img class="oe_edi_paypal_button" src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"', html,
         'html_sanitize removed valid img')
     self.assertNotIn('</body></html>', html, 'html_sanitize did not remove extra closing tags')
Esempio n. 40
0
 def test_basic_sanitizer(self):
     cases = [
         ("yop", "<p>yop</p>"),  # simple
         ("lala<p>yop</p>xxx", "<p>lala</p><p>yop</p>xxx"),  # trailing text
         ("Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci",
             u"<p>Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci</p>"),  # unicode
     ]
     for content, expected in cases:
         html = html_sanitize(content)
         self.assertEqual(html, expected, 'html_sanitize is broken')
Esempio n. 41
0
 def test_edi_source(self):
     html = html_sanitize(test_mail_examples.EDI_LIKE_HTML_SOURCE)
     self.assertIn(
         'font-family: \'Lucida Grande\', Ubuntu, Arial, Verdana, sans-serif;',
         html, 'html_sanitize removed valid styling')
     self.assertIn(
         'src="https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif"',
         html, 'html_sanitize removed valid img')
     self.assertNotIn('</body></html>', html,
                      'html_sanitize did not remove extra closing tags')
Esempio n. 42
0
 def test_sanitize_escape_emails_cite(self):
     not_emails = [
         '<blockquote cite="mid:CAEJSRZvWvud8c6Qp=wfNG6O1+wK3i_jb33qVrF7XyrgPNjnyUA@mail.gmail.com" type="cite">cat</blockquote>']
     for email in not_emails:
         sanitized = html_sanitize(email)
         self.assertNotIn(cgi.escape(email), sanitized, 'html_sanitize stripped emails of original html')
         self.assertIn(
             '<blockquote cite="mid:CAEJSRZvWvud8c6Qp=wfNG6O1+wK3i_jb33qVrF7XyrgPNjnyUA@mail.gmail.com"',
             sanitized,
             'html_sanitize escaped valid address-like')
Esempio n. 43
0
 def test_sanitize_escape_emails(self):
     emails = [
         "Charles <*****@*****.**>",
         "Dupuis <'tr/-: ${dupuis#$'@truc.baz.fr>",
         "Technical <service/[email protected]>",
         "Div nico <*****@*****.**>"
     ]
     for email in emails:
         self.assertIn(cgi.escape(email), html_sanitize(email),
                       'html_sanitize stripped emails of original html')
Esempio n. 44
0
    def test_evil_malicious_code(self):
        # taken from https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Tests
        cases = [
            ("<IMG SRC=javascript:alert('XSS')>"),  # no quotes and semicolons
            ("<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>"),  # UTF-8 Unicode encoding
            ("<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>"),  # hex encoding
            ("<IMG SRC=\"jav&#x0D;ascript:alert('XSS');\">"),  # embedded carriage return
            ("<IMG SRC=\"jav&#x0A;ascript:alert('XSS');\">"),  # embedded newline
            ("<IMG SRC=\"jav   ascript:alert('XSS');\">"),  # embedded tab
            ("<IMG SRC=\"jav&#x09;ascript:alert('XSS');\">"),  # embedded encoded tab
            ("<IMG SRC=\" &#14;  javascript:alert('XSS');\">"),  # spaces and meta-characters
            ("<IMG SRC=\"javascript:alert('XSS')\""),  # half-open html
            ("<IMG \"\"\"><SCRIPT>alert(\"XSS\")</SCRIPT>\">"),  # malformed tag
            ("<SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>"),  # non-alpha-non-digits
            ("<SCRIPT/SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>"),  # non-alpha-non-digits
            ("<<SCRIPT>alert(\"XSS\");//<</SCRIPT>"),  # extraneous open brackets
            ("<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >"),  # non-closing script tags
            ("<INPUT TYPE=\"IMAGE\" SRC=\"javascript:alert('XSS');\">"),  # input image
            ("<BODY BACKGROUND=\"javascript:alert('XSS')\">"),  # body image
            ("<IMG DYNSRC=\"javascript:alert('XSS')\">"),  # img dynsrc
            ("<IMG LOWSRC=\"javascript:alert('XSS')\">"),  # img lowsrc
            ("<TABLE BACKGROUND=\"javascript:alert('XSS')\">"),  # table
            ("<TABLE><TD BACKGROUND=\"javascript:alert('XSS')\">"),  # td
            ("<DIV STYLE=\"background-image: url(javascript:alert('XSS'))\">"),  # div background
            ("<DIV STYLE=\"background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029\">"),  # div background with unicoded exploit
            ("<DIV STYLE=\"background-image: url(&#1;javascript:alert('XSS'))\">"),  # div background + extra characters
            ("<IMG SRC='vbscript:msgbox(\"XSS\")'>"),  # VBscrip in an image
            ("<BODY ONLOAD=alert('XSS')>"),  # event handler
            ("<BR SIZE=\"&{alert('XSS')}\>"),  # & javascript includes
            ("<LINK REL=\"stylesheet\" HREF=\"javascript:alert('XSS');\">"),  # style sheet
            ("<LINK REL=\"stylesheet\" HREF=\"http://ha.ckers.org/xss.css\">"),  # remote style sheet
            ("<STYLE>@import'http://ha.ckers.org/xss.css';</STYLE>"),  # remote style sheet 2
            ("<META HTTP-EQUIV=\"Link\" Content=\"<http://ha.ckers.org/xss.css>; REL=stylesheet\">"),  # remote style sheet 3
            ("<STYLE>BODY{-moz-binding:url(\"http://ha.ckers.org/xssmoz.xml#xss\")}</STYLE>"),  # remote style sheet 4
            ("<IMG STYLE=\"xss:expr/*XSS*/ession(alert('XSS'))\">"),  # style attribute using a comment to break up expression
        ]
        for content in cases:
            html = html_sanitize(content)
            self.assertNotIn('javascript', html, 'html_sanitize did not remove a malicious javascript')
            self.assertTrue('ha.ckers.org' not in html or 'http://ha.ckers.org/xss.css' in html, 'html_sanitize did not remove a malicious code in %s (%s)' % (content, html))

        content = "<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->"  # down-level hidden block
        self.assertEquals(html_sanitize(content, silent=False), '')
Esempio n. 45
0
 def test_basic_sanitizer(self):
     cases = [
         ("yop", "<p>yop</p>"),  # simple
         ("lala<p>yop</p>xxx", "<p>lala</p><p>yop</p>xxx"),  # trailing text
         ("Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci",
             u"<p>Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci</p>"),  # unicode
     ]
     for content, expected in cases:
         html = html_sanitize(content)
         self.assertEqual(html, expected, 'html_sanitize is broken')
Esempio n. 46
0
 def test_quote_signature(self):
     test_data = [
         (
             """<div>Hello<pre>--<br />Administrator</pre></div>""",
             ["<pre data-o-mail-quote=\"1\">--", "<br data-o-mail-quote=\"1\">"],
         )
     ]
     for test, in_lst in test_data:
         new_html = html_sanitize(test)
         for text in in_lst:
             self.assertIn(text, new_html)
Esempio n. 47
0
 def test_sanitize_escape_emails_cite(self):
     not_emails = [
         '<blockquote cite="mid:CAEJSRZvWvud8c6Qp=wfNG6O1+wK3i_jb33qVrF7XyrgPNjnyUA@mail.gmail.com" type="cite">cat</blockquote>'
     ]
     for email in not_emails:
         sanitized = html_sanitize(email)
         self.assertNotIn(cgi.escape(email), sanitized,
                          'html_sanitize stripped emails of original html')
         self.assertIn(
             '<blockquote cite="mid:CAEJSRZvWvud8c6Qp=wfNG6O1+wK3i_jb33qVrF7XyrgPNjnyUA@mail.gmail.com"',
             sanitized, 'html_sanitize escaped valid address-like')
 def test_html(self):
     sanitized_html = html_sanitize(HTML_SOURCE)
     for tag in [
             '<font>', '<div>', '<b>', '<i>', '<u>', '<strike>', '<li>',
             '<blockquote>', '<a href'
     ]:
         self.assertIn(tag, sanitized_html,
                       'html_sanitize stripped too much of original html')
     for attr in ['style', 'javascript']:
         self.assertNotIn(
             attr, sanitized_html,
             'html_sanitize did not remove enough unwanted attributes')
Esempio n. 49
0
 def test_quote_signature(self):
     test_data = [(
         """<div>Hello<pre>--<br />Administrator</pre></div>""",
         [
             "<pre data-o-mail-quote=\"1\">--",
             "<br data-o-mail-quote=\"1\">"
         ],
     )]
     for test, in_lst in test_data:
         new_html = html_sanitize(test)
         for text in in_lst:
             self.assertIn(text, new_html)
Esempio n. 50
0
 def test_html(self):
     sanitized_html = html_sanitize(test_mail_examples.MISC_HTML_SOURCE)
     for tag in [
             '<div', '<b', '<i', '<u', '<strike', '<li', '<blockquote',
             '<a href'
     ]:
         self.assertIn(tag, sanitized_html,
                       'html_sanitize stripped too much of original html')
     for attr in ['javascript']:
         self.assertNotIn(
             attr, sanitized_html,
             'html_sanitize did not remove enough unwanted attributes')
Esempio n. 51
0
 def _get_desc(self, cr, uid, ids, field_name=None, arg=None, context=None):
     res = dict.fromkeys(ids, '')
     for module in self.browse(cr, uid, ids, context=context):
         path = get_module_resource(module.name, 'static/description/index.html')
         if path:
             with tools.file_open(path, 'rb') as desc_file:
                 doc = desc_file.read()
                 html = lxml.html.document_fromstring(doc)
                 for element, attribute, link, pos in html.iterlinks():
                     if element.get('src') and not '//' in element.get('src') and not 'static/' in element.get('src'):
                         element.set('src', "/%s/static/description/%s" % (module.name, element.get('src')))
                 res[module.id] = html_sanitize(lxml.html.tostring(html))
         else:
             overrides = {
                 'embed_stylesheet': False,
                 'doctitle_xform': False,
                 'output_encoding': 'unicode',
                 'xml_declaration': False,
             }
             output = publish_string(source=module.description or '', settings_overrides=overrides, writer=MyWriter())
             res[module.id] = html_sanitize(output)
     return res
Esempio n. 52
0
    def test_mako(self):
        cases = [
            ('''<p>Some text</p>
<% set signup_url = object.get_signup_url() %>
% if signup_url:
<p>
    You can access this document and pay online via our Customer Portal:
</p>''', '''<p>Some text</p>
<% set signup_url = object.get_signup_url() %>
% if signup_url:
<p>
    You can access this document and pay online via our Customer Portal:
</p>''')
        ]
        for content, expected in cases:
            html = html_sanitize(content, silent=False)
            self.assertEqual(html, expected, 'html_sanitize: broken mako management')
Esempio n. 53
0
    def message_update(self, msg_dict, update_vals=None):
        """ Override to update the support ticket according to the email. """

        body_short = tools.html_sanitize(msg_dict['body'])
        #body_short = tools.html_email_clean(msg_dict['body'], shorten=True, remove=True)

        #Add to message history to keep HTML clean
        self.conversation_history.create({'ticket_id': self.id, 'by': 'customer', 'content': body_short })

        #If the to email address is to the customer then it must be a staff member
        if msg_dict.get('to') == self.email:
            change_state = self.env['ir.model.data'].get_object('website_support','website_ticket_state_staff_replied')
        else:
            change_state = self.env['ir.model.data'].get_object('website_support','website_ticket_state_customer_replied')

        self.state = change_state.id

        return super(WebsiteSupportTicket, self).message_update(msg_dict, update_vals=update_vals)
Esempio n. 54
0
 def test_quote_thunderbird(self):
     html = html_sanitize(test_mail_examples.QUOTE_THUNDERBIRD_1)
     for ext in test_mail_examples.QUOTE_THUNDERBIRD_1_IN:
         self.assertIn(ext, html)
     for ext in test_mail_examples.QUOTE_THUNDERBIRD_1_OUT:
         self.assertIn('<span data-o-mail-quote="1">%s</span>' % cgi.escape(ext.decode('utf-8')), html)
Esempio n. 55
0
    def generate_email(self, res_ids, fields=None):
        """Generates an email from the template for given the given model based on
        records given by res_ids.

        :param template_id: id of the template to render.
        :param res_id: id of the record to use for rendering the template (model
                       is taken from template definition)
        :returns: a dict containing all relevant fields for creating a new
                  mail.mail entry, with one extra key ``attachments``, in the
                  format [(report_name, data)] where data is base64 encoded.
        """
        self.ensure_one()
        multi_mode = True
        if isinstance(res_ids, (int, long)):
            res_ids = [res_ids]
            multi_mode = False
        if fields is None:
            fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to']

        res_ids_to_templates = self.get_email_template_batch(res_ids)

        # templates: res_id -> template; template -> res_ids
        templates_to_res_ids = {}
        for res_id, template in res_ids_to_templates.iteritems():
            templates_to_res_ids.setdefault(template, []).append(res_id)

        results = dict()
        for template, template_res_ids in templates_to_res_ids.iteritems():
            Template = self.env['mail.template']
            # generate fields value for all res_ids linked to the current template
            if template.lang:
                Template = Template.with_context(lang=template._context.get('lang'))
            for field in fields:
                generated_field_values = Template.render_template(
                    getattr(template, field), template.model, template_res_ids,
                    post_process=(field == 'body_html'))
                for res_id, field_value in generated_field_values.iteritems():
                    results.setdefault(res_id, dict())[field] = field_value
            # compute recipients
            if any(field in fields for field in ['email_to', 'partner_to', 'email_cc']):
                results = template.generate_recipients(results, template_res_ids)
            # update values for all res_ids
            for res_id in template_res_ids:
                values = results[res_id]
                # body: add user signature, sanitize
                if 'body_html' in fields and template.user_signature:
                    signature = self.env.user.signature
                    if signature:
                        values['body_html'] = tools.append_content_to_html(values['body_html'], signature, plaintext=False)
                if values.get('body_html'):
                    values['body'] = tools.html_sanitize(values['body_html'])
                # technical settings
                values.update(
                    mail_server_id=template.mail_server_id.id or False,
                    auto_delete=template.auto_delete,
                    model=template.model,
                    res_id=res_id or False,
                    attachment_ids=[attach.id for attach in template.attachment_ids],
                )

            # Add report in attachments: generate once for all template_res_ids
            if template.report_template:
                for res_id in template_res_ids:
                    attachments = []
                    report_name = self.render_template(template.report_name, template.model, res_id)
                    report = template.report_template
                    report_service = report.report_name

                    if report.report_type in ['qweb-html', 'qweb-pdf']:
                        result, format = self.pool['report'].get_pdf(self._cr, self._uid, [res_id], report_service, context=Template._context), 'pdf'
                    else:
                        result, format = odoo_report.render_report(self._cr, self._uid, [res_id], report_service, {'model': template.model}, Template._context)

                    # TODO in trunk, change return format to binary to match message_post expected format
                    result = base64.b64encode(result)
                    if not report_name:
                        report_name = 'report.' + report_service
                    ext = "." + format
                    if not report_name.endswith(ext):
                        report_name += ext
                    attachments.append((report_name, result))
                    results[res_id]['attachments'] = attachments

        return multi_mode and results or results[res_ids[0]]
Esempio n. 56
0
 def test_quote_thunderbird_html(self):
     html = html_sanitize(test_mail_examples.QUOTE_THUNDERBIRD_HTML)
     for ext in test_mail_examples.QUOTE_THUNDERBIRD_HTML_IN:
         self.assertIn(ext, html)
     for ext in test_mail_examples.QUOTE_THUNDERBIRD_HTML_OUT:
         self.assertIn(ext, html)
Esempio n. 57
0
 def test_quote_bugs(self):
     html = html_sanitize(test_mail_examples.BUG1)
     for ext in test_mail_examples.BUG_1_IN:
         self.assertIn(ext, html)
     for ext in test_mail_examples.BUG_1_OUT:
         self.assertIn('<span data-o-mail-quote="1">%s</span>' % cgi.escape(ext.decode('utf-8')), html)
Esempio n. 58
0
 def test_html(self):
     sanitized_html = html_sanitize(test_mail_examples.MISC_HTML_SOURCE)
     for tag in ['<div', '<b', '<i', '<u', '<strike', '<li', '<blockquote', '<a href']:
         self.assertIn(tag, sanitized_html, 'html_sanitize stripped too much of original html')
     for attr in ['javascript']:
         self.assertNotIn(attr, sanitized_html, 'html_sanitize did not remove enough unwanted attributes')
Esempio n. 59
0
    def generate_email_batch(self, cr, uid, template_id, res_ids, context=None, fields=None):
        """Generates an email from the template for given the given model based on
        records given by res_ids.

        :param template_id: id of the template to render.
        :param res_id: id of the record to use for rendering the template (model
                       is taken from template definition)
        :returns: a dict containing all relevant fields for creating a new
                  mail.mail entry, with one extra key ``attachments``, in the
                  format [(report_name, data)] where data is base64 encoded.
        """
        if context is None:
            context = {}
        if fields is None:
            fields = ["subject", "body_html", "email_from", "email_to", "partner_to", "email_cc", "reply_to"]

        report_xml_pool = self.pool.get("ir.actions.report.xml")
        res_ids_to_templates = self.get_email_template_batch(cr, uid, template_id, res_ids, context)

        # templates: res_id -> template; template -> res_ids
        templates_to_res_ids = {}
        for res_id, template in res_ids_to_templates.iteritems():
            templates_to_res_ids.setdefault(template, []).append(res_id)

        results = dict()
        for template, template_res_ids in templates_to_res_ids.iteritems():
            # generate fields value for all res_ids linked to the current template
            for field in fields:
                generated_field_values = self.render_template_batch(
                    cr,
                    uid,
                    getattr(template, field),
                    template.model,
                    template_res_ids,
                    post_process=(field == "body_html"),
                    context=context,
                )
                for res_id, field_value in generated_field_values.iteritems():
                    results.setdefault(res_id, dict())[field] = field_value
            # compute recipients
            results = self.generate_recipients_batch(cr, uid, results, template.id, template_res_ids, context=context)
            # update values for all res_ids
            for res_id in template_res_ids:
                values = results[res_id]
                # body: add user signature, sanitize
                if "body_html" in fields and template.user_signature:
                    signature = self.pool.get("res.users").browse(cr, uid, uid, context).signature
                    values["body_html"] = tools.append_content_to_html(values["body_html"], signature, plaintext=False)
                if values.get("body_html"):
                    values["body"] = tools.html_sanitize(values["body_html"])
                # technical settings
                values.update(
                    mail_server_id=template.mail_server_id.id or False,
                    auto_delete=template.auto_delete,
                    model=template.model,
                    res_id=res_id or False,
                    attachment_ids=[attach.id for attach in template.attachment_ids],
                )

            # Add report in attachments: generate once for all template_res_ids
            if template.report_template:
                for res_id in template_res_ids:
                    attachments = []
                    report_name = self.render_template(
                        cr, uid, template.report_name, template.model, res_id, context=context
                    )
                    report = report_xml_pool.browse(cr, uid, template.report_template.id, context)
                    report_service = report.report_name
                    # Ensure report is rendered using template's language
                    ctx = context.copy()
                    if template.lang:
                        ctx["lang"] = self.render_template_batch(
                            cr, uid, template.lang, template.model, [res_id], context
                        )[
                            res_id
                        ]  # take 0 ?

                    if report.report_type in ["qweb-html", "qweb-pdf"]:
                        result, format = (
                            self.pool["report"].get_pdf(cr, uid, [res_id], report_service, context=ctx),
                            "pdf",
                        )
                    else:
                        result, format = openerp.report.render_report(
                            cr, uid, [res_id], report_service, {"model": template.model}, ctx
                        )

                    # TODO in trunk, change return format to binary to match message_post expected format
                    result = base64.b64encode(result)
                    if not report_name:
                        report_name = "report." + report_service
                    ext = "." + format
                    if not report_name.endswith(ext):
                        report_name += ext
                    attachments.append((report_name, result))
                    results[res_id]["attachments"] = attachments

        return results
    def generate_email_batch(self, cr, uid, template_id, res_ids, context=None, fields=None):
        """Generates an email from the template for given the given model based on
        records given by res_ids.

        :param template_id: id of the template to render.
        :param res_id: id of the record to use for rendering the template (model
                       is taken from template definition)
        :returns: a dict containing all relevant fields for creating a new
                  mail.mail entry, with one extra key ``attachments``, in the
                  format expected by :py:meth:`mail_thread.message_post`.
        """
        if context is None:
            context = {}
        if fields is None:
            fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to']

        report_xml_pool = self.pool.get('ir.actions.report.xml')
        res_ids_to_templates = self.get_email_template_batch(cr, uid, template_id, res_ids, context)

        # templates: res_id -> template; template -> res_ids
        templates_to_res_ids = {}
        for res_id, template in res_ids_to_templates.iteritems():
            templates_to_res_ids.setdefault(template, []).append(res_id)

        results = dict()
        for template, template_res_ids in templates_to_res_ids.iteritems():
            # generate fields value for all res_ids linked to the current template
            for field in ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to']:
                generated_field_values = self.render_template_batch(cr, uid, getattr(template, field), template.model, template_res_ids, context=context)
                for res_id, field_value in generated_field_values.iteritems():
                    results.setdefault(res_id, dict())[field] = field_value
            # update values for all res_ids
            for res_id in template_res_ids:
                values = results[res_id]
                if template.user_signature:
                    signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
                    values['body_html'] = tools.append_content_to_html(values['body_html'], signature)
                if values['body_html']:
                    values['body'] = tools.html_sanitize(values['body_html'])
                values.update(
                    mail_server_id=template.mail_server_id.id or False,
                    auto_delete=template.auto_delete,
                    model=template.model,
                    res_id=res_id or False,
                    attachment_ids=[attach.id for attach in template.attachment_ids],
                )

            # Add report in attachments: generate once for all template_res_ids
            if template.report_template:
                for res_id in template_res_ids:
                    attachments = []
                    report_name = self.render_template(cr, uid, template.report_name, template.model, res_id, context=context)
                    report_service = report_xml_pool.browse(cr, uid, template.report_template.id, context).report_name
                    # Ensure report is rendered using template's language
                    ctx = context.copy()
                    if template.lang:
                        ctx['lang'] = self.render_template_batch(cr, uid, template.lang, template.model, [res_id], context)[res_id]  # take 0 ?
                    result, format = openerp.report.render_report(cr, uid, [res_id], report_service, {'model': template.model}, ctx)
                    result = base64.b64encode(result)
                    if not report_name:
                        report_name = 'report.' + report_service
                    ext = "." + format
                    if not report_name.endswith(ext):
                        report_name += ext
                    attachments.append((report_name, result))
                    results[res_id]['attachments'] = attachments

        return results