Markdown in Django XSS safe - python

I am using Markdown in an app to display a user biography. I want the user to be able to slightly format the biography, so I'm letting them use the TinyMCE editor.
Then, displaying it in the Django Template like this
{% load markup %}
<div id="biography">
{{ biography|markdown }}
</div>
The problem is, if there is a tag in the biography, it is not being escaped as django does everywhere else. This is the source output from a biography test:
<p><strong>asdfsdafsadf</strong></p>
<p><strong>sd<em>fdfdsfsd</em></strong><em>sdfsdfsdfdsf</em>sdfsdfsdf</p>
<p><strong>sdafasdfasdf</strong></p>
<script>document.location='http://test.com'</script>
How do I set Markdown to escape these malicious scripts?

According to django.contrib.markup.templatetags.markup.markdown's docstrings:
To enable safe mode, which strips raw HTML and only returns HTML
generated by actual Markdown syntax, pass "safe" as the first
extension in the list.
This should work:
{{ biography|markdown:"safe" }}

Markdown in safe mode would remove all html tags, which means your users cannot input HTML segments in the biography. In some cases, this is not preferable. I would recommend you use force_escape before markdown, so anything fed into markdown is safe.
For example, if your biography is <html>I'm really a HTML fan!</html>, using
{{ biography|markdown:"safe"}}
would produce HTML REMOVED.. Instead, if you use
{{ biography|force_escape|markdown }}
The output would be something like
<p><html>I'm really a HTML fan!</html&gt</p>

Related

How to convert markdown to html in python without <p> tag

I'm using the Todoist API in Python to pull a list of tasks which allow basic markdown formatting (**bold**, _italics_, [link](url), etc.) in the task names. I'm using this code:
for i in tasklist:
print("<div class='taskgroup'>")
if not i.parent_id:
print(f"<div class='parent priority{str(i.priority)}'>")
else:
print(f"<div class='child priority{str(i.priority)}'>")
print(f"<span class='content'>{i.content}</span>")
if i.due:
print(f"<span class='duedate {dueday(i.due)}'>{dueday(i.due)}</span>")
print("</div></div>")
which generates this HTML:
<div class='taskgroup'>
<div class='parent priority1'>
<span class='content'>**Todo** _list_ item</span>
<span class='duedate Tomorrow'>Tomorrow</span>
</div>
</div>
I'm trying to find the easiest way to convert the markdown formatting to HTML so that first span looks like:
<span class='content'><strong>Todo</strong> <em>list</em> item</span>
But every markdown library I've come across adds <p> tags around anything it converts, so:
import markdown
print(f"<span class='content'>{markdown.markdown(i.content)}</span>")
generates:
<span class='content'><p><strong>Todo</strong> <em>list</em> item</p></span>
which is not what I want. I understand that you're not "supposed" to have markdown within block elements, and most markdown libraries are designed to handle entire documents that are expected to be markdown compliant. But I wouldn't think that short markdown strings like this are that rare of a use case, so there must be some library that can do this, right?
I do realize that I can modify my CSS to account for the additional <p>, or I could use regex to remove the <p> tags after generating them, but those are bodges and I'd like to have cleaner code if the option exists.

How do I avoid Django's `|linebreaksbr` and Bootstrap style conflict?

I'm trying to store newline character inside model's CharField but it's not rendering correctly. <br/> and <br> both display as plain text, while |linebreaksbr is destroing bootstrap's styles in that text.
3 of my attempts to make it work:
<p>{{ greetings.title_2 }}</p>
<p><pre>{{ greetings.title_2 }}</pre></p>
<p>{{ greetings.title_2 | linebreaksbr }}</p>
And the result looks like this:
I want both bootstrap's style (like in first line) and new line stored inside variable. There is a way to do that by deviding one model field in two and adding <br/> tag into html file, but I wonder if there is better and simpler way for that. Any ideas?

Jinja2 highlight

I have installed jinja highlight in a small flask app. The highlighting works great when used directly in templates, but when I call it from the database and try to apply it on an entry, it does not render - it gives me the raw output from the db.
The code from the example:
{% highlight 'python' %}
from fridge import Beer
glass = Beer(lt=500)
glass.drink()
{% endhighlight %}
works in the template, but when I try to write and save an entry with the same content, the output is exactly the same, no formatting. I have tried {{ entry }}, {{entry|safe }} and the result is the same.
What am I doing wrong? While this is a particular jinja2 extension, I suppose the problem is more general.

Jinja 2 safe keyword

I have a little problem understanding what an expression like {{ something.render() | safe }} does .
From what I have seen, without the safe keyword it outputs the entire html document, not just the true content.
What I would like to know, is what it actually does, how it functions .
The safe filter explicitly marks a string as "safe", i.e., it should not be automatically-escaped if auto-escaping is enabled.
The documentation on this filter is here.
See the section on manual escaping to see which characters qualify for escaping.
Normally text is HTML-escaped (so <b> would be written out as <b>, which would render as <b>).
When you put |safe after something, you're telling the template engine that you have already escaped the text yourself, i.e. "it's safe to render this directly". So it will not do that encoding for you.
For more information: http://jinja.pocoo.org/docs/templates/#html-escaping
For anyone coming here looking to use the safe filter programmatically: wrap it in a markupsafe.Markup class, on which Jinja2 depends on.
Expanding on #data's answer, here's an example of using markupsafe.Markup:
import markupsafe
vals = {}
vals["name"] = markupsafe.Markup("<b>Duck</b>, Donald")
html = template.render(vals)
The resulting HTML will show Donald's last name in bold wherever the template contains {{name}}.
You can go this way
post.body is the variable that is getting the data from the database or any file.
{{ post.body | safe }}
I hope you will get it.

rendering of textfield and charfield chomps out extra whitespace (Django/Python)

I've noticed that my template is rendering my model.CharField and model.TextField without any excess whitespace.
For example, if I enter data such as...
This is a test
to see what happens.
The rendered object field will appear as...
This is a test to see what happens.
Is this an intentional feature of Django or have I missed some filter or parameter somewhere?
I've checked the field itself with some debug code (print object.field) and it does contains the extra whitespace, so the problem is in the rendering side.
How can I allow the user to enter paragraphs of data in TextFields? How can I preserve the whitespace that the user may have entered?
As you can see even in StackOverflow your spaces do not display, this is from the source of your question:
This is a test
to see what happens.
Will save in the database as:
This is a test\n\n\nto see what happens.
You have to problems when rendering as html:
Extra spaces between words are stripped on display by the browser, unless it is between <pre></pre> tags
Linebreaks will be rendered as plain text linebreaks, which do not display in the browser unless between <pre></pre> tags.
For spaces, you can use such a template filter to replace them with their html entity equivalent: .
To convert database linebreaks in HTML linebreaks, use linebreaksbr built-in filters. For example, if {{ foo }} is: test\nbar, then {{ foo|linebreaksbr }} will render: test<br />bar
Create a "templatetags" folder in some of your apps with an __init__.py file in it.
Save the snippet for example in someapp/templatetags/replace_tag.py
Load the template filter in the template as such {% load replace_tag %}
Combine replace and linebreaksbr as such: {{ foo|linebreaksbr|replace:" "," " }}
You can also make your own template filter that will process the text into the HTML you need. In any case, refer to the custom template filter documentation for complete information.

Categories