Commit e150f8937394756aba549981732e55910f601c4f
Committed by
GitHub
Merge pull request #794 from christian-intra2net/enabled-tests-for-issue215
tests: Re-enable samples skipped because of #215
Showing
3 changed files
with
86 additions
and
50 deletions
tests/msodde/test_basic.py
| @@ -12,13 +12,11 @@ import unittest | @@ -12,13 +12,11 @@ import unittest | ||
| 12 | import sys | 12 | import sys |
| 13 | import os | 13 | import os |
| 14 | from os.path import join, basename | 14 | from os.path import join, basename |
| 15 | -from traceback import print_exc | ||
| 16 | -import json | ||
| 17 | -from collections import OrderedDict | ||
| 18 | from oletools import msodde | 15 | from oletools import msodde |
| 19 | from oletools.crypto import \ | 16 | from oletools.crypto import \ |
| 20 | WrongEncryptionPassword, CryptoLibNotImported, check_msoffcrypto | 17 | WrongEncryptionPassword, CryptoLibNotImported, check_msoffcrypto |
| 21 | -from tests.test_utils import call_and_capture, DATA_BASE_DIR as BASE_DIR | 18 | +from tests.test_utils import call_and_capture, decrypt_sample,\ |
| 19 | + DATA_BASE_DIR as BASE_DIR | ||
| 22 | 20 | ||
| 23 | 21 | ||
| 24 | class TestReturnCode(unittest.TestCase): | 22 | class TestReturnCode(unittest.TestCase): |
| @@ -26,14 +24,13 @@ class TestReturnCode(unittest.TestCase): | @@ -26,14 +24,13 @@ class TestReturnCode(unittest.TestCase): | ||
| 26 | def test_valid_doc(self): | 24 | def test_valid_doc(self): |
| 27 | """ check that a valid doc file leads to 0 exit status """ | 25 | """ check that a valid doc file leads to 0 exit status """ |
| 28 | for filename in ( | 26 | for filename in ( |
| 29 | - 'harmless-clean', | ||
| 30 | - # TODO: TEMPORARILY DISABLED UNTIL ISSUE #215 IS FIXED: | ||
| 31 | - # 'dde-test-from-office2003', | ||
| 32 | - # 'dde-test-from-office2016', | ||
| 33 | - # 'dde-test-from-office2013-utf_16le-korean' | 27 | + 'harmless-clean.doc', |
| 28 | + 'dde-test-from-office2003.doc.zip', | ||
| 29 | + 'dde-test-from-office2016.doc.zip', | ||
| 30 | + 'dde-test-from-office2013-utf_16le-korean.doc.zip', | ||
| 34 | ): | 31 | ): |
| 35 | - self.do_test_validity(join(BASE_DIR, 'msodde', | ||
| 36 | - filename + '.doc')) | 32 | + with decrypt_sample(join('msodde', filename)) as temp_name: |
| 33 | + self.do_test_validity(temp_name) | ||
| 37 | 34 | ||
| 38 | def test_valid_docx(self): | 35 | def test_valid_docx(self): |
| 39 | """ check that a valid docx file leads to 0 exit status """ | 36 | """ check that a valid docx file leads to 0 exit status """ |
| @@ -52,11 +49,11 @@ class TestReturnCode(unittest.TestCase): | @@ -52,11 +49,11 @@ class TestReturnCode(unittest.TestCase): | ||
| 52 | for filename in ( | 49 | for filename in ( |
| 53 | 'harmless-clean-2003.xml', | 50 | 'harmless-clean-2003.xml', |
| 54 | 'dde-in-excel2003.xml', | 51 | 'dde-in-excel2003.xml', |
| 55 | - # TODO: TEMPORARILY DISABLED UNTIL ISSUE #215 IS FIXED: | ||
| 56 | - # 'dde-in-word2003.xml', | ||
| 57 | - # 'dde-in-word2007.xml' | 52 | + 'dde-in-word2003.xml.zip', |
| 53 | + 'dde-in-word2007.xml.zip' | ||
| 58 | ): | 54 | ): |
| 59 | - self.do_test_validity(join(BASE_DIR, 'msodde', filename)) | 55 | + with decrypt_sample(join('msodde', filename)) as temp_name: |
| 56 | + self.do_test_validity(temp_name) | ||
| 60 | 57 | ||
| 61 | def test_invalid_none(self): | 58 | def test_invalid_none(self): |
| 62 | """ check that no file argument leads to non-zero exit status """ | 59 | """ check that no file argument leads to non-zero exit status """ |
| @@ -99,13 +96,11 @@ class TestReturnCode(unittest.TestCase): | @@ -99,13 +96,11 @@ class TestReturnCode(unittest.TestCase): | ||
| 99 | def do_test_validity(self, filename, expect_error=None): | 96 | def do_test_validity(self, filename, expect_error=None): |
| 100 | """ helper for test_[in]valid_* """ | 97 | """ helper for test_[in]valid_* """ |
| 101 | found_error = None | 98 | found_error = None |
| 102 | - # DEBUG: print('Testing file {}'.format(filename)) | ||
| 103 | try: | 99 | try: |
| 104 | msodde.process_maybe_encrypted(filename, | 100 | msodde.process_maybe_encrypted(filename, |
| 105 | - field_filter_mode=msodde.FIELD_FILTER_BLACKLIST) | 101 | + field_filter_mode=msodde.FIELD_FILTER_BLACKLIST) |
| 106 | except Exception as exc: | 102 | except Exception as exc: |
| 107 | found_error = exc | 103 | found_error = exc |
| 108 | - # DEBUG: print_exc() | ||
| 109 | 104 | ||
| 110 | if expect_error and not found_error: | 105 | if expect_error and not found_error: |
| 111 | self.fail('Expected {} but msodde finished without errors for {}' | 106 | self.fail('Expected {} but msodde finished without errors for {}' |
| @@ -145,15 +140,14 @@ class TestDdeLinks(unittest.TestCase): | @@ -145,15 +140,14 @@ class TestDdeLinks(unittest.TestCase): | ||
| 145 | """ | 140 | """ |
| 146 | return [o for o in output.splitlines()] | 141 | return [o for o in output.splitlines()] |
| 147 | 142 | ||
| 148 | - # TODO: TEMPORARILY DISABLED UNTIL ISSUE #215 IS FIXED: | ||
| 149 | - # def test_with_dde(self): | ||
| 150 | - # """ check that dde links appear on stdout """ | ||
| 151 | - # filename = 'dde-test-from-office2003.doc' | ||
| 152 | - # output = msodde.process_maybe_encrypted( | ||
| 153 | - # join(BASE_DIR, 'msodde', filename), | ||
| 154 | - # field_filter_mode=msodde.FIELD_FILTER_BLACKLIST) | ||
| 155 | - # self.assertNotEqual(len(self.get_dde_from_output(output)), 0, | ||
| 156 | - # msg='Found no dde links in output of ' + filename) | 143 | + def test_with_dde(self): |
| 144 | + """ check that dde links appear on stdout """ | ||
| 145 | + filename = 'dde-test-from-office2003.doc.zip' | ||
| 146 | + with decrypt_sample(join('msodde', filename)) as temp_file: | ||
| 147 | + output = msodde.process_maybe_encrypted(temp_file, | ||
| 148 | + field_filter_mode=msodde.FIELD_FILTER_BLACKLIST) | ||
| 149 | + self.assertNotEqual(len(self.get_dde_from_output(output)), 0, | ||
| 150 | + msg='Found no dde links in output of ' + filename) | ||
| 157 | 151 | ||
| 158 | def test_no_dde(self): | 152 | def test_no_dde(self): |
| 159 | """ check that no dde links appear on stdout """ | 153 | """ check that no dde links appear on stdout """ |
| @@ -164,15 +158,14 @@ class TestDdeLinks(unittest.TestCase): | @@ -164,15 +158,14 @@ class TestDdeLinks(unittest.TestCase): | ||
| 164 | self.assertEqual(len(self.get_dde_from_output(output)), 0, | 158 | self.assertEqual(len(self.get_dde_from_output(output)), 0, |
| 165 | msg='Found dde links in output of ' + filename) | 159 | msg='Found dde links in output of ' + filename) |
| 166 | 160 | ||
| 167 | - # TODO: TEMPORARILY DISABLED UNTIL ISSUE #215 IS FIXED: | ||
| 168 | - # def test_with_dde_utf16le(self): | ||
| 169 | - # """ check that dde links appear on stdout """ | ||
| 170 | - # filename = 'dde-test-from-office2013-utf_16le-korean.doc' | ||
| 171 | - # output = msodde.process_maybe_encrypted( | ||
| 172 | - # join(BASE_DIR, 'msodde', filename), | ||
| 173 | - # field_filter_mode=msodde.FIELD_FILTER_BLACKLIST) | ||
| 174 | - # self.assertNotEqual(len(self.get_dde_from_output(output)), 0, | ||
| 175 | - # msg='Found no dde links in output of ' + filename) | 161 | + def test_with_dde_utf16le(self): |
| 162 | + """ check that dde links appear on stdout """ | ||
| 163 | + filename = 'dde-test-from-office2013-utf_16le-korean.doc.zip' | ||
| 164 | + with decrypt_sample(join('msodde', filename)) as temp_file: | ||
| 165 | + output = msodde.process_maybe_encrypted(temp_file, | ||
| 166 | + field_filter_mode=msodde.FIELD_FILTER_BLACKLIST) | ||
| 167 | + self.assertNotEqual(len(self.get_dde_from_output(output)), 0, | ||
| 168 | + msg='Found no dde links in output of ' + filename) | ||
| 176 | 169 | ||
| 177 | def test_excel(self): | 170 | def test_excel(self): |
| 178 | """ check that dde links are found in excel 2007+ files """ | 171 | """ check that dde links are found in excel 2007+ files """ |
| @@ -188,19 +181,19 @@ class TestDdeLinks(unittest.TestCase): | @@ -188,19 +181,19 @@ class TestDdeLinks(unittest.TestCase): | ||
| 188 | 181 | ||
| 189 | def test_xml(self): | 182 | def test_xml(self): |
| 190 | """ check that dde in xml from word / excel is found """ | 183 | """ check that dde in xml from word / excel is found """ |
| 191 | - # TODO: TEMPORARILY DISABLED UNTIL ISSUE #215 IS FIXED: | ||
| 192 | - for name_part in ('excel2003',): #, 'word2003', 'word2007': | ||
| 193 | - filename = 'dde-in-' + name_part + '.xml' | ||
| 194 | - output = msodde.process_maybe_encrypted( | ||
| 195 | - join(BASE_DIR, 'msodde', filename), | ||
| 196 | - field_filter_mode=msodde.FIELD_FILTER_BLACKLIST) | ||
| 197 | - links = self.get_dde_from_output(output) | ||
| 198 | - self.assertEqual(len(links), 1, 'found {0} dde-links in {1}' | ||
| 199 | - .format(len(links), filename)) | ||
| 200 | - self.assertTrue('cmd' in links[0], 'no "cmd" in dde-link for {0}' | ||
| 201 | - .format(filename)) | ||
| 202 | - self.assertTrue('calc' in links[0], 'no "calc" in dde-link for {0}' | ||
| 203 | - .format(filename)) | 184 | + for filename in ('dde-in-excel2003.xml', |
| 185 | + 'dde-in-word2003.xml.zip', | ||
| 186 | + 'dde-in-word2007.xml.zip'): | ||
| 187 | + with decrypt_sample(join('msodde', filename)) as temp_file: | ||
| 188 | + output = msodde.process_maybe_encrypted(temp_file, | ||
| 189 | + field_filter_mode=msodde.FIELD_FILTER_BLACKLIST) | ||
| 190 | + links = self.get_dde_from_output(output) | ||
| 191 | + self.assertEqual(len(links), 1, 'found {0} dde-links in {1}' | ||
| 192 | + .format(len(links), filename)) | ||
| 193 | + self.assertTrue('cmd' in links[0], 'no "cmd" in dde-link for {0}' | ||
| 194 | + .format(filename)) | ||
| 195 | + self.assertTrue('calc' in links[0], 'no "calc" in dde-link for {0}' | ||
| 196 | + .format(filename)) | ||
| 204 | 197 | ||
| 205 | def test_clean_rtf_blacklist(self): | 198 | def test_clean_rtf_blacklist(self): |
| 206 | """ find a lot of hyperlinks in rtf spec """ | 199 | """ find a lot of hyperlinks in rtf spec """ |
tests/test_utils/__init__.py
tests/test_utils/testdata_reader.py
| @@ -7,7 +7,10 @@ using them. | @@ -7,7 +7,10 @@ using them. | ||
| 7 | """ | 7 | """ |
| 8 | 8 | ||
| 9 | import os, sys, zipfile | 9 | import os, sys, zipfile |
| 10 | -from os.path import dirname, abspath, normpath, relpath, join, basename | 10 | +from os.path import relpath, join, isfile |
| 11 | +from contextlib import contextmanager | ||
| 12 | +from tempfile import mkstemp | ||
| 13 | + | ||
| 11 | from . import DATA_BASE_DIR | 14 | from . import DATA_BASE_DIR |
| 12 | 15 | ||
| 13 | # Passwort used to encrypt problematic test samples inside a zip container | 16 | # Passwort used to encrypt problematic test samples inside a zip container |
| @@ -82,3 +85,42 @@ def loop_over_files(subdir=''): | @@ -82,3 +85,42 @@ def loop_over_files(subdir=''): | ||
| 82 | yield relative_path, read_encrypted(relative_path) | 85 | yield relative_path, read_encrypted(relative_path) |
| 83 | else: | 86 | else: |
| 84 | yield relative_path, read(relative_path) | 87 | yield relative_path, read(relative_path) |
| 88 | + | ||
| 89 | + | ||
| 90 | +@contextmanager | ||
| 91 | +def decrypt_sample(relpath): | ||
| 92 | + """ | ||
| 93 | + Decrypt test sample, save to tempfile, yield temp file name. | ||
| 94 | + | ||
| 95 | + Use as context-manager, deletes tempfile after use. | ||
| 96 | + | ||
| 97 | + If sample is not encrypted at all (filename does not end in '.zip'), | ||
| 98 | + yields absolute path to sample itself, so can apply this code also | ||
| 99 | + to non-encrypted samples. | ||
| 100 | + | ||
| 101 | + Code based on test_encoding_handler.temp_file(). | ||
| 102 | + | ||
| 103 | + :param relpath: path inside `DATA_BASE_DIR`, should end in '.zip' | ||
| 104 | + :return: absolute path name to decrypted sample. | ||
| 105 | + """ | ||
| 106 | + if not relpath.endswith('.zip'): | ||
| 107 | + yield get_path_from_root(relpath) | ||
| 108 | + else: | ||
| 109 | + tmp_descriptor = None | ||
| 110 | + tmp_name = None | ||
| 111 | + try: | ||
| 112 | + tmp_descriptor, tmp_name = mkstemp(text=False) | ||
| 113 | + with zipfile.ZipFile(get_path_from_root(relpath), 'r') as unzipper: | ||
| 114 | + # no need to iterate over blobs, our test files are all small | ||
| 115 | + os.write(tmp_descriptor, unzipper.read(unzipper.namelist()[0], | ||
| 116 | + pwd=ENCRYPTED_FILES_PASSWORD)) | ||
| 117 | + os.close(tmp_descriptor) | ||
| 118 | + tmp_descriptor = None | ||
| 119 | + yield tmp_name | ||
| 120 | + except Exception: | ||
| 121 | + raise | ||
| 122 | + finally: | ||
| 123 | + if tmp_descriptor is not None: | ||
| 124 | + os.close(tmp_descriptor) | ||
| 125 | + if tmp_name is not None and isfile(tmp_name): | ||
| 126 | + os.unlink(tmp_name) | ||
| 85 | \ No newline at end of file | 127 | \ No newline at end of file |