diff --git a/netconan/sensitive_item_removal.py b/netconan/sensitive_item_removal.py index b8ebbc6..7fac3be 100644 --- a/netconan/sensitive_item_removal.py +++ b/netconan/sensitive_item_removal.py @@ -439,14 +439,20 @@ def _split_line_preserve_whitespace(line): - parts: list of alternating whitespace and word tokens - word_indices: indices of the word (non-whitespace) elements in parts """ - # Split on whitespace, keeping whitespace as separate list elements + # Split on whitespace, keeping whitespace as separate list elements. + # re.split produces empty strings at boundaries when the string starts/ends + # with a delimiter, so drop those to simplify subsequent logic. parts = re.split(r"(\s+)", line) + if parts and parts[0] == "": + parts = parts[1:] + if parts and parts[-1] == "": + parts = parts[:-1] # Identify which indices contain words (non-whitespace, non-empty) word_indices = [i for i, part in enumerate(parts) if part and not part.isspace()] # Derive leading/trailing whitespace from parts words = [parts[i] for i in word_indices] - leading = parts[0] if parts and parts[0] and parts[0].isspace() else "" - trailing = parts[-1] if parts and parts[-1] and parts[-1].isspace() else "" + leading = parts[0] if parts and parts[0].isspace() else "" + trailing = parts[-1] if parts and parts[-1].isspace() else "" return leading, words, trailing, parts, word_indices diff --git a/tests/unit/test_sensitive_item_removal.py b/tests/unit/test_sensitive_item_removal.py index 79e0bab..03b7376 100644 --- a/tests/unit/test_sensitive_item_removal.py +++ b/tests/unit/test_sensitive_item_removal.py @@ -679,6 +679,17 @@ def test_pwd_removal_preserve_trailing_whitespace(regexes, whitespace): assert processed_line.endswith(whitespace) +@pytest.mark.parametrize("whitespace", [" ", "\t", "\n", " \t\n"]) +def test_line_scrub_preserve_trailing_whitespace(regexes, whitespace): + """Test trailing whitespace is preserved when line is scrubbed (encrypted-password case).""" + # encrypted-password triggers line scrubbing (sensitive_item_num=None) + config_line = " encrypted-password SECRET{}".format(whitespace) + pwd_lookup = {} + processed_line = replace_matching_item(regexes, config_line, pwd_lookup, SALT) + assert _LINE_SCRUBBED_MESSAGE in processed_line + assert processed_line.endswith(whitespace) + + @pytest.mark.parametrize("config_line,sensitive_text", sensitive_lines) @pytest.mark.parametrize( "prepend_text",