mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
Issue #28563: Fixed possible DoS and arbitrary code execution when handle
plural form selections in the gettext module. The expression parser now supports exact syntax supported by GNU gettext.
This commit is contained in:
commit
1c3fdd900d
3 changed files with 214 additions and 43 deletions
|
@ -236,7 +236,9 @@ class PluralFormsTestCase(GettextBaseTest):
|
|||
x = t.ngettext('There is %s file', 'There are %s files', 2)
|
||||
eq(x, 'Hay %s ficheros')
|
||||
|
||||
def test_hu(self):
|
||||
# Examples from http://www.gnu.org/software/gettext/manual/gettext.html
|
||||
|
||||
def test_ja(self):
|
||||
eq = self.assertEqual
|
||||
f = gettext.c2py('0')
|
||||
s = ''.join([ str(f(x)) for x in range(200) ])
|
||||
|
@ -254,6 +256,12 @@ class PluralFormsTestCase(GettextBaseTest):
|
|||
s = ''.join([ str(f(x)) for x in range(200) ])
|
||||
eq(s, "00111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")
|
||||
|
||||
def test_lv(self):
|
||||
eq = self.assertEqual
|
||||
f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2')
|
||||
s = ''.join([ str(f(x)) for x in range(200) ])
|
||||
eq(s, "20111111111111111111101111111110111111111011111111101111111110111111111011111111101111111110111111111011111111111111111110111111111011111111101111111110111111111011111111101111111110111111111011111111")
|
||||
|
||||
def test_gd(self):
|
||||
eq = self.assertEqual
|
||||
f = gettext.c2py('n==1 ? 0 : n==2 ? 1 : 2')
|
||||
|
@ -267,6 +275,12 @@ class PluralFormsTestCase(GettextBaseTest):
|
|||
s = ''.join([ str(f(x)) for x in range(200) ])
|
||||
eq(s, "20122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
|
||||
|
||||
def test_ro(self):
|
||||
eq = self.assertEqual
|
||||
f = gettext.c2py('n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2')
|
||||
s = ''.join([ str(f(x)) for x in range(200) ])
|
||||
eq(s, "10111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222")
|
||||
|
||||
def test_lt(self):
|
||||
eq = self.assertEqual
|
||||
f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2')
|
||||
|
@ -279,6 +293,12 @@ class PluralFormsTestCase(GettextBaseTest):
|
|||
s = ''.join([ str(f(x)) for x in range(200) ])
|
||||
eq(s, "20111222222222222222201112222220111222222011122222201112222220111222222011122222201112222220111222222011122222222222222220111222222011122222201112222220111222222011122222201112222220111222222011122222")
|
||||
|
||||
def test_cs(self):
|
||||
eq = self.assertEqual
|
||||
f = gettext.c2py('(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2')
|
||||
s = ''.join([ str(f(x)) for x in range(200) ])
|
||||
eq(s, "20111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
|
||||
|
||||
def test_pl(self):
|
||||
eq = self.assertEqual
|
||||
f = gettext.c2py('n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2')
|
||||
|
@ -291,10 +311,73 @@ class PluralFormsTestCase(GettextBaseTest):
|
|||
s = ''.join([ str(f(x)) for x in range(200) ])
|
||||
eq(s, "30122333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333012233333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333")
|
||||
|
||||
def test_ar(self):
|
||||
eq = self.assertEqual
|
||||
f = gettext.c2py('n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5')
|
||||
s = ''.join([ str(f(x)) for x in range(200) ])
|
||||
eq(s, "01233333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444445553333333344444444444444444444444444444444444444444444444444444444444444444444444444444444444444444")
|
||||
|
||||
def test_security(self):
|
||||
raises = self.assertRaises
|
||||
# Test for a dangerous expression
|
||||
raises(ValueError, gettext.c2py, "os.chmod('/etc/passwd',0777)")
|
||||
# issue28563
|
||||
raises(ValueError, gettext.c2py, '"(eval(foo) && ""')
|
||||
raises(ValueError, gettext.c2py, 'f"{os.system(\'sh\')}"')
|
||||
# Maximum recursion depth exceeded during compilation
|
||||
raises(ValueError, gettext.c2py, 'n+'*10000 + 'n')
|
||||
self.assertEqual(gettext.c2py('n+'*100 + 'n')(1), 101)
|
||||
# MemoryError during compilation
|
||||
raises(ValueError, gettext.c2py, '('*100 + 'n' + ')'*100)
|
||||
# Maximum recursion depth exceeded in C to Python translator
|
||||
raises(ValueError, gettext.c2py, '('*10000 + 'n' + ')'*10000)
|
||||
self.assertEqual(gettext.c2py('('*20 + 'n' + ')'*20)(1), 1)
|
||||
|
||||
def test_chained_comparison(self):
|
||||
# C doesn't chain comparison as Python so 2 == 2 == 2 gets different results
|
||||
f = gettext.c2py('n == n == n')
|
||||
self.assertEqual(''.join(str(f(x)) for x in range(3)), '010')
|
||||
f = gettext.c2py('1 < n == n')
|
||||
self.assertEqual(''.join(str(f(x)) for x in range(3)), '100')
|
||||
f = gettext.c2py('n == n < 2')
|
||||
self.assertEqual(''.join(str(f(x)) for x in range(3)), '010')
|
||||
f = gettext.c2py('0 < n < 2')
|
||||
self.assertEqual(''.join(str(f(x)) for x in range(3)), '111')
|
||||
|
||||
def test_decimal_number(self):
|
||||
self.assertEqual(gettext.c2py('0123')(1), 123)
|
||||
|
||||
def test_invalid_syntax(self):
|
||||
invalid_expressions = [
|
||||
'x>1', '(n>1', 'n>1)', '42**42**42', '0xa', '1.0', '1e2',
|
||||
'n>0x1', '+n', '-n', 'n()', 'n(1)', '1+', 'nn', 'n n',
|
||||
]
|
||||
for expr in invalid_expressions:
|
||||
with self.assertRaises(ValueError):
|
||||
gettext.c2py(expr)
|
||||
|
||||
def test_nested_condition_operator(self):
|
||||
self.assertEqual(gettext.c2py('n?1?2:3:4')(0), 4)
|
||||
self.assertEqual(gettext.c2py('n?1?2:3:4')(1), 2)
|
||||
self.assertEqual(gettext.c2py('n?1:3?4:5')(0), 4)
|
||||
self.assertEqual(gettext.c2py('n?1:3?4:5')(1), 1)
|
||||
|
||||
def test_division(self):
|
||||
f = gettext.c2py('2/n*3')
|
||||
self.assertEqual(f(1), 6)
|
||||
self.assertEqual(f(2), 3)
|
||||
self.assertEqual(f(3), 0)
|
||||
self.assertEqual(f(-1), -6)
|
||||
self.assertRaises(ZeroDivisionError, f, 0)
|
||||
|
||||
def test_plural_number(self):
|
||||
f = gettext.c2py('1')
|
||||
self.assertEqual(f(1), 1)
|
||||
self.assertRaises(ValueError, f, 1.0)
|
||||
self.assertRaises(ValueError, f, '1')
|
||||
self.assertRaises(ValueError, f, [])
|
||||
self.assertRaises(ValueError, f, object())
|
||||
|
||||
|
||||
class GNUTranslationParsingTest(GettextBaseTest):
|
||||
def test_plural_form_error_issue17898(self):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue