From a765c854e5bf018b90f8eccc303ebcd31d6b20d6 Mon Sep 17 00:00:00 2001 From: Terje Kvernes Date: Tue, 28 Nov 2023 12:26:02 +0100 Subject: [PATCH 1/2] Avoid applying filtering inside of quoted strings. - If a string is quoted, don't treat | as a filter command. - Handles and matches both " and '. Fixes #177 --- mreg_cli/outputmanager.py | 50 +++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/mreg_cli/outputmanager.py b/mreg_cli/outputmanager.py index 40c6c295..6e57a854 100644 --- a/mreg_cli/outputmanager.py +++ b/mreg_cli/outputmanager.py @@ -80,28 +80,58 @@ def add_formatted_line_with_source( # We want to use re.Pattern as the type here, but python 3.6 and older re-modules # don't have that type. So we use Any instead. + def _find_split_index(self, command: str) -> int: + """Finds the index to split the command for filtering. + + It handles both single and double quotes, ensuring that the split + occurs outside of any quoted sections. + + :param command: The command string to be processed. + :return: The index at which to split the command, or -1 if not found. + """ + in_quotes = False + current_quote = None + + for i, char in enumerate(command): + if char in ["'", '"']: + if in_quotes and char == current_quote: + in_quotes = False + current_quote = None + elif not in_quotes: + in_quotes = True + current_quote = char + elif char == "|" and not in_quotes: + return i + + return -1 + def get_filter(self, command: str) -> Tuple[str, Any, bool]: """Returns the filter for the output. - :param command: The command to parse for the filter. + Parses the command string and extracts a filter if present, taking into + account both single and double quoted strings to avoid incorrect + splitting. + :param command: The command to parse for the filter. :return: The filter and whether it is a negated filter. """ - self.command = command negate = False filter_re = None - if command and "|" in command: - command, filter_str = command.split("|", 1) - filter_str = filter_str.strip() + if command: + split_index = self._find_split_index(command) + + if split_index != -1: + filter_str = command[split_index + 1 :].strip() + command = command[:split_index].strip() - if filter_str.startswith("!"): - negate = True - filter_str = filter_str[1:].strip() + if filter_str.startswith("!"): + negate = True + filter_str = filter_str[1:].strip() - filter_re = re.compile(filter_str) + filter_re = re.compile(filter_str) - return (command, filter_re, negate) + return command, filter_re, negate def lines(self) -> List[str]: """Return the lines of output. From bc59d684900e9edbafbf9ba6c0b2c5c6088c0ced Mon Sep 17 00:00:00 2001 From: Terje Kvernes Date: Tue, 28 Nov 2023 12:34:39 +0100 Subject: [PATCH 2/2] Cleanup after a bit of copy-paste. --- mreg_cli/outputmanager.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mreg_cli/outputmanager.py b/mreg_cli/outputmanager.py index 6e57a854..1d35eeb2 100644 --- a/mreg_cli/outputmanager.py +++ b/mreg_cli/outputmanager.py @@ -78,10 +78,8 @@ def add_formatted_line_with_source( formatted_line = "{1:<{0}} {2:<{0}} {3}".format(padding, key, value, source) self.add_line(formatted_line) - # We want to use re.Pattern as the type here, but python 3.6 and older re-modules - # don't have that type. So we use Any instead. def _find_split_index(self, command: str) -> int: - """Finds the index to split the command for filtering. + """Find the index to split the command for filtering. It handles both single and double quotes, ensuring that the split occurs outside of any quoted sections. @@ -105,6 +103,8 @@ def _find_split_index(self, command: str) -> int: return -1 + # We want to use re.Pattern as the type here, but python 3.6 and older re-modules + # don't have that type. So we use Any instead. def get_filter(self, command: str) -> Tuple[str, Any, bool]: """Returns the filter for the output. @@ -113,7 +113,7 @@ def get_filter(self, command: str) -> Tuple[str, Any, bool]: splitting. :param command: The command to parse for the filter. - :return: The filter and whether it is a negated filter. + :return: The command, the filter, and whether it is a negated filter. """ negate = False filter_re = None @@ -131,7 +131,7 @@ def get_filter(self, command: str) -> Tuple[str, Any, bool]: filter_re = re.compile(filter_str) - return command, filter_re, negate + return (command, filter_re, negate) def lines(self) -> List[str]: """Return the lines of output.