[1.11.x] Fixed CVE-2020-9402 -- Properly escaped tolerance parameter in GIS functions and aggregates on Oracle.
Thanks to Norbert Szetei for the report.
This commit is contained in:
parent
e643833562
commit
02d97f3c9a
@ -1,4 +1,5 @@
|
||||
from django.contrib.gis.db.models.fields import ExtentField
|
||||
from django.db.models import Value
|
||||
from django.db.models.aggregates import Aggregate
|
||||
|
||||
__all__ = ['Collect', 'Extent', 'Extent3D', 'MakeLine', 'Union']
|
||||
@ -16,11 +17,14 @@ class GeoAggregate(Aggregate):
|
||||
return super(GeoAggregate, self).as_sql(compiler, connection)
|
||||
|
||||
def as_oracle(self, compiler, connection):
|
||||
if not hasattr(self, 'tolerance'):
|
||||
self.tolerance = 0.05
|
||||
self.extra['tolerance'] = self.tolerance
|
||||
if not self.is_extent:
|
||||
self.template = '%(function)s(SDOAGGRTYPE(%(expressions)s,%(tolerance)s))'
|
||||
tolerance = self.extra.get('tolerance') or getattr(self, 'tolerance', 0.05)
|
||||
clone = self.copy()
|
||||
expressions = clone.get_source_expressions()
|
||||
expressions.append(Value(tolerance))
|
||||
clone.set_source_expressions(expressions)
|
||||
clone.template = '%(function)s(SDOAGGRTYPE(%(expressions)s))'
|
||||
return clone.as_sql(compiler, connection)
|
||||
return self.as_sql(compiler, connection)
|
||||
|
||||
def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
|
||||
|
@ -117,7 +117,11 @@ class OracleToleranceMixin(object):
|
||||
tolerance = 0.05
|
||||
|
||||
def as_oracle(self, compiler, connection):
|
||||
tol = self.extra.get('tolerance', self.tolerance)
|
||||
tol = self._handle_param(
|
||||
self.extra.get('tolerance', self.tolerance),
|
||||
'tolerance',
|
||||
NUMERIC_TYPES,
|
||||
)
|
||||
self.template = "%%(function)s(%%(expressions)s, %s)" % tol
|
||||
return super(OracleToleranceMixin, self).as_sql(compiler, connection)
|
||||
|
||||
|
13
docs/releases/1.11.29.txt
Normal file
13
docs/releases/1.11.29.txt
Normal file
@ -0,0 +1,13 @@
|
||||
============================
|
||||
Django 1.11.29 release notes
|
||||
============================
|
||||
|
||||
*March 4, 2020*
|
||||
|
||||
Django 1.11.29 fixes a security issue in 1.11.29.
|
||||
|
||||
CVE-2020-9402: Potential SQL injection via ``tolerance`` parameter in GIS functions and aggregates on Oracle
|
||||
============================================================================================================
|
||||
|
||||
GIS functions and aggregates on Oracle were subject to SQL injection,
|
||||
using a suitably crafted ``tolerance``.
|
@ -26,6 +26,7 @@ versions of the documentation contain the release notes for any later releases.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
1.11.29
|
||||
1.11.28
|
||||
1.11.27
|
||||
1.11.26
|
||||
|
@ -1,6 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from unittest import skipIf
|
||||
from unittest import skipIf, skipUnless
|
||||
|
||||
from django.contrib.gis.db.models.functions import (
|
||||
Area, Distance, Length, Perimeter, Transform,
|
||||
@ -588,6 +588,37 @@ class DistanceFunctionsTests(TestCase):
|
||||
for i, c in enumerate(qs):
|
||||
self.assertAlmostEqual(sphere_distances[i], c.distance.m, tol)
|
||||
|
||||
@skipUnless(
|
||||
connection.vendor == 'oracle',
|
||||
'Oracle supports tolerance paremeter.',
|
||||
)
|
||||
def test_distance_function_tolerance_escaping(self):
|
||||
qs = AustraliaCity.objects.annotate(
|
||||
d=Distance(
|
||||
'point',
|
||||
Point(0, 0, srid=3857),
|
||||
tolerance='0.05) = 1 OR 1=1 OR (1+1',
|
||||
),
|
||||
).filter(d=1).values('pk')
|
||||
msg = 'The tolerance parameter has the wrong type'
|
||||
with self.assertRaisesMessage(TypeError, msg):
|
||||
qs.exists()
|
||||
|
||||
@skipUnless(
|
||||
connection.vendor == 'oracle',
|
||||
'Oracle supports tolerance paremeter.',
|
||||
)
|
||||
def test_distance_function_tolerance(self):
|
||||
# Tolerance is greater than distance.
|
||||
qs = AustraliaCity.objects.annotate(
|
||||
d=Distance(
|
||||
'point',
|
||||
Point(151.23, -33.95, srid=4326),
|
||||
tolerance=340.7,
|
||||
),
|
||||
).filter(d=0).values('pk')
|
||||
self.assertIs(qs.exists(), True)
|
||||
|
||||
@no_oracle # Oracle already handles geographic distance calculation.
|
||||
@skipUnlessDBFeature("has_Distance_function", 'has_Transform_function')
|
||||
def test_distance_transform(self):
|
||||
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from django.contrib.gis import gdal
|
||||
from django.contrib.gis.db.models import Extent, MakeLine, Union
|
||||
@ -10,7 +11,7 @@ from django.contrib.gis.geos import (
|
||||
MultiPoint, MultiPolygon, Point, Polygon, fromstr,
|
||||
)
|
||||
from django.core.management import call_command
|
||||
from django.db import connection
|
||||
from django.db import DatabaseError, connection
|
||||
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
|
||||
from django.utils import six
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
@ -881,6 +882,42 @@ class GeoQuerySetTest(TestCase):
|
||||
qs = City.objects.filter(name='NotACity')
|
||||
self.assertIsNone(qs.aggregate(Union('point'))['point__union'])
|
||||
|
||||
@unittest.skipUnless(
|
||||
connection.vendor == 'oracle',
|
||||
'Oracle supports tolerance paremeter.',
|
||||
)
|
||||
def test_unionagg_tolerance(self):
|
||||
City.objects.create(
|
||||
point=fromstr('POINT(-96.467222 32.751389)', srid=4326),
|
||||
name='Forney',
|
||||
)
|
||||
tx = Country.objects.get(name='Texas').mpoly
|
||||
# Tolerance is greater than distance between Forney and Dallas, that's
|
||||
# why Dallas is ignored.
|
||||
forney_houston = GEOSGeometry(
|
||||
'MULTIPOINT(-95.363151 29.763374, -96.467222 32.751389)',
|
||||
srid=4326,
|
||||
)
|
||||
self.assertIs(
|
||||
forney_houston.equals(
|
||||
City.objects.filter(point__within=tx).aggregate(
|
||||
Union('point', tolerance=32000),
|
||||
)['point__union'],
|
||||
),
|
||||
True,
|
||||
)
|
||||
|
||||
@unittest.skipUnless(
|
||||
connection.vendor == 'oracle',
|
||||
'Oracle supports tolerance paremeter.',
|
||||
)
|
||||
def test_unionagg_tolerance_escaping(self):
|
||||
tx = Country.objects.get(name='Texas').mpoly
|
||||
with self.assertRaises(DatabaseError):
|
||||
City.objects.filter(point__within=tx).aggregate(
|
||||
Union('point', tolerance='0.05))), (((1'),
|
||||
)
|
||||
|
||||
def test_within_subquery(self):
|
||||
"""
|
||||
Using a queryset inside a geo lookup is working (using a subquery)
|
||||
|
Loading…
x
Reference in New Issue
Block a user