Skip to content

Commit

Permalink
Merge pull request #46 from zopefoundation/plonehotfix2020
Browse files Browse the repository at this point in the history
Escape more characters in sql_quote. [master]
  • Loading branch information
dataflake committed Jan 31, 2020
2 parents 23593fb + bcbb484 commit d0f06bc
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -24,3 +24,4 @@ default.profraw
develop-eggs/
eggs/
pip-selfcheck.json
pyvenv.cfg
5 changes: 5 additions & 0 deletions CHANGES.rst
Expand Up @@ -4,6 +4,11 @@ Changelog
3.1 (unreleased)
----------------

Bug fixes
+++++++++

- Escape more characters in ``sql_quote``. Taken over from PloneHotfix20200121.


3.1b2 (2019-05-16)
------------------
Expand Down
44 changes: 41 additions & 3 deletions src/DocumentTemplate/DT_Var.py
Expand Up @@ -512,15 +512,53 @@ def structured_text(v, name='(Unknown name)', md={}):
return HTML()(doc, level, header=False)


# Searching and replacing a byte in text, or text in bytes,
# may give various errors on Python 2 or 3. So we make separate functions
REMOVE_BYTES = (b'\x00', b'\x1a', b'\r')
REMOVE_TEXT = (u'\x00', u'\x1a', u'\r')
DOUBLE_BYTES = (b"'", b'\\')
DOUBLE_TEXT = (u"'", u'\\')
ESCAPE_BYTES = (b'"',)
ESCAPE_TEXT = (u'"',)


def bytes_sql_quote(v):
# Helper function for sql_quote, handling only bytes.
# Remove bad characters.
for char in REMOVE_BYTES:
v = v.replace(char, b'')
# Double untrusted characters to make them harmless.
for char in DOUBLE_BYTES:
v = v.replace(char, char * 2)
# Backslash-escape untrusted characters to make them harmless.
for char in ESCAPE_BYTES:
v = v.replace(char, b'\\%s' % char)
return v


def text_sql_quote(v):
# Helper function for sql_quote, handling only text.
# Remove bad characters.
for char in REMOVE_TEXT:
v = v.replace(char, u'')
# Double untrusted characters to make them harmless.
for char in DOUBLE_TEXT:
v = v.replace(char, char * 2)
# Backslash-escape untrusted characters to make them harmless.
for char in ESCAPE_TEXT:
v = v.replace(char, u'\\%s' % char)
return v


def sql_quote(v, name='(Unknown name)', md={}):
"""Quote single quotes in a string by doubling them.
This is needed to securely insert values into sql
string literals in templates that generate sql.
"""
if v.find("'") >= 0:
return v.replace("'", "''")
return v
if isinstance(v, bytes):
return bytes_sql_quote(v)
return text_sql_quote(v)


special_formats = {
Expand Down
89 changes: 89 additions & 0 deletions src/DocumentTemplate/tests/test_DT_Var.py
Expand Up @@ -94,3 +94,92 @@ def test_url_quoting_plus(self):
url_unquote_plus(quoted_unicode_value), unicode_value)
self.assertEqual(
url_unquote_plus(quoted_utf8_value), utf8_value)

def test_bytes_sql_quote(self):
from DocumentTemplate.DT_Var import bytes_sql_quote
self.assertEqual(bytes_sql_quote(b""), b"")
self.assertEqual(bytes_sql_quote(b"a"), b"a")

self.assertEqual(bytes_sql_quote(b"Can't"), b"Can''t")
self.assertEqual(bytes_sql_quote(b"Can\'t"), b"Can''t")
self.assertEqual(bytes_sql_quote(br"Can\'t"), b"Can\\\\''t")

self.assertEqual(bytes_sql_quote(b"Can\\ I?"), b"Can\\\\ I?")
self.assertEqual(bytes_sql_quote(br"Can\ I?"), b"Can\\\\ I?")

self.assertEqual(
bytes_sql_quote(b'Just say "Hello"'), b'Just say \\"Hello\\"')

self.assertEqual(
bytes_sql_quote(b'Hello\x00World'), b'HelloWorld')
self.assertEqual(
bytes_sql_quote(b'\x00Hello\x00\x00World\x00'), b'HelloWorld')

self.assertEqual(
bytes_sql_quote(b"carriage\rreturn"), b"carriagereturn")
self.assertEqual(bytes_sql_quote(b"line\nbreak"), b"line\nbreak")
self.assertEqual(bytes_sql_quote(b"tab\t"), b"tab\t")

def test_text_sql_quote(self):
from DocumentTemplate.DT_Var import text_sql_quote
self.assertEqual(text_sql_quote(u""), u"")
self.assertEqual(text_sql_quote(u"a"), u"a")

self.assertEqual(text_sql_quote(u"Can't"), u"Can''t")
self.assertEqual(text_sql_quote(u"Can\'t"), u"Can''t")
# SyntaxError on Python 3.
# self.assertEqual(text_sql_quote(ur"Can\'t"), u"Can\\\\''t")

self.assertEqual(text_sql_quote(u"Can\\ I?"), u"Can\\\\ I?")
# SyntaxError on Python 3.
# self.assertEqual(text_sql_quote(ur"Can\ I?"), u"Can\\\\ I?")

self.assertEqual(
text_sql_quote(u'Just say "Hello"'), u'Just say \\"Hello\\"')

self.assertEqual(
text_sql_quote(u'Hello\x00World'), u'HelloWorld')
self.assertEqual(
text_sql_quote(u'\x00Hello\x00\x00World\x00'), u'HelloWorld')

self.assertEqual(
text_sql_quote(u"carriage\rreturn"), u"carriagereturn")
self.assertEqual(text_sql_quote(u"line\nbreak"), u"line\nbreak")
self.assertEqual(text_sql_quote(u"tab\t"), u"tab\t")

def test_sql_quote(self):
from DocumentTemplate.DT_Var import sql_quote
self.assertEqual(sql_quote(u""), u"")
self.assertEqual(sql_quote(u"a"), u"a")
self.assertEqual(sql_quote(b"a"), b"a")

self.assertEqual(sql_quote(u"Can't"), u"Can''t")
self.assertEqual(sql_quote(u"Can\'t"), u"Can''t")
# SyntaxError on Python 3.
# self.assertEqual(sql_quote(ur"Can\'t"), u"Can\\\\''t")

self.assertEqual(sql_quote(u"Can\\ I?"), u"Can\\\\ I?")
# SyntaxError on Python 3.
# self.assertEqual(sql_quote(ur"Can\ I?"), u"Can\\\\ I?")

self.assertEqual(
sql_quote(u'Just say "Hello"'), u'Just say \\"Hello\\"')

self.assertEqual(
sql_quote(u'Hello\x00World'), u'HelloWorld')
self.assertEqual(
sql_quote(u'\x00Hello\x00\x00World\x00'), u'HelloWorld')

self.assertEqual(
sql_quote(u'\x00Hello\x00\x00World\x00'), u'HelloWorld')

self.assertEqual(u"\xea".encode("utf-8"), b"\xc3\xaa")
self.assertEqual(sql_quote(u"\xea'"), u"\xea''")
self.assertEqual(sql_quote(b"\xc3\xaa'"), b"\xc3\xaa''")

self.assertEqual(
sql_quote(b"carriage\rreturn"), b"carriagereturn")
self.assertEqual(
sql_quote(u"carriage\rreturn"), u"carriagereturn")
self.assertEqual(sql_quote(u"line\nbreak"), u"line\nbreak")
self.assertEqual(sql_quote(u"tab\t"), u"tab\t")

0 comments on commit d0f06bc

Please sign in to comment.