Improve markdown escaping

Fixes #118
This commit is contained in:
rubenwardy 2020-01-22 22:10:02 +00:00
parent 2b66193969
commit 981ae74e5c
6 changed files with 74 additions and 7 deletions

@ -20,7 +20,6 @@ from flask_user import *
from flask_gravatar import Gravatar from flask_gravatar import Gravatar
import flask_menu as menu import flask_menu as menu
from flask_mail import Mail from flask_mail import Mail
from flaskext.markdown import Markdown
from flask_github import GitHub from flask_github import GitHub
from flask_wtf.csrf import CsrfProtect from flask_wtf.csrf import CsrfProtect
from flask_flatpages import FlatPages from flask_flatpages import FlatPages
@ -35,7 +34,6 @@ app.config.from_pyfile(os.environ["FLASK_CONFIG"])
r = redis.Redis.from_url(app.config["REDIS_URL"]) r = redis.Redis.from_url(app.config["REDIS_URL"])
menu.Menu(app=app) menu.Menu(app=app)
markdown = Markdown(app, extensions=["fenced_code"], safe_mode=True, output_format="html5")
github = GitHub(app) github = GitHub(app)
csrf = CsrfProtect(app) csrf = CsrfProtect(app)
mail = Mail(app) mail = Mail(app)
@ -59,6 +57,10 @@ if not app.debug and app.config["MAIL_UTILS_ERROR_SEND_TO"]:
register_mail_error_handler(app, mail) register_mail_error_handler(app, mail)
from .markdown import init_app
init_app(app)
@babel.localeselector @babel.localeselector
def get_locale(): def get_locale():
return request.accept_languages.best_match(app.config['LANGUAGES'].keys()) return request.accept_languages.best_match(app.config['LANGUAGES'].keys())

@ -18,7 +18,7 @@
from flask import * from flask import *
from flask_user import * from flask_user import *
from flask_login import login_user, logout_user from flask_login import login_user, logout_user
from app import markdown from app.markdown import render_markdown
from . import bp from . import bp
from app.models import * from app.models import *
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
@ -153,7 +153,7 @@ def send_email(username):
form = SendEmailForm(request.form) form = SendEmailForm(request.form)
if form.validate_on_submit(): if form.validate_on_submit():
text = form.text.data text = form.text.data
html = markdown(text) html = render_markdown(text)
task = sendEmailRaw.delay([user.email], form.subject.data, text, html) task = sendEmailRaw.delay([user.email], form.subject.data, text, html)
return redirect(url_for("tasks.check", id=task.id, r=next_url)) return redirect(url_for("tasks.check", id=task.id, r=next_url))

63
app/markdown.py Normal file

@ -0,0 +1,63 @@
import bleach
from markdown import Markdown
from flask import Markup
# Whitelist source: MIT
#
# https://github.com/Wenzil/mdx_bleach/blob/master/mdx_bleach/whitelist.py
"""
Default whitelist of allowed HTML tags. Any other HTML tags will be escaped or
stripped from the text. This applies to the html output that Markdown produces.
"""
ALLOWED_TAGS = [
'ul',
'ol',
'li',
'p',
'pre',
'code',
'blockquote',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'hr',
'br',
'strong',
'em',
'a',
'img'
]
"""
Default whitelist of attributes. It allows the href and title attributes for <a>
tags and the src, title and alt attributes for <img> tags. Any other attribute
will be stripped from its tag.
"""
ALLOWED_ATTRIBUTES = {
'a': ['href', 'title'],
'img': ['src', 'title', 'alt']
}
"""
If you allow tags that have attributes containing a URI value
(like the href attribute of an anchor tag,) you may want to adapt
the accepted protocols. The default list only allows http, https and mailto.
"""
ALLOWED_PROTOCOLS = ['http', 'https', 'mailto']
md = Markdown(extensions=["fenced_code"], output_format="html5")
def render_markdown(source):
return bleach.clean(md.convert(source), \
tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES, \
styles=[], protocols=ALLOWED_PROTOCOLS)
def init_app(app):
@app.template_filter()
def markdown(source):
return Markup(render_markdown(source))

@ -364,7 +364,7 @@
</ul> </ul>
</div> </div>
<div class="card my-4""> <div class="card my-4">
<div class="card-header"> <div class="card-header">
{% if package.approved and package.checkPerm(current_user, "CREATE_THREAD") %} {% if package.approved and package.checkPerm(current_user, "CREATE_THREAD") %}
<div class="btn-group float-right"> <div class="btn-group float-right">

@ -60,7 +60,7 @@ Topics to be Added
{% set perc = 100 * (total - topic_count) / total %} {% set perc = 100 * (total - topic_count) / total %}
<div class="progress-bar bg-success" role="progressbar" <div class="progress-bar bg-success" role="progressbar"
style="width: {{ perc }}%" aria-valuenow="{{ perc }}" aria-valuemin="0" aria-valuemax="100"></div> style="width: {{ perc }}%" aria-valuenow="{{ perc }}" aria-valuemin="0" aria-valuemax="100"></div>
</div> </div>
{% else %} {% else %}
<p> <p>
The forum topic crawler needs to run at least once for this section to work. The forum topic crawler needs to run at least once for this section to work.

@ -2,7 +2,6 @@ Flask~=1.1
Flask-FlatPages~=0.7 Flask-FlatPages~=0.7
Flask-Gravatar~=0.5 Flask-Gravatar~=0.5
Flask-Login~=0.4.1 Flask-Login~=0.4.1
Flask-Markdown~=0.3
Flask-Menu~=0.7 Flask-Menu~=0.7
Flask-Migrate~=2.3 Flask-Migrate~=2.3
Flask-SQLAlchemy~=2.3 Flask-SQLAlchemy~=2.3
@ -11,6 +10,9 @@ Flask-Babel
GitHub-Flask~=3.2 GitHub-Flask~=3.2
SQLAlchemy-Searchable~=1.1 SQLAlchemy-Searchable~=1.1
markdown ~= 3.1
bleach ~= 3.1
beautifulsoup4~=4.6 beautifulsoup4~=4.6
celery~=4.4 celery~=4.4
kombu~=4.6 kombu~=4.6