escape syndication target before displaying
closes possible XSS vector fixes #51
This commit is contained in:
parent
8cb2124251
commit
93e751c058
4 changed files with 30 additions and 13 deletions
|
@ -1,7 +1,7 @@
|
||||||
import flask
|
import flask
|
||||||
import flask.ext.login as flask_login
|
import flask.ext.login as flask_login
|
||||||
import requests
|
import requests
|
||||||
|
from woodwind import util
|
||||||
|
|
||||||
api = flask.Blueprint('api', __name__)
|
api = flask.Blueprint('api', __name__)
|
||||||
|
|
||||||
|
@ -13,6 +13,9 @@ def publish():
|
||||||
content = flask.request.form.get('content')
|
content = flask.request.form.get('content')
|
||||||
syndicate_to = flask.request.form.getlist('syndicate-to[]')
|
syndicate_to = flask.request.form.getlist('syndicate-to[]')
|
||||||
|
|
||||||
|
if syndicate_to:
|
||||||
|
syndicate_to = [util.html_unescape(id) for id in syndicate_to]
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'h': 'entry',
|
'h': 'entry',
|
||||||
'syndicate-to[]': syndicate_to,
|
'syndicate-to[]': syndicate_to,
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
{% for target in current_user.get_setting('syndicate-to', []) %}
|
{% for target in current_user.get_setting('syndicate-to', []) %}
|
||||||
|
|
||||||
<div class="syndication-toggle">
|
<div class="syndication-toggle">
|
||||||
<input id="sc-{{entry.id}}-{{loop.index}}" type="checkbox" name="syndicate-to[]" value="{{ target | syndication_target_id }}"{% if entry is syndicated_to(target) %} checked{% endif %} />
|
<input id="sc-{{entry.id}}-{{loop.index}}" type="checkbox" name="syndicate-to[]" value="{{ target | render_syndication_target_id }}"{% if entry is syndicated_to(target) %} checked{% endif %} />
|
||||||
<label for="sc-{{entry.id}}-{{loop.index}}">{{ target | render_syndication_target }}</label>
|
<label for="sc-{{entry.id}}-{{loop.index}}">{{ target | render_syndication_target }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import pickle
|
import pickle
|
||||||
import re
|
import re
|
||||||
|
from xml.sax import saxutils
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from redis import StrictRedis
|
from redis import StrictRedis
|
||||||
|
@ -56,3 +57,13 @@ def clean(text):
|
||||||
if text is not None:
|
if text is not None:
|
||||||
text = re.sub('<script.*?</script>', '', text, flags=re.DOTALL)
|
text = re.sub('<script.*?</script>', '', text, flags=re.DOTALL)
|
||||||
return bleach.clean(text, strip=True)
|
return bleach.clean(text, strip=True)
|
||||||
|
|
||||||
|
|
||||||
|
def html_escape(text):
|
||||||
|
# https://wiki.python.org/moin/EscapingHtml
|
||||||
|
return saxutils.escape(text, {'"': '"', "'": '''})
|
||||||
|
|
||||||
|
|
||||||
|
def html_unescape(text):
|
||||||
|
# https://wiki.python.org/moin/EscapingHtml
|
||||||
|
return saxutils.unescape(text, {'"': '"', ''': "'"})
|
||||||
|
|
|
@ -16,7 +16,6 @@ import pyquerystring
|
||||||
import requests
|
import requests
|
||||||
import re
|
import re
|
||||||
import urllib
|
import urllib
|
||||||
import cgi
|
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
import sqlalchemy.sql.expression
|
import sqlalchemy.sql.expression
|
||||||
|
|
||||||
|
@ -257,11 +256,11 @@ def login():
|
||||||
@micropub.authenticated_handler
|
@micropub.authenticated_handler
|
||||||
def login_callback(resp):
|
def login_callback(resp):
|
||||||
if not resp.me:
|
if not resp.me:
|
||||||
flask.flash(cgi.escape('Login error: ' + resp.error))
|
flask.flash(util.html_escape('Login error: ' + resp.error))
|
||||||
return flask.redirect(flask.url_for('.index'))
|
return flask.redirect(flask.url_for('.index'))
|
||||||
|
|
||||||
if resp.error:
|
if resp.error:
|
||||||
flask.flash(cgi.escape('Warning: ' + resp.error))
|
flask.flash(util.html_escape('Warning: ' + resp.error))
|
||||||
|
|
||||||
user = load_user(resp.me)
|
user = load_user(resp.me)
|
||||||
if not user:
|
if not user:
|
||||||
|
@ -287,12 +286,12 @@ def authorize():
|
||||||
@micropub.authorized_handler
|
@micropub.authorized_handler
|
||||||
def micropub_callback(resp):
|
def micropub_callback(resp):
|
||||||
if not resp.me or resp.error:
|
if not resp.me or resp.error:
|
||||||
flask.flash(cgi.escape('Authorize error: ' + resp.error))
|
flask.flash(util.html_escape('Authorize error: ' + resp.error))
|
||||||
return flask.redirect(flask.url_for('.index'))
|
return flask.redirect(flask.url_for('.index'))
|
||||||
|
|
||||||
user = load_user(resp.me)
|
user = load_user(resp.me)
|
||||||
if not user:
|
if not user:
|
||||||
flask.flash(cgi.escape('Unknown user for url: ' + resp.me))
|
flask.flash(util.html_escape('Unknown user for url: ' + resp.me))
|
||||||
return flask.redirect(flask.url_for('.index'))
|
return flask.redirect(flask.url_for('.index'))
|
||||||
|
|
||||||
user.micropub_endpoint = resp.micropub_endpoint
|
user.micropub_endpoint = resp.micropub_endpoint
|
||||||
|
@ -764,21 +763,25 @@ def font_awesome_class_for_service(service):
|
||||||
return 'fa fa-send'
|
return 'fa fa-send'
|
||||||
|
|
||||||
|
|
||||||
@views.app_template_filter('syndication_target_id')
|
@views.app_template_filter('render_syndication_target_id')
|
||||||
def syndication_target_id(target):
|
def render_syndication_target_id(target):
|
||||||
if isinstance(target, dict):
|
if isinstance(target, dict):
|
||||||
return target.get('uid') or target.get('id')
|
id = target.get('uid') or target.get('id')
|
||||||
return target
|
else:
|
||||||
|
id = target
|
||||||
|
return util.html_escape(id)
|
||||||
|
|
||||||
|
|
||||||
@views.app_template_filter('render_syndication_target')
|
@views.app_template_filter('render_syndication_target')
|
||||||
def render_syndication_target(target):
|
def render_syndication_target(target):
|
||||||
if isinstance(target, dict):
|
if isinstance(target, dict):
|
||||||
full_name = target.get('name')
|
full_name = target.get('name')
|
||||||
return full_name
|
return util.html_escape(full_name)
|
||||||
|
|
||||||
return '<img src="{}" alt="{}" /> {}'.format(
|
return '<img src="{}" alt="{}" /> {}'.format(
|
||||||
favicon_for_url(target), target, prettify_url(target))
|
favicon_for_url(target),
|
||||||
|
util.html_escape(target),
|
||||||
|
util.html_escape(prettify_url(target)))
|
||||||
|
|
||||||
|
|
||||||
@views.app_template_test('syndicated_to')
|
@views.app_template_test('syndicated_to')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue