Skip to content

Commit

Permalink
Add options to sanitize HTML panes (#5516)
Browse files Browse the repository at this point in the history
* Add options to sanitize HTML panes

* Fixes and test

* Fix test
  • Loading branch information
philippjfr authored Sep 15, 2023
1 parent a640d70 commit bafe011
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 22 deletions.
12 changes: 7 additions & 5 deletions examples/reference/panes/HTML.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The ``HTML`` pane allows rendering arbitrary HTML in a panel. It renders strings containing valid HTML as well as objects with a ``_repr_html_`` method and may define custom CSS styles.\n",
"The `HTML` pane allows rendering arbitrary HTML in a panel. It renders strings containing valid HTML as well as objects with a `_repr_html_` method and may define custom CSS styles.\n",
"\n",
"#### Parameters:\n",
"\n",
"For details on other options for customizing the component see the [layout](../../how_to/layout/index.md) and [styling](../../how_to/styling/index.md) how-to guides.\n",
"\n",
"* **``disable_math``** (boolean, `default=True`): Whether to disable MathJax math rendering for strings escaped with $$ delimiters.\n",
"* **``object``** (str or object): The string or object with ``_repr_html_`` method to display\n",
"* **``style``** (dict): Dictionary specifying CSS styles\n",
"* **`disable_math`** (boolean, `default=True`): Whether to disable MathJax math rendering for strings escaped with `$$` delimiters.\n",
"* **`object`** (str or object): The string or object with ``_repr_html_`` method to display\n",
"* **`sanitize_html`** (boolean, `default=False`): Whether to sanitize HTML sent to the frontend.\n",
"* **`sanitize_hook`** (Callable, `default=bleach.clean`): Sanitization callback to apply if `sanitize_html=True`.\n",
"* **`styles`** (dict): Dictionary specifying CSS styles\n",
"\n",
"___"
]
Expand All @@ -32,7 +34,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The ``HTML`` pane accepts the entire HTML5 spec including any embedded script tags, which will be executed. It also supports a ``style`` dictionary to apply styles to control the style of the ``<div>`` tag the HTML contents will be rendered in."
"The `HTML` pane accepts the entire HTML5 spec including any embedded script tags, which will be executed. It also supports a `styles` dictionary to apply styles to control the style of the `<div>` tag the HTML contents will be rendered in."
]
},
{
Expand Down
17 changes: 8 additions & 9 deletions examples/reference/panes/Markdown.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,18 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The ``Markdown`` pane allows rendering arbitrary [Markdown](https://python-markdown.github.io) in a panel. It renders strings containing valid Markdown as well as objects with a ``_repr_markdown_`` method, and may define custom CSS styles.\n",
"The `Markdown` pane allows rendering arbitrary [Markdown](https://python-markdown.github.io) in a panel. It renders strings containing valid Markdown as well as objects with a `_repr_markdown_` method, and may define custom CSS styles.\n",
"\n",
"#### Parameters:\n",
"\n",
"For details on other options for customizing the component see the [layout](../../how_to/layout/index.md) and [styling](../../how_to/styling/index.md) how-to guides.\n",
"\n",
"* **``dedent``** (bool): Whether to dedent common whitespace across all lines.\n",
"* **``disable_math``** (boolean, `default=False`): Whether to disable MathJax math rendering for strings escaped with $$ delimiters.\n",
"* **``extensions``** (list): A list of [Python-Markdown extensions](https://python-markdown.github.io/extensions/) to use (does not apply for 'markdown-it' and 'myst' renderers).\n",
"* **``object``** (str or object): A string containing Markdown, or an object with a ``_repr_markdown_`` method.\n",
"* **``plugins``** (function): A list of additional markdown-it-py plugins to apply.\n",
"* **``renderer``** (literal: `'markdown-it'`, `'markdown'`, `'myst'`): Markdown renderer implementation.\n",
"* \n",
"* **`dedent`** (bool): Whether to dedent common whitespace across all lines.\n",
"* **`disable_math`** (boolean, `default=False`): Whether to disable MathJax math rendering for strings escaped with `$$` delimiters.\n",
"* **`extensions`** (list): A list of [Python-Markdown extensions](https://python-markdown.github.io/extensions/) to use (does not apply for 'markdown-it' and 'myst' renderers).\n",
"* **`object`** (str or object): A string containing Markdown, or an object with a ``_repr_markdown_`` method.\n",
"* **`plugins`** (function): A list of additional markdown-it-py plugins to apply.\n",
"* **`renderer`** (literal: `'markdown-it'`, `'markdown'`, `'myst'`): Markdown renderer implementation.\n",
"* **`renderer_options`** (dict): Options to pass to the markdown renderer.\n",
"\n",
"___"
Expand All @@ -39,7 +38,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The ``Markdown`` pane accepts all *basic* Markdown syntax, including embedded HTML. It also supports most of the *extended* Markdown syntax."
"The `Markdown` pane accepts all *basic* Markdown syntax, including embedded HTML. It also supports most of the *extended* Markdown syntax."
]
},
{
Expand Down
20 changes: 18 additions & 2 deletions panel/pane/markup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from ..io.resources import CDN_DIST
from ..models import HTML as _BkHTML, JSON as _BkJSON
from ..util import escape
from ..util import HTML_SANITIZER, escape
from ..util.warnings import deprecated
from .base import ModelPane

Expand Down Expand Up @@ -69,9 +69,23 @@ class HTML(HTMLBasePane):
Whether to disable support for MathJax math rendering for
strings escaped with $$ delimiters.""")

sanitize_html = param.Boolean(default=False, doc="""
Whether to sanitize HTML sent to the frontend.""")

sanitize_hook = param.Callable(default=HTML_SANITIZER.clean, doc="""
Sanitization callback to apply if `sanitize_html=True`.""")

# Priority is dependent on the data type
priority: ClassVar[float | bool | None] = None

_rename: ClassVar[Mapping[str, str | None]] = {
'sanitize_html': None, 'sanitize_hook': None
}

_rerender_params: ClassVar[List[str]] = [
'object', 'sanitize_html', 'sanitize_hook'
]

@classmethod
def applies(cls, obj: Any) -> float | bool | None:
module, name = getattr(obj, '__module__', ''), type(obj).__name__
Expand All @@ -87,6 +101,8 @@ def _transform_object(self, obj: Any) -> Dict[str, Any]:
text = '' if obj is None else obj
if hasattr(text, '_repr_html_'):
text = text._repr_html_()
if self.sanitize_html:
text = self.sanitize_hook(text)
return dict(object=escape(text))


Expand Down Expand Up @@ -342,7 +358,7 @@ class Markdown(HTMLBasePane):
}

_rerender_params: ClassVar[List[str]] = [
'object', 'dedent', 'extensions', 'css_classes', 'plugins'
'object', 'dedent', 'extensions', 'css_classes', 'plugins',
]

_target_transforms: ClassVar[Mapping[str, str | None]] = {
Expand Down
11 changes: 5 additions & 6 deletions panel/reactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
Optional, Set, Tuple, Type, Union,
)

import bleach
import numpy as np
import param

Expand All @@ -45,8 +44,8 @@
DOMEvent, ReactiveHTML as _BkReactiveHTML, ReactiveHTMLParser,
)
from .util import (
BOKEH_JS_NAT, classproperty, edit_readonly, escape, eval_function,
extract_dependencies, updating,
BOKEH_JS_NAT, HTML_SANITIZER, classproperty, edit_readonly, escape,
eval_function, extract_dependencies, updating,
)
from .viewable import Layoutable, Renderable, Viewable

Expand Down Expand Up @@ -1786,7 +1785,7 @@ def _init_params(self) -> Dict[str, Any]:
):
continue
if isinstance(v, str):
v = bleach.clean(v)
v = HTML_SANITIZER.clean(v)
data_params[k] = v
html, nodes, self._attrs = self._get_template()
params.update({
Expand Down Expand Up @@ -2056,7 +2055,7 @@ def _update_model(
if prop in child_params:
new_children[prop] = prop
if self._child_config.get(prop) == 'literal':
data_msg[prop] = bleach.clean(v)
data_msg[prop] = HTML_SANITIZER.clean(v)
elif prop in model.data.properties():
data_msg[prop] = v
elif prop in list(Reactive.param)+['events']:
Expand All @@ -2069,7 +2068,7 @@ def _update_model(
):
continue
elif isinstance(v, str):
data_msg[prop] = bleach.clean(v)
data_msg[prop] = HTML_SANITIZER.clean(v)
else:
data_msg[prop] = v
if new_children:
Expand Down
12 changes: 12 additions & 0 deletions panel/tests/pane/test_markup.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ def test_html_pane(document, comm):
pane._cleanup(model)
assert pane._models == {}

def test_html_pane_sanitize_html(document, comm):
pane = HTML("<h1><strong>HTML</h1></strong>", sanitize_html=True)

# Create pane
model = pane.get_root(document, comm=comm)
assert pane._models[model.ref['id']][0] is model
assert model.text.endswith("&lt;strong&gt;HTML&lt;/strong&gt;")

pane.sanitize_html = False

assert model.text.endswith('&lt;h1&gt;&lt;strong&gt;HTML&lt;/h1&gt;&lt;/strong&gt;')

def test_dataframe_pane_pandas(document, comm):
pane = DataFrame(pd._testing.makeDataFrame())

Expand Down
3 changes: 3 additions & 0 deletions panel/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from importlib import import_module
from typing import Any, AnyStr, Iterator

import bleach
import bokeh
import numpy as np
import param
Expand All @@ -45,6 +46,8 @@

PARAM_NAME_PATTERN = re.compile(r'^.*\d{5}$')

HTML_SANITIZER = bleach.sanitizer.Cleaner(strip=True)


def hashable(x):
if isinstance(x, MutableSequence):
Expand Down

0 comments on commit bafe011

Please sign in to comment.