Show More
@@ -121,7 +121,7 b' mysqlclient==2.1.1' | |||
|
121 | 121 | nbconvert==7.7.3 |
|
122 | 122 | beautifulsoup4==4.11.2 |
|
123 | 123 | soupsieve==2.4 |
|
124 |
bleach==6. |
|
|
124 | bleach==6.1.0 | |
|
125 | 125 | six==1.16.0 |
|
126 | 126 | webencodings==0.5.1 |
|
127 | 127 | defusedxml==0.7.1 |
@@ -424,11 +424,13 b' class MarkupRenderer(object):' | |||
|
424 | 424 | @classmethod |
|
425 | 425 | def jupyter(cls, source, safe=True): |
|
426 | 426 | from rhodecode.lib import helpers |
|
427 | from .html_sanitizer_defs import markdown_attrs, all_tags, all_styles | |
|
427 | 428 | |
|
428 | 429 | from traitlets import default, config |
|
429 | 430 | import nbformat |
|
430 | 431 | from nbconvert import HTMLExporter |
|
431 | 432 | from nbconvert.preprocessors import Preprocessor |
|
433 | from nbconvert.preprocessors.sanitize import SanitizeHTML | |
|
432 | 434 | |
|
433 | 435 | class CustomHTMLExporter(HTMLExporter): |
|
434 | 436 | |
@@ -439,24 +441,20 b' class MarkupRenderer(object):' | |||
|
439 | 441 | |
|
440 | 442 | class Sandbox(Preprocessor): |
|
441 | 443 | |
|
442 |
def preprocess(self, |
|
|
444 | def preprocess_cell(self, cell, resources, cell_index): | |
|
445 | if not safe: | |
|
446 | return cell, resources | |
|
443 | 447 | sandbox_text = 'SandBoxed(IPython.core.display.Javascript object)' |
|
444 |
f |
|
|
445 | if not safe: | |
|
446 |
|
|
|
448 | if cell.cell_type == "markdown": | |
|
449 | cell.source = cls.sanitize_html(cell.source) | |
|
450 | return cell, resources | |
|
447 | 451 | |
|
448 |
|
|
|
449 |
|
|
|
450 |
|
|
|
451 | if 'application/javascript' in cell_output['data']: | |
|
452 |
|
|
|
453 | cell_output['data'].pop('application/javascript', None) | |
|
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 | |
|
452 | for cell_output in cell.outputs: | |
|
453 | if 'data' in cell_output: | |
|
454 | if 'application/javascript' in cell_output['data']: | |
|
455 | cell_output['data']['text/plain'] = sandbox_text | |
|
456 | cell_output['data'].pop('application/javascript', None) | |
|
457 | return cell, resources | |
|
460 | 458 | |
|
461 | 459 | def _sanitize_resources(input_resources): |
|
462 | 460 | """ |
@@ -475,8 +473,29 b' class MarkupRenderer(object):' | |||
|
475 | 473 | |
|
476 | 474 | def as_html(notebook): |
|
477 | 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 | 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 | 499 | html_exporter = CustomHTMLExporter(config=conf) |
|
481 | 500 | |
|
482 | 501 | (body, resources) = html_exporter.from_notebook_node(notebook) |
@@ -17,6 +17,7 b'' | |||
|
17 | 17 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | 18 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | 19 | |
|
20 | import mock | |
|
20 | 21 | import pytest |
|
21 | 22 | |
|
22 | 23 | from rhodecode.lib.markup_renderer import ( |
@@ -778,8 +779,119 b' def test_relative_links(src_html, expect' | |||
|
778 | 779 | """, "Hello, World!") |
|
779 | 780 | ]) |
|
780 | 781 | def test_jp_notebook_html_generation(notebook_source, expected_output): |
|
781 | import mock | |
|
782 | 782 | with mock.patch('rhodecode.lib.helpers.asset'): |
|
783 | 783 | body = MarkupRenderer.jupyter(notebook_source) |
|
784 | 784 | assert "<!-- ## IPYTHON NOTEBOOK RENDERING ## -->" in body |
|
785 | 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\">"Also not safe to click here"</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