There are two reasonable approaches.
Solution 1
Since you combine unsafe input with HTML into a single flask.Markup variable, this is actually a pretty convenient way to do this. The basic idea is to split the text into newlines, make sure you select HTML from each line that you donβt trust, and then glue them together by adding the <br /> tags you trust.
Here's the full app to demonstrate this. It uses the same bar.html template as in your question. Please note that I added insecure HTML to footext as a demonstration of why disabling autoescaping is not a safe solution to your problem.
import flask app = flask.Flask(__name__) footext = """f o <script>alert('oops')</script> o""" @app.route("/foo") def foo(): text = "" for line in footext.split('\n'): text += flask.Markup.escape(line) + flask.Markup('<br />') return flask.render_template("bar.html", text=text) if __name__ == "__main__": app.run(debug=True)
Decision 2
Another option would be that you add complexity to your template, leaving you with a much simpler look. Just split the footext into lines, then you can footext over it in your template, and autoescaping will take care to keep it safe.
Simplified view:
@app.route("/foo") def foo(): return flask.render_template("bar.html", text=footext.split('\n'))
The bar.html template becomes:
<html> {%- for line in text -%} {{ line }} {%- if not loop.last -%} <br /> {%- endif -%} {%- endfor -%} </html>
Conclusion
I personally prefer solution 2 because it poses rendering problems (lines are separated by <br /> tags) in the template where they belong. If you ever want to change this in the future, say, show the lines in a bulleted list, you just need to change your template, not your code.
Day
source share