Skip to content

Commit b96765c

Browse files
authored
OGR: sanitize attribute filter values (#2391)
1 parent 8496628 commit b96765c

2 files changed

Lines changed: 54 additions & 2 deletions

File tree

pygeoapi/provider/ogr.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ def query(self, offset=0, limit=10, resulttype='results',
322322
LOGGER.debug('processing properties')
323323

324324
attribute_filter = ' and '.join(
325-
map(lambda x: f'{x[0]} = \'{x[1]}\'', properties)
325+
map(lambda x: f'{x[0]} = {sanitize_attribute_value(x[1])}', properties) # noqa
326326
)
327327

328328
LOGGER.debug(attribute_filter)
@@ -410,7 +410,9 @@ def get(self, identifier, crs_transform_spec=None, **kwargs):
410410
LOGGER.debug(f'Fetching identifier {identifier}')
411411
layer = self._get_layer()
412412

413-
layer.SetAttributeFilter(f"{self.id_field} = '{identifier}'")
413+
identifier2 = sanitize_attribute_value(identifier)
414+
415+
layer.SetAttributeFilter(f'{self.id_field} = {identifier2}')
414416

415417
ogr_feature = self._get_next_feature(layer, identifier)
416418
result = self._ogr_feature_to_json(
@@ -902,3 +904,25 @@ def _ignore_gdal_error(inst, fn, *args, **kwargs) -> Any:
902904
"""
903905
value = getattr(inst, fn)(*args, **kwargs)
904906
return value
907+
908+
909+
def sanitize_attribute_value(value) -> str:
910+
"""
911+
Sanitize an attribute value used in an
912+
OGR layer SetAttributeFilter function
913+
914+
:param value: `str` of attribute value
915+
916+
:returns: `str` of sanitized attribute value
917+
"""
918+
919+
if value is None:
920+
return 'NULL'
921+
922+
if isinstance(value, bool):
923+
return '1' if value else '0'
924+
925+
if isinstance(value, (int, float)):
926+
return f"'{value}'"
927+
928+
return "'" + str(value).replace("'", "''") + "'"

tests/provider/test_ogr_gpkg_provider.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ def test_get(config_poi_portugal):
8080
assert result['id'] == 536678593
8181
assert 'cafe' in result['properties']['fclass']
8282

83+
with pytest.raises(ProviderItemNotFoundError):
84+
item_id = 'foo%27%20OR%20%271%27%3D%271'
85+
p.get(item_id)
86+
87+
with pytest.raises(ProviderItemNotFoundError):
88+
item_id = "x' OR (SELECT substr(sql,1,1) FROM sqlite_master WHERE name='secret_table')='C" # noqa
89+
p.get(item_id)
90+
8391

8492
def test_get_not_existing_feature_raise_exception(
8593
config_poi_portugal
@@ -397,3 +405,23 @@ def test_query_with_property_filtering(config_gpkg_4326):
397405
assert 'straatnaam' in feature['properties']
398406

399407
assert feature['properties']['straatnaam'] == 'Arnhemseweg'
408+
409+
feature_collection = p.query(
410+
properties=[
411+
('straatnaam', "Arnhemseweg' OR '1'='1")
412+
]
413+
)
414+
415+
assert feature_collection.get('type') == 'FeatureCollection'
416+
features = feature_collection.get('features')
417+
assert len(features) == 0
418+
419+
feature_collection = p.query(
420+
properties=[
421+
('straatnaam', "doesnotexist' OR '1'='1")
422+
]
423+
)
424+
425+
assert feature_collection.get('type') == 'FeatureCollection'
426+
features = feature_collection.get('features')
427+
assert len(features) == 0

0 commit comments

Comments
 (0)