From e485ed30c7ffad07276f1ea2189bf472a5f2cc7e Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Sat, 9 Sep 2023 14:41:19 +0200 Subject: [PATCH] Rewrite parse_alphanumeric_range to fix #13722 --- netbox/utilities/forms/constants.py | 5 ++++ netbox/utilities/forms/utils.py | 41 ++++++++++------------------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/netbox/utilities/forms/constants.py b/netbox/utilities/forms/constants.py index 624ad5dac..5984494ce 100644 --- a/netbox/utilities/forms/constants.py +++ b/netbox/utilities/forms/constants.py @@ -2,6 +2,11 @@ NUMERIC_EXPANSION_PATTERN = r'\[((?:\d+[?:,-])+\d+)\]' ALPHANUMERIC_EXPANSION_PATTERN = r'\[((?:[a-zA-Z0-9]+[?:,-])+[a-zA-Z0-9]+)\]' +# Patterns for parts of string expansion patterns +ALPHABETIC_RANGE_PATTERN = fr'(?:[A-Z]-[A-Z]|[a-z]-[a-z])' +NUMERIC_RANGE_PATTERN = r'(?:[0-9]+-[0-9]+)' +ALPHANUMERIC_SINGLETON_PATTERN = r'(?:[a-zA-Z0-9]+)' + # IP address expansion patterns IP4_EXPANSION_PATTERN = r'\[((?:[0-9]{1,3}[?:,-])+[0-9]{1,3})\]' IP6_EXPANSION_PATTERN = r'\[((?:[0-9a-f]{1,4}[?:,-])+[0-9a-f]{1,4})\]' diff --git a/netbox/utilities/forms/utils.py b/netbox/utilities/forms/utils.py index 31f763bae..0cb57e787 100644 --- a/netbox/utilities/forms/utils.py +++ b/netbox/utilities/forms/utils.py @@ -48,38 +48,25 @@ def parse_alphanumeric_range(string): Expand an alphanumeric range (continuous or not) into a list. 'a-d,f' => [a, b, c, d, f] '0-3,a-d' => [0, 1, 2, 3, a, b, c, d] + '9-11' => [9, 10, 11] """ values = [] for dash_range in string.split(','): - try: - begin, end = dash_range.split('-') - vals = begin + end - # Break out of loop if there's an invalid pattern to return an error - if (not (vals.isdigit() or vals.isalpha())) or (vals.isalpha() and not (vals.isupper() or vals.islower())): - return [] - except ValueError: - begin, end = dash_range, dash_range - if begin.isdigit() and end.isdigit(): - if int(begin) >= int(end): - raise forms.ValidationError(f'Range "{dash_range}" is invalid.') - - for n in list(range(int(begin), int(end) + 1)): - values.append(n) + if re.fullmatch(ALPHABETIC_RANGE_PATTERN, dash_range): + begin, end = map(ord, dash_range.split('-')) + if begin > end: + raise forms.ValidationError(f'Range "{dash_range}" is invalid, because {begin} comes after {end}') + values.extend(map(chr, range(begin, end + 1))) + elif re.fullmatch(NUMERIC_RANGE_PATTERN, dash_range): + begin, end = map(int, dash_range.split('-')) + if begin > end: + raise forms.ValidationError(f'Range "{dash_range}" is invalid, because {begin} comes after {end}') + values.extend(map(str, range(begin, end + 1))) + elif re.fullmatch(ALPHANUMERIC_SINGLETON_PATTERN, dash_range): + values.append(dash_range) else: - # Value-based - if begin == end: - values.append(begin) - # Range-based - else: - # Not a valid range (more than a single character) - if not len(begin) == len(end) == 1: - raise forms.ValidationError(f'Range "{dash_range}" is invalid.') + raise forms.ValidationError(f'Range "{dash_range}" is invalid, must be a range of numbers (e.g. 7-11) or a range of letters (e.g. f-h or F-H)') - if ord(begin) >= ord(end): - raise forms.ValidationError(f'Range "{dash_range}" is invalid.') - - for n in list(range(ord(begin), ord(end) + 1)): - values.append(chr(n)) return values