From aeb667af7a40e943db9773111bbd980e65054657 Mon Sep 17 00:00:00 2001 From: Luigi Gubello Date: Mon, 18 Sep 2023 05:25:50 +0200 Subject: [PATCH 1/3] Add Dockerfile for Py script Signed-off-by: Luigi Gubello --- validators/python/Dockerfile | 11 + validators/python/README.md | 42 ++ validators/python/requirements.txt | 6 + validators/python/validator-and-generator.py | 390 +++++++++++++++++++ 4 files changed, 449 insertions(+) create mode 100644 validators/python/Dockerfile create mode 100644 validators/python/README.md create mode 100644 validators/python/requirements.txt create mode 100644 validators/python/validator-and-generator.py diff --git a/validators/python/Dockerfile b/validators/python/Dockerfile new file mode 100644 index 0000000..8cf923e --- /dev/null +++ b/validators/python/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.11-bullseye + +WORKDIR /app + +COPY . . + +RUN pip install -r requirements.txt + +RUN chmod +x validator-and-generator.py + +RUN ln --symbolic /app/validator-and-generator.py /usr/bin/si-validator \ No newline at end of file diff --git a/validators/python/README.md b/validators/python/README.md new file mode 100644 index 0000000..23e232f --- /dev/null +++ b/validators/python/README.md @@ -0,0 +1,42 @@ +# Command-line Generator and Validator + +A Python command-line tool to help maintainters, developers, and contributors to generate or validate the SECURITY INSIGHTS yaml file. + +## Usage + +[Docker](https://www.docker.com/) needs to be installed. Build the container image: + +``` +docker build -t sec-insights . +``` + +If you want to validate a `SECURITY-INSIGHTS.yml`, run the following command: + +``` +docker run -v $(pwd)/../..:/security-insights -it sec-insights si-validator verify /security-insights/examples/security-insights-minimal-sample.yml /security-insights/security-insights-schema-1.0.0.yaml +``` + +If you want to create a new `SECURITY-INSIGHTS.yml` by complying the YAML schema, launch this command: + +``` +docker run -v $(pwd)/../..:/security-insights -it sec-insights si-validator create /security-insights/examples/security-insights-minimal-sample.yml /security-insights/security-insights-schema-1.0.0.yaml +``` + +and fill out the questionnaire by following the wizard. The questions labeled with `(optional)` are not mandatory and can be skipped. + +## Bugs + +If you find any bugs, please open an issue or submit a pull request. + +### Known Bugs + +- [ ] Value type and format are not printed in the wizard +- [ ] The script accepts just `True` or `False` for boolean values (case sensitive) +- [ ] The script supports just single-line comments + +## Security + +If you find a security vulnerability, please report it to me privately at luigi[-dot-]gubello[-at-]protonmail.com. + + + diff --git a/validators/python/requirements.txt b/validators/python/requirements.txt new file mode 100644 index 0000000..46086c5 --- /dev/null +++ b/validators/python/requirements.txt @@ -0,0 +1,6 @@ +click==8.1.3 +iteration_utilities==0.11.0 +jsonschema==4.9.0 +PyYAML==6.0 +rfc3339_validator==0.1.4 +rfc3987==1.3.8 \ No newline at end of file diff --git a/validators/python/validator-and-generator.py b/validators/python/validator-and-generator.py new file mode 100644 index 0000000..45f12e4 --- /dev/null +++ b/validators/python/validator-and-generator.py @@ -0,0 +1,390 @@ +#!/usr/bin/env python + +import collections +import click +import jsonschema +import yaml +import re +import rfc3339_validator +import datetime +import rfc3987 +import iteration_utilities + + +def validate_security_insights(security_insights_path, schema_path="./security-insights-schema-1.0.0.yaml"): + with open(schema_path, 'r') as file: + schema = file.read() + with open(security_insights_path, 'r') as file: + security_insights = file.read() + validator = jsonschema.Draft7Validator(yaml.full_load(schema), format_checker=jsonschema.draft7_format_checker) + errors = validator.iter_errors(yaml.full_load(security_insights)) + errors_json = {"errors": []} + for error in errors: + errors_json['errors'].append(error.__dict__) + return errors_json + + +def beautiful_print(json_response): + if json_response['errors']: + print('Errors') + print('-----') + for item in json_response['errors']: + print("Message: {}".format(item['message'])) + print("Relative schema path: {}".format(list(collections.deque(item['relative_schema_path'])))) + print("$id: {}".format(item['schema']['$id'])) + print('-----') + else: + print('No errors found') + + +def security_insights_yaml(yaml_dict): + # need to be improved + # really, need help :( + security_insights_json = {} + for key in yaml_dict['properties']: + if key in security_insights_generic.keys(): + if 'properties' in yaml_dict['properties'][key].keys(): + for subkey in security_insights_generic[key]: + if security_insights_generic[key][subkey] is None: + security_insights_generic[key][subkey] = security_insights_generic[subkey] + security_insights_json[key] = security_insights_generic[key] + else: + security_insights_json[key] = security_insights_generic[key][key] + remove_null_values(security_insights_generic, key) + return security_insights_json + + +def remove_null_values(mydict, key): + if type(mydict[key]) is dict: + for item in mydict[key].keys(): + if mydict[key][item] is None: + mydict[key][item] = security_insights_generic[item] + if type(mydict[key][item]) is list or type(mydict[key][item]) is dict: + remove_null_values(mydict[key], item) + if type(mydict[key]) is list: + i = 0 + for item in mydict[key]: + if type(item) is list or type(item) is dict: + remove_null_values(mydict[key], i) + i += 1 + + +def save_yaml(yaml_dict, path): + with open(path, 'w') as file: + file.write(yaml_dict) + + +printable_result = {"result": ""} + + +# this custom function should fix the norway problem caused by pyyaml +# norway is nice, it's a yaml problem +def fix_norway_problem(security_insights, num, is_list, key=None): + if type(security_insights) is dict: + counter = 0 + for my_key in security_insights.keys(): + if type(security_insights[my_key]) is dict or type(security_insights[my_key]) is list: + if is_list and not counter: + counter += 1 + printable_value = (' ' * (num-1)) + '- ' + my_key + ':' + printable_result['result'] += printable_value + '\n' + print(printable_value) + else: + if is_list: + printable_value = (' ' * (num-1)) + my_key + ':' + else: + printable_value = (' ' * num) + my_key + ':' + printable_result['result'] += printable_value + '\n' + print(printable_value) + num += 1 + if is_list and not counter: + counter += 1 + fix_norway_problem(security_insights[my_key], num, True, my_key) + else: + if not is_list: + fix_norway_problem(security_insights[my_key], num, False, my_key) + else: + num -= 1 + fix_norway_problem(security_insights[my_key], num, False, my_key) + num += 1 + num -= 1 + elif type(security_insights) is list: + i = 0 + while i < len(security_insights): + num += 1 + fix_norway_problem(security_insights[i], num, True) + i += 1 + num -= 1 + else: + if not is_list: + if isinstance(security_insights, str): + printable_value = (' ' * (num - 1)) + key + ': \'' + security_insights + '\'' + printable_result['result'] += printable_value + '\n' + print(printable_value) + else: + printable_value = (' ' * (num - 1)) + key + ': ' + str(security_insights) + printable_result['result'] += printable_value + '\n' + print(printable_value) + else: + if key is None: + if isinstance(security_insights, str): + printable_value = (' ' * (num - 2)) + '- ' + '\'' + security_insights + '\'' + printable_result['result'] += printable_value + '\n' + print(printable_value) + else: + printable_value = (' ' * (num - 2)) + '- ' + str(security_insights) + printable_result['result'] += printable_value + '\n' + print(printable_value) + else: + if isinstance(security_insights, str): + printable_value = (' ' * (num - 3)) + '- ' + key + ': \'' + security_insights + '\'' + printable_result['result'] += printable_value + '\n' + print(printable_value) + else: + printable_value = (' ' * (num - 3)) + '- ' + key + ': ' + str(security_insights) + printable_result['result'] += printable_value + '\n' + print(printable_value) + + +# defined according security-insights-schema-v1.0.0 +def unwind_if_then(dictionary): + opt_requirement = {} + for key in dictionary['if']['properties'].keys(): + opt_requirement[key] = dictionary['if']['properties'][key]['const'] + opt_requirement['required'] = dictionary['then']['required'] + return opt_requirement + + +security_insights_generic = {} + + +def unwind_properties(dictionary): + if 'properties' in dictionary: + security_insights_temp = {} + requirements = [] + opt_requirements = [] + if 'required' in dictionary: + requirements = dictionary['required'] + if 'if' in dictionary: + opt_requirements.append(unwind_if_then(dictionary)) + if 'else' in dictionary: + opt_requirements.append(unwind_if_then(dictionary['else'])) + for key in requirements: + security_insights_generic[key] = security_insights_temp + if 'items' in dictionary['properties'][key]: + security_insights_temp[key] = unwind_items(dictionary['properties'][key], True) + else: + unwind_properties(dictionary['properties'][key]) + security_insights_temp[key] = properties_dialog(dictionary['properties'][key], True) + for item in opt_requirements: + for key in item.keys(): + if key != 'required': + for element in item['required']: + if element not in requirements and security_insights_temp[key] == item[key]: + security_insights_generic[element] = security_insights_temp + if 'items' in dictionary['properties'][element]: + security_insights_temp[element] = unwind_items(dictionary['properties'][element], + True) + else: + unwind_properties(dictionary['properties'][element]) + security_insights_temp[element] = properties_dialog(dictionary['properties'][element], + True) + requirements.append(element) + for key in list(set(dictionary['properties'].keys()) - set(requirements)): + question = input("The property [{}] is optional. Do you want to add it? [Y/n]".format(key)) + if question.lower() == 'y': + security_insights_generic[key] = security_insights_temp + if 'items' in dictionary['properties'][key]: + security_insights_temp[key] = unwind_items(dictionary['properties'][key], False) + else: + unwind_properties(dictionary['properties'][key]) + security_insights_temp[key] = properties_dialog(dictionary['properties'][key], False) + if '$id' in dictionary.keys(): + security_insights_generic[str(dictionary['$id'].split('/')[-1])] = security_insights_temp + return security_insights_temp + + +def unwind_items(dictionary, required=False): + if 'items' in dictionary: + for item in dictionary['items']['anyOf']: + if 'properties' in item.keys(): + items_array = [] + if required: + items_array = [unwind_properties(dictionary['items']['anyOf'][0])] + question = input('Do you want to add a new element? [y/N]') + else: + question = 'y' + while question == 'y': + items_array.append(unwind_properties(dictionary['items']['anyOf'][0])) + question = input('Do you want to add a new element? [y/N]') + return list(iteration_utilities.unique_everseen(items_array)) + else: + if required: + item_value = properties_dialog(item, required) + else: + question = input('Do you want to add a new element? [y/N]') + if question == 'y': + item_value = properties_dialog(item, required) + else: + item_value = '' + if not item_value: + return [] + else: + items_array = [item_value] + question = input('Do you want to add a new element? [y/N]') + while question == 'y': + item_value = properties_dialog(item, False) + items_array.append(item_value) + question = input('Do you want to add a new element? [y/N]') + return list(set(items_array)) + + +def properties_dialog(schema_property, required=False): + # need to improve + if 'description' in schema_property.keys() or schema_property['$id'].split('/')[-1] == '0': + if schema_property['$id'].split('/')[-1] != '0': + if required: + print("(required) Property: {}".format(schema_property['$id'].split('/')[-1])) + else: + print("Property: {}".format(schema_property['$id'].split('/')[-1])) + else: + if required: + print("(required) Property: {}".format(schema_property['$id'].split('/')[-4])) + else: + print("Property: {}".format(schema_property['$id'].split('/')[-4])) + if 'description' in schema_property.keys(): + print("{}".format(schema_property['description'])) + value = input("Value: ") + if required: + while not value: + value = input("Value: ") + else: + if not value: + return value + if 'type' in schema_property.keys(): + type_boolean = type_check(value, schema_property['type']) + while not type_boolean: + value = input("Value: ") + type_boolean = type_check(value, schema_property['type']) + if schema_property['type'] == 'boolean': + if value == 'True': + value = bool('True') + else: + value = bool('') + if schema_property['type'] == 'integer': + value = int(value) + if 'format' in schema_property.keys(): + format_boolean = format_check(value, schema_property['format']) + while not format_boolean: + value = input("Value: ") + format_boolean = format_check(value, schema_property['format']) + if 'pattern' in schema_property.keys(): + pattern_boolean = pattern_check(value, schema_property['pattern']) + while not pattern_boolean: + value = input("Value: ") + pattern_boolean = pattern_check(value, schema_property['pattern']) + if 'enum' in schema_property.keys(): + enum_boolean = enum_check(value, schema_property['enum']) + while not enum_boolean: + value = input("Value: ") + enum_boolean = enum_check(value, schema_property['enum']) + return value + + +def type_check(value, input_type): + if input_type == 'string': + return isinstance(value, str) + if input_type == 'boolean': + if str(value) in ['True', 'False']: + return True + else: + return False + if input_type == 'integer': + return value.isdigit() + + +def format_check(value, input_format): + if input_format == 'iri': + try: + rfc3987.parse(value, rule='URI') + return True + except ValueError: + return False + if input_format == 'date-time': + return rfc3339_validator.validate_rfc3339(value) + if input_format == 'date': + try: + datetime.datetime.strptime(value, '%Y-%m-%d') + return True + except ValueError: + return False + if input_format == 'idn-email': + try: + return True + except ValueError: + return False + + +def pattern_check(value, pattern): + if re.match(pattern, value): + return True + else: + print('regex error') + return False + + +def enum_check(value, enum): + if value in enum: + return True + else: + return False + + +# not used in the proper way :( +class NaturalOrderGroup(click.Group): + def list_commands(self, ctx): + return self.commands.keys() + + +@click.group(cls=NaturalOrderGroup) +def security_insights_cli(): + """ + 🌈 SECURITY INSIGHTS Generator and Validator\n + Made with ❤️ by Luigi Gubello\n + """ + pass + + +@click.command(help='Verify the SECURITY INSIGHTS yaml according to the schema.') +@click.argument('path', nargs=1, type=click.Path(exists=True)) +@click.argument('schema', nargs=1, type=click.Path(exists=True), required=False) +@click.option('--json', 'json_dict', is_flag=True, help='Optional. Print JSON result.') +def verify(path, schema, json_dict): + if not json_dict: + response = validate_security_insights(path, schema) + beautiful_print(response) + else: + response = validate_security_insights(path, schema) + print(response) + + +@click.command(help='Create the SECURITY INSIGHTS yaml satisfying the schema.') +@click.argument('path', nargs=1, type=click.Path(), required=False, default="./SECURITY-INSIGHTS.yml") +@click.argument('schema', nargs=1, type=click.Path(exists=True), required=False, + default="./security-insights-schema-1.0.0.yaml") +def create(path, schema): + with open(schema, 'r') as file: + schema = file.read() + yaml_dict = yaml.full_load(schema) + unwind_properties(yaml_dict) + security_insights_json = security_insights_yaml(yaml_dict) + print('\nSECURITY INSIGHTS\n') + fix_norway_problem(security_insights_json, 0, False) + save_yaml(printable_result['result'][:-1], path) + + +security_insights_cli.add_command(verify) +security_insights_cli.add_command(create) + +if __name__ == "__main__": + security_insights_cli() From 99510fcb3693c1c2fec4808bc19977e708668369 Mon Sep 17 00:00:00 2001 From: Luigi Gubello Date: Mon, 18 Sep 2023 05:44:21 +0200 Subject: [PATCH 2/3] Delete files Signed-off-by: Luigi Gubello --- command-line-tool/README.md | 42 -- command-line-tool/requirements.txt | 6 - command-line-tool/validator-and-generator.py | 388 ------------------- 3 files changed, 436 deletions(-) delete mode 100644 command-line-tool/README.md delete mode 100644 command-line-tool/requirements.txt delete mode 100644 command-line-tool/validator-and-generator.py diff --git a/command-line-tool/README.md b/command-line-tool/README.md deleted file mode 100644 index 0a40cd7..0000000 --- a/command-line-tool/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Command-line Generator and Validator - -A Python command-line tool to help maintainters, developers, and contributors to generate or validate the SECURITY INSIGHTS yaml file. - -## Usage - -Install the requirements using `pip` - -``` -pip install -r requirements.txt --user -``` - -If you want to validate a `SECURITY-INSIGHTS.yml`, launch the following command: - -``` -python validator-and-generator.py verify /path/to/SECURITY-INSIGHTS.yml /path/to/security-insights-schema.yml -``` - -If you want to create a new `SECURITY-INSIGHTS.yml` by complying the YAML schema, launch this command: - -``` -python validator-and-generator.py create /path/to/save/SECURITY-INSIGHTS.yml /path/to/security-insights-schema.yml -``` - -and fill out the questionnaire by following the wizard. The questions labeled with `(optional)` are not mandatory and can be skipped. - -## Bugs - -If you find any bugs, please open an issue or submit a pull request. - -### Known Bugs - -- [ ] Value type and format are not printed in the wizard -- [ ] The script accepts just `True` or `False` for boolean values (case sensitive) -- [ ] The script supports just single-line comments - -## Security - -If you find a security vulnerability, please report it to me privately at luigi[-dot-]gubello[-at-]protonmail.com. - - - diff --git a/command-line-tool/requirements.txt b/command-line-tool/requirements.txt deleted file mode 100644 index 46086c5..0000000 --- a/command-line-tool/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -click==8.1.3 -iteration_utilities==0.11.0 -jsonschema==4.9.0 -PyYAML==6.0 -rfc3339_validator==0.1.4 -rfc3987==1.3.8 \ No newline at end of file diff --git a/command-line-tool/validator-and-generator.py b/command-line-tool/validator-and-generator.py deleted file mode 100644 index 4a18b8c..0000000 --- a/command-line-tool/validator-and-generator.py +++ /dev/null @@ -1,388 +0,0 @@ -import collections -import click -import jsonschema -import yaml -import re -import rfc3339_validator -import datetime -import rfc3987 -import iteration_utilities - - -def validate_security_insights(security_insights_path, schema_path="./security-insights-schema-1.0.0.yaml"): - with open(schema_path, 'r') as file: - schema = file.read() - with open(security_insights_path, 'r') as file: - security_insights = file.read() - validator = jsonschema.Draft7Validator(yaml.full_load(schema), format_checker=jsonschema.draft7_format_checker) - errors = validator.iter_errors(yaml.full_load(security_insights)) - errors_json = {"errors": []} - for error in errors: - errors_json['errors'].append(error.__dict__) - return errors_json - - -def beautiful_print(json_response): - if json_response['errors']: - print('Errors') - print('-----') - for item in json_response['errors']: - print("Message: {}".format(item['message'])) - print("Relative schema path: {}".format(list(collections.deque(item['relative_schema_path'])))) - print("$id: {}".format(item['schema']['$id'])) - print('-----') - else: - print('No errors found') - - -def security_insights_yaml(yaml_dict): - # need to be improved - # really, need help :( - security_insights_json = {} - for key in yaml_dict['properties']: - if key in security_insights_generic.keys(): - if 'properties' in yaml_dict['properties'][key].keys(): - for subkey in security_insights_generic[key]: - if security_insights_generic[key][subkey] is None: - security_insights_generic[key][subkey] = security_insights_generic[subkey] - security_insights_json[key] = security_insights_generic[key] - else: - security_insights_json[key] = security_insights_generic[key][key] - remove_null_values(security_insights_generic, key) - return security_insights_json - - -def remove_null_values(mydict, key): - if type(mydict[key]) is dict: - for item in mydict[key].keys(): - if mydict[key][item] is None: - mydict[key][item] = security_insights_generic[item] - if type(mydict[key][item]) is list or type(mydict[key][item]) is dict: - remove_null_values(mydict[key], item) - if type(mydict[key]) is list: - i = 0 - for item in mydict[key]: - if type(item) is list or type(item) is dict: - remove_null_values(mydict[key], i) - i += 1 - - -def save_yaml(yaml_dict, path): - with open(path, 'w') as file: - file.write(yaml_dict) - - -printable_result = {"result": ""} - - -# this custom function should fix the norway problem caused by pyyaml -# norway is nice, it's a yaml problem -def fix_norway_problem(security_insights, num, is_list, key=None): - if type(security_insights) is dict: - counter = 0 - for my_key in security_insights.keys(): - if type(security_insights[my_key]) is dict or type(security_insights[my_key]) is list: - if is_list and not counter: - counter += 1 - printable_value = (' ' * (num-1)) + '- ' + my_key + ':' - printable_result['result'] += printable_value + '\n' - print(printable_value) - else: - if is_list: - printable_value = (' ' * (num-1)) + my_key + ':' - else: - printable_value = (' ' * num) + my_key + ':' - printable_result['result'] += printable_value + '\n' - print(printable_value) - num += 1 - if is_list and not counter: - counter += 1 - fix_norway_problem(security_insights[my_key], num, True, my_key) - else: - if not is_list: - fix_norway_problem(security_insights[my_key], num, False, my_key) - else: - num -= 1 - fix_norway_problem(security_insights[my_key], num, False, my_key) - num += 1 - num -= 1 - elif type(security_insights) is list: - i = 0 - while i < len(security_insights): - num += 1 - fix_norway_problem(security_insights[i], num, True) - i += 1 - num -= 1 - else: - if not is_list: - if isinstance(security_insights, str): - printable_value = (' ' * (num - 1)) + key + ': \'' + security_insights + '\'' - printable_result['result'] += printable_value + '\n' - print(printable_value) - else: - printable_value = (' ' * (num - 1)) + key + ': ' + str(security_insights) - printable_result['result'] += printable_value + '\n' - print(printable_value) - else: - if key is None: - if isinstance(security_insights, str): - printable_value = (' ' * (num - 2)) + '- ' + '\'' + security_insights + '\'' - printable_result['result'] += printable_value + '\n' - print(printable_value) - else: - printable_value = (' ' * (num - 2)) + '- ' + str(security_insights) - printable_result['result'] += printable_value + '\n' - print(printable_value) - else: - if isinstance(security_insights, str): - printable_value = (' ' * (num - 3)) + '- ' + key + ': \'' + security_insights + '\'' - printable_result['result'] += printable_value + '\n' - print(printable_value) - else: - printable_value = (' ' * (num - 3)) + '- ' + key + ': ' + str(security_insights) - printable_result['result'] += printable_value + '\n' - print(printable_value) - - -# defined according security-insights-schema-v1.0.0 -def unwind_if_then(dictionary): - opt_requirement = {} - for key in dictionary['if']['properties'].keys(): - opt_requirement[key] = dictionary['if']['properties'][key]['const'] - opt_requirement['required'] = dictionary['then']['required'] - return opt_requirement - - -security_insights_generic = {} - - -def unwind_properties(dictionary): - if 'properties' in dictionary: - security_insights_temp = {} - requirements = [] - opt_requirements = [] - if 'required' in dictionary: - requirements = dictionary['required'] - if 'if' in dictionary: - opt_requirements.append(unwind_if_then(dictionary)) - if 'else' in dictionary: - opt_requirements.append(unwind_if_then(dictionary['else'])) - for key in requirements: - security_insights_generic[key] = security_insights_temp - if 'items' in dictionary['properties'][key]: - security_insights_temp[key] = unwind_items(dictionary['properties'][key], True) - else: - unwind_properties(dictionary['properties'][key]) - security_insights_temp[key] = properties_dialog(dictionary['properties'][key], True) - for item in opt_requirements: - for key in item.keys(): - if key != 'required': - for element in item['required']: - if element not in requirements and security_insights_temp[key] == item[key]: - security_insights_generic[element] = security_insights_temp - if 'items' in dictionary['properties'][element]: - security_insights_temp[element] = unwind_items(dictionary['properties'][element], - True) - else: - unwind_properties(dictionary['properties'][element]) - security_insights_temp[element] = properties_dialog(dictionary['properties'][element], - True) - requirements.append(element) - for key in list(set(dictionary['properties'].keys()) - set(requirements)): - question = input("The property [{}] is optional. Do you want to add it? [Y/n]".format(key)) - if question.lower() == 'y': - security_insights_generic[key] = security_insights_temp - if 'items' in dictionary['properties'][key]: - security_insights_temp[key] = unwind_items(dictionary['properties'][key], False) - else: - unwind_properties(dictionary['properties'][key]) - security_insights_temp[key] = properties_dialog(dictionary['properties'][key], False) - if '$id' in dictionary.keys(): - security_insights_generic[str(dictionary['$id'].split('/')[-1])] = security_insights_temp - return security_insights_temp - - -def unwind_items(dictionary, required=False): - if 'items' in dictionary: - for item in dictionary['items']['anyOf']: - if 'properties' in item.keys(): - items_array = [] - if required: - items_array = [unwind_properties(dictionary['items']['anyOf'][0])] - question = input('Do you want to add a new element? [y/N]') - else: - question = 'y' - while question == 'y': - items_array.append(unwind_properties(dictionary['items']['anyOf'][0])) - question = input('Do you want to add a new element? [y/N]') - return list(iteration_utilities.unique_everseen(items_array)) - else: - if required: - item_value = properties_dialog(item, required) - else: - question = input('Do you want to add a new element? [y/N]') - if question == 'y': - item_value = properties_dialog(item, required) - else: - item_value = '' - if not item_value: - return [] - else: - items_array = [item_value] - question = input('Do you want to add a new element? [y/N]') - while question == 'y': - item_value = properties_dialog(item, False) - items_array.append(item_value) - question = input('Do you want to add a new element? [y/N]') - return list(set(items_array)) - - -def properties_dialog(schema_property, required=False): - # need to improve - if 'description' in schema_property.keys() or schema_property['$id'].split('/')[-1] == '0': - if schema_property['$id'].split('/')[-1] != '0': - if required: - print("(required) Property: {}".format(schema_property['$id'].split('/')[-1])) - else: - print("Property: {}".format(schema_property['$id'].split('/')[-1])) - else: - if required: - print("(required) Property: {}".format(schema_property['$id'].split('/')[-4])) - else: - print("Property: {}".format(schema_property['$id'].split('/')[-4])) - if 'description' in schema_property.keys(): - print("{}".format(schema_property['description'])) - value = input("Value: ") - if required: - while not value: - value = input("Value: ") - else: - if not value: - return value - if 'type' in schema_property.keys(): - type_boolean = type_check(value, schema_property['type']) - while not type_boolean: - value = input("Value: ") - type_boolean = type_check(value, schema_property['type']) - if schema_property['type'] == 'boolean': - if value == 'True': - value = bool('True') - else: - value = bool('') - if schema_property['type'] == 'integer': - value = int(value) - if 'format' in schema_property.keys(): - format_boolean = format_check(value, schema_property['format']) - while not format_boolean: - value = input("Value: ") - format_boolean = format_check(value, schema_property['format']) - if 'pattern' in schema_property.keys(): - pattern_boolean = pattern_check(value, schema_property['pattern']) - while not pattern_boolean: - value = input("Value: ") - pattern_boolean = pattern_check(value, schema_property['pattern']) - if 'enum' in schema_property.keys(): - enum_boolean = enum_check(value, schema_property['enum']) - while not enum_boolean: - value = input("Value: ") - enum_boolean = enum_check(value, schema_property['enum']) - return value - - -def type_check(value, input_type): - if input_type == 'string': - return isinstance(value, str) - if input_type == 'boolean': - if str(value) in ['True', 'False']: - return True - else: - return False - if input_type == 'integer': - return value.isdigit() - - -def format_check(value, input_format): - if input_format == 'iri': - try: - rfc3987.parse(value, rule='URI') - return True - except ValueError: - return False - if input_format == 'date-time': - return rfc3339_validator.validate_rfc3339(value) - if input_format == 'date': - try: - datetime.datetime.strptime(value, '%Y-%m-%d') - return True - except ValueError: - return False - if input_format == 'idn-email': - try: - return True - except ValueError: - return False - - -def pattern_check(value, pattern): - if re.match(pattern, value): - return True - else: - print('regex error') - return False - - -def enum_check(value, enum): - if value in enum: - return True - else: - return False - - -# not used in the proper way :( -class NaturalOrderGroup(click.Group): - def list_commands(self, ctx): - return self.commands.keys() - - -@click.group(cls=NaturalOrderGroup) -def security_insights_cli(): - """ - 🌈 SECURITY INSIGHTS Generator and Validator\n - Made with ❤️ by Luigi Gubello\n - """ - pass - - -@click.command(help='Verify the SECURITY INSIGHTS yaml according to the schema.') -@click.argument('path', nargs=1, type=click.Path(exists=True)) -@click.argument('schema', nargs=1, type=click.Path(exists=True), required=False) -@click.option('--json', 'json_dict', is_flag=True, help='Optional. Print JSON result.') -def verify(path, schema, json_dict): - if not json_dict: - response = validate_security_insights(path, schema) - beautiful_print(response) - else: - response = validate_security_insights(path, schema) - print(response) - - -@click.command(help='Create the SECURITY INSIGHTS yaml satisfying the schema.') -@click.argument('path', nargs=1, type=click.Path(), required=False, default="./SECURITY-INSIGHTS.yml") -@click.argument('schema', nargs=1, type=click.Path(exists=True), required=False, - default="./security-insights-schema-1.0.0.yaml") -def create(path, schema): - with open(schema, 'r') as file: - schema = file.read() - yaml_dict = yaml.full_load(schema) - unwind_properties(yaml_dict) - security_insights_json = security_insights_yaml(yaml_dict) - print('\nSECURITY INSIGHTS\n') - fix_norway_problem(security_insights_json, 0, False) - save_yaml(printable_result['result'][:-1], path) - - -security_insights_cli.add_command(verify) -security_insights_cli.add_command(create) - -if __name__ == "__main__": - security_insights_cli() From 7b2b88c4d104cf34211ff9bda30adde5f8806224 Mon Sep 17 00:00:00 2001 From: Luigi Gubello Date: Mon, 18 Sep 2023 05:50:15 +0200 Subject: [PATCH 3/3] edit README Signed-off-by: Luigi Gubello --- validators/python/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validators/python/README.md b/validators/python/README.md index 23e232f..43966e6 100644 --- a/validators/python/README.md +++ b/validators/python/README.md @@ -36,7 +36,7 @@ If you find any bugs, please open an issue or submit a pull request. ## Security -If you find a security vulnerability, please report it to me privately at luigi[-dot-]gubello[-at-]protonmail.com. +If you find a security vulnerability, please report it via [GitHub private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability).