##// END OF EJS Templates
fix(jupiter): sanitizing rendering (HTML) of jupyter notebooks, upgraded bleach version. Fixes RCCE-15, RCCE-14
ilin.s -
r5252:1ecdda64 default
parent child Browse files
Show More
@@ -121,7 +121,7 b' mysqlclient==2.1.1'
121 nbconvert==7.7.3
121 nbconvert==7.7.3
122 beautifulsoup4==4.11.2
122 beautifulsoup4==4.11.2
123 soupsieve==2.4
123 soupsieve==2.4
124 bleach==6.0.0
124 bleach==6.1.0
125 six==1.16.0
125 six==1.16.0
126 webencodings==0.5.1
126 webencodings==0.5.1
127 defusedxml==0.7.1
127 defusedxml==0.7.1
@@ -424,11 +424,13 b' class MarkupRenderer(object):'
424 @classmethod
424 @classmethod
425 def jupyter(cls, source, safe=True):
425 def jupyter(cls, source, safe=True):
426 from rhodecode.lib import helpers
426 from rhodecode.lib import helpers
427 from .html_sanitizer_defs import markdown_attrs, all_tags, all_styles
427
428
428 from traitlets import default, config
429 from traitlets import default, config
429 import nbformat
430 import nbformat
430 from nbconvert import HTMLExporter
431 from nbconvert import HTMLExporter
431 from nbconvert.preprocessors import Preprocessor
432 from nbconvert.preprocessors import Preprocessor
433 from nbconvert.preprocessors.sanitize import SanitizeHTML
432
434
433 class CustomHTMLExporter(HTMLExporter):
435 class CustomHTMLExporter(HTMLExporter):
434
436
@@ -439,24 +441,20 b' class MarkupRenderer(object):'
439
441
440 class Sandbox(Preprocessor):
442 class Sandbox(Preprocessor):
441
443
442 def preprocess(self, nb, resources):
444 def preprocess_cell(self, cell, resources, cell_index):
445 if not safe:
446 return cell, resources
443 sandbox_text = 'SandBoxed(IPython.core.display.Javascript object)'
447 sandbox_text = 'SandBoxed(IPython.core.display.Javascript object)'
444 for cell in nb['cells']:
448 if cell.cell_type == "markdown":
445 if not safe:
449 cell.source = cls.sanitize_html(cell.source)
446 continue
450 return cell, resources
447
451
448 if 'outputs' in cell:
452 for cell_output in cell.outputs:
449 for cell_output in cell['outputs']:
453 if 'data' in cell_output:
450 if 'data' in cell_output:
454 if 'application/javascript' in cell_output['data']:
451 if 'application/javascript' in cell_output['data']:
455 cell_output['data']['text/plain'] = sandbox_text
452 cell_output['data']['text/plain'] = sandbox_text
456 cell_output['data'].pop('application/javascript', None)
453 cell_output['data'].pop('application/javascript', None)
457 return cell, resources
454
455 if 'source' in cell and cell['cell_type'] == 'markdown':
456 # sanitize similar like in markdown
457 cell['source'] = cls.sanitize_html(cell['source'])
458
459 return nb, resources
460
458
461 def _sanitize_resources(input_resources):
459 def _sanitize_resources(input_resources):
462 """
460 """
@@ -475,8 +473,29 b' class MarkupRenderer(object):'
475
473
476 def as_html(notebook):
474 def as_html(notebook):
477 conf = config.Config()
475 conf = config.Config()
478 conf.CustomHTMLExporter.default_preprocessors = [Sandbox]
476 # TODO: Keep an eye on the order of preprocessors
477 conf.CustomHTMLExporter.default_preprocessors = [Sandbox, SanitizeHTML]
479 conf.Sandbox.enabled = True
478 conf.Sandbox.enabled = True
479 conf.SanitizeHTML.enabled = True
480 conf.SanitizeHTML.attributes = markdown_attrs
481 conf.SanitizeHTML.tags = all_tags
482 conf.SanitizeHTML.styles = all_styles
483 conf.SanitizeHTML.sanitized_output_types = {
484 "text/html",
485 "text/markdown",
486 }
487 conf.SanitizeHTML.safe_output_keys = {
488 "metadata",
489 "text/plain",
490 "text/latex",
491 "application/json",
492 "image/png",
493 "image/jpg"
494 "image/jpeg",
495 "image/svg",
496 "image/svg+xml"
497 }
498
480 html_exporter = CustomHTMLExporter(config=conf)
499 html_exporter = CustomHTMLExporter(config=conf)
481
500
482 (body, resources) = html_exporter.from_notebook_node(notebook)
501 (body, resources) = html_exporter.from_notebook_node(notebook)
@@ -17,6 +17,7 b''
17 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19
19
20 import mock
20 import pytest
21 import pytest
21
22
22 from rhodecode.lib.markup_renderer import (
23 from rhodecode.lib.markup_renderer import (
@@ -778,8 +779,119 b' def test_relative_links(src_html, expect'
778 """, "Hello, World!")
779 """, "Hello, World!")
779 ])
780 ])
780 def test_jp_notebook_html_generation(notebook_source, expected_output):
781 def test_jp_notebook_html_generation(notebook_source, expected_output):
781 import mock
782 with mock.patch('rhodecode.lib.helpers.asset'):
782 with mock.patch('rhodecode.lib.helpers.asset'):
783 body = MarkupRenderer.jupyter(notebook_source)
783 body = MarkupRenderer.jupyter(notebook_source)
784 assert "<!-- ## IPYTHON NOTEBOOK RENDERING ## -->" in body
784 assert "<!-- ## IPYTHON NOTEBOOK RENDERING ## -->" in body
785 assert expected_output in body
785 assert expected_output in body
786
787
788 @pytest.mark.parametrize("notebook_source, expected_output", [
789 ({"cells": [
790 {
791 "cell_type": "code",
792 "execution_count": 0,
793 "metadata": {},
794 "outputs": [
795 {
796 "data": {
797 "text/html": [
798 "<select><iframe></select><img src=x: onerror=alert('xss')>\n"
799 ],
800 "text/plain": []
801 },
802 "metadata": {},
803 "output_type": "display_data"
804 }
805 ],
806 "source": [
807 ""
808 ]
809 }
810 ],
811 "metadata": {
812 "kernelspec": {
813 "display_name": "Python 3",
814 "language": "python",
815 "name": "python3"
816 },
817 "language_info": {
818 "codemirror_mode": {
819 "name": "ipython",
820 "version": 3
821 },
822 "file_extension": ".py",
823 "mimetype": "text/x-python",
824 "name": "python",
825 "nbconvert_exporter": "python",
826 "pygments_lexer": "ipython3",
827 "version": "3.9.6"
828 }
829 },
830 "nbformat": 4,
831 "nbformat_minor": 5
832 }, '<select></select><img alt="No description has been provided for this image"/>'),
833 ({
834 "cells": [
835 {
836 "cell_type": "markdown",
837 "metadata": {},
838 "source": [
839 "<label for=buttonid style=\"cursor: text\">not safe to click here</label>\n"
840 ]
841 },
842 {
843 "cell_type": "markdown",
844 "metadata": {
845 "highlighter": "codemirror"
846 },
847 "source": "<div class=\"jp-InputArea-editor\"><div class=\"CodeMirror cm-s-jupyter\"><div class=\"CodeMirror-scroll\"><div class=\"CodeMirror-sizer\"><div style=\"top:0px; position:relative\"><div class=\"CodeMirror-lines\"><div style=\"outline:none; position:relative\"><div class=\"CodeMirror-code\"><div style=\"position: relative\"><label for=buttonid style=\"cursor: text\"><pre class=\"CodeMirror-line\" style=\"background:transparent\"><span style=\"padding-right: 0.1px\"><span class=\"cm-builtin\">print</span>(<span class=\"cm-string\">&quot;Also not safe to click here&quot;</span>)</span></pre></label></div></div></div></div></div></div></div></div></div>"
848 },
849 {
850 "cell_type": "code",
851 "execution_count": 0,
852 "metadata": {
853 "xrender": True
854 },
855 "outputs": [
856 {
857 "data": {
858 "text/html": [
859 "<form id=jp-mk-edit action='javascript:alert(1)' style='display:none'><button type=submit id=buttonid></form>\n"
860 ],
861 "text/plain": []
862 },
863 "metadata": {},
864 "output_type": "display_data"
865 }
866 ],
867 "source": ""
868 }
869 ],
870 "metadata": {
871 "kernelspec": {
872 "display_name": "Python 3",
873 "language": "python",
874 "name": "python3"
875 },
876 "language_info": {
877 "codemirror_mode": {
878 "name": "ipython",
879 "version": 3
880 },
881 "file_extension": ".py",
882 "mimetype": "text/x-python",
883 "name": "python",
884 "nbconvert_exporter": "python",
885 "pygments_lexer": "ipython3",
886 "version": "3.9.6"
887 }
888 },
889 "nbformat": 4,
890 "nbformat_minor": 5
891 }, '<form style="display:none;">')
892 ])
893 def test_jp_notebook_against_xss(notebook_source, expected_output):
894 import json
895 with mock.patch('rhodecode.lib.helpers.asset'):
896 body = MarkupRenderer.jupyter(json.dumps(notebook_source))
897 assert expected_output in body
General Comments 0
You need to be logged in to leave comments. Login now