1
0
Fork 0
mirror of https://git.code.sf.net/p/archivemail/code synced 2025-01-03 05:34:58 +00:00

test_archivemail.py: fixed unsafe creation of temporary files.

Derive all testcases that create temporary files from the new class
TestCaseInTempdir, which provides standard fixtures to set up a secure temporary
root directory for tempfiles and cleaning up afterwards.  This also simplifies
the code. 

This addresses Debian bug #385253, and reading the BTS log, it seems this issue
was assigned CVE-2006-4245, although I cannot find any further reference to that
CVE.  Note that the bug was initially reported to affect archivemail itself,
too.  This is not correct.  There *are* race conditions with archivemail, but
they were not subject of that report, and are not that critical.

Also bumped python dependency to version 2.3 since we use tempfile.mkstemp() and
other recent stuff.
This commit is contained in:
Nikolaus Schulz 2006-10-27 02:12:45 +00:00
parent 0a666a10a6
commit e491da3a11

View file

@ -33,22 +33,20 @@ TODO: add tests for:
import sys import sys
def check_python_version(): def check_python_version():
"""Abort if we are running on python < v2.1""" """Abort if we are running on python < v2.3"""
too_old_error = """This test script requires python version 2.1 or later. too_old_error = "This test script requires python version 2.3 or later. " + \
This is because it requires the pyUnit 'unittest' module, which only got "Your version of python is:\n%s" % sys.version
released in python version 2.1. You should still be able to run archivemail on
python versions 2.0 and above, however -- just not test it.
Your version of python is: %s""" % sys.version
try: try:
version = sys.version_info # we might not even have this function! :) version = sys.version_info # we might not even have this function! :)
if (version[0] < 2) or ((version[0] == 2) and (version[1] < 1)): if (version[0] < 2) or (version[0] == 2 and version[1] < 3):
print too_old_error print too_old_error
sys.exit(1) sys.exit(1)
except AttributeError: except AttributeError:
print too_old_error print too_old_error
sys.exit(1) sys.exit(1)
check_python_version() # define & run this early because 'unittest' is new # define & run this early because 'unittest' requires Python >= 2.1
check_python_version()
import copy import copy
import fcntl import fcntl
@ -72,11 +70,37 @@ except ImportError:
sys.exit(1) sys.exit(1)
class TestCaseInTempdir(unittest.TestCase):
"""Base class for testcases that need to create temporary files.
All testcases that create temporary files should be derived from this
class, not directly from unittest.TestCase.
TestCaseInTempdir provides these methods:
setUp() Creates a safe temporary directory and sets tempfile.tempdir.
tearDown() Recursively removes the temporary directory and unsets
tempfile.tempdir.
Overriding methods should call the ones above."""
temproot = None
def setUp(self):
if not self.temproot:
assert(not tempfile.tempdir)
self.temproot = tempfile.tempdir = tempfile.mkdtemp()
def tearDown(self):
assert(tempfile.tempdir == self.temproot)
if self.temproot:
shutil.rmtree(self.temproot)
tempfile.tempdir = self.temproot = None
############ Mbox Class testing ############## ############ Mbox Class testing ##############
class TestMboxIsEmpty(unittest.TestCase): class TestMboxIsEmpty(TestCaseInTempdir):
def setUp(self): def setUp(self):
super(TestMboxIsEmpty, self).setUp()
self.empty_name = make_mbox(messages=0) self.empty_name = make_mbox(messages=0)
self.not_empty_name = make_mbox(messages=1) self.not_empty_name = make_mbox(messages=1)
@ -90,14 +114,9 @@ class TestMboxIsEmpty(unittest.TestCase):
mbox = archivemail.Mbox(self.not_empty_name) mbox = archivemail.Mbox(self.not_empty_name)
assert(not mbox.is_empty()) assert(not mbox.is_empty())
def tearDown(self): class TestMboxLeaveEmpty(TestCaseInTempdir):
for name in (self.empty_name, self.not_empty_name):
if os.path.exists(name):
os.remove(name)
class TestMboxLeaveEmpty(unittest.TestCase):
def setUp(self): def setUp(self):
super(TestMboxLeaveEmpty, self).setUp()
self.mbox_name = make_mbox() self.mbox_name = make_mbox()
self.mbox_mode = os.stat(self.mbox_name)[stat.ST_MODE] self.mbox_mode = os.stat(self.mbox_name)[stat.ST_MODE]
self.mbox = archivemail.Mbox(self.mbox_name) self.mbox = archivemail.Mbox(self.mbox_name)
@ -110,13 +129,9 @@ class TestMboxLeaveEmpty(unittest.TestCase):
new_mode = os.stat(self.mbox_name)[stat.ST_MODE] new_mode = os.stat(self.mbox_name)[stat.ST_MODE]
self.assertEqual(new_mode, self.mbox_mode) self.assertEqual(new_mode, self.mbox_mode)
def tearDown(self): class TestMboxProcmailLock(TestCaseInTempdir):
if os.path.exists(self.mbox_name):
os.remove(self.mbox_name)
class TestMboxProcmailLock(unittest.TestCase):
def setUp(self): def setUp(self):
super(TestMboxProcmailLock, self).setUp()
self.mbox_name = make_mbox() self.mbox_name = make_mbox()
self.mbox_mode = os.stat(self.mbox_name)[stat.ST_MODE] self.mbox_mode = os.stat(self.mbox_name)[stat.ST_MODE]
self.mbox = archivemail.Mbox(self.mbox_name) self.mbox = archivemail.Mbox(self.mbox_name)
@ -130,13 +145,9 @@ class TestMboxProcmailLock(unittest.TestCase):
self.mbox.procmail_unlock() self.mbox.procmail_unlock()
assert(not os.path.isfile(lock)) assert(not os.path.isfile(lock))
def tearDown(self): class TestMboxRemove(TestCaseInTempdir):
if os.path.exists(self.mbox_name):
os.remove(self.mbox_name)
class TestMboxRemove(unittest.TestCase):
def setUp(self): def setUp(self):
super(TestMboxRemove, self).setUp()
self.mbox_name = make_mbox() self.mbox_name = make_mbox()
self.mbox = archivemail.Mbox(self.mbox_name) self.mbox = archivemail.Mbox(self.mbox_name)
@ -146,13 +157,10 @@ class TestMboxRemove(unittest.TestCase):
self.mbox.remove() self.mbox.remove()
assert(not os.path.exists(self.mbox_name)) assert(not os.path.exists(self.mbox_name))
def tearDown(self):
if os.path.exists(self.mbox_name):
os.remove(self.mbox_name)
class TestMboxExclusiveLock(TestCaseInTempdir):
class TestMboxExclusiveLock(unittest.TestCase):
def setUp(self): def setUp(self):
super(TestMboxExclusiveLock, self).setUp()
self.mbox_name = make_mbox() self.mbox_name = make_mbox()
self.mbox = archivemail.Mbox(self.mbox_name) self.mbox = archivemail.Mbox(self.mbox_name)
@ -198,13 +206,10 @@ class TestMboxExclusiveLock(unittest.TestCase):
fcntl.flock(fd, fcntl.LOCK_UN) fcntl.flock(fd, fcntl.LOCK_UN)
# END robbery # END robbery
def tearDown(self):
if os.path.exists(self.mbox_name):
os.remove(self.mbox_name)
class TestMboxNext(TestCaseInTempdir):
class TestMboxNext(unittest.TestCase):
def setUp(self): def setUp(self):
super(TestMboxNext, self).setUp()
self.not_empty_name = make_mbox(messages=18) self.not_empty_name = make_mbox(messages=18)
self.empty_name = make_mbox(messages=0) self.empty_name = make_mbox(messages=0)
@ -223,14 +228,10 @@ class TestMboxNext(unittest.TestCase):
msg = mbox.next() msg = mbox.next()
self.assertEqual(msg, None) self.assertEqual(msg, None)
def tearDown(self):
for name in (self.not_empty_name, self.empty_name):
if os.path.exists(name):
os.remove(name)
class TestMboxWrite(TestCaseInTempdir):
class TestMboxWrite(unittest.TestCase):
def setUp(self): def setUp(self):
super(TestMboxWrite, self).setUp()
self.mbox_read = make_mbox(messages=3) self.mbox_read = make_mbox(messages=3)
self.mbox_write = make_mbox(messages=0) self.mbox_write = make_mbox(messages=0)
@ -251,10 +252,6 @@ class TestMboxWrite(unittest.TestCase):
write = archivemail.Mbox(self.mbox_write, mode="w") write = archivemail.Mbox(self.mbox_write, mode="w")
self.assertRaises(AssertionError, write.write, None) self.assertRaises(AssertionError, write.write, None)
def tearDown(self):
for name in (self.mbox_write, self.mbox_read):
if os.path.exists(name):
os.remove(name)
########## options class testing ################# ########## options class testing #################
@ -335,7 +332,7 @@ class TestIsTooOld(unittest.TestCase):
########## acceptance testing ########### ########## acceptance testing ###########
class TestArchiveMbox(unittest.TestCase): class TestArchiveMbox(TestCaseInTempdir):
"""archiving should work based on the date of messages given""" """archiving should work based on the date of messages given"""
old_mbox = None old_mbox = None
new_mbox = None new_mbox = None
@ -344,6 +341,8 @@ class TestArchiveMbox(unittest.TestCase):
def setUp(self): def setUp(self):
archivemail.options.quiet = 1 archivemail.options.quiet = 1
super(TestArchiveMbox, self).setUp()
def testOld(self): def testOld(self):
"""archiving an old mailbox""" """archiving an old mailbox"""
@ -584,16 +583,13 @@ This is after the ^From line"""
def tearDown(self): def tearDown(self):
archivemail.options.quiet = 0 archivemail.options.quiet = 0
archive = self.mbox_name + "_archive" super(TestArchiveMbox, self).tearDown()
for name in (self.mbox_name, self.old_mbox, self.new_mbox,
self.copy_name, archive, archive + ".gz"):
if name and os.path.exists(name):
os.remove(name)
class TestArchiveMboxTimestamp(unittest.TestCase): class TestArchiveMboxTimestamp(TestCaseInTempdir):
"""original mbox timestamps should always be preserved""" """original mbox timestamps should always be preserved"""
def setUp(self): def setUp(self):
super(TestArchiveMboxTimestamp, self).setUp()
archivemail.options.quiet = 1 archivemail.options.quiet = 1
def testNew(self): def testNew(self):
@ -666,14 +662,13 @@ class TestArchiveMboxTimestamp(unittest.TestCase):
def tearDown(self): def tearDown(self):
archivemail.options.quiet = 0 archivemail.options.quiet = 0
for name in (self.mbox_name, self.mbox_name + "_archive.gz"): super(TestArchiveMboxTimestamp, self).tearDown()
if os.path.exists(name):
os.remove(name)
class TestArchiveMboxPreserveStatus(unittest.TestCase): class TestArchiveMboxPreserveStatus(TestCaseInTempdir):
"""make sure the 'preserve_unread' option works""" """make sure the 'preserve_unread' option works"""
def setUp(self): def setUp(self):
super(TestArchiveMboxPreserveStatus, self).setUp()
archivemail.options.quiet = 1 archivemail.options.quiet = 1
archivemail.options.preserve_unread = 1 archivemail.options.preserve_unread = 1
@ -733,15 +728,13 @@ class TestArchiveMboxPreserveStatus(unittest.TestCase):
def tearDown(self): def tearDown(self):
archivemail.options.quiet = 0 archivemail.options.quiet = 0
archivemail.options.preserve_unread = 0 archivemail.options.preserve_unread = 0
archive = self.mbox_name + "_archive" super(TestArchiveMboxPreserveStatus, self).tearDown()
for name in (self.mbox_name, self.copy_name, archive, archive + ".gz"):
if os.path.exists(name):
os.remove(name)
class TestArchiveMboxSuffix(unittest.TestCase): class TestArchiveMboxSuffix(TestCaseInTempdir):
"""make sure the 'suffix' option works""" """make sure the 'suffix' option works"""
def setUp(self): def setUp(self):
super(TestArchiveMboxSuffix, self).setUp()
archivemail.options.quiet = 1 archivemail.options.quiet = 1
def testSuffix(self): def testSuffix(self):
@ -786,15 +779,13 @@ class TestArchiveMboxSuffix(unittest.TestCase):
def tearDown(self): def tearDown(self):
archivemail.options.quiet = 0 archivemail.options.quiet = 0
archivemail.options.archive_suffix = "_archive" archivemail.options.archive_suffix = "_archive"
archive = self.mbox_name + "_archive" super(TestArchiveMboxSuffix, self).tearDown()
for name in (self.mbox_name, self.copy_name, archive, archive + ".gz"):
if os.path.exists(name):
os.remove(name)
class TestArchiveDryRun(unittest.TestCase): class TestArchiveDryRun(TestCaseInTempdir):
"""make sure the 'dry-run' option works""" """make sure the 'dry-run' option works"""
def setUp(self): def setUp(self):
super(TestArchiveDryRun, self).setUp()
archivemail.options.quiet = 1 archivemail.options.quiet = 1
archivemail.options.dry_run = 1 archivemail.options.dry_run = 1
@ -824,15 +815,13 @@ class TestArchiveDryRun(unittest.TestCase):
def tearDown(self): def tearDown(self):
archivemail.options.dry_run = 0 archivemail.options.dry_run = 0
archivemail.options.quiet = 0 archivemail.options.quiet = 0
archive = self.mbox_name + "_archive" super(TestArchiveDryRun, self).tearDown()
for name in (self.mbox_name, self.copy_name, archive, archive + ".gz"):
if os.path.exists(name):
os.remove(name)
class TestArchiveDays(unittest.TestCase): class TestArchiveDays(TestCaseInTempdir):
"""make sure the 'days' option works""" """make sure the 'days' option works"""
def setUp(self): def setUp(self):
super(TestArchiveDays, self).setUp()
archivemail.options.quiet = 1 archivemail.options.quiet = 1
def testOld(self): def testOld(self):
@ -890,13 +879,10 @@ class TestArchiveDays(unittest.TestCase):
def tearDown(self): def tearDown(self):
archivemail.options.days_old_max = 180 archivemail.options.days_old_max = 180
archivemail.options.quiet = 0 archivemail.options.quiet = 0
archive = self.mbox_name + "_archive" super(TestArchiveDays, self).tearDown()
for name in (self.mbox_name, self.copy_name, archive, archive + ".gz"):
if name and os.path.exists(name):
os.remove(name)
class TestArchiveDelete(unittest.TestCase): class TestArchiveDelete(TestCaseInTempdir):
"""make sure the 'delete' option works""" """make sure the 'delete' option works"""
old_mbox = None old_mbox = None
new_mbox = None new_mbox = None
@ -904,6 +890,7 @@ class TestArchiveDelete(unittest.TestCase):
mbox_name = None mbox_name = None
def setUp(self): def setUp(self):
super(TestArchiveDelete, self).setUp()
archivemail.options.quiet = 1 archivemail.options.quiet = 1
archivemail.options.delete_old_mail = 1 archivemail.options.delete_old_mail = 1
@ -972,16 +959,13 @@ class TestArchiveDelete(unittest.TestCase):
def tearDown(self): def tearDown(self):
archivemail.options.delete_old_mail = 0 archivemail.options.delete_old_mail = 0
archivemail.options.quiet = 0 archivemail.options.quiet = 0
archive = self.mbox_name + "_archive" super(TestArchiveDelete, self).tearDown()
for name in (self.mbox_name, self.copy_name, self.new_mbox,
self.old_mbox, archive, archive + ".gz"):
if name and os.path.exists(name):
os.remove(name)
class TestArchiveMboxFlagged(unittest.TestCase): class TestArchiveMboxFlagged(TestCaseInTempdir):
"""make sure the 'include_flagged' option works""" """make sure the 'include_flagged' option works"""
def setUp(self): def setUp(self):
super(TestArchiveMboxFlagged, self).setUp()
archivemail.options.quiet = 1 archivemail.options.quiet = 1
def testOld(self): def testOld(self):
@ -1059,15 +1043,13 @@ class TestArchiveMboxFlagged(unittest.TestCase):
def tearDown(self): def tearDown(self):
archivemail.options.include_flagged = 0 archivemail.options.include_flagged = 0
archivemail.options.quiet = 0 archivemail.options.quiet = 0
archive = self.mbox_name + "_archive" super(TestArchiveMboxFlagged, self).tearDown()
for name in (self.mbox_name, self.copy_name, archive, archive + ".gz"):
if os.path.exists(name):
os.remove(name)
class TestArchiveMboxOutputDir(unittest.TestCase): class TestArchiveMboxOutputDir(TestCaseInTempdir):
"""make sure that the 'output-dir' option works""" """make sure that the 'output-dir' option works"""
def setUp(self): def setUp(self):
super(TestArchiveMboxOutputDir, self).setUp()
archivemail.options.quiet = 1 archivemail.options.quiet = 1
def testOld(self): def testOld(self):
@ -1106,16 +1088,10 @@ class TestArchiveMboxOutputDir(unittest.TestCase):
def tearDown(self): def tearDown(self):
archivemail.options.quiet = 0 archivemail.options.quiet = 0
archivemail.options.output_dir = None archivemail.options.output_dir = None
archive = self.dir_name + "/" + os.path.basename(self.mbox_name) \ super(TestArchiveMboxOutputDir, self).tearDown()
+ "_archive"
for name in (self.mbox_name, self.copy_name, archive, archive + ".gz"):
if name and os.path.exists(name):
os.remove(name)
if self.dir_name and os.path.isdir(self.dir_name):
os.rmdir(self.dir_name)
class TestArchiveMboxUncompressed(unittest.TestCase): class TestArchiveMboxUncompressed(TestCaseInTempdir):
"""make sure that the 'no_compress' option works""" """make sure that the 'no_compress' option works"""
mbox_name = None mbox_name = None
new_mbox = None new_mbox = None
@ -1125,6 +1101,7 @@ class TestArchiveMboxUncompressed(unittest.TestCase):
def setUp(self): def setUp(self):
archivemail.options.quiet = 1 archivemail.options.quiet = 1
archivemail.options.no_compress = 1 archivemail.options.no_compress = 1
super(TestArchiveMboxUncompressed, self).setUp()
def testOld(self): def testOld(self):
"""archiving an old mailbox uncompressed""" """archiving an old mailbox uncompressed"""
@ -1234,16 +1211,13 @@ class TestArchiveMboxUncompressed(unittest.TestCase):
def tearDown(self): def tearDown(self):
archivemail.options.quiet = 0 archivemail.options.quiet = 0
archivemail.options.no_compress = 0 archivemail.options.no_compress = 0
archive = self.mbox_name + "_archive" super(TestArchiveMboxUncompressed, self).tearDown()
for name in (self.mbox_name, self.new_mbox, self.old_mbox,
self.copy_name, archive, archive + ".gz"):
if name and os.path.exists(name):
os.remove(name)
class TestArchiveSize(unittest.TestCase): class TestArchiveSize(TestCaseInTempdir):
"""check that the 'size' argument works""" """check that the 'size' argument works"""
def setUp(self): def setUp(self):
super(TestArchiveSize, self).setUp()
archivemail.options.quiet = 1 archivemail.options.quiet = 1
def testSmaller(self): def testSmaller(self):
@ -1307,15 +1281,13 @@ class TestArchiveSize(unittest.TestCase):
def tearDown(self): def tearDown(self):
archivemail.options.quiet = 0 archivemail.options.quiet = 0
archivemail.options.min_size = None archivemail.options.min_size = None
archive = self.mbox_name + "_archive" super(TestArchiveSize, self).tearDown()
for name in (self.mbox_name, self.copy_name, archive, archive + ".gz"):
if name and os.path.exists(name):
os.remove(name)
class TestArchiveMboxMode(unittest.TestCase): class TestArchiveMboxMode(TestCaseInTempdir):
"""file mode (permissions) of the original mbox should be preserved""" """file mode (permissions) of the original mbox should be preserved"""
def setUp(self): def setUp(self):
super(TestArchiveMboxMode, self).setUp()
archivemail.options.quiet = 1 archivemail.options.quiet = 1
def testOld(self): def testOld(self):
@ -1365,10 +1337,7 @@ class TestArchiveMboxMode(unittest.TestCase):
def tearDown(self): def tearDown(self):
archivemail.options.quiet = 0 archivemail.options.quiet = 0
archive = self.mbox_name + "_archive" super(TestArchiveMboxMode, self).tearDown()
for name in (self.mbox_name, archive, archive + ".gz"):
if name and os.path.exists(name):
os.remove(name)
########## helper routines ############ ########## helper routines ############
@ -1414,8 +1383,9 @@ def append_file(source, dest):
def make_mbox(body=None, headers=None, hours_old=0, messages=1): def make_mbox(body=None, headers=None, hours_old=0, messages=1):
name = tempfile.mktemp() assert(tempfile.tempdir)
file = open(name, "w") fd, name = tempfile.mkstemp()
file = os.fdopen(fd, "w")
for count in range(messages): for count in range(messages):
msg = make_message(body=body, default_headers=headers, msg = make_message(body=body, default_headers=headers,
hours_old=hours_old) hours_old=hours_old)