Skip to content

Commit

Permalink
Sanitize text before making repr_html (#3378)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenworsley authored and pp-mo committed Oct 16, 2019
1 parent 8d79dba commit 30b0dab
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 15 deletions.
10 changes: 6 additions & 4 deletions lib/iris/experimental/representation.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from __future__ import (absolute_import, division, print_function)
from six.moves import (filter, input, map, range, zip) # noqa

from html import escape
import re


Expand Down Expand Up @@ -96,7 +97,7 @@ class CubeRepresentation(object):
def __init__(self, cube):
self.cube = cube
self.cube_id = id(self.cube)
self.cube_str = str(self.cube)
self.cube_str = escape(str(self.cube))

self.str_headings = {
'Dimension coordinates:': None,
Expand All @@ -115,9 +116,9 @@ def __init__(self, cube):
self.scalar_cube = self.shapes == ()
self.ndims = self.cube.ndim

self.name = self.cube.name().title().replace('_', ' ')
self.names = self._dim_names()
self.units = self.cube.units
self.name = escape(self.cube.name().title().replace('_', ' '))
self.names = [escape(dim_name) for dim_name in self._dim_names()]
self.units = escape(str(self.cube.units))

def _get_dim_names(self):
"""
Expand Down Expand Up @@ -401,6 +402,7 @@ def make_content(self):
for i, cube in enumerate(self.cubelist):
title = '{i}: {summary}'.format(i=i,
summary=cube.summary(shorten=True))
title = escape(title)
content = cube._repr_html_()
html.append(self._accordian_panel.format(uid=self.cubelist_id,
title=title,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# (C) British Crown Copyright 2018, Met Office
# (C) British Crown Copyright 2018 - 2019, Met Office
#
# This file is part of Iris.
#
Expand All @@ -18,6 +18,7 @@

from __future__ import (absolute_import, division, print_function)
from six.moves import (filter, input, map, range, zip) # noqa
from html import escape

# Import iris.tests first so that some things can be initialised before
# importing anything else.
Expand Down Expand Up @@ -136,12 +137,12 @@ def test_identfication(self):

def test_header__name(self):
header = self.representer._make_header()
expected_name = self.cube.name().title().replace('_', ' ')
expected_name = escape(self.cube.name().title().replace('_', ' '))
self.assertIn(expected_name, header)

def test_header__units(self):
header = self.representer._make_header()
expected_units = self.cube.units.symbol
expected_units = escape(self.cube.units.symbol)
self.assertIn(expected_units, header)

def test_header__scalar_str(self):
Expand All @@ -160,9 +161,9 @@ def test_content__specific_scalar_coord(self):
# Check a specific scalar coord is present in the main content.
content = self.representer._make_content()
expected_coord = self.cube.coords()[0]
expected_coord_name = expected_coord.name()
expected_coord_name = escape(expected_coord.name())
self.assertIn(expected_coord_name, content)
expected_coord_val = str(expected_coord.points[0])
expected_coord_val = escape(str(expected_coord.points[0]))
self.assertIn(expected_coord_val, content)

def test_content__attributes(self):
Expand Down
20 changes: 20 additions & 0 deletions lib/iris/tests/unit/experimental/representation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# (C) British Crown Copyright 2019, Met Office
#
# This file is part of Iris.
#
# Iris is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Iris is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Iris. If not, see <http://www.gnu.org/licenses/>.
"""Unit tests for the :mod:`iris.experimental.representation` package."""

from __future__ import (absolute_import, division, print_function)
from six.moves import (filter, input, map, range, zip) # noqa
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from __future__ import (absolute_import, division, print_function)
from six.moves import (filter, input, map, range, zip) # noqa
from html import escape

# Import iris.tests first so that some things can be initialised before
# importing anything else.
Expand All @@ -44,6 +45,7 @@ class Test_make_content(tests.IrisTest):
def setUp(self):
self.cubes = CubeList([stock.simple_3d(),
stock.lat_lon_cube()])
self.cubes[0].rename('name & <html>')
self.representer = CubeListRepresentation(self.cubes)
self.content = self.representer.make_content()

Expand All @@ -53,17 +55,18 @@ def test_repr_len(self):
def test_summary_lines(self):
names = [c.name() for c in self.cubes]
for name, content in zip(names, self.content):
name = escape(name)
self.assertIn(name, content)

def test__cube_name_summary_consistency(self):
# Just check the first cube in the CubeList.
single_cube_html = self.content[0]
first_contents_line = single_cube_html.split('\n')[1]
# Get the cube name out of the repr html...
cube_name = first_contents_line.split('>0: ')[1].split('/')[0]
# ... and prettify it (to be the same as in the following cube repr).
# Get a "prettified" cube name, as it should be in the cubelist repr.
cube_name = self.cubes[0].name()
pretty_cube_name = cube_name.strip().replace('_', ' ').title()
self.assertIn(pretty_cube_name, single_cube_html)
pretty_escaped_name = escape(pretty_cube_name)
self.assertIn(pretty_escaped_name, single_cube_html)


@tests.skip_data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from __future__ import (absolute_import, division, print_function)
from six.moves import (filter, input, map, range, zip) # noqa
from html import escape

# Import iris.tests first so that some things can be initialised before
# importing anything else.
Expand Down Expand Up @@ -81,13 +82,13 @@ class Test__summary_content(tests.IrisTest):
def setUp(self):
self.cube = stock.lat_lon_cube()
# Check we're not tripped up by names containing spaces.
self.cube.rename('Electron density')
self.cube.rename('Electron density (&<html>)')
self.cube.units = '1e11 e/m^3'
self.representer = CubeRepresentation(self.cube)

def test_name(self):
# Check the cube name is being set and formatted correctly.
expected = self.cube.name().replace('_', ' ').title()
expected = escape(self.cube.name().replace('_', ' ').title())
result = self.representer.name
self.assertEqual(expected, result)

Expand Down

0 comments on commit 30b0dab

Please sign in to comment.