Files
edx-platform/common/lib/xmodule/xmodule/template_module.py
David Ormsbee 483e654fcf Make CustomTagModule safe (remove Mako) [SEC-609]
Prior to this commit, it was possible for course authoring teams
to import and execute Mako templates using the obscure "customtag"
tag (CustomTagModule). Since Mako templates can run Python code
(e.g. imports, database queries, etc.), this would give a course
team the ability to execute arbitrary, unsandboxed code on the
server.

This commit converts CustomTagModule to use the Python library's
string.Template instead. This should be broadly compatible with
the most basic and common usage of customtag, which is simple
variable substitution in the style of ${var_name}.
2019-08-06 13:54:58 -04:00

80 lines
2.5 KiB
Python

"""
Template module
"""
from __future__ import absolute_import
from string import Template
from lxml import etree
from xmodule.raw_module import RawDescriptor
from xmodule.x_module import DEPRECATION_VSCOMPAT_EVENT, XModule
class CustomTagModule(XModule):
"""
This module supports tags of the form
<customtag option="val" option2="val2" impl="tagname"/>
In this case, $tagname should refer to a file in data/custom_tags, which
contains a Python string.Template formatted template that uses ${option} and
${option2} for the content.
For instance:
data/mycourse/custom_tags/book::
More information given in <a href="/book/${page}">the text</a>
course.xml::
...
<customtag page="234" impl="book"/>
...
Renders to::
More information given in <a href="/book/234">the text</a>
"""
def get_html(self):
return self.descriptor.rendered_html
class CustomTagDescriptor(RawDescriptor):
""" Descriptor for custom tags. Loads the template when created."""
module_class = CustomTagModule
resources_dir = None
template_dir_name = 'customtag'
def render_template(self, system, xml_data):
'''Render the template, given the definition xml_data'''
xmltree = etree.fromstring(xml_data)
if 'impl' in xmltree.attrib:
template_name = xmltree.attrib['impl']
else:
# VS[compat] backwards compatibility with old nested customtag structure
child_impl = xmltree.find('impl')
if child_impl is not None:
template_name = child_impl.text
else:
# TODO (vshnayder): better exception type
raise Exception("Could not find impl attribute in customtag {0}"
.format(self.location))
params = dict(list(xmltree.items()))
# cdodge: look up the template as a module
template_loc = self.location.replace(category='custom_tag_template', name=template_name)
template_module = system.load_item(template_loc)
template_module_data = template_module.data
template = Template(template_module_data)
return template.safe_substitute(params)
@property
def rendered_html(self):
return self.render_template(self.system, self.data)
def export_to_file(self):
"""
Custom tags are special: since they're already pointers, we don't want
to export them in a file with yet another layer of indirection.
"""
return False