diff --git a/common/lib/chem/chem/chemcalc.py b/common/lib/chem/chem/chemcalc.py
index dd4abefed2c0dad3c85a29eeed8ac506597250b0..196c99ae5ce0ef1dce153dad581ba587d77eb23c 100644
--- a/common/lib/chem/chem/chemcalc.py
+++ b/common/lib/chem/chem/chemcalc.py
@@ -2,10 +2,12 @@ from __future__ import division
 
 from fractions import Fraction
 
+import markupsafe
 import nltk
 from nltk.tree import Tree
 from pyparsing import Literal, OneOrMore, ParseException, StringEnd
 
+
 ARROWS = ('<->', '->')
 
 # Defines a simple pyparsing tokenizer for chemical equations
@@ -26,6 +28,16 @@ phases = ["(s)", "(l)", "(g)", "(aq)"]
 tokens = reduce(lambda a, b: a ^ b, map(Literal, elements + digits + symbols + phases))
 tokenizer = OneOrMore(tokens) + StringEnd()
 
+# HTML, Text are temporarily copied from openedx.core.djangolib.markup
+# These libraries need to be moved out of edx-platform to be used by
+# other applications.
+# See LEARNER-5853 for more details.
+Text = markupsafe.escape                        # pylint: disable=invalid-name
+
+
+def HTML(html):                                 # pylint: disable=invalid-name
+    return markupsafe.Markup(html)
+
 
 def _orjoin(l):
     return "'" + "' | '".join(l) + "'"
@@ -166,20 +178,20 @@ def _render_to_html(tree):
             return tree[0][0]
         # If a fraction, return the fraction
         if len(tree) == 3:
-            return " <sup>{num}</sup>&frasl;<sub>{den}</sub> ".format(num=tree[0][0], den=tree[2][0])
+            return HTML(" <sup>{num}</sup>&frasl;<sub>{den}</sub> ").format(num=tree[0][0], den=tree[2][0])
         return "Error"
 
     def subscript(tree, children):
-        return "<sub>{sub}</sub>".format(sub=children)
+        return HTML("<sub>{sub}</sub>").format(sub=children)
 
     def superscript(tree, children):
-        return "<sup>{sup}</sup>".format(sup=children)
+        return HTML("<sup>{sup}</sup>").format(sup=children)
 
     def round_brackets(tree, children):
-        return "({insider})".format(insider=children)
+        return HTML("({insider})").format(insider=children)
 
     def square_brackets(tree, children):
-        return "[{insider}]".format(insider=children)
+        return HTML("[{insider}]").format(insider=children)
 
     dispatch = {'count': molecule_count,
                 'number_suffix': subscript,
@@ -190,7 +202,7 @@ def _render_to_html(tree):
     if isinstance(tree, str):
         return tree
     else:
-        children = "".join(map(_render_to_html, tree))
+        children = HTML("").join(map(_render_to_html, tree))
         if tree.label() in dispatch:
             return dispatch[tree.label()](tree, children)
         else:
@@ -207,20 +219,20 @@ def render_to_html(eq):
         """
         Render as an error span
         """
-        return '<span class="inline-error inline">{0}</span>'.format(s)
+        return HTML('<span class="inline-error inline">{0}</span>').format(s)
 
     def render_arrow(arrow):
         """
         Turn text arrows into pretty ones
         """
         if arrow == '->':
-            return u'\u2192'
+            return HTML(u'\u2192')
         if arrow == '<->':
-            return u'\u2194'
+            return HTML(u'\u2194')
 
         # this won't be reached unless we add more arrow types, but keep it to avoid explosions when
-        # that happens.
-        return arrow
+        # that happens. HTML-escape this unknown arrow just in case.
+        return Text(arrow)
 
     def render_expression(ex):
         """
@@ -232,7 +244,7 @@ def render_to_html(eq):
             return err(ex)
 
     def spanify(s):
-        return u'<span class="math">{0}</span>'.format(s)
+        return HTML(u'<span class="math">{0}</span>').format(s)
 
     left, arrow, right = split_on_arrow(eq)
     if arrow == '':
diff --git a/common/lib/chem/chem/tests.py b/common/lib/chem/chem/tests.py
index 93f6132798af6d8bc3367a061d3d606c814618d4..7625095aa9963dad4b675695cbac4b15251de085 100644
--- a/common/lib/chem/chem/tests.py
+++ b/common/lib/chem/chem/tests.py
@@ -296,13 +296,20 @@ class Test_Render_Equations(unittest.TestCase):
         log(out + ' ------- ' + correct, 'html')
         self.assertEqual(out, correct)
 
-    def test_render_simple_brackets(self):
+    def test_render_simple_round_brackets(self):
         test_string = "(Ar)"
         out = render_to_html(test_string)
         correct = u'<span class="math">(Ar)</span>'
         log(out + ' ------- ' + correct, 'html')
         self.assertEqual(out, correct)
 
+    def test_render_simple_square_brackets(self):
+        test_string = "[Ar]"
+        out = render_to_html(test_string)
+        correct = u'<span class="math">[Ar]</span>'
+        log(out + ' ------- ' + correct, 'html')
+        self.assertEqual(out, correct)
+
     def test_render_eq1(self):
         test_string = "H^+ + OH^- -> H2O"
         out = render_to_html(test_string)
@@ -320,7 +327,24 @@ class Test_Render_Equations(unittest.TestCase):
     def test_render_eq3(self):
         test_string = "H^+ + OH^- <= H2O"   # unsupported arrow
         out = render_to_html(test_string)
-        correct = u'<span class="math"><span class="inline-error inline">H^+ + OH^- <= H2O</span></span>'
+        correct = u'<span class="math"><span class="inline-error inline">H^+ + OH^- &lt;= H2O</span></span>'
+        log(out + ' ------- ' + correct, 'html')
+        self.assertEqual(out, correct)
+
+    def test_render_eq4(self):
+        test_string = "[H^+] + OH^- <-> (H2O)"  # with brackets
+        out = render_to_html(test_string)
+        correct = u'<span class="math">[H<sup>+</sup>]+OH<sup>-</sup>\u2194(H<sub>2</sub>O)</span>'
+        log(out + ' ------- ' + correct, 'html')
+        self.assertEqual(out, correct)
+
+    def test_escaping(self):
+        """
+        Tests that invalid input is escaped.
+        """
+        test_string = "<script>f()</script>"
+        out = render_to_html(test_string)
+        correct = u'<span class="math"><span class="inline-error inline">&lt;script&gt;f()&lt;/script&gt;</span></span>'
         log(out + ' ------- ' + correct, 'html')
         self.assertEqual(out, correct)
 
diff --git a/common/lib/chem/setup.py b/common/lib/chem/setup.py
index 005836c1f0994f1b9f0784198d40884c88a097d9..12f8bddefd7a756f9bfe249ebf04267b73a9fedb 100644
--- a/common/lib/chem/setup.py
+++ b/common/lib/chem/setup.py
@@ -2,12 +2,13 @@ from setuptools import setup
 
 setup(
     name="chem",
-    version="0.1.2",
+    version="0.2.0",
     packages=["chem"],
     install_requires=[
         "pyparsing==2.2.0",
         "numpy==1.6.2",
         "scipy==0.14.0",
         "nltk",
+        "markupsafe",  # Should be replaced by other utilities. See LEARNER-5853 for more details.
     ],
 )