Skip to content

Commit b3db1aa

Browse files
committed
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.
1 parent b909588 commit b3db1aa

2 files changed

Lines changed: 16 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: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,17 @@ def test_handle_multiline_command_shell_ifthen(self):
178178
want = 'if condition; then innercommand; innercommand2; fi'
179179
assert AtomicService._handle_multiline_commands(commands, 'sh') == want
180180

181+
def test_handle_multiline_command_no_extra_semicolon_after_fi(self):
182+
"""Regression test for issue #3097: whitespace-only lines between prereq block
183+
(ending with 'fi;') and the ability command must not produce a stray '; ' separator,
184+
which resulted in commands like 'fi; ; ip neighbour show'."""
185+
# Simulate the precmd built by _prepare_executor:
186+
# dep_construct ends with 'fi;', then ' \n ' separates it from the ability command.
187+
commands = 'if [ -x "$(command -v ip)" ]; then : ; else apt-get install iproute2 -y; fi;\n \n ip neighbour show'
188+
result = AtomicService._handle_multiline_commands(commands, 'sh')
189+
assert '; ;' not in result, f"Unexpected stray semicolon in: {result!r}"
190+
assert 'ip neighbour show' in result
191+
181192
def test_use_default_inputs(self, atomic_svc, atomic_test):
182193
platform = 'windows'
183194
string_to_analyze = '#{recon_commands} -a'

0 commit comments

Comments
 (0)