Пример #1
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
Пример #2
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')
Пример #3
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
Пример #4
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')
Пример #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), '')
Пример #6
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')
Пример #7
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
Пример #8
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')
Пример #9
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
Пример #10
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:
                Template = Template.with_context(safe=field in {'subject'})
                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 and not 'report_template_in_attachment' in self.env.context:
                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 = ecore_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]]