diff --git a/CHANGELOG.md b/CHANGELOG.md
index 733e9c0c..b7284caa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,35 @@
Records breaking changes from major version bumps
+## 22.0.0
+
+PR: [#286](https://github.com/alphagov/digitalmarketplace-utils/pull/286)
+
+### What changed
+
+We used to be able to use a `|markdown` filter in our jinja templates which would turn markdown formatted strings into [Markup](http://jinja.pocoo.org/docs/dev/api/#jinja2.Markup) strings as well as permit HTML tags.
+This opened us up to vulnerabilities where untrusted input might end up going through one of these filters and expose us to a cross-site scripting (XSS) exploit.
+
+Going forward, markdown formatted text will be allowed in specific fields (documented in the [README for digitalmarketplace-frameworks](https://github.com/alphagov/digitalmarketplace-frameworks/blob/4c0502379910d8248f062b8aaf35fc58ce912370/README.md#template-fields)) and then rendered by TemplateFields, handled by the Content Loader.
+
+### Example app change
+
+Old:
+```jinja
+
+
Question name: {{ question.name|markdown }}
+
+```
+
+New:
+```jinja
+
+
+Question name: {{ question.name }}
+
+```
+
+
## 21.0.0
PR: [#266](https://github.com/alphagov/digitalmarketplace-utils/pull/266)
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..cb825994
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+SHELL := /bin/bash
+VIRTUALENV_ROOT := $(shell [ -z $$VIRTUAL_ENV ] && echo $$(pwd)/venv || echo $$VIRTUAL_ENV)
+
+virtualenv:
+ [ -z $$VIRTUAL_ENV ] && [ ! -d venv ] && virtualenv venv || true
+
+requirements_for_test: virtualenv requirements_for_test.txt
+ ${VIRTUALENV_ROOT}/bin/pip install -r requirements_for_test.txt
+
+test: show_environment test_pep8 test_python
+
+test_pep8: virtualenv
+ ${VIRTUALENV_ROOT}/bin/pep8 .
+
+test_python: virtualenv
+ ${VIRTUALENV_ROOT}/bin/py.test ${PYTEST_ARGS}
+
+show_environment:
+ @echo "Environment variables in use:"
+ @env | grep DM_ || true
+
+.PHONY: virtualenv requirements_for_test test_pep8 test_python show_environment
diff --git a/dmutils/__init__.py b/dmutils/__init__.py
index d961b51d..299ea468 100644
--- a/dmutils/__init__.py
+++ b/dmutils/__init__.py
@@ -3,4 +3,4 @@
import flask_featureflags
-__version__ = '21.11.0'
+__version__ = '22.0.0'
diff --git a/dmutils/filters.py b/dmutils/filters.py
index 71759c2c..88cdb4d1 100644
--- a/dmutils/filters.py
+++ b/dmutils/filters.py
@@ -1,13 +1,8 @@
from __future__ import unicode_literals
import re
-from markdown import markdown
from flask import Markup
-def markdown_filter(text, *args, **kwargs):
- return markdown(text, ['markdown.extensions.abbr'], *args, **kwargs)
-
-
def smartjoin(input):
list_to_join = list(input)
if len(list_to_join) > 1:
diff --git a/dmutils/flask_init.py b/dmutils/flask_init.py
index 4c1e266e..83c1f823 100644
--- a/dmutils/flask_init.py
+++ b/dmutils/flask_init.py
@@ -1,7 +1,6 @@
import os
from flask_featureflags.contrib.inline import InlineFeatureFlag
from . import config, logging, proxy_fix, request_id, formats, filters
-from flask import Markup
from flask.ext.script import Manager, Server
@@ -47,9 +46,6 @@ def add_header(response):
response.headers['X-Frame-Options'] = 'DENY'
return response
- @application.template_filter('markdown')
- def markdown_filter_flask(data):
- return Markup(filters.markdown_filter(data))
application.add_template_filter(filters.format_links)
application.add_template_filter(formats.timeformat)
application.add_template_filter(formats.shortdateformat)
diff --git a/requirements.txt b/requirements.txt
index 097daab1..986eaadc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,7 +10,6 @@ mandrill==1.0.57
monotonic==0.3
pytz==2015.4
Flask-WTF==0.12
-markdown==2.6.2
Flask-Script==2.0.5
workdays==1.4
unicodecsv==0.14.1
diff --git a/tests/test_filters.py b/tests/test_filters.py
index b6950fc1..3fb3c1ff 100644
--- a/tests/test_filters.py
+++ b/tests/test_filters.py
@@ -1,35 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from dmutils.filters import markdown_filter, smartjoin, format_links
-
-
-def test_markdown_filter_produces_markup():
-
- markdown_string = """## H2 title
-
-- List item 1
-- List item 2
-
-Paragraph
-**Bold**
-*Emphasis*
-
-HTML is an abbreviation.
-
-*[HTML]: Hyper Text Markup Language
-"""
-
- html_string = """H2 title
-
-- List item 1
-- List item 2
-
-Paragraph
-Bold
-Emphasis
-HTML is an abbreviation.
"""
-
- assert markdown_filter(markdown_string) == html_string
+from dmutils.filters import smartjoin, format_links
def test_smartjoin_for_more_than_one_item():