From 50e9294057697a53f1a0f145c982ce21d4e8ef25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubom=C3=ADr=20Sedl=C3=A1=C5=99?= Date: Wed, 12 Oct 2016 09:38:59 +0200 Subject: [PATCH] config: Validate variant regular expressions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the mapping in configuration specifies incorrect regular expression to match regular expressions, we should raise an error immediately and not wait until the part of config is actually used. This patch does not cover `live_media`, `image_build` and `osbs` sections, as they use plain dicts and not the list of tuples format. Fixes: #424 Signed-off-by: Lubomír Sedlář --- pungi/checks.py | 64 +++++++++++++++++++++++++++++--------------- tests/test_config.py | 11 ++++++++ 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/pungi/checks.py b/pungi/checks.py index e32a6994..1998ab75 100644 --- a/pungi/checks.py +++ b/pungi/checks.py @@ -38,6 +38,7 @@ When a new config option is added, the schema must be updated (see the import os.path import platform import jsonschema +import re from . import util @@ -174,7 +175,9 @@ def validate(config): """ schema = _make_schema() DefaultValidator = _extend_with_default(jsonschema.Draft4Validator) - validator = DefaultValidator(schema, {'array': (tuple, list)}) + validator = DefaultValidator(schema, + {'array': (tuple, list), + 'regex': (str, unicode)}) errors = [] for error in validator.iter_errors(config): if isinstance(error, ConfigDeprecation): @@ -220,8 +223,13 @@ UNKNOWN_SUGGEST = 'Unrecognized config option: {0}. Did you mean {1}?' def _extend_with_default(validator_class): validate_properties = validator_class.VALIDATORS["properties"] + validate_type = validator_class.VALIDATORS['type'] def set_defaults(validator, properties, instance, schema): + """ + Assign default values to options that have them defined and are not + specified. + """ for property, subschema in properties.iteritems(): if "default" in subschema and property not in instance: instance.setdefault(property, subschema["default"]) @@ -230,13 +238,32 @@ def _extend_with_default(validator_class): yield error def error_on_deprecated(validator, properties, instance, schema): + """Unconditionally raise deprecation error if encountered.""" yield ConfigDeprecation( 'use %s instead' % properties ) + def validate_regex_type(validator, properties, instance, schema): + """ + Extend standard type validation to check correctness in regular + expressions. + """ + if properties == 'regex': + try: + re.compile(instance) + except re.error as exc: + yield jsonschema.ValidationError( + 'incorrect regular expression: %s' % str(exc), + ) + else: + # Not a regular expression, delegate to original validator. + for error in validate_type(validator, properties, instance, schema): + yield error + return jsonschema.validators.extend( validator_class, {"properties": set_defaults, - "deprecated": error_on_deprecated}, + "deprecated": error_on_deprecated, + "type": validate_regex_type}, ) @@ -258,25 +285,9 @@ def _make_schema(): "additionalProperties": False, }, - "package_mapping": { - "type": "array", - "items": { - "type": "array", - "items": [ - { - "type": "string", - }, - { - "type": "object", - "patternProperties": { - ".+": {"$ref": "#/definitions/list_of_strings"}, - }, - "additionalProperties": False, - } - ], - "additionalItems": False, - }, - }, + "package_mapping": _variant_arch_mapping( + {"$ref": "#/definitions/list_of_strings"} + ), "scm_dict": { "type": "object", @@ -618,6 +629,9 @@ def _make_schema(): "live_media": { "type": "object", "patternProperties": { + # Warning: this pattern is a variant uid regex, but the + # format does not let us validate it as there is no regular + # expression to describe all regular expressions. ".+": { "type": "array", "items": { @@ -695,6 +709,9 @@ def _make_schema(): "image_build": { "type": "object", "patternProperties": { + # Warning: this pattern is a variant uid regex, but the + # format does not let us validate it as there is no regular + # expression to describe all regular expressions. ".+": { "type": "array", "items": { @@ -764,6 +781,9 @@ def _make_schema(): "osbs": { "type": "object", "patternProperties": { + # Warning: this pattern is a variant uid regex, but the + # format does not let us validate it as there is no regular + # expression to describe all regular expressions. ".+": { "type": "object", "properties": { @@ -839,7 +859,7 @@ def _variant_arch_mapping(value): "items": { "type": "array", "items": [ - {"type": "string"}, + {"type": "regex"}, { "type": "object", "patternProperties": {".+": value}, diff --git a/tests/test_config.py b/tests/test_config.py index 17bf5c6e..75971046 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -379,5 +379,16 @@ class TestSuggestions(unittest.TestCase): [checks.UNKNOWN_SUGGEST.format('product_pid', 'product_id')]) +class TestRegexValidation(unittest.TestCase): + def test_incorrect_regular_expression(self): + cfg = load_config(PKGSET_REPOS, + multilib=[('^*$', {'*': []})]) + + self.assertEqual( + checks.validate(cfg), + ['Failed validation in multilib.0.0: incorrect regular ' + 'expression: nothing to repeat']) + + if __name__ == '__main__': unittest.main()