From 53c2b953db579e43821f040f44cdd55f4aefa4e4 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 12 Jan 2017 18:42:01 +0100 Subject: [PATCH] add a binding for `hoedown_escape_html()` We need an escape function to implement XSS protection. --- build_ffi.py | 7 +++++++ docs/index.rst | 3 +++ misaka/api.py | 27 +++++++++++++++++++++++++++ tests/test_xss_protection.py | 12 ++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 tests/test_xss_protection.py diff --git a/build_ffi.py b/build_ffi.py index f5b5bd0..89a0d62 100644 --- a/build_ffi.py +++ b/build_ffi.py @@ -55,6 +55,7 @@ ffi.set_source( """\ #include "hoedown/buffer.h" #include "hoedown/document.h" +#include "hoedown/escape.h" #include "hoedown/html.h" #include "extra.h" """, @@ -255,6 +256,12 @@ hoedown_document *hoedown_document_new( void hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size); void hoedown_document_free(hoedown_document *doc); +// ------------------------ +// --- hoedown/escape.h --- +// ------------------------ + +void hoedown_escape_html(hoedown_buffer *ob, const uint8_t *data, size_t size, int secure); + // ---------------------- // --- hoedown/html.h --- // ---------------------- diff --git a/docs/index.rst b/docs/index.rst index c5b9543..b6fd302 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -214,6 +214,9 @@ Functions .. autofunction:: smartypants +.. autofunction:: escape_html + + Classes ^^^^^^^ diff --git a/misaka/api.py b/misaka/api.py index 2d57225..5a08121 100644 --- a/misaka/api.py +++ b/misaka/api.py @@ -8,6 +8,7 @@ from .utils import extension_map, html_flag_map, args_to_int, \ __all__ = [ + 'escape_html', 'html', 'smartypants', 'Markdown', @@ -58,6 +59,32 @@ OUNIT = 64 MAX_NESTING = 16 +def escape_html(text, escape_slash=False): + """ + Binding for Hoedown's HTML escaping function. + + The implementation is inspired by the OWASP XSS Prevention recommendations: + + .. code-block:: none + + & --> & + < --> < + > --> > + " --> " + ' --> ' + / --> / when escape_slash is set to True + + """ + byte_str = text.encode('utf-8') + ob = lib.hoedown_buffer_new(OUNIT) + lib.hoedown_escape_html(ob, byte_str, len(byte_str), int(escape_slash)) + + try: + return to_string(ob) + finally: + lib.hoedown_buffer_free(ob) + + def html(text, extensions=0, render_flags=0): """ Convert markdown text to HTML. diff --git a/tests/test_xss_protection.py b/tests/test_xss_protection.py new file mode 100644 index 0000000..e329c62 --- /dev/null +++ b/tests/test_xss_protection.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from chibitest import TestCase, ok +from misaka import escape_html + + +class EscapeHtmlTest(TestCase): + def test_escape_html(self): + ok(escape_html('a&<>"\'/')) == 'a&<>"'/' + + def test_escape_html_slash(self): + ok(escape_html('a&<>"\'/', True)) == 'a&<>"'/'