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",
"directed-smoke-test",
"--restler_grammar",
"d:\\demo\\restler_working_dir\\Compile\\grammar.py",
"d:\\test\\demo_server\\Compile\\grammar.py",
"--custom_mutations",
"d:\\demo\\restler_working_dir\\Compile\\dict.json",
"d:\\test\\demo_server\\Compile\\dict.json",
"--settings",
"d:\\demo\\restler_working_dir\\Compile\\engine_settings.json",
"d:\\test\\demo_server\\Compile\\engine_settings.json",
"--no_ssl",
"--host",
"localhost",

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

@ -87,7 +87,7 @@ def get_test_values(max_values: int, req: Request, static_dict=None,
quoted = request_block[2]
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],
vg_pool,
log_dict_err_to_main=False)

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

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

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

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

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

@ -45,7 +45,7 @@ class CandidateValuesTest(unittest.TestCase):
current_file_dir = os.path.dirname(os.path.abspath(__file__))
checkers_file_dir = os.path.join(current_file_dir, "..", "checkers")
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]
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.
# 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
req_definition = [fuzzable_int]
temp_req = Request(req_definition)