Skip to content
Open
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 oletools/olevba.py
Original file line number Diff line number Diff line change
Expand Up @@ -640,8 +640,13 @@ def __init__(self, stream_path, variable, expected, value):
'Runs when the file is opened (using InkPicture ActiveX object)':
# ref:https://twitter.com/joe4security/status/770691099988025345
(r'\w+_Painted', r'\w+_Painting'),
# Interaction-only events. A \w+_Click handler fires when the user clicks an
# embedded control, never on document load, so it must not be described as
# running when the file is opened. Detection is unchanged (added in #499).
'Runs when the user interacts with an embedded ActiveX control (e.g. clicks a button)':
(r'\w+_Click',),
'Runs when the file is opened and ActiveX objects trigger events':
(r'\w+_GotFocus', r'\w+_LostFocus', r'\w+_MouseHover', r'\w+_Click',
(r'\w+_GotFocus', r'\w+_LostFocus', r'\w+_MouseHover',
r'\w+_Change', r'\w+_Resize', r'\w+_BeforeNavigate2', r'\w+_BeforeScriptExecute',
r'\w+_DocumentComplete', r'\w+_DownloadBegin', r'\w+_DownloadComplete',
r'\w+_FileDownload', r'\w+_NavigateComplete2', r'\w+_NavigateError',
Expand Down
76 changes: 76 additions & 0 deletions tests/olevba/test_autoexec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""
Tests for detect_autoexec keyword classification in olevba.

Covers the interaction-only control events that were previously described as
"Runs when the file is opened ..." (see issue #891): detection must not change,
only the description, so that downstream consumers do not read a user-clicked
button as an auto-executing entry point.
"""

import unittest

from oletools.olevba import detect_autoexec


# Descriptions that assert the code runs when the document is opened. Used to
# check that interaction-only events are not reported with one of these.
ON_OPEN_MARKERS = ('file is opened', 'document is opened', 'Workbook is opened')

CLICK_DESCRIPTION = (
'Runs when the user interacts with an embedded ActiveX control '
'(e.g. clicks a button)'
)


class TestAutoexecInteractionEvents(unittest.TestCase):
"""detect_autoexec must describe user-interaction events accurately"""

def test_click_event_is_not_described_as_on_open(self):
"""A _Click handler is described as interaction, not as on-open."""
code = 'Private Sub CommandButton1_Click()\nEnd Sub\n'
results = detect_autoexec(code)

self.assertEqual(len(results), 1, msg='expected exactly one match: %r' % (results, ))
keyword, description = results[0]
self.assertEqual(keyword, 'CommandButton1_Click')
self.assertEqual(description, CLICK_DESCRIPTION)
for marker in ON_OPEN_MARKERS:
self.assertNotIn(marker, description)

def test_click_event_is_still_detected(self):
"""Splitting the description must not drop the detection from #499."""
code = 'Sub CommandButton1_Click()\nEnd Sub'
keywords = [keyword for keyword, _desc in detect_autoexec(code)]
self.assertIn('CommandButton1_Click', keywords)

def test_gotfocus_event_keeps_the_on_open_description(self):
"""Other control events still use the on-open description."""
code = 'Sub TextBox1_GotFocus()\nEnd Sub'
results = detect_autoexec(code)
self.assertEqual(len(results), 1, msg='expected exactly one match: %r' % (results, ))
_keyword, description = results[0]
self.assertEqual(
description, 'Runs when the file is opened and ActiveX objects trigger events'
)

def test_workbook_open_keeps_the_on_open_description(self):
"""Plain workbook-open handlers are unaffected by this change."""
code = 'Private Sub Workbook_Open()\nEnd Sub'
results = detect_autoexec(code)
self.assertEqual(len(results), 1, msg='expected exactly one match: %r' % (results, ))
_keyword, description = results[0]
self.assertEqual(description, 'Runs when the Excel Workbook is opened')

def test_click_and_gotfocus_are_reported_separately(self):
"""Both events are reported, each with its own description."""
code = 'Sub CommandButton1_Click()\nEnd Sub\nSub TextBox1_GotFocus()\nEnd Sub'
results = dict(detect_autoexec(code))
self.assertEqual(results.get('CommandButton1_Click'), CLICK_DESCRIPTION)
self.assertEqual(
results.get('TextBox1_GotFocus'),
'Runs when the file is opened and ActiveX objects trigger events',
)


if __name__ == '__main__':
unittest.main()