Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/attackmate/executors/common/regexexecutor.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ def register_outputvars(self, outputvars: dict, matches):
if not matches:
self.logger.debug('no match!')
self.varstore.set_variable('REGEX_MATCHES_LIST', [])
# Clear the output variables so a stale value from a previous
# loop iteration doesn't produce a false positive when the
# current input has no match.
for k in outputvars:
self.varstore.set_variable(k, '')
return

for k, v in outputvars.items():
Expand Down Expand Up @@ -73,7 +78,7 @@ async def _exec_cmd(self, command: RegExCommand) -> Result:
if m3 is not None and isinstance(m3, Match):
self.forge_and_register_variables(command.output, m3.group())
else:
self.varstore.set_variable('REGEX_MATCHES_LIST', [])
self.forge_and_register_variables(command.output, None)
if command.mode == 'sub':
if command.replace:
replaced = re.sub(command.cmd, command.replace, self.varstore.get_str(command.input))
Expand Down
21 changes: 19 additions & 2 deletions test/units/test_regexexecutor.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ async def test_exec_cmd_findall_no_match(self):
output={'output_var': 'Found: $MATCH_0'},
)
await self.executor._exec_cmd(command)
assert 'output_var' not in self.varstore.variables
assert self.varstore.get_variable('output_var') == ''
assert self.varstore.get_variable('REGEX_MATCHES_LIST') == []

@pytest.mark.asyncio
Expand All @@ -156,7 +156,7 @@ async def test_exec_cmd_search_no_match(self):
output={'output_var': 'Found: $MATCH_0'},
)
await self.executor._exec_cmd(command)
assert 'output_var' not in self.varstore.variables
assert self.varstore.get_variable('output_var') == ''
assert self.varstore.get_variable('REGEX_MATCHES_LIST') == []

@pytest.mark.asyncio
Expand All @@ -175,3 +175,20 @@ async def test_exec_cmd_sub_no_match(self):
await self.executor._exec_cmd(command)
assert self.varstore.get_variable('output_var') == 'Replaced: no matches here'
assert self.varstore.get_variable('REGEX_MATCHES_LIST') == ['no matches here']

@pytest.mark.asyncio
async def test_exec_cmd_search_no_match_clears_stale_value(self):
# Regression: in a loop a previous iteration might set output
# variable. A following no-match must clear it, not leave the old value.
self.varstore.set_variable('PORT_STATUS', 'open')
self.varstore.set_variable('input_var', 'closed')
command = RegExCommand(
type='regex',
cmd='open',
mode='search',
input='input_var',
output={'PORT_STATUS': '$MATCH_0'},
)
await self.executor._exec_cmd(command)
assert self.varstore.get_variable('PORT_STATUS') == ''
assert self.varstore.get_variable('REGEX_MATCHES_LIST') == []
Loading