From f1840d6deff8e97ffd4d7a0b3842fee09e8911f7 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 8 Feb 2016 23:32:02 +0000 Subject: [PATCH] Sanitizing property names in queries and creation commands. --- pyorient/ogm/graph.py | 23 ++++++++++++++--------- pyorient/ogm/property.py | 20 +++++++++++++++----- pyorient/ogm/query.py | 6 +++--- pyorient/ogm/query_utils.py | 4 ++-- tests/test_ogm.py | 8 ++++++++ 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/pyorient/ogm/graph.py b/pyorient/ogm/graph.py index a4b69326..a9c26cb8 100644 --- a/pyorient/ogm/graph.py +++ b/pyorient/ogm/graph.py @@ -295,7 +295,7 @@ def create_class(self, cls): self.client.command( 'ALTER PROPERTY {0} DEFAULT {1}' .format(class_prop, - PropertyEncoder.encode(prop_value.default))) + PropertyEncoder.encode_value(prop_value.default))) self.client.command( 'ALTER PROPERTY {0} NOTNULL {1}' @@ -366,7 +366,6 @@ def drop_all(self, registry): def create_vertex(self, vertex_cls, **kwargs): result = self.client.command( - # to_unicode(str()) to_unicode(self.create_vertex_command(vertex_cls, **kwargs)))[0] props = result.oRecordData @@ -379,8 +378,9 @@ def create_vertex_command(self, vertex_cls, **kwargs): if kwargs: db_props = Graph.props_to_db(vertex_cls, kwargs, self.strict) set_clause = u' SET {}'.format( - u','.join(u'{}={}'.format(k,PropertyEncoder.encode(v)) - for k,v in db_props.items())) + u','.join(u'{}={}'.format( + PropertyEncoder.encode_name(k), PropertyEncoder.encode_value(v)) + for k, v in db_props.items())) else: set_clause = u'' @@ -402,8 +402,9 @@ def create_edge_command(self, edge_cls, from_vertex, to_vertex, **kwargs): if kwargs: db_props = Graph.props_to_db(edge_cls, kwargs, self.strict) set_clause = u' SET {}'.format( - u','.join(u'{}={}'.format(k,PropertyEncoder.encode(v)) - for k,v in db_props.items())) + u','.join(u'{}={}'.format( + PropertyEncoder.encode_name(k), PropertyEncoder.encode_value(v)) + for k, v in db_props.items())) else: set_clause = '' @@ -411,7 +412,6 @@ def create_edge_command(self, edge_cls, from_vertex, to_vertex, **kwargs): u'CREATE EDGE {} FROM {} TO {}{}'.format( class_name, from_vertex._id, to_vertex._id, set_clause)) - def get_vertex(self, vertex_id): record = self.client.command('SELECT FROM {}'.format(vertex_id)) return self.vertex_from_record(record[0]) if record else None @@ -436,8 +436,9 @@ def save_element(self, element_class, props, elem_id): if props: db_props = Graph.props_to_db(element_class, props, self.strict) set_clause = u' SET {}'.format( - u','.join(u'{}={}'.format(k,PropertyEncoder.encode(v)) - for k,v in db_props.items())) + u','.join(u'{}={}'.format( + PropertyEncoder.encode_name(k), PropertyEncoder.encode_value(v)) + for k, v in db_props.items())) else: set_clause = '' @@ -658,6 +659,10 @@ def create_props_mapping(db_to_element): def props_to_db(element_class, props, strict): db_props = {} for k, v in props.items(): + # sanitize the property name -- this line + # will raise an error if the name is invalid + PropertyEncoder.encode_name(k) + if hasattr(element_class, k): prop = getattr(element_class, k) db_props[prop.name or k] = v diff --git a/pyorient/ogm/property.py b/pyorient/ogm/property.py index 5ef8505c..c926b6dd 100644 --- a/pyorient/ogm/property.py +++ b/pyorient/ogm/property.py @@ -1,8 +1,9 @@ from .operators import Operand, ArithmeticMixin -import sys -import decimal import datetime +import decimal +import string +import sys class Property(Operand): @@ -76,8 +77,17 @@ def __str__(self): return 'UUID()' class PropertyEncoder: + PROHIBITED_NAME_CHARS = set(''.join([string.whitespace, '"\''])) + + @staticmethod + def encode_name(name): + for c in name: + if c in PropertyEncoder.PROHIBITED_NAME_CHARS: + raise ValueError('Prohibited character in property name: {}'.format(name)) + return name + @staticmethod - def encode(value): + def encode_value(value): if isinstance(value, decimal.Decimal): return repr(str(value)) elif isinstance(value, datetime.datetime) or isinstance(value, datetime.date): @@ -89,10 +99,10 @@ def encode(value): elif value is None: return 'null' elif isinstance(value, list) or isinstance(value, set): - return u'[{}]'.format(u','.join([PropertyEncoder.encode(v) for v in value])) + return u'[{}]'.format(u','.join([PropertyEncoder.encode_value(v) for v in value])) elif isinstance(value, dict): contents = u','.join([ - '{}: {}'.format(PropertyEncoder.encode(k), PropertyEncoder.encode(v)) + '{}: {}'.format(PropertyEncoder.encode_value(k), PropertyEncoder.encode_value(v)) for k, v in value.items() ]) return u'{{ {} }}'.format(contents) diff --git a/pyorient/ogm/query.py b/pyorient/ogm/query.py index 7dcd8b3a..8042d6f3 100644 --- a/pyorient/ogm/query.py +++ b/pyorient/ogm/query.py @@ -316,7 +316,7 @@ def filter_string(self, expression_root): left_str, ArgConverter.convert_to(ArgConverter.Value , right, self)) elif op is Operator.Between: - far_right = PropertyEncoder.encode(expression_root.operands[2]) + far_right = PropertyEncoder.encode_value(expression_root.operands[2]) return u'{0} BETWEEN {1} and {2}'.format( left_str, right, far_right) elif op is Operator.Contains: @@ -325,7 +325,7 @@ def filter_string(self, expression_root): left_str, self.filter_string(right)) else: return u'{} in {}'.format( - PropertyEncoder.encode(right), left_str) + PropertyEncoder.encode_value(right), left_str) elif op is Operator.EndsWith: return u'{0} like \'%{1}\''.format(left_str, right) elif op is Operator.Is: @@ -412,7 +412,7 @@ def build_props(self, params, prop_names=None, for_iterator=False): def build_wheres(self, params): kw_filters = params.get('kw_filters') kw_where = [u' and '.join(u'{0}={1}' - .format(k, PropertyEncoder.encode(v)) + .format(PropertyEncoder.encode_name(k), PropertyEncoder.encode_value(v)) for k,v in kw_filters.items())] if kw_filters else [] filter_exp = params.get('filter') diff --git a/pyorient/ogm/query_utils.py b/pyorient/ogm/query_utils.py index 5921af54..350ae285 100644 --- a/pyorient/ogm/query_utils.py +++ b/pyorient/ogm/query_utils.py @@ -16,7 +16,7 @@ class ArgConverter(object): @staticmethod def convert_to(conversion, arg, for_query): if conversion is ArgConverter.Label: - return '{}'.format(PropertyEncoder.encode(arg)) + return '{}'.format(PropertyEncoder.encode_value(arg)) elif conversion is ArgConverter.Expression: if isinstance(arg, LogicalConnective): return '\'{}\''.format(for_query.filter_string(arg)) @@ -46,7 +46,7 @@ def convert_to(conversion, arg, for_query): elif isinstance(arg, What): return for_query.build_what(arg) else: - return PropertyEncoder.encode(arg) + return PropertyEncoder.encode_value(arg) elif conversion is ArgConverter.Boolean: if isinstance(arg, What): return for_query.build_what(arg) diff --git a/tests/test_ogm.py b/tests/test_ogm.py index e29d2be3..c25bbc28 100644 --- a/tests/test_ogm.py +++ b/tests/test_ogm.py @@ -66,6 +66,14 @@ def testGraph(self): assert rat == queried_rat + invalid_query_args = {'name': 'rat', 'name="rat" OR 1': 1} + try: + g.animals.query(**invalid_query_args).all() + except: + pass + else: + assert False and 'Invalid params did not raise an exception!' + queried_mouse = g.query(mouse).one() assert mouse == queried_mouse assert mouse == g.get_vertex(mouse._id)