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 | 10 | from oletools import olevba3 as olevba |
| 11 | 11 | import os |
| 12 | 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 | 19 | # Directory with test data, independent of current working directory |
| 15 | 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 | 46 | class TestOlevbaBasic(unittest.TestCase): |
| 19 | 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 | 116 | def test_crypt_return(self): |
| 22 | 117 | """ |
| 23 | 118 | Tests that encrypted files give a certain return code. | ... | ... |