Fix missing custom payload handling (#788)

Closes #787 restler_custom_payload is not correctly plugged into examples

Also fix incorrectly passed 'quoted' parameters - this was caught by manually inspecting the logs and seeing incorrect quoting for several primitives when using the generated schema only.

Updated test baselines for invalidvalue after the bug fix.

Testing:
- confirmed the manual repro from #787 now works correctly
This commit is contained in:
marina-p 2023-07-12 21:52:14 -07:00 коммит произвёл GitHub
Родитель 9750bf91d5
Коммит a3abcb60b8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 17053 добавлений и 43505 удалений

6
.vscode/launch.json поставляемый
Просмотреть файл

@ -91,11 +91,11 @@
"--fuzzing_mode", "--fuzzing_mode",
"directed-smoke-test", "directed-smoke-test",
"--restler_grammar", "--restler_grammar",
"d:\\demo\\restler_working_dir\\Compile\\grammar.py", "d:\\test\\demo_server\\Compile\\grammar.py",
"--custom_mutations", "--custom_mutations",
"d:\\demo\\restler_working_dir\\Compile\\dict.json", "d:\\test\\demo_server\\Compile\\dict.json",
"--settings", "--settings",
"d:\\demo\\restler_working_dir\\Compile\\engine_settings.json", "d:\\test\\demo_server\\Compile\\engine_settings.json",
"--no_ssl", "--no_ssl",
"--host", "--host",
"localhost", "localhost",

Просмотреть файл

@ -87,7 +87,7 @@ def get_test_values(max_values: int, req: Request, static_dict=None,
quoted = request_block[2] quoted = request_block[2]
examples = request_block[3] examples = request_block[3]
fuzzable_string = primitives.restler_fuzzable_string("fuzzstring", is_quoted=quoted, examples=examples) fuzzable_string = primitives.restler_fuzzable_string("fuzzstring", quoted=quoted, examples=examples)
vgen_fuzzable_values, _, _ = req.init_fuzzable_values([fuzzable_string], vgen_fuzzable_values, _, _ = req.init_fuzzable_values([fuzzable_string],
vg_pool, vg_pool,
log_dict_err_to_main=False) log_dict_err_to_main=False)

Просмотреть файл

@ -172,12 +172,14 @@ class HeaderParam(KeyValueParamBase):
class ParamBase: class ParamBase:
""" Base class for all body parameters """ """ Base class for all body parameters """
def __init__(self, param_properties=None, dynamic_object=None, param_name=None, content_type=None, is_quoted=False): def __init__(self, param_properties=None, dynamic_object=None, param_name=None, content_type=None, is_quoted=False,
custom_payload_type=None):
self._fuzzable = False self._fuzzable = False
self._example_values = [] self._example_values = []
self._tag = '' self._tag = ''
self._param_properties = param_properties self._param_properties = param_properties
self._dynamic_object = dynamic_object self._dynamic_object = dynamic_object
self._custom_payload_type = custom_payload_type
self._param_name = param_name self._param_name = param_name
self._content_type = content_type self._content_type = content_type
self._is_quoted = is_quoted self._is_quoted = is_quoted
@ -293,6 +295,47 @@ class ParamBase:
self._is_quoted = src.is_quoted self._is_quoted = src.is_quoted
self._param_name = src._param_name self._param_name = src._param_name
def get_custom_payload(self):
if self._custom_payload_type is None:
return None
if self._custom_payload_type == "String":
return [primitives.restler_custom_payload(self._content, quoted=self.is_quoted,
param_name=self.param_name,
writer=self.dynamic_object_writer_variable)]
elif self._custom_payload_type == "Header":
return [primitives.restler_custom_payload_header(self._content, quoted=self.is_quoted,
param_name=self.param_name,
writer=self.dynamic_object_writer_variable)]
elif self._custom_payload_type == "Query":
return [primitives.restler_custom_payload_query(self._content, quoted=self.is_quoted,
param_name=self.param_name,
writer=self.dynamic_object_writer_variable)]
elif self._custom_payload_type == "UuidSuffix":
return [primitives.restler_custom_payload_uuid4_suffix(self._content, quoted=self.is_quoted,
param_name=self.param_name,
writer=self.dynamic_object_writer_variable)]
else:
raise Exception(f"Unexpected custom payload type: {self._custom_payload_type}")
def get_dynamic_object(self):
if self.is_dynamic_object_reader:
content = dependencies.RDELIM + self._content + dependencies.RDELIM
return [primitives.restler_static_string(content, quoted=self.is_quoted)]
return None
def get_blocks(self):
"""Returns the blocks for this payload if it defines either a custom payload or dynamic object.
Otherwise, returns None"""
custom_payload_blocks = self.get_custom_payload()
if custom_payload_blocks is not None:
return custom_payload_blocks
dynamic_object_blocks = self.get_dynamic_object()
if dynamic_object_blocks is not None:
return dynamic_object_blocks
return None
def set_fuzzable(self, is_fuzzable): def set_fuzzable(self, is_fuzzable):
""" Sets param as fuzzable """ Sets param as fuzzable
@ -379,9 +422,9 @@ class ParamValue(ParamBase):
@rtype: None @rtype: None
""" """
ParamBase.__init__(self, param_properties=param_properties, dynamic_object=dynamic_object, is_quoted=is_quoted) ParamBase.__init__(self, param_properties=param_properties, dynamic_object=dynamic_object, is_quoted=is_quoted,
custom_payload_type=custom_payload_type)
self._content = None self._content = None
self._custom_payload_type = custom_payload_type
def __eq__(self, other): def __eq__(self, other):
""" Operator equals """ Operator equals
@ -447,24 +490,11 @@ class ParamValue(ParamBase):
@rtype : List[str] @rtype : List[str]
""" """
if self._custom_payload_type is not None: base_blocks = ParamBase.get_blocks(self)
# TODO: support 'writer' variable in the schema parser and request parameters below if base_blocks is not None:
if self._custom_payload_type == "String": return base_blocks
return [primitives.restler_custom_payload(self._content)]
elif self._custom_payload_type == "Query":
return [primitives.restler_custom_payload(self._content)]
elif self._custom_payload_type == "Header":
return [primitives.restler_custom_payload_header(self._content)]
elif self._custom_payload_type == "UuidSuffix":
return [primitives.restler_custom_payload_uuid4_suffix(self._content)]
else:
raise Exception(f"Unknown custom payload type: {self._custom_payload_type}")
content = self._content return [primitives.restler_static_string(self._content)]
if self.is_dynamic_object_reader:
content = dependencies.RDELIM + self._content + dependencies.RDELIM
return [primitives.restler_static_string(content)]
def get_fuzzing_blocks(self, visitor): def get_fuzzing_blocks(self, visitor):
""" Gets the fuzzing blocks for this param """ """ Gets the fuzzing blocks for this param """
@ -912,9 +942,9 @@ class ParamString(ParamValue):
@rtype: None @rtype: None
""" """
ParamValue.__init__(self, param_properties=param_properties, dynamic_object=dynamic_object, is_quoted=is_quoted) ParamValue.__init__(self, param_properties=param_properties, dynamic_object=dynamic_object, is_quoted=is_quoted,
custom_payload_type=custom_payload_type)
self._custom_payload_type=custom_payload_type
self._content_type = content_type self._content_type = content_type
self._unknown = False self._unknown = False
@ -936,20 +966,11 @@ class ParamString(ParamValue):
@rtype : List[str] @rtype : List[str]
""" """
if self._custom_payload_type is not None: base_blocks = ParamBase.get_blocks(self)
if self._custom_payload_type == "String": if base_blocks is not None:
return [primitives.restler_custom_payload(self._content, quoted=self.is_quoted)] return base_blocks
elif self._custom_payload_type == "Header":
return [primitives.restler_custom_payload_header(self._content, quoted=self.is_quoted)] return [primitives.restler_static_string(self._content, quoted=self.is_quoted)]
elif self._custom_payload_type == "Query":
return [primitives.restler_custom_payload_query(self._content, quoted=self.is_quoted)]
else:
raise Exception(f"Unexpected custom payload type: {self._custom_payload_type}")
if self.is_dynamic_object_reader:
content = dependencies.RDELIM + self._content + dependencies.RDELIM
else:
content = self._content
return [primitives.restler_static_string(content, quoted=self.is_quoted)]
def get_original_blocks(self, config=None): def get_original_blocks(self, config=None):
""" Gets the original request blocks for the String Parameters. """ Gets the original request blocks for the String Parameters.
@ -958,25 +979,11 @@ class ParamString(ParamValue):
@rtype : List[str] @rtype : List[str]
""" """
if self._custom_payload_type is not None: base_blocks = ParamBase.get_blocks(self)
if self._custom_payload_type == "String": if base_blocks is not None:
return [primitives.restler_custom_payload(self._content, quoted=self.is_quoted, return base_blocks
param_name=self.param_name,
writer=self.dynamic_object_writer_variable)] content = self._content
elif self._custom_payload_type == "Header":
return [primitives.restler_custom_payload_header(self._content, quoted=self.is_quoted,
param_name=self.param_name,
writer=self.dynamic_object_writer_variable)]
elif self._custom_payload_type == "Query":
return [primitives.restler_custom_payload_query(self._content, quoted=self.is_quoted,
param_name=self.param_name,
writer=self.dynamic_object_writer_variable)]
else:
raise Exception(f"Unexpected custom payload type: {self._custom_payload_type}")
if self.is_dynamic_object_reader:
content = dependencies.RDELIM + self._content + dependencies.RDELIM
else:
content = self._content
if self.is_fuzzable: if self.is_fuzzable:
if self._content_type == "String": if self._content_type == "String":
@ -1122,7 +1129,7 @@ class DynamicObject:
class ParamNumber(ParamValue): class ParamNumber(ParamValue):
""" Class for number type parameters """ """ Class for number type parameters """
def __init__(self, param_properties=None, dynamic_object=None, is_quoted=False, number_type="Int"): def __init__(self, param_properties=None, custom_payload_type=None, dynamic_object=None, is_quoted=False, number_type="Int"):
""" Initialize a number type parameter """ Initialize a number type parameter
@param custom: Whether or not this is a custom payload @param custom: Whether or not this is a custom payload
@ -1132,7 +1139,8 @@ class ParamNumber(ParamValue):
@rtype: None @rtype: None
""" """
ParamValue.__init__(self, param_properties=param_properties, dynamic_object=dynamic_object, is_quoted=is_quoted) ParamValue.__init__(self, param_properties=param_properties, dynamic_object=dynamic_object, is_quoted=is_quoted,
custom_payload_type=custom_payload_type)
self._number_type = number_type self._number_type = number_type
@property @property
@ -1150,9 +1158,9 @@ class ParamNumber(ParamValue):
@rtype : List[str] @rtype : List[str]
""" """
if self.is_dynamic_object_reader: base_blocks = ParamBase.get_blocks(self)
content = dependencies.RDELIM + self._content + dependencies.RDELIM if base_blocks is not None:
return [primitives.restler_static_string(content)] return base_blocks
content = self._content content = self._content
if self._number_type == "Int": if self._number_type == "Int":
@ -1216,6 +1224,12 @@ class ParamNumber(ParamValue):
return fuzzer._fuzz_number(self) return fuzzer._fuzz_number(self)
class ParamBoolean(ParamValue): class ParamBoolean(ParamValue):
def __init__(self, param_properties=None, custom_payload_type=None, dynamic_object=None, is_quoted=False):
""" Initialize a boolean type parameter
"""
ParamValue.__init__(self, param_properties=param_properties, dynamic_object=dynamic_object, is_quoted=is_quoted,
custom_payload_type=custom_payload_type)
""" Class for Boolean type parameters """ """ Class for Boolean type parameters """
@property @property
def type(self): def type(self):
@ -1232,9 +1246,9 @@ class ParamBoolean(ParamValue):
@rtype : List[str] @rtype : List[str]
""" """
if self.is_dynamic_object_reader: base_blocks = ParamBase.get_blocks(self)
content = dependencies.RDELIM + self._content + dependencies.RDELIM if base_blocks is not None:
return [primitives.restler_static_string(content)] return base_blocks
return [primitives.restler_fuzzable_bool(self._content, quoted=self.is_quoted, examples=self.example_values, return [primitives.restler_fuzzable_bool(self._content, quoted=self.is_quoted, examples=self.example_values,
param_name=self.param_name, param_name=self.param_name,
@ -1292,6 +1306,13 @@ class ParamBoolean(ParamValue):
class ParamObjectLeaf(ParamValue): class ParamObjectLeaf(ParamValue):
""" Class for leaf object type parameters """ """ Class for leaf object type parameters """
def __init__(self, param_properties=None, custom_payload_type=None, dynamic_object=None, is_quoted=False):
""" Initialize an object leaf type parameter
"""
ParamValue.__init__(self, param_properties=param_properties, dynamic_object=dynamic_object, is_quoted=is_quoted,
custom_payload_type=custom_payload_type)
@property @property
def type(self): def type(self):
return dict return dict
@ -1327,9 +1348,9 @@ class ParamObjectLeaf(ParamValue):
@rtype : List[str] @rtype : List[str]
""" """
if self.is_dynamic_object_reader: base_blocks = ParamBase.get_blocks(self)
content = dependencies.RDELIM + self._content + dependencies.RDELIM if base_blocks is not None:
return [primitives.restler_static_string(content)] return base_blocks
# This check is present to support older grammars, and can be removed in the future. # This check is present to support older grammars, and can be removed in the future.
if self._content is None: if self._content is None:
@ -1413,10 +1434,9 @@ class ParamObjectLeaf(ParamValue):
class ParamEnum(ParamValue): class ParamEnum(ParamValue):
""" Class for Enum type parameters """ """ Class for Enum type parameters """
def __init__(self, contents, content_type,
def __init__(self, contents, content_type, is_quoted=False, enum_name=FUZZABLE_GROUP_TAG,
param_properties=None, body_param=True, param_properties=None, custom_payload_type=None, dynamic_object=None, is_quoted=False):
enum_name=FUZZABLE_GROUP_TAG):
""" Initialize an Enum type parameter """ Initialize an Enum type parameter
@param contents: A list of enum contents @param contents: A list of enum contents
@ -1428,8 +1448,8 @@ class ParamEnum(ParamValue):
@rtype: None @rtype: None
""" """
ParamBase.__init__(self, param_properties) ParamValue.__init__(self, param_properties=param_properties, dynamic_object=dynamic_object, is_quoted=is_quoted,
custom_payload_type=custom_payload_type)
self._contents = contents self._contents = contents
self._type = content_type self._type = content_type
self._is_quoted = is_quoted self._is_quoted = is_quoted
@ -1486,6 +1506,10 @@ class ParamEnum(ParamValue):
@rtype : List[str] @rtype : List[str]
""" """
base_blocks = ParamBase.get_blocks(self)
if base_blocks is not None:
return base_blocks
return [primitives.restler_fuzzable_group(self._enum_name, return [primitives.restler_fuzzable_group(self._enum_name,
self._contents, self._contents,
quoted=self.is_quoted, examples=self.example_values, quoted=self.is_quoted, examples=self.example_values,

Просмотреть файл

@ -258,13 +258,23 @@ def des_param_payload(param_payload_json, tag='', body_param=True):
dynamic_object=dynamic_object, is_quoted=is_quoted, content_type=content_type) dynamic_object=dynamic_object, is_quoted=is_quoted, content_type=content_type)
elif content_type == 'Int': elif content_type == 'Int':
value = ParamNumber(param_properties=param_properties, dynamic_object=dynamic_object, number_type=content_type) value = ParamNumber(custom_payload_type=custom_payload_type,
param_properties=param_properties,
dynamic_object=dynamic_object,
number_type=content_type)
elif content_type == 'Number': elif content_type == 'Number':
value = ParamNumber(param_properties=param_properties, dynamic_object=dynamic_object, number_type=content_type) value = ParamNumber(custom_payload_type=custom_payload_type,
param_properties=param_properties,
dynamic_object=dynamic_object,
number_type=content_type)
elif content_type == 'Bool': elif content_type == 'Bool':
value = ParamBoolean(param_properties=param_properties, dynamic_object=dynamic_object) value = ParamBoolean(custom_payload_type=custom_payload_type,
param_properties=param_properties,
dynamic_object=dynamic_object)
elif content_type == 'Object': elif content_type == 'Object':
value = ParamObjectLeaf(param_properties=param_properties, dynamic_object=dynamic_object) value = ParamObjectLeaf(custom_payload_type=custom_payload_type,
param_properties=param_properties,
dynamic_object=dynamic_object)
elif 'Enum' in content_type: elif 'Enum' in content_type:
# unique case for Enums, as they are defined as # unique case for Enums, as they are defined as
# "fuzzable" types in the schema, but are not fuzzable # "fuzzable" types in the schema, but are not fuzzable
@ -290,7 +300,8 @@ def des_param_payload(param_payload_json, tag='', body_param=True):
else: else:
is_quoted = False is_quoted = False
value = ParamEnum(contents, enum_content_type, is_quoted=is_quoted, value = ParamEnum(contents, enum_content_type, is_quoted=is_quoted,
param_properties=param_properties, body_param=body_param, enum_name=enum_name) custom_payload_type=custom_payload_type,
param_properties=param_properties, enum_name=enum_name)
else: else:
logger.write_to_main(f'Unexpected enum schema {name}') logger.write_to_main(f'Unexpected enum schema {name}')
else: else:

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -45,7 +45,7 @@ class CandidateValuesTest(unittest.TestCase):
current_file_dir = os.path.dirname(os.path.abspath(__file__)) current_file_dir = os.path.dirname(os.path.abspath(__file__))
checkers_file_dir = os.path.join(current_file_dir, "..", "checkers") checkers_file_dir = os.path.join(current_file_dir, "..", "checkers")
file_path = os.path.join(checkers_file_dir, "invalid_value_checker_value_gen.py") file_path = os.path.join(checkers_file_dir, "invalid_value_checker_value_gen.py")
fuzzable_string = primitives.restler_fuzzable_string("fuzzstring", is_quoted=False, examples=[]) fuzzable_string = primitives.restler_fuzzable_string("fuzzstring", quoted=False, examples=[])
req_definition = [fuzzable_string] req_definition = [fuzzable_string]
temp_req = Request(req_definition) temp_req = Request(req_definition)
@ -77,7 +77,7 @@ class CandidateValuesTest(unittest.TestCase):
# Default generators with 'int' in request block, which is not present there. # Default generators with 'int' in request block, which is not present there.
# Expected: the string generator should be used. # Expected: the string generator should be used.
fuzzable_int = primitives.restler_fuzzable_int("20", is_quoted=False, examples=[]) fuzzable_int = primitives.restler_fuzzable_int("20", quoted=False, examples=[])
override_value_gen["restler_fuzzable_int"] = None override_value_gen["restler_fuzzable_int"] = None
req_definition = [fuzzable_int] req_definition = [fuzzable_int]
temp_req = Request(req_definition) temp_req = Request(req_definition)