Test more schema combinations by default to increase coverage (#576)

Test mode is modified as follows:
- instead of just testing the first schema, test all examples (if available), then
the request with all parameters from the specification, and then
the same request with optional parameters filtered out.
- 'max_combinations' is applied as usual: if there are 7 examples, and the schema has 10 value combinations, RESTler will test: 7 value combinations, 1 for each example; 10 value combinations for schema with all parameters; 3 value combinations for schema with required parameters

- Fixed the examples 'get_blocks' to return the constant rather than enum type definition.

Test changes:
- Modify quick start test baseline - now that more combinations are being explored in the 'Test' phase, coverage goes up to 6 / 6
This commit is contained in:
marina-p 2022-07-27 12:42:22 -07:00 коммит произвёл GitHub
Родитель 0fe2ce4300
Коммит 1151fe1e84
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 2015 добавлений и 86 удалений

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

@ -11,8 +11,10 @@ import random
from engine.fuzzing_parameters.fuzzing_utils import *
from engine.fuzzing_parameters.request_params import *
class BodySchemaStructuralFuzzer():
""" Body Schema Fuzzer Base Class """
from engine.fuzzing_parameters.param_combinations import JsonBodySchemaFuzzerBase
class BodySchemaStructuralFuzzer(JsonBodySchemaFuzzerBase):
""" Body Schema Fuzzer Class """
def __init__(self, LOG=print, strategy=''):
""" Initialize the body schema fuzzer
@ -24,6 +26,7 @@ class BodySchemaStructuralFuzzer():
@rtype: None
"""
JsonBodySchemaFuzzerBase.__init__(self)
# setup customized log
self._log = LOG
@ -137,6 +140,10 @@ class BodySchemaStructuralFuzzer():
"""
return self._strategy.upper() == 'ALL'
def _apply_shuffle_propagation(self, values_pool):
if self._shuffle_propagation:
random.Random(self._random_seed).shuffle(values_pool)
def _fuzz_member(self, param_member, fuzzed_value):
""" Fuzz a ParamMember node

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

@ -58,8 +58,9 @@ def test_test_task(restler_working_dir, swagger_path, restler_drop_dir):
shell=True, capture_output=True
)
expected_strings = [
'Request coverage (successful / total): 5 / 6',
'No bugs were found.' ,
'Request coverage (successful / total): 6 / 6',
'Attempted requests: 6 / 6',
'No bugs were found.',
'Task Test succeeded.'
]
check_output_errors(output)
@ -72,7 +73,8 @@ def test_fuzzlean_task(restler_working_dir, swagger_path, restler_drop_dir):
shell=True, capture_output=True
)
expected_strings = [
'Request coverage (successful / total): 5 / 6',
'Request coverage (successful / total): 6 / 6',
'Attempted requests: 6 / 6',
'Bugs were found!' ,
'InvalidDynamicObjectChecker_20x: 2',
'PayloadBodyChecker_500: 2',
@ -93,6 +95,7 @@ def test_fuzz_task(restler_working_dir, swagger_path, restler_drop_dir):
expected_strings = [
'Request coverage (successful / total): 6 / 6',
'Attempted requests: 6 / 6',
'Bugs were found!' ,
'InvalidDynamicObjectChecker_20x: 2',
'InvalidDynamicObjectChecker_500: 1',

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

@ -21,6 +21,7 @@ from engine.fuzzing_parameters.body_schema import BodySchema
from engine.fuzzing_parameters.parameter_schema import QueryList
from engine.fuzzing_parameters.parameter_schema import HeaderList
import engine.fuzzing_parameters.param_combinations as param_combinations
from engine.fuzzing_parameters.fuzzing_config import FuzzingConfig
from engine.errors import InvalidDictionaryException
import utils.logger as logger
@ -515,6 +516,21 @@ class Request(object):
"""
self._examples = examples
def set_schemas(self, other):
""" Sets the schema of this request to the schema of
the input parameter (without deep copying).
@param other: The request whose schemas should be equal
to this request's schemas.
@type other: Request
@return: None
"""
self.set_examples(other.examples)
self.set_body_schema(other.body_schema)
self.set_query_schema(other.query_schema)
self.set_headers_schema(other.headers_schema)
def set_body_schema(self, body_schema: BodySchema):
""" Sets the Request's body schema
@ -685,7 +701,7 @@ class Request(object):
return i + 1
return -1
def get_schema_combinations(self):
def get_schema_combinations(self, use_grammar_py_schema=True):
""" A generator that lazily iterates over a pool of schema combinations
for this request, determined the specified settings.
@ -703,12 +719,16 @@ class Request(object):
@rtype : (Request)
"""
def get_all_example_schemas():
if self.examples is not None:
ex_schemas = self.get_example_payloads()
for ex_schema in itertools.islice(ex_schemas, Settings().max_examples):
yield ex_schema
tested_example_payloads = False
example_payloads = Settings().example_payloads
if example_payloads is not None and self.examples is not None:
if Settings().example_payloads is not None:
tested_example_payloads = True
example_schemas = self.get_example_payloads()
for ex in itertools.islice(example_schemas, Settings().max_examples):
for ex in get_all_example_schemas():
yield ex
tested_param_combinations = False
@ -725,7 +745,48 @@ class Request(object):
yield hpc
if not (tested_param_combinations or tested_example_payloads):
yield self
# When no test combination settings are specified, RESTler will try
# a small number of schema combinations that are likely to get the request to
# successfully execute.
tested_all_params = False
tested_first_example = False
# To minimize breaking changes, always execute the first request from the grammar
# This is necessary because not all schema elements are implemented in 'request_params' yet (for example,
# writer variables are not yet supported).
# In the future, this may still be necessary for cases when the user manually modifies the grammar.
# This will be controlled with an 'allow_grammar_py_modifications' option.
if use_grammar_py_schema:
# Remember which case was already tested, to avoid duplication.
if self.examples is None:
tested_all_params = True
else:
tested_first_example = True
yield self
# If examples are available, test all the examples (up to the maximum in the settings)
example_schemas = get_all_example_schemas()
# If there is at least one example, skip the first one because it was already tested above (the first
# example is always present in the grammar).
if tested_first_example:
next(example_schemas)
for ex in example_schemas:
yield ex
if not tested_all_params:
param_schema_combinations = {
"max_combinations": 1,
"param_kind": "all",
"choose_n": "max"
}
yield self.get_parameters_from_schema(param_schema_combinations)
# Test all required parameters (obtained from the schema, without examples)
param_schema_combinations = {
"max_combinations": 1,
"param_kind": "optional"
}
yield self.get_parameters_from_schema(param_schema_combinations)
def init_fuzzable_values(self, req_definition, candidate_values_pool, preprocessing=False):
def _raise_dict_err(type, tag):
@ -938,6 +999,8 @@ class Request(object):
next_combination = 0
schema_idx = -1
schema_combinations = itertools.islice(self.get_schema_combinations(), Settings().max_schema_combinations)
remaining_combinations_count = Settings().max_combinations - skip
for req in schema_combinations:
schema_idx += 1
parser = None
@ -993,6 +1056,9 @@ class Request(object):
# for each combination's values render dynamic primitives and resolve
# dependent variables
for ind, values in enumerate(combinations_pool):
if remaining_combinations_count == 0:
break
values = list(values)
# Use saved value generators.
@ -1043,7 +1109,7 @@ class Request(object):
yield rendered_data, parser, tracked_parameter_values
next_combination = next_combination + 1
remaining_combinations_count = remaining_combinations_count - 1
def render_current(self, candidate_values_pool, preprocessing=False, use_last_cached_rendering=False):
""" Renders the next combination for the current request.
@ -1191,15 +1257,21 @@ class Request(object):
return new_request
def get_query_param_combinations(self, query_param_combinations_setting):
""" Gets the query parameter combinations according to the specified setting.
"""
"""
fuzzing_config = FuzzingConfig()
for param_list in param_combinations.get_param_combinations(self, query_param_combinations_setting,
self.query_schema.param_list, "query"):
query_schema = QueryList(param=param_list)
query_blocks = query_schema.get_blocks()
query_blocks = query_schema.get_original_blocks(fuzzing_config)
# query_blocks = query_schema.get_blocks()
new_request = self.substitute_query(query_blocks)
if new_request:
# The schemas need to be copied because this new request will be passed into checkers,
# and the schema needs to exist.
# TODO: are there any cases where it needs to correspond to the request definition?
new_request.set_schemas(self)
yield new_request
else:
# For malformed requests, it is possible that the place to insert parameters is not found,
@ -1207,23 +1279,50 @@ class Request(object):
logger.write_to_main(f"Warning: could not substitute query parameters.")
def get_header_param_combinations(self, header_param_combinations_setting):
""" Gets the header parameter combinations according to the specified setting.
"""
"""
fuzzing_config = FuzzingConfig()
for param_list in param_combinations.get_param_combinations(self, header_param_combinations_setting,
self.headers_schema.param_list, "header"):
headers_schema = HeaderList(param=param_list)
header_blocks = headers_schema.get_blocks()
header_blocks = headers_schema.get_original_blocks(fuzzing_config)
# header_blocks = headers_schema.get_blocks()
new_request = self.substitute_headers(header_blocks)
if new_request:
# The schemas need to be copied because this new request will be passed into checkers,
# and the schema needs to exist.
# TODO: are there any cases where it needs to correspond to the request definition?
new_request.set_schemas(self)
yield new_request
else:
# For malformed requests, it is possible that the place to insert parameters is not found,
# In such cases, skip the combination.
logger.write_to_main(f"Warning: could not substitute header parameters.")
def get_example_payloads(self, example_payloads_setting=None):
def get_body_param_combinations(self, body_param_combinations_setting):
fuzzing_config = FuzzingConfig()
for new_body_schema in param_combinations.get_body_param_combinations(self, body_param_combinations_setting,
self.body_schema):
new_body_schema.set_config(fuzzing_config) # This line is required for legacy reasons
new_body_blocks = new_body_schema.get_original_blocks(fuzzing_config)
# new_body_blocks = new_body_schema.get_blocks()
if new_body_blocks:
new_request = self.substitute_body(new_body_blocks)
if new_request:
# The schemas need to be copied because this new request will be passed into checkers,
# and the schema needs to exist.
# TODO: are there any cases where it needs to correspond to the request definition?
new_request.set_schemas(self)
yield new_request
else:
# For malformed requests, it is possible that the place to insert parameters is not found,
# In such cases, skip the combination.
logger.write_to_main(f"Warning: could not substitute body when generating parameter combinations.")
def get_example_payloads(self):
"""
Replaces the body, query, and headers of this request by the available examples.
"""
@ -1249,6 +1348,9 @@ class Request(object):
check_example_schema_is_valid(num_query_payloads, max_example_payloads, "query")
check_example_schema_is_valid(num_body_payloads, max_example_payloads, "body")
fuzzing_config = FuzzingConfig()
fuzzing_config.use_constant_enum_value = True
for payload_idx in range(max_example_payloads):
body_example = None
query_example = None
@ -1261,12 +1363,18 @@ class Request(object):
if num_header_payloads > 0:
header_example = self.examples.header_examples[payload_idx]
# Copy the request definition and reset it here.
# Create the new request
# Note: the code below that generates the python grammar from the schema *must*
# use 'get_blocks' instead of 'get_original_blocks'. This is because in the main algorithm, only one
# combination for each example (the example itself) should be generated.
# For example, 'get_original_blocks' may have a 'restler_fuzzable_group' for enum values, but the
# example may only be applicable to one enum value.
new_request = self
body_blocks = None
query_blocks = None
header_blocks = None
if body_example:
body_example.set_config(fuzzing_config)
body_blocks = body_example.get_blocks()
# Only substitute the body if there is a body.
if body_blocks:
@ -1289,6 +1397,19 @@ class Request(object):
# so the query parameters cannot be inserted. In such cases, skip the example.
logger.write_to_main(f"Warning: could not substitute example parameters for example {payload_idx}.")
def get_parameters_from_schema(self, combination_settings=None):
""" Get the parameters for this request schema, as specified in combination_settings.
"""
req_current = self
if self.headers_schema:
req_current = next(req_current.get_header_param_combinations(combination_settings))
if self.query_schema:
req_current = next(req_current.get_query_param_combinations(combination_settings))
if self.body_schema:
req_current = next(req_current.get_body_param_combinations(combination_settings))
return req_current
def get_body_start(self):
""" Get the starting index of the request body

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

@ -26,6 +26,7 @@ class FuzzingConfig(object):
self.merge_fuzzable_values = False
self.max_depth = sys.maxsize
self.filter_fn = None
self.use_constant_enum_value = False
# Traversal depth state
self.depth = 0
@ -88,6 +89,7 @@ class FuzzingConfig(object):
new_config.merge_fuzzable_values = self.merge_fuzzable_values
new_config.max_depth = self.max_depth
new_config.filter_fn = self.filter_fn
new_config.use_constant_enum_value = self.use_constant_enum_value
return new_config

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

@ -9,13 +9,27 @@ import json
import engine.primitives as primitives
import utils.logger as logger
from restler_settings import Settings
from engine.fuzzing_parameters.fuzzing_utils import *
from engine.fuzzing_parameters.request_params import *
def get_param_list_combinations(param_list, max_combinations):
"""
Generator that takes the specified list and returns all combinations of the elements.
def get_param_list_combinations(param_list, max_combinations, choose_n):
""" Generator that takes the specified list and returns all combinations of the elements.
"""
def generate_combinations():
for i in range(1, len(param_list) + 2):
param_count = len(param_list)
if choose_n is None:
combination_size_range = range(1, param_count + 2)
elif choose_n == "max":
combination_size_range = range(param_count + 1, param_count + 2)
else:
c = int(choose_n)
if 0 < c <= param_count:
combination_size_range = range(c + 1, c + 2)
else:
raise Exception(f"Invalid choose_n specified: {choose_n}, there are only {param_count} parameters.")
for i in combination_size_range:
num_items = i - 1
combinations_num_i = itertools.combinations(param_list, num_items)
for new_param_list in combinations_num_i:
@ -24,35 +38,60 @@ def get_param_list_combinations(param_list, max_combinations):
return itertools.islice(generate_combinations(), 0, max_combinations)
def get_param_combinations(req, param_combinations_setting, param_list, param_type):
def filter_required(p_list, required_val=True):
""" Given a list of parameters, returns a list
containing only the required parameters.
"""
param_type is either "header" or "query"
"""
def filter_required(p_list, required_val=True):
rp=[]
for p in p_list:
if p.is_required == required_val:
rp.append(p)
return rp
rp = []
for p in p_list:
if p.is_required == required_val:
rp.append(p)
return rp
if 'param_kind' in param_combinations_setting:
param_kind = param_combinations_setting['param_kind']
else:
param_kind = "all"
def get_max_combinations(param_combinations_setting):
if 'max_combinations' in param_combinations_setting:
max_combinations = param_combinations_setting['max_combinations']
else:
max_combinations = Settings().max_combinations
return max_combinations
def get_choose_n(param_combinations_setting):
if 'choose_n' in param_combinations_setting:
return param_combinations_setting['choose_n']
else:
return None
def get_param_kind(param_combinations_setting):
if 'param_kind' in param_combinations_setting:
param_kind = param_combinations_setting['param_kind']
else:
param_kind = "all"
return param_kind
def get_param_combinations(req, param_combinations_setting, param_list, param_type):
"""
param_type is either "header" or "query"
"""
if param_type not in ["header", "query"]:
raise Exception(f"Invalid param_type: {param_type}. Specify 'header' or 'query'.")
param_kind = get_param_kind(param_combinations_setting)
max_combinations = get_max_combinations(param_combinations_setting)
choose_n = get_choose_n(param_combinations_setting)
if param_kind == "all":
# Send combinations of all available parameters.
for x in get_param_list_combinations(param_list, max_combinations):
for x in get_param_list_combinations(param_list, max_combinations, choose_n):
yield x
elif param_kind == "required":
# Only send required parameter combinations, and omit optional parameters.
required_params_list = filter_required(param_list)
for x in get_param_list_combinations(required_params_list, max_combinations):
for x in get_param_list_combinations(required_params_list, max_combinations, choose_n):
yield x
elif param_kind == "optional":
# Send required parameters, and additionally send combinations
@ -60,7 +99,7 @@ def get_param_combinations(req, param_combinations_setting, param_list, param_ty
required_params_list = filter_required(param_list)
optional_params_list = filter_required(param_list, required_val=False)
optional_param_combinations = get_param_list_combinations(optional_params_list, max_combinations)
optional_param_combinations = get_param_list_combinations(optional_params_list, max_combinations, choose_n)
for opc in optional_param_combinations:
yield required_params_list + opc
@ -68,3 +107,297 @@ def get_param_combinations(req, param_combinations_setting, param_list, param_ty
raise Exception("Invalid setting for parameter combinations:"
f"{param_type}_{param_combinations_setting}. \
Valid values are: required, optional, all.")
class JsonBodySchemaFuzzerBase:
""" Base Class for generating structural json body combinations.
The functions named 'fuzz_<schema member>' correspond to schema members defined in 'request_params.py'.
When 'schema.get_fuzzing_pool' is invoked, 'get_fuzzing_pool' will be invoked on each node, which
will, in turn, invoke the 'fuzz_<_>' functions defined below (or overridden in a subclass).
The 'fuzz_<_>' do not generate any combinations, and should by default return a fuzzing pool
with one schema, equal to the seed.
When inheriting from this base class, override the desired 'fuzz_<member kind>' function to
generate more than one fuzzed schema.
"""
def __init__(self):
""" Initialize the body schema fuzzer
@return: None
@rtype: None
"""
self._max_combination = 1000
self._max_propagation = 1000
self._filter_fn = None
def set_filter_fn(self, filter_fn):
self._filter_fn = filter_fn
def _get_propagation_product(self, children, bound):
""" Return the product sets for propagation
@param children: A list of children (variants)
@type children: List
@param bound: Max num of combination
@type bound: Int
@return: A list of product
@rtype: List
"""
return get_product_linear_fair(children, bound)
def run(self, schema_seed, config={}):
""" Fuzz the seed body schema
@param schema_seed: Seed body schema to fuzz
@type schema_seed: BodySchema
@param config: Run-time configuration
@type config: Dict
@return: A list of body schema variants
@rtype: List [ParamObject]
"""
pool = schema_seed.get_fuzzing_pool(self, config)
if len(pool) <= self._max_combination:
return pool
else:
return pool[:self._max_combination]
def _fuzz_member(self, param_member, fuzzed_value):
""" Fuzz a ParamMember node
@param param_member: Current fuzzing node
@type param_member: ParamMember
@param fuzzed_value: List of value variants (of the member)
@type fuzzed_value: List [ParamValue]
@return: A list of member variants
@rtype: List [ParamMember]
"""
include_param_member = self._filter_fn is None or self._filter_fn(param_member)
if not include_param_member:
return []
fuzzed_members = []
# compose
for new_value in fuzzed_value:
new_member = ParamMember(param_member.name, new_value, param_member.is_required)
new_member.meta_copy(param_member)
fuzzed_members.append(new_member)
return fuzzed_members
def _fuzz_object(self, param_object, fuzzed_members):
""" Fuzz a ParamObject node (with members)
@param param_object: Current fuzzing node
@type param_object: ParamObject
@param fuzzed_members: List of members variants (of the object)
@type fuzzed_members: List [ [ParamMember] ]
@return: A list of object variants
@rtype: List [ParamObject]
"""
# structurally fuzz the object node
structurally_fuzzed_fuzzed_members = self._fuzz_members_in_object(
fuzzed_members
)
# Each element in structurally_fuzzed_fuzzed_members is a list of
# member variants, whose products define an object.
members_pool = []
for new_fuzzed_members in structurally_fuzzed_fuzzed_members:
# new_fuzzed_members =
# [ member1_variants, member2_variants, member3_variants ]
members_pool += self._get_propagation_product(
new_fuzzed_members, self._max_propagation
)
# shuffle
self._apply_shuffle_propagation(members_pool)
# compose
fuzzed_objects = []
for members in members_pool:
new_object = ParamObject(members)
new_object.meta_copy(param_object)
fuzzed_objects.append(new_object)
return fuzzed_objects
def _fuzz_object_leaf(self, param_object):
""" Fuzz a ParamObject node (without members)
@param param_object: Current fuzzing node
@type param_object: ParamObject
@return: A list of object variants
@rtype: List [ParamObject]
"""
return [param_object]
def _apply_shuffle_propagation(self, values_pool):
""" Shuffle (re-order) the values in the list. No shuffling is done by default.
"""
pass
def _fuzz_array(self, param_array, fuzzed_values):
""" Fuzz a ParamArray node
@param param_array: Current fuzzing node
@type param_array: ParamArray
@param fuzzed_values: List of values variants (of the array)
@type fuzzed_values: List [ [ParamValue] ]
@return: A list of array variants
@rtype: List [ParamArray]
"""
# structurally fuzz the array node
structurally_fuzzed_fuzzed_values = self._fuzz_values_in_array(
fuzzed_values
)
# Each element in structurally_fuzzed_fuzzed_values is a list of value
# variants, whose products define an array.
values_pool = []
for new_fuzzed_values in structurally_fuzzed_fuzzed_values:
# new_fuzzed_values =
# [ value1_variants, value2_variants, value3_variants ]
values_pool += self._get_propagation_product(
new_fuzzed_values, self._max_propagation
)
# shuffle
self._apply_shuffle_propagation(values_pool)
# compose
fuzzed_array = []
for values in values_pool:
new_array = ParamArray(values)
new_array.meta_copy(param_array)
fuzzed_array.append(new_array)
return fuzzed_array
def _fuzz_string(self, param_string):
""" Fuzz a ParamString node
@param param_string: Current fuzzing node
@type param_string: ParamString
@return: A list of string variants
@rtype: List [ParamString]
"""
return [param_string]
def _fuzz_number(self, param_number):
""" Fuzz a ParamNumber node
@param param_number: Current fuzzing node
@type param_number: ParamNumber
@return: A list of number variants
@rtype: List [ParamNumber]
"""
return [param_number]
def _fuzz_boolean(self, param_boolean):
""" Fuzz a ParamBoolean node
@param param_number: Current fuzzing node
@type: param_number: ParamBoolean
@return: A lsit of Boolean variants
@rtype: List [ParamBoolean]
"""
return [param_boolean]
def _fuzz_enum(self, param_enum):
""" Fuzz a ParamEnum node
@param param_enum: Current fuzzing node
@type param_enum: ParamEnum
@return: A list of enum variants
@rtype: List [ParamEnum]
"""
return [param_enum]
def _fuzz_members_in_object(self, fuzzed_members):
""" Fuzz members in a ParamObject node
@param fuzzed_members: A list of member variants (length == object size)
@type fuzzed_members: List [ [ParamMember] ]
@return: A list of variants of member variants
@rtype: List [ [ [ParamMember] ] ]
"""
# fuzzed_members =
# [ member1_variants, member2_variants, ..., memberN_variants ]
return [fuzzed_members]
def _fuzz_values_in_array(self, fuzzed_values):
""" Fuzz values in a ParamArray node
@param fuzzed_values: A list of value variants (length == array size)
@type fuzzed_values: List [ [ParamValue] ]
@return: A list of variants of value variants
@rtype: List [ [ [ParamValue] ] ]
"""
# fuzzed_values =
# [ value1_variants, value2_variants, ..., valueN_variants ]
return [fuzzed_values]
class JsonBodyPropertyCombinations(JsonBodySchemaFuzzerBase):
"""
Generates combinations of JSON body properties.
"""
def __init__(self):
JsonBodySchemaFuzzerBase.__init__(self)
def _generate_member_combinations(self, fuzzed_items, max_combinations=None):
""" Compute combinations of the fuzzed items, which are
properties.
Supports returning n-wise combinations up to 'max_combinations'.
"""
member_combinations = get_param_list_combinations(fuzzed_items, max_combinations, choose_n=None)
combination_list = list(member_combinations)
return combination_list
def _fuzz_members_in_object(self, fuzzed_members):
return self._generate_member_combinations(fuzzed_members)
def get_body_param_combinations(req, param_combinations_setting, body_schema):
"""
Gets the body parameter combinations according to the specified setting.
TODO: not fully implemented. Currently, this function supports filtering
the schema only. The rest of the 'param_combination_setting' properties
are not used.
"""
max_combinations = get_max_combinations(param_combinations_setting)
param_kind = get_param_kind(param_combinations_setting)
schema_generator = JsonBodySchemaFuzzerBase()
if param_kind == "optional":
schema_generator.set_filter_fn(lambda x: x.is_required)
schema_pool = schema_generator.run(body_schema)
for new_schema in schema_pool:
yield new_schema

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

@ -67,26 +67,26 @@ class KeyValueParamList():
"""
self.param_list.append(param_item)
# def get_fuzzing_pool(self, fuzzer, config):
# """ Returns the fuzzing pool
#
# @param fuzzer: The body fuzzer object to use for fuzzing
# @type fuzzer: BodySchemaStructuralFuzzer
#
# @return: The fuzzing pool
# @rtype : List[ParamObject]
#
# """
# fuzzed_members = []
# for param_item in self.param_list:
# item_fuzzing_pool = param_item.get_fuzzing_pool(fuzzer, config) #list of key=value pairs
# # It is possible that this member was excluded from fuzzing by
# # a filter configured by the fuzzer. If so, do not add it to
# # 'fuzzed_members'.
# if len(item_fuzzing_pool) > 0:
# fuzzed_members.append(item_fuzzing_pool)
#
# return fuzzer._fuzz_param_list(fuzzed_members) # list of lists of key=value pairs
def get_fuzzing_pool(self, fuzzer, config):
""" Returns the fuzzing pool
@param fuzzer: The body fuzzer object to use for fuzzing
@type fuzzer: BodySchemaStructuralFuzzer
@return: The fuzzing pool
@rtype : List[ParamObject]
"""
fuzzed_members = []
for param_item in self.param_list:
item_fuzzing_pool = param_item.get_fuzzing_pool(fuzzer, config) #list of key=value pairs
# It is possible that this member was excluded from fuzzing by
# a filter configured by the fuzzer. If so, do not add it to
# 'fuzzed_members'.
if len(item_fuzzing_pool) > 0:
fuzzed_members.append(item_fuzzing_pool)
return fuzzer._fuzz_param_list(fuzzed_members) # list of lists of key=value pairs
class QueryList(KeyValueParamList):
def __init__(self, request_schema_json=None, param=None):

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

@ -43,6 +43,7 @@ class RequestExamples():
except Exception as err:
msg = f'Fail deserializing request schema header examples: {err!s}'
logger.write_to_main(msg, print_to_console=True)
raise Exception(msg)
try:
self._set_body_params(request_schema_json['bodyParameters'])

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

@ -377,6 +377,8 @@ class ParamValue(ParamBase):
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}")
@ -859,6 +861,8 @@ class ParamString(ParamValue):
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)]
elif self._custom_payload_type == "UuidSuffix":
return [primitives.restler_custom_payload_uuid4_suffix(self._content)]
else:
raise Exception(f"Unexpected custom payload type: {self._custom_payload_type}")
if self.is_dynamic_object:
@ -881,6 +885,8 @@ class ParamString(ParamValue):
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)]
elif self._custom_payload_type == "UuidSuffix":
return [primitives.restler_custom_payload_uuid4_suffix(self._content)]
else:
raise Exception(f"Unexpected custom payload type: {self._custom_payload_type}")
if self.is_dynamic_object:
@ -1190,6 +1196,10 @@ class ParamObjectLeaf(ParamValue):
content = dependencies.RDELIM + self._content + dependencies.RDELIM
return [primitives.restler_static_string(content)]
# If the leaf value is null, return a static string with the value null
if self._content is None:
return [primitives.restler_static_string("null")]
formalized_content = self._content.replace("'", '"')
formalized_content = formalized_content.replace('u"', '"')
@ -1278,7 +1288,7 @@ class ParamObjectLeaf(ParamValue):
"""
return fuzzer._fuzz_object_leaf(self)
class ParamEnum(ParamBase):
class ParamEnum(ParamValue):
""" Class for Enum type parameters """
def __init__(self, contents, content_type, is_required=True, body_param=True, is_dynamic_object=False,
@ -1334,13 +1344,7 @@ class ParamEnum(ParamBase):
"""
return 0
def get_original_blocks(self, config=None):
""" Gets the original request blocks for the Enum Parameters
@return: Request blocks
@rtype : List[str]
"""
def _get_fuzzable_group_values(self):
contents_str = []
for content in self._contents:
@ -1349,8 +1353,16 @@ class ParamEnum(ParamBase):
else:
content_str = content
contents_str.append(content_str)
return contents_str
return [primitives.restler_fuzzable_group(self._enum_name, contents_str)]
def get_original_blocks(self, config=None):
""" Gets the original request blocks for the Enum Parameters
@return: Request blocks
@rtype : List[str]
"""
return [primitives.restler_fuzzable_group(self._enum_name, self._get_fuzzable_group_values())]
def get_blocks(self, config=None):
""" Gets request blocks for the Enum Parameters
@ -1361,14 +1373,14 @@ class ParamEnum(ParamBase):
"""
contents_str = []
for content in self._contents:
if config is not None and config.use_constant_enum_value:
if self._is_quoted and (self.content_type in ['String', 'Uuid', 'DateTime', 'Date']):
content_str = f'"{content}"'
content_str = f'"{self._content}"'
else:
content_str = content
contents_str.append(content_str)
return [primitives.restler_fuzzable_group(FUZZABLE_GROUP_TAG, contents_str)]
content_str = self._content
return [primitives.restler_static_string(content_str)]
else:
return [primitives.restler_fuzzable_group(FUZZABLE_GROUP_TAG, self._get_fuzzable_group_values())]
def get_fuzzing_pool(self, fuzzer, config):
""" Returns the fuzzing pool

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

@ -0,0 +1,477 @@
{
"Requests": [
{
"id": {
"endpoint": "/customer",
"method": "Post"
},
"method": "Post",
"basePath": "",
"path": [
{
"Constant": [
"String",
"customer"
]
}
],
"queryParameters": [
[
"Examples",
{
"ParameterList": [
{
"name": "api-version",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Constant": [
"String",
"zzz"
]
},
"isRequired": true,
"isReadOnly": false
}
}
}
]
}
],
[
"Schema",
{
"ParameterList": [
{
"name": "api-version",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Fuzzable": {
"primitiveType": "String",
"defaultValue": "fuzzstring"
}
},
"isRequired": true,
"isReadOnly": false
}
}
}
]
}
]
],
"bodyParameters": [
[
"Examples",
{
"ParameterList": [
{
"name": "body",
"payload": {
"InternalNode": [
{
"name": "",
"propertyType": "Object",
"isRequired": true,
"isReadOnly": false
},
[
{
"LeafNode": {
"name": "id",
"payload": {
"Constant": [
"String",
"zzz"
]
},
"isRequired": false,
"isReadOnly": false
}
},
{
"InternalNode": [
{
"name": "Person",
"propertyType": "Property",
"isRequired": false,
"isReadOnly": false
},
[
{
"InternalNode": [
{
"name": "",
"propertyType": "Object",
"isRequired": false,
"isReadOnly": false
},
[
{
"LeafNode": {
"name": "name",
"payload": {
"Constant": [
"String",
"zzz"
]
},
"isRequired": true,
"isReadOnly": false
}
},
{
"LeafNode": {
"name": "address",
"payload": {
"Constant": [
"Object",
null
]
},
"isRequired": true,
"isReadOnly": false
}
}
]
]
}
]
]
}
]
]
}
}
]
}
],
[
"Schema",
{
"ParameterList": [
{
"name": "body",
"payload": {
"InternalNode": [
{
"name": "",
"propertyType": "Object",
"isRequired": true,
"isReadOnly": false
},
[
{
"LeafNode": {
"name": "id",
"payload": {
"Fuzzable": {
"primitiveType": "String",
"defaultValue": "fuzzstring"
}
},
"isRequired": false,
"isReadOnly": false
}
},
{
"InternalNode": [
{
"name": "Person",
"propertyType": "Property",
"isRequired": false,
"isReadOnly": false
},
[
{
"InternalNode": [
{
"name": "",
"propertyType": "Object",
"isRequired": false,
"isReadOnly": false
},
[
{
"LeafNode": {
"name": "name",
"payload": {
"Fuzzable": {
"primitiveType": "String",
"defaultValue": "fuzzstring"
}
},
"isRequired": true,
"isReadOnly": false
}
},
{
"LeafNode": {
"name": "address",
"payload": {
"Fuzzable": {
"primitiveType": "Object",
"defaultValue": "{ \"fuzz\": false }"
}
},
"isRequired": true,
"isReadOnly": false
}
}
]
]
}
]
]
}
]
]
}
}
]
}
]
],
"headerParameters": [
[
"Examples",
{
"ParameterList": [
{
"name": "schema-version",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Constant": [
"String",
"zzz"
]
},
"isRequired": false,
"isReadOnly": false
}
}
}
]
}
],
[
"Schema",
{
"ParameterList": [
{
"name": "schema-version",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Fuzzable": {
"primitiveType": "String",
"defaultValue": "fuzzstring"
}
},
"isRequired": false,
"isReadOnly": false
}
}
}
]
}
],
[
"DictionaryCustomPayload",
{
"ParameterList": [
{
"name": "Content-Type",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Constant": [
"String",
"application/json"
]
},
"isRequired": true,
"isReadOnly": false
}
}
}
]
}
]
],
"token": "Refreshable",
"headers": [
[
"Accept",
"application/json"
],
[
"Host",
null
]
],
"httpVersion": "1.1",
"dependencyData": {
"responseParser": {
"writerVariables": [
{
"requestId": {
"endpoint": "/customer",
"method": "Post"
},
"accessPathParts": {
"path": [
"id"
]
},
"primitiveType": "String",
"kind": "BodyResponseProperty"
}
],
"headerWriterVariables": []
},
"inputWriterVariables": [],
"orderingConstraintWriterVariables": [],
"orderingConstraintReaderVariables": []
},
"requestMetadata": {
"isLongRunningOperation": false
}
},
{
"id": {
"endpoint": "/customer/{customerId}",
"method": "Get"
},
"method": "Get",
"basePath": "",
"path": [
{
"Constant": [
"String",
"customer"
]
},
{
"DynamicObject": {
"primitiveType": "String",
"variableName": "_customer_post_id",
"isWriter": false
}
}
],
"queryParameters": [
[
"Schema",
{
"ParameterList": [
{
"name": "api-version",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Fuzzable": {
"primitiveType": "String",
"defaultValue": "fuzzstring"
}
},
"isRequired": true,
"isReadOnly": false
}
}
}
]
}
]
],
"bodyParameters": [
[
"Schema",
{
"ParameterList": []
}
]
],
"headerParameters": [
[
"Schema",
{
"ParameterList": [
{
"name": "schema-version",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Fuzzable": {
"primitiveType": "String",
"defaultValue": "fuzzstring"
}
},
"isRequired": false,
"isReadOnly": false
}
}
},
{
"name": "view-option",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Fuzzable": {
"primitiveType": {
"Enum": [
"view-option",
"String",
[
"detailed",
"minimal"
],
null
]
},
"defaultValue": "detailed"
}
},
"isRequired": false,
"isReadOnly": false
}
}
}
]
}
],
[
"DictionaryCustomPayload",
{
"ParameterList": []
}
]
],
"token": "Refreshable",
"headers": [
[
"Accept",
"application/json"
],
[
"Host",
null
]
],
"httpVersion": "1.1",
"requestMetadata": {
"isLongRunningOperation": false
}
}
]
}

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

@ -0,0 +1,124 @@
""" THIS IS AN AUTOMATICALLY GENERATED FILE!"""
from __future__ import print_function
import json
from engine import primitives
from engine.core import requests
from engine.errors import ResponseParsingException
from engine import dependencies
_customer_post_id = dependencies.DynamicVariable("_customer_post_id")
def parse_customerpost(data, **kwargs):
""" Automatically generated response parser """
# Declare response variables
temp_7262 = None
if 'headers' in kwargs:
headers = kwargs['headers']
# Parse body if needed
if data:
try:
data = json.loads(data)
except Exception as error:
raise ResponseParsingException("Exception parsing response, data was not valid json: {}".format(error))
pass
# Try to extract each dynamic object
try:
temp_7262 = str(data["id"])
except Exception as error:
# This is not an error, since some properties are not always returned
pass
# If no dynamic objects were extracted, throw.
if not (temp_7262):
raise ResponseParsingException("Error: all of the expected dynamic objects were not present in the response.")
# Set dynamic variables
if temp_7262:
dependencies.set_variable("_customer_post_id", temp_7262)
req_collection = requests.RequestCollection([])
# Endpoint: /customer, method: Post
request = requests.Request([
primitives.restler_static_string("POST "),
primitives.restler_basepath(""),
primitives.restler_static_string("/"),
primitives.restler_static_string("customer"),
primitives.restler_static_string("?"),
primitives.restler_static_string("api-version="),
primitives.restler_static_string("zzz"),
primitives.restler_static_string(" HTTP/1.1\r\n"),
primitives.restler_static_string("Accept: application/json\r\n"),
primitives.restler_static_string("Host: \r\n"),
primitives.restler_static_string("schema-version: "),
primitives.restler_static_string("zzz"),
primitives.restler_static_string("\r\n"),
primitives.restler_static_string("Content-Type: "),
primitives.restler_static_string("application/json"),
primitives.restler_static_string("\r\n"),
primitives.restler_refreshable_authentication_token("authentication_token_tag"),
primitives.restler_static_string("\r\n"),
primitives.restler_static_string("{"),
primitives.restler_static_string("""
"id":"zzz",
"Person":
{
"name":"zzz",
"address":null
}
}"""),
primitives.restler_static_string("\r\n"),
{
'post_send':
{
'parser': parse_customerpost,
'dependencies':
[
_customer_post_id.writer()
]
}
},
],
requestId="/customer"
)
req_collection.add_request(request)
# Endpoint: /customer/{customerId}, method: Get
request = requests.Request([
primitives.restler_static_string("GET "),
primitives.restler_basepath(""),
primitives.restler_static_string("/"),
primitives.restler_static_string("customer"),
primitives.restler_static_string("/"),
primitives.restler_static_string(_customer_post_id.reader(), quoted=False),
primitives.restler_static_string("?"),
primitives.restler_static_string("api-version="),
primitives.restler_fuzzable_string("fuzzstring", quoted=False),
primitives.restler_static_string(" HTTP/1.1\r\n"),
primitives.restler_static_string("Accept: application/json\r\n"),
primitives.restler_static_string("Host: \r\n"),
primitives.restler_static_string("schema-version: "),
primitives.restler_fuzzable_string("fuzzstring", quoted=False),
primitives.restler_static_string("\r\n"),
primitives.restler_static_string("view-option: "),
primitives.restler_fuzzable_group("view-option", ['detailed','minimal'] ,quoted=False),
primitives.restler_static_string("\r\n"),
primitives.restler_refreshable_authentication_token("authentication_token_tag"),
primitives.restler_static_string("\r\n"),
],
requestId="/customer/{customerId}"
)
req_collection.add_request(request)

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

@ -0,0 +1,130 @@
{
"swagger": "2.0",
"info": {
"title": "Simple Swagger with all parameter types",
"version": "1"
},
"paths": {
"/customer": {
"post": {
"operationId": "post_customer",
"examples": {
"null example": {
"parameters": {
"api-version": "zzz",
"schema-version": "zzz",
"body": {
"id": "zzz",
"Person": {
"name": "zzz",
"address": null
}
}
}
}
},
"parameters": [
{
"$ref": "#/parameters/ApiVersionParameter"
},
{
"name": "schema-version",
"type": "string",
"in": "header",
"required": false
},
{
"name": "body",
"required": true,
"in": "body",
"schema": {
"$ref": "#/definitions/Customer"
}
}
],
"responses": {
"201": {
"description": "Success",
"schema": {
"$ref": "#/definitions/Customer"
}
}
}
}
},
"/customer/{customerId}": {
"get": {
"operationId": "post_customer",
"parameters": [
{
"name": "customerId",
"type": "string",
"in": "path",
"required": true
},
{
"$ref": "#/parameters/ApiVersionParameter"
},
{
"name": "schema-version",
"type": "string",
"in": "header",
"required": false
},
{
"name": "view-option",
"type": "string",
"enum": [
"detailed",
"minimal"
],
"in": "header",
"required": false
}
],
"responses": {
"201": {
"description": "Success",
"schema": {
"$ref": "#/definitions/Customer"
}
}
}
}
}
},
"definitions": {
"Customer": {
"properties": {
"id": {
"type": "string"
},
"Person": {
"$ref": "#/definitions/Person"
}
}
},
"Person": {
"properties": {
"name": {
"type": "string"
},
"address": {
"type": "object"
}
},
"required": [
"name",
"address"
]
}
},
"parameters": {
"ApiVersionParameter": {
"name": "api-version",
"in": "query",
"type": "string",
"required": true
}
}
}

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

@ -0,0 +1,348 @@
{
"Requests": [
{
"id": {
"endpoint": "/customer",
"method": "Post"
},
"method": "Post",
"basePath": "",
"path": [
{
"Constant": [
"String",
"customer"
]
}
],
"queryParameters": [
[
"Schema",
{
"ParameterList": [
{
"name": "api-version",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Fuzzable": {
"primitiveType": "String",
"defaultValue": "fuzzstring"
}
},
"isRequired": true,
"isReadOnly": false
}
}
}
]
}
]
],
"bodyParameters": [
[
"Schema",
{
"ParameterList": [
{
"name": "body",
"payload": {
"InternalNode": [
{
"name": "",
"propertyType": "Object",
"isRequired": true,
"isReadOnly": false
},
[
{
"LeafNode": {
"name": "id",
"payload": {
"Custom": {
"payloadType": "UuidSuffix",
"primitiveType": "String",
"payloadValue": "id",
"isObject": false
}
},
"isRequired": false,
"isReadOnly": false
}
},
{
"InternalNode": [
{
"name": "Person",
"propertyType": "Property",
"isRequired": false,
"isReadOnly": false
},
[
{
"InternalNode": [
{
"name": "",
"propertyType": "Object",
"isRequired": false,
"isReadOnly": false
},
[
{
"LeafNode": {
"name": "name",
"payload": {
"Fuzzable": {
"primitiveType": "String",
"defaultValue": "fuzzstring"
}
},
"isRequired": true,
"isReadOnly": false
}
},
{
"LeafNode": {
"name": "address",
"payload": {
"Fuzzable": {
"primitiveType": "Object",
"defaultValue": "{ \"fuzz\": false }"
}
},
"isRequired": true,
"isReadOnly": false
}
}
]
]
}
]
]
}
]
]
}
}
]
}
]
],
"headerParameters": [
[
"Schema",
{
"ParameterList": [
{
"name": "schema-version",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Fuzzable": {
"primitiveType": "String",
"defaultValue": "fuzzstring"
}
},
"isRequired": false,
"isReadOnly": false
}
}
}
]
}
],
[
"DictionaryCustomPayload",
{
"ParameterList": [
{
"name": "Content-Type",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Constant": [
"String",
"application/json"
]
},
"isRequired": true,
"isReadOnly": false
}
}
}
]
}
]
],
"token": "Refreshable",
"headers": [
[
"Accept",
"application/json"
],
[
"Host",
null
]
],
"httpVersion": "1.1",
"dependencyData": {
"responseParser": {
"writerVariables": [
{
"requestId": {
"endpoint": "/customer",
"method": "Post"
},
"accessPathParts": {
"path": [
"id"
]
},
"primitiveType": "String",
"kind": "BodyResponseProperty"
}
],
"headerWriterVariables": []
},
"inputWriterVariables": [],
"orderingConstraintWriterVariables": [],
"orderingConstraintReaderVariables": []
},
"requestMetadata": {
"isLongRunningOperation": false
}
},
{
"id": {
"endpoint": "/customer/{customerId}",
"method": "Get"
},
"method": "Get",
"basePath": "",
"path": [
{
"Constant": [
"String",
"customer"
]
},
{
"DynamicObject": {
"primitiveType": "String",
"variableName": "_customer_post_id",
"isWriter": false
}
}
],
"queryParameters": [
[
"Schema",
{
"ParameterList": [
{
"name": "api-version",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Fuzzable": {
"primitiveType": "String",
"defaultValue": "fuzzstring"
}
},
"isRequired": true,
"isReadOnly": false
}
}
}
]
}
]
],
"bodyParameters": [
[
"Schema",
{
"ParameterList": []
}
]
],
"headerParameters": [
[
"Schema",
{
"ParameterList": [
{
"name": "schema-version",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Fuzzable": {
"primitiveType": "String",
"defaultValue": "fuzzstring"
}
},
"isRequired": false,
"isReadOnly": false
}
}
},
{
"name": "view-option",
"payload": {
"LeafNode": {
"name": "",
"payload": {
"Fuzzable": {
"primitiveType": {
"Enum": [
"view-option",
"String",
[
"detailed",
"minimal"
],
null
]
},
"defaultValue": "detailed"
}
},
"isRequired": false,
"isReadOnly": false
}
}
}
]
}
],
[
"DictionaryCustomPayload",
{
"ParameterList": []
}
]
],
"token": "Refreshable",
"headers": [
[
"Accept",
"application/json"
],
[
"Host",
null
]
],
"httpVersion": "1.1",
"requestMetadata": {
"isLongRunningOperation": false
}
}
]
}

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

@ -0,0 +1,131 @@
""" THIS IS AN AUTOMATICALLY GENERATED FILE!"""
from __future__ import print_function
import json
from engine import primitives
from engine.core import requests
from engine.errors import ResponseParsingException
from engine import dependencies
_customer_post_id = dependencies.DynamicVariable("_customer_post_id")
def parse_customerpost(data, **kwargs):
""" Automatically generated response parser """
# Declare response variables
temp_7262 = None
if 'headers' in kwargs:
headers = kwargs['headers']
# Parse body if needed
if data:
try:
data = json.loads(data)
except Exception as error:
raise ResponseParsingException("Exception parsing response, data was not valid json: {}".format(error))
pass
# Try to extract each dynamic object
try:
temp_7262 = str(data["id"])
except Exception as error:
# This is not an error, since some properties are not always returned
pass
# If no dynamic objects were extracted, throw.
if not (temp_7262):
raise ResponseParsingException("Error: all of the expected dynamic objects were not present in the response.")
# Set dynamic variables
if temp_7262:
dependencies.set_variable("_customer_post_id", temp_7262)
req_collection = requests.RequestCollection([])
# Endpoint: /customer, method: Post
request = requests.Request([
primitives.restler_static_string("POST "),
primitives.restler_basepath(""),
primitives.restler_static_string("/"),
primitives.restler_static_string("customer"),
primitives.restler_static_string("?"),
primitives.restler_static_string("api-version="),
primitives.restler_fuzzable_string("fuzzstring", quoted=False),
primitives.restler_static_string(" HTTP/1.1\r\n"),
primitives.restler_static_string("Accept: application/json\r\n"),
primitives.restler_static_string("Host: \r\n"),
primitives.restler_static_string("schema-version: "),
primitives.restler_fuzzable_string("fuzzstring", quoted=False),
primitives.restler_static_string("\r\n"),
primitives.restler_static_string("Content-Type: "),
primitives.restler_static_string("application/json"),
primitives.restler_static_string("\r\n"),
primitives.restler_refreshable_authentication_token("authentication_token_tag"),
primitives.restler_static_string("\r\n"),
primitives.restler_static_string("{"),
primitives.restler_static_string("""
"id":"""),
primitives.restler_static_string('"'),
primitives.restler_custom_payload_uuid4_suffix("id"),
primitives.restler_static_string("""",
"Person":
{
"name":"""),
primitives.restler_fuzzable_string("fuzzstring", quoted=True),
primitives.restler_static_string(""",
"address":"""),
primitives.restler_fuzzable_object("{ \"fuzz\": false }"),
primitives.restler_static_string("""
}
}"""),
primitives.restler_static_string("\r\n"),
{
'post_send':
{
'parser': parse_customerpost,
'dependencies':
[
_customer_post_id.writer()
]
}
},
],
requestId="/customer"
)
req_collection.add_request(request)
# Endpoint: /customer/{customerId}, method: Get
request = requests.Request([
primitives.restler_static_string("GET "),
primitives.restler_basepath(""),
primitives.restler_static_string("/"),
primitives.restler_static_string("customer"),
primitives.restler_static_string("/"),
primitives.restler_static_string(_customer_post_id.reader(), quoted=False),
primitives.restler_static_string("?"),
primitives.restler_static_string("api-version="),
primitives.restler_fuzzable_string("fuzzstring", quoted=False),
primitives.restler_static_string(" HTTP/1.1\r\n"),
primitives.restler_static_string("Accept: application/json\r\n"),
primitives.restler_static_string("Host: \r\n"),
primitives.restler_static_string("schema-version: "),
primitives.restler_fuzzable_string("fuzzstring", quoted=False),
primitives.restler_static_string("\r\n"),
primitives.restler_static_string("view-option: "),
primitives.restler_fuzzable_group("view-option", ['detailed','minimal'] ,quoted=False),
primitives.restler_static_string("\r\n"),
primitives.restler_refreshable_authentication_token("authentication_token_tag"),
primitives.restler_static_string("\r\n"),
],
requestId="/customer/{customerId}"
)
req_collection.add_request(request)

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

@ -0,0 +1,115 @@
{
"swagger": "2.0",
"info": {
"title": "Simple Swagger with all parameter types",
"version": "1"
},
"paths": {
"/customer": {
"post": {
"operationId": "post_customer",
"parameters": [
{
"$ref": "#/parameters/ApiVersionParameter"
},
{
"name": "schema-version",
"type": "string",
"in": "header",
"required": false
},
{
"name": "body",
"required": true,
"in": "body",
"schema": {
"$ref": "#/definitions/Customer"
}
}
],
"responses": {
"201": {
"description": "Success",
"schema": {
"$ref": "#/definitions/Customer"
}
}
}
}
},
"/customer/{customerId}": {
"get": {
"operationId": "post_customer",
"parameters": [
{
"name": "customerId",
"type": "string",
"in": "path",
"required": true
},
{
"$ref": "#/parameters/ApiVersionParameter"
},
{
"name": "schema-version",
"type": "string",
"in": "header",
"required": false
},
{
"name": "view-option",
"type": "string",
"enum": [
"detailed",
"minimal"
],
"in": "header",
"required": false
}
],
"responses": {
"201": {
"description": "Success",
"schema": {
"$ref": "#/definitions/Customer"
}
}
}
}
}
},
"definitions": {
"Customer": {
"properties": {
"id": {
"type": "string"
},
"Person": {
"$ref": "#/definitions/Person"
}
}
},
"Person": {
"properties": {
"name": {
"type": "string"
},
"address": {
"type": "object"
}
},
"required": [
"name",
"address"
]
}
},
"parameters": {
"ApiVersionParameter": {
"name": "api-version",
"in": "query",
"type": "string",
"required": true
}
}
}

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

@ -12,5 +12,8 @@
"reset_after_success": false
}
]
},
"test_combinations_settings": {
"max_schema_combinations": 1
}
}

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

@ -2,5 +2,8 @@
"sequence_exploration_settings": {
"create_prefix_once": [
]
},
"test_combinations_settings": {
"max_schema_combinations": 1
}
}

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

@ -0,0 +1,5 @@
{
"test_combinations_settings": {
"max_schema_combinations": 1
}
}

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

@ -1,4 +1,7 @@
{
"test_combinations_settings": {
"max_schema_combinations": 1
},
"per_resource_settings": {
"/item/{itemName}": {
"producer_timing_delay": 0,

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

@ -1 +1 @@
{"custom_value_generators": "D:\\git\\restler-fuzzer\\restler\\unit_tests\\log_baseline_test_files\\custom_value_gen.py", "max_combinations": 6}
{"custom_value_generators": "D:\\git\\restler-fuzzer\\restler\\unit_tests\\log_baseline_test_files\\custom_value_gen.py", "max_combinations": 6, "test_combinations_settings": {"max_schema_combinations": 1}}

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

@ -93,7 +93,7 @@ class FunctionalityTests(unittest.TestCase):
"Experiments directory was not deleted.")
def test_abc_invalid_b_smoke_test(self):
self.run_abc_smoke_test(Test_File_Directory, "abc_test_grammar_invalid_b.py", "directed-smoke-test")
self.run_abc_smoke_test(Test_File_Directory, "abc_test_grammar_invalid_b.py", "directed-smoke-test", settings_file="test_one_schema_settings.json")
experiments_dir = self.get_experiments_dir()
# Make sure all requests were successfully rendered. This is because the comparisons below do not
@ -170,7 +170,8 @@ class FunctionalityTests(unittest.TestCase):
"""
self.run_abc_smoke_test(Test_File_Directory, "abc_test_grammar_combinations.py", "test-all-combinations")
self.run_abc_smoke_test(Test_File_Directory, "abc_test_grammar_combinations.py", "test-all-combinations",
settings_file="test_one_schema_settings.json")
experiments_dir = self.get_experiments_dir()
## Make sure all requests were successfully rendered. This is because the comparisons below do not
@ -462,10 +463,12 @@ class FunctionalityTests(unittest.TestCase):
bugs planted for each checker, and a main driver bug, will produce the
appropriate bug buckets and the requests will be sent in the correct order.
"""
settings_file_path = os.path.join(Test_File_Directory, "test_one_schema_settings.json")
args = Common_Settings + [
'--fuzzing_mode', 'directed-smoke-test',
'--restler_grammar', f'{os.path.join(Test_File_Directory, "test_grammar_bugs.py")}',
'--enable_checkers', '*'
'--enable_checkers', '*',
'--settings', f'{settings_file_path}'
]
result = subprocess.run(args, capture_output=True)
@ -541,12 +544,15 @@ class FunctionalityTests(unittest.TestCase):
"""
Fuzz_Time = 0.1 # 6 minutes
Num_Sequences = 300
settings_file_path = os.path.join(Test_File_Directory, "test_one_schema_settings.json")
args = Common_Settings + [
'--fuzzing_mode', 'bfs-cheap',
'--restler_grammar',f'{os.path.join(Test_File_Directory, "test_grammar.py")}',
'--time_budget', f'{Fuzz_Time}',
'--enable_checkers', '*',
'--disable_checkers', 'namespacerule'
'--disable_checkers', 'namespacerule',
'--settings', f'{settings_file_path}'
]
result = subprocess.run(args, capture_output=True)
@ -578,10 +584,14 @@ class FunctionalityTests(unittest.TestCase):
unexpected changes exist.
"""
settings_file_path = os.path.join(Test_File_Directory, "test_one_schema_settings.json")
args = Common_Settings + [
'--fuzzing_mode', 'directed-smoke-test',
'--restler_grammar', f'{os.path.join(Test_File_Directory, "test_grammar.py")}',
'--enable_checkers', 'payloadbody'
'--enable_checkers', 'payloadbody',
'--settings', f'{settings_file_path}'
]
result = subprocess.run(args, capture_output=True)
@ -626,10 +636,12 @@ class FunctionalityTests(unittest.TestCase):
unexpected changes exist.
"""
settings_file_path = os.path.join(Test_File_Directory, "test_one_schema_settings.json")
args = Common_Settings + [
'--fuzzing_mode', 'directed-smoke-test',
'--restler_grammar', f'{os.path.join(Test_File_Directory, "test_grammar_body.py")}',
'--enable_checkers', 'payloadbody'
'--enable_checkers', 'payloadbody',
'--settings', f'{settings_file_path}'
]
result = subprocess.run(args, capture_output=True)

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

@ -55,20 +55,31 @@ class SchemaParserTest(unittest.TestCase):
def tearDown(self):
restler_settings.RestlerSettings.TEST_DeleteInstance()
def generate_new_request(self, req, headers_schema, query_schema, body_schema):
def generate_new_request(self, req, headers_schema, query_schema, body_schema, use_get_blocks=False):
fuzzing_config = FuzzingConfig()
query_blocks = query_schema.get_original_blocks(fuzzing_config)
if use_get_blocks:
query_blocks = query_schema.get_blocks()
else:
query_blocks = query_schema.get_original_blocks(fuzzing_config)
new_request = req.substitute_query(query_blocks)
self.assertTrue(new_request is not None)
header_blocks = headers_schema.get_original_blocks(fuzzing_config)
if use_get_blocks:
header_blocks = headers_schema.get_blocks()
else:
header_blocks = headers_schema.get_original_blocks(fuzzing_config)
new_request = new_request.substitute_headers(header_blocks)
self.assertTrue(new_request is not None)
if body_schema is not None:
body_schema.set_config(fuzzing_config) # This line is required for legacy reasons
body_blocks = body_schema.get_original_blocks(fuzzing_config)
body_schema.set_config(fuzzing_config) # This line is required for legacy reasons
if use_get_blocks:
body_blocks = body_schema.get_blocks()
else:
body_blocks = body_schema.get_original_blocks(fuzzing_config)
new_request = new_request.substitute_body(body_blocks)
self.assertTrue(new_request is not None)
@ -178,3 +189,91 @@ class SchemaParserTest(unittest.TestCase):
self.check_equivalence(req, generated_req, request_collection)
# Now generate the request with required parameters only
combination_settings = {
"max_combinations": 1,
"param_kind": "optional"
}
req_current = req
req_current = next(req_current.get_header_param_combinations(combination_settings))
req_current = next(req_current.get_query_param_combinations(combination_settings))
if req.body_schema:
req_current = next(req_current.get_body_param_combinations(combination_settings))
required_only_generated_req = req_current
original_rendering, generated_rendering =\
self.check_equivalence(req, required_only_generated_req, request_collection, equal=False)
# Confirm that none of the optional parameters are present in the generated request.
optional_param_names = {
0: ['schema-version', 'id', 'address'],
1: ['schema-version', 'view-option']
}
for optional_param in optional_param_names[idx]:
# The original rendering currently has all parameters, optional and required.
self.assertTrue(optional_param in original_rendering, optional_param)
self.assertFalse(optional_param in generated_rendering, optional_param)
def test_schema_pool_no_fuzzing(self):
"""
This test checks that the schema pool returns the correct schema when no
fuzzing has been requested.
"""
self.setup()
grammar_name = "simple_swagger_all_param_types_grammar"
schema_json_file_name = f"{grammar_name}.json"
request_collection = get_python_grammar(grammar_name)
set_grammar_schema(schema_json_file_name, request_collection)
req_with_body = next(iter(request_collection))
# Fuzz the body using the base class for fuzzing the schema, which should be a no-op.
schema_pool = JsonBodySchemaFuzzerBase().run(req_with_body.body_schema)
self.assertEqual(len(schema_pool), 1)
generated_req = self.generate_new_request(req_with_body, req_with_body.headers_schema,
req_with_body.query_schema, schema_pool[0])
self.check_equivalence(req_with_body, generated_req, request_collection)
# Now generate combinations of the body properties according to the default strategy
schema_pool_2 = JsonBodyPropertyCombinations().run(req_with_body.body_schema)
# TODO: Confirm the expected combinations are in the schema pool.
self.assertTrue(len(schema_pool_2) > 1)
pass
def test_schema_with_null_example(self):
"""Regression test for generating a python grammar from a schema with a null example. """
self.setup()
grammar_name = "null_test_example_grammar"
schema_json_file_name = f"{grammar_name}.json"
request_collection = get_python_grammar(grammar_name)
set_grammar_schema(schema_json_file_name, request_collection)
req_with_body = next(iter(request_collection))
print(f"req{req_with_body.endpoint} {req_with_body.method}")
# Just go through and get all schema combinations. This makes sure there are no crashes.
for x in req_with_body.get_schema_combinations(use_grammar_py_schema=False):
self.assertTrue(len(x.definition) > 0)
def test_schema_with_uuid4_suffix_example(self):
"""Regression test for generating a python grammar from a schema with a uuid4_suffix in the body. """
self.setup()
grammar_name = "uuidsuffix_test_grammar"
schema_json_file_name = f"{grammar_name}.json"
request_collection = get_python_grammar(grammar_name)
set_grammar_schema(schema_json_file_name, request_collection)
req_with_body = next(iter(request_collection))
print(f"req{req_with_body.endpoint} {req_with_body.method}")
# Just go through and get all schema combinations. This makes sure there are no crashes.
for x in req_with_body.get_schema_combinations(use_grammar_py_schema=False):
self.assertTrue(len(x.definition) > 0)