Skip to content

Commit 61ffe3c

Browse files
committed
Esri: sanitize attribute values
1 parent fcd3938 commit 61ffe3c

5 files changed

Lines changed: 47 additions & 30 deletions

File tree

pygeoapi/provider/base.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,27 @@ def _load_and_prepare_item(self, item, identifier=None,
294294

295295
return identifier2, json_data
296296

297+
def sanitize_attribute_value(self, value) -> str:
298+
"""
299+
Sanitize an attribute value used in an
300+
OGR layer SetAttributeFilter function
301+
302+
:param value: `str` of attribute value
303+
304+
:returns: `str` of sanitized attribute value
305+
"""
306+
307+
if value is None:
308+
return 'NULL'
309+
310+
if isinstance(value, bool):
311+
return '1' if value else '0'
312+
313+
if isinstance(value, (int, float)):
314+
return f"'{value}'"
315+
316+
return "'" + str(value).replace("'", "''") + "'"
317+
297318
def __repr__(self):
298319
return f'<BaseProvider> {self.type}'
299320

pygeoapi/provider/esri.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Authors: Benjamin Webb <bwebb@lincolninst.edu>
44
#
55
# Copyright (c) 2022 Benjamin Webb
6+
# Copyright (c) 2026 Tom Kralidis
67
#
78
# Permission is hereby granted, free of charge, to any person
89
# obtaining a copy of this software and associated documentation
@@ -309,13 +310,9 @@ def _make_where(self, properties=[], datetime_=None):
309310

310311
p = []
311312

312-
if properties != []:
313-
313+
if properties:
314314
for (k, v) in properties:
315-
if 'String' in self.fields[k]['type']:
316-
p.append(f"{k} = '{v}'")
317-
else:
318-
p.append(f"{k} = {v}")
315+
p.append(f'{k} = {self.sanitize_attribute_value(v)}')
319316

320317
if datetime_ is not None:
321318

pygeoapi/provider/ogr.py

Lines changed: 2 additions & 24 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]} = {sanitize_attribute_value(x[1])}', properties) # noqa
325+
map(lambda x: f'{x[0]} = {self.sanitize_attribute_value(x[1])}', properties) # noqa
326326
)
327327

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

413-
identifier2 = sanitize_attribute_value(identifier)
413+
identifier2 = self.sanitize_attribute_value(identifier)
414414

415415
layer.SetAttributeFilter(f'{self.id_field} = {identifier2}')
416416

@@ -904,25 +904,3 @@ def _ignore_gdal_error(inst, fn, *args, **kwargs) -> Any:
904904
"""
905905
value = getattr(inst, fn)(*args, **kwargs)
906906
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_base_provider.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,13 @@ def test_unique_subclass_query_types():
416416
assert BaseEDRProvider.query_types != SensorThingsEDRProvider.query_types
417417
assert SensorThingsEDRProvider.query_types == \
418418
['items', 'locations', 'cube', 'area']
419+
420+
421+
def test_sanitize_attribute_value(basic_provider_def):
422+
provider = BaseProvider(basic_provider_def)
423+
424+
assert provider.sanitize_attribute_value(None) == 'NULL'
425+
assert provider.sanitize_attribute_value(True) == '1'
426+
assert provider.sanitize_attribute_value(False) == '0'
427+
assert provider.sanitize_attribute_value(11.53) == "'11.53'"
428+
assert provider.sanitize_attribute_value("NAME=4' OR '1'='1") == "'NAME=4'' OR ''1''=''1'" # noqa

tests/provider/test_esri_provider.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Authors: Benjamin Webb <bwebb@lincolninst.edu>
44
#
55
# Copyright (c) 2022 Benjamin Webb
6+
# Copyright (c) 2026 Tom Kralidis
67
#
78
# Permission is hereby granted, free of charge, to any person
89
# obtaining a copy of this software and associated documentation
@@ -168,6 +169,16 @@ def test_query_properties(config):
168169
results = p.query(select_properties=['GEOGSTATE', ])
169170
assert len(results['features'][0]['properties']) == 1
170171

172+
results = p.query(
173+
properties=[
174+
('OBJECTID', "1' OR '1'='1")
175+
]
176+
)
177+
178+
assert results.get('type') == 'FeatureCollection'
179+
features = results.get('features')
180+
assert len(features) == 0
181+
171182

172183
def test_query_sortby_datetime(config):
173184

0 commit comments

Comments
 (0)