diff --git a/requirements-dev.txt b/requirements-dev.txt index dfab816a..65230277 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,6 @@ asn1crypto==0.24.0 bcrypt==3.1.6 +bleach==5.0.0 boto3==1.14.10 celery==5.2.6 cffi==1.14.1 diff --git a/requirements.txt b/requirements.txt index 1581a00c..b0d75bc6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ asn1crypto==0.24.0 bcrypt==3.1.6 +bleach==5.0.0 boto3==1.14.10 celery==5.2.6 cffi==1.14.1 diff --git a/storymap/api.py b/storymap/api.py index 57afcc87..c3112128 100644 --- a/storymap/api.py +++ b/storymap/api.py @@ -20,6 +20,7 @@ from urllib.parse import urlparse, urljoin, quote, urlencode from flask_cors import cross_origin from zipfile import ZipFile +import bleach from .tasks import storymap_cleanup @@ -288,8 +289,6 @@ def google_auth_verify(): "following information to support@knightlab.zendesk.com: " \ "stg-storymap.knightlab.com unauthorized %s" % uid) - print('upsert user', uid) - # Upsert user record user = get_user(uid, db=db()) if user: @@ -768,6 +767,26 @@ def storymap_get(user, id): traceback.print_exc() return jsonify({'error': str(e)}) + +def clean_html(html): + # bleach.sanitizer.ALLOWED_TAGS: + # ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'strong', 'ul'] + allowed_tags = bleach.sanitizer.ALLOWED_TAGS + [ + "br", + "div", + "iframe", + "img", + "span" + ] + allowed_attrs = { + "*": ["class"], + "a": ["href", "rel"], + "iframe": ["src"], + "img": ["alt", "title"], + } + return bleach.clean(html, tags=allowed_tags, attributes=allowed_attrs) + + @app.route('/storymap/save/', methods=['POST']) @require_user @require_user_id() @@ -775,14 +794,24 @@ def storymap_save(user, id): """Save draft storymap""" try: data = _request_get_required('d') - key_name = storage.key_name(user['uid'], id, 'draft.json') content = json.loads(data) + slides = [] + for slide in content["storymap"]["slides"]: + _slide = copy.copy(slide) + for field in ["caption", "credit", "url"]: + _slide["media"][field] = clean_html(_slide["media"][field]) + for field in ["headline", "text"]: + _slide["text"][field] = clean_html(_slide["text"][field]) + slides.append(_slide) + content["storymap"]["slides"] = slides storage.save_json(key_name, content) - user['storymaps'][id]['draft_on'] = _utc_now() save_user(user, db=db()) - return jsonify({'meta': user['storymaps'][id]}) + return jsonify({ + "meta": user["storymaps"][id], + "data": content + }) except storage.StorageException as e: traceback.print_exc() app.logger.error(f"StorageException uid:{user['uid']} id:{id}") diff --git a/storymap/templates/edit.html b/storymap/templates/edit.html index 22ffc1e0..5cd0f788 100644 --- a/storymap/templates/edit.html +++ b/storymap/templates/edit.html @@ -450,6 +450,7 @@ }, function(data) { _storymap_meta = data.meta; // update meta + _storymap_data = data.data; // TODO: this does not update the rendered content in the editor storymap_dirty(0); }, function(error) {