diff --git a/CHANGELOG.md b/CHANGELOG.md
index d30ccf5..a5fc7d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
* Removed `type` and `info` fields from `Message` table and merged `privacy` field, previously into a separate table, into that table. In order to make the app work, when upgrading you should run the `migrate_0_4_to_0_5.py` script.
* Added flask-login dependency. Now, user logins can be persistent up to 365 days.
+* Rewritten `enrich` filter, correcting a serious security flaw. The new filter uses a tokenizer and escapes all non-markup text. Plus, now the `+` of the mention is visible, but weakened; newlines are now visible in the message.
## 0.4.0
diff --git a/app.py b/app.py
index 855254c..ffc2625 100644
--- a/app.py
+++ b/app.py
@@ -3,7 +3,7 @@ from flask import (
send_from_directory, session, url_for)
import hashlib
from peewee import *
-import datetime, time, re, os, sys, string, json
+import datetime, time, re, os, sys, string, json, html
from functools import wraps
import argparse
from flask_login import LoginManager, login_user, logout_user, login_required
@@ -566,10 +566,44 @@ def username_availability(username):
is_available = False
return jsonify({'is_valid':is_valid, 'is_available':is_available, 'status':'ok'})
+_enrich_symbols = [
+ (r'\n', 'NEWLINE'),
+ (r'https?://(?:[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*|\[[A-Fa-f0-9:]+\])'
+ r'(?::\d+)?(?:/.*)?(?:\?.*)?(?:#.*)?', 'URL'),
+ (_mention_re, 'MENTION'),
+ (r'[^\n+]+', 'TEXT'),
+ (r'.', 'TEXT')
+]
+
+def _tokenize(characters, table):
+ pos = 0
+ tokens = []
+ while pos < len(characters):
+ mo = None
+ for pattern, tag in table:
+ mo = re.compile(pattern).match(characters, pos)
+ if mo:
+ if tag:
+ text = mo.group(0)
+ tokens.append((text, tag))
+ break
+ pos = mo.end(0)
+ return tokens
+
@app.template_filter()
def enrich(s):
- '''Filter for mentioning users.'''
- return Markup(re.sub(_mention_re, r'\1', s))
+ tokens = _tokenize(s, _enrich_symbols)
+ r = []
+ for text, tag in tokens:
+ if tag == 'TEXT':
+ r.append(html.escape(text))
+ elif tag == 'URL':
+ r.append('{0}'.format(html.escape(text)))
+ elif tag == 'MENTION':
+ r.append('+{1}'.format(text, text.lstrip('+')))
+ elif tag == 'NEWLINE':
+ r.append('
')
+ return Markup(''.join(r))
@app.template_filter('is_following')
def is_following(from_user, to_user):
diff --git a/static/style.css b/static/style.css
index ca2d8b8..5db922b 100644
--- a/static/style.css
+++ b/static/style.css
@@ -8,6 +8,7 @@ body{margin:0}
.metanav{float:right}
.header h1{margin:0;display:inline-block}
.flash{background-color:#ff9;border:yellow 1px solid}
+.weak{opacity:.5}
.message-visual img{max-width:100%;max-height:8em}
.create_text{width:100%;height:8em}
.follow_button,input[type="submit"]{background-color:#ff3018;color:white;border-radius:3px;border:1px solid #ff3018}