Commit f537ec1c584dba651a5c3c0d8515f9fb19085deb
1 parent
14e68768
tests: Check behaviour of olevba for rtf, text, empty
Showing
1 changed file
with
95 additions
and
0 deletions
tests/olevba/test_basic.py
| @@ -10,14 +10,109 @@ else: | @@ -10,14 +10,109 @@ else: | ||
| 10 | from oletools import olevba3 as olevba | 10 | from oletools import olevba3 as olevba |
| 11 | import os | 11 | import os |
| 12 | from os.path import join | 12 | from os.path import join |
| 13 | +from contextlib import contextmanager | ||
| 14 | +try: | ||
| 15 | + from cStringIO import StringIO | ||
| 16 | +except ImportError: # py3: | ||
| 17 | + from io import StringIO | ||
| 13 | 18 | ||
| 14 | # Directory with test data, independent of current working directory | 19 | # Directory with test data, independent of current working directory |
| 15 | from tests.test_utils import DATA_BASE_DIR | 20 | from tests.test_utils import DATA_BASE_DIR |
| 16 | 21 | ||
| 17 | 22 | ||
| 23 | +@contextmanager | ||
| 24 | +def capture_output(): | ||
| 25 | + """ | ||
| 26 | + Temporarily replace stdout/stderr with buffers to capture output. | ||
| 27 | + | ||
| 28 | + Once we only support python>=3.4: this is already built into python as | ||
| 29 | + :py:func:`contextlib.redirect_stdout`. | ||
| 30 | + | ||
| 31 | + Not quite sure why, but seems to only work once per test function ... | ||
| 32 | + """ | ||
| 33 | + orig_stdout = sys.stdout | ||
| 34 | + orig_stderr = sys.stderr | ||
| 35 | + | ||
| 36 | + try: | ||
| 37 | + sys.stdout = StringIO() | ||
| 38 | + sys.stderr = StringIO() | ||
| 39 | + yield sys.stdout, sys.stderr | ||
| 40 | + | ||
| 41 | + finally: | ||
| 42 | + sys.stdout = orig_stdout | ||
| 43 | + sys.stderr = orig_stderr | ||
| 44 | + | ||
| 45 | + | ||
| 18 | class TestOlevbaBasic(unittest.TestCase): | 46 | class TestOlevbaBasic(unittest.TestCase): |
| 19 | """Tests olevba basic functionality""" | 47 | """Tests olevba basic functionality""" |
| 20 | 48 | ||
| 49 | + def test_text_behaviour(self): | ||
| 50 | + """Test behaviour of olevba when presented with pure text file.""" | ||
| 51 | + self.do_test_behaviour('text') | ||
| 52 | + | ||
| 53 | + def test_empty_behaviour(self): | ||
| 54 | + """Test behaviour of olevba when presented with pure text file.""" | ||
| 55 | + self.do_test_behaviour('empty') | ||
| 56 | + | ||
| 57 | + def do_test_behaviour(self, filename): | ||
| 58 | + input_file = join(DATA_BASE_DIR, 'basic', filename) | ||
| 59 | + ret_code = -1 | ||
| 60 | + | ||
| 61 | + # run olevba, capturing its output and return code | ||
| 62 | + with capture_output() as (stdout, stderr): | ||
| 63 | + with self.assertRaises(SystemExit) as raise_context: | ||
| 64 | + olevba.main([input_file, ]) | ||
| 65 | + ret_code = raise_context.exception.code | ||
| 66 | + | ||
| 67 | + # check that return code is 0 | ||
| 68 | + self.assertEqual(ret_code, 0) | ||
| 69 | + | ||
| 70 | + # check there are only warnings in stderr | ||
| 71 | + stderr = stderr.getvalue() | ||
| 72 | + skip_line = False | ||
| 73 | + for line in stderr.splitlines(): | ||
| 74 | + if skip_line: | ||
| 75 | + skip_line = False | ||
| 76 | + continue | ||
| 77 | + self.assertTrue(line.startswith('WARNING ') or | ||
| 78 | + 'ResourceWarning' in line, | ||
| 79 | + msg='Line "{}" in stderr is unexpected for {}' | ||
| 80 | + .format(line.rstrip(), filename)) | ||
| 81 | + if 'ResourceWarning' in line: | ||
| 82 | + skip_line = True | ||
| 83 | + self.assertIn('not encrypted', stderr) | ||
| 84 | + | ||
| 85 | + # check stdout | ||
| 86 | + stdout = stdout.getvalue().lower() | ||
| 87 | + self.assertIn(input_file.lower(), stdout) | ||
| 88 | + self.assertIn('type: text', stdout) | ||
| 89 | + self.assertIn('no suspicious', stdout) | ||
| 90 | + self.assertNotIn('error', stdout) | ||
| 91 | + self.assertNotIn('warn', stdout) | ||
| 92 | + | ||
| 93 | + def test_rtf_behaviour(self): | ||
| 94 | + """Test behaviour of olevba when presented with an rtf file.""" | ||
| 95 | + input_file = join(DATA_BASE_DIR, 'msodde', 'RTF-Spec-1.7.rtf') | ||
| 96 | + ret_code = -1 | ||
| 97 | + | ||
| 98 | + # run olevba, capturing its output and return code | ||
| 99 | + with capture_output() as (stdout, stderr): | ||
| 100 | + with self.assertRaises(SystemExit) as raise_context: | ||
| 101 | + olevba.main([input_file, ]) | ||
| 102 | + ret_code = raise_context.exception.code | ||
| 103 | + | ||
| 104 | + # check that return code is olevba.RETURN_OPEN_ERROR | ||
| 105 | + self.assertEqual(ret_code, 5) | ||
| 106 | + stdout = stdout.getvalue().lower() | ||
| 107 | + self.assertNotIn('error', stdout) | ||
| 108 | + self.assertNotIn('warn', stdout) | ||
| 109 | + | ||
| 110 | + stderr = stderr.getvalue().lower() | ||
| 111 | + self.assertIn('fileopenerror', stderr) | ||
| 112 | + self.assertIn('is rtf', stderr) | ||
| 113 | + self.assertIn('rtfobj.py', stderr) | ||
| 114 | + self.assertIn('not encrypted', stderr) | ||
| 115 | + | ||
| 21 | def test_crypt_return(self): | 116 | def test_crypt_return(self): |
| 22 | """ | 117 | """ |
| 23 | Tests that encrypted files give a certain return code. | 118 | Tests that encrypted files give a certain return code. |