Skip to content

Commit 5bcf11e

Browse files
authored
fix: skip whitespace-only lines in _concatenate_shell_commands to prevent stray semicolons (caldera#3097) (#48)
* fix: skip whitespace-only lines in _concatenate_shell_commands to prevent stray semicolons When the prereq block (ending with 'fi;') is combined with the ability command via 'dep_construct \n command', a whitespace-only line between them caused _concatenate_shell_commands to append '; ' after it, producing commands like 'fi; ; ip neighbour show' (issue #3097 on mitre/caldera). Filtering out whitespace-only lines before concatenation eliminates the stray separator. A regression test is included. * fix: update regression test to use double-space pattern that triggers the actual bug The original regression test used a single-space whitespace line (' \n ') between the prereq block and the command, but the real bug (issue #3097) produced 'fi; ; ip neighbour show' with double spaces. Update the test input to ' \n ' and the assertion to check for '; ;' so the test accurately reproduces the triggering condition. * fix: use regex to assert against general whitespace-between-semicolons pattern Replace the exact string check for '; ;' (double-space only) with a regex assertion re.search(r';\s+;', result) so the regression test catches any amount of whitespace between consecutive semicolons, not just the double-space case from the original bug report.
1 parent 880231f commit 5bcf11e

2 files changed

Lines changed: 19 additions & 3 deletions

File tree

app/atomic_svc.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,12 @@ def _handle_multiline_commands(cmd, executor):
189189
@staticmethod
190190
def _concatenate_shell_commands(command_lines):
191191
"""Concatenate multiple shell command lines. The ; character won't be added at the end of each command if the
192-
command line ends in "then" or "do" or already ends with a ; character."""
192+
command line ends in "then" or "do" or already ends with a ; character.
193+
Whitespace-only lines are skipped to avoid producing stray ';' separators."""
193194
to_concat = []
194-
num_lines = len(command_lines)
195-
for index, cmd in enumerate(command_lines):
195+
non_empty_lines = [cmd for cmd in command_lines if cmd.strip()]
196+
num_lines = len(non_empty_lines)
197+
for index, cmd in enumerate(non_empty_lines):
196198
to_concat.append(cmd)
197199
if re.search(r'do\s*$', cmd) or re.search(r'then\s*$', cmd) or re.search(r';\s*$', cmd):
198200
if not re.search(r'\s+$', cmd):

tests/test_atomic_svc.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import hashlib
22
import os
3+
import re
34
import pytest
45

56
from plugins.atomic.app.atomic_svc import AtomicService
@@ -178,6 +179,19 @@ def test_handle_multiline_command_shell_ifthen(self):
178179
want = 'if condition; then innercommand; innercommand2; fi'
179180
assert AtomicService._handle_multiline_commands(commands, 'sh') == want
180181

182+
def test_handle_multiline_command_no_extra_semicolon_after_fi(self):
183+
"""Regression test for issue #3097: whitespace-only lines between prereq block
184+
(ending with 'fi;') and the ability command must not produce a stray '; ' separator,
185+
which resulted in commands like 'fi; ; ip neighbour show'."""
186+
# Simulate the precmd built by _prepare_executor:
187+
# dep_construct ends with 'fi;', then ' \n ' (two spaces) separates it from the
188+
# ability command — matching the double-space pattern of the actual bug report.
189+
commands = 'if [ -x "$(command -v ip)" ]; then : ; else apt-get install iproute2 -y; fi;\n \n ip neighbour show'
190+
result = AtomicService._handle_multiline_commands(commands, 'sh')
191+
assert not re.search(r';\s+;', result), \
192+
f"Unexpected consecutive semicolons with only whitespace between them in: {result!r}"
193+
assert 'ip neighbour show' in result
194+
181195
def test_use_default_inputs(self, atomic_svc, atomic_test):
182196
platform = 'windows'
183197
string_to_analyze = '#{recon_commands} -a'

0 commit comments

Comments
 (0)