From f8d699e62b751be5c5aa49aa7d93981bcd59cc8f Mon Sep 17 00:00:00 2001 From: zanefisher Date: Tue, 13 Aug 2013 18:41:38 -0400 Subject: [PATCH 1/7] Randomized order of aggragate tests. Added teardown code to some unit tests. --- tuf/tests/aggregate_tests.py | 3 +++ tuf/tests/test_signercli.py | 4 ++++ tuf/tests/test_signerlib.py | 4 ++++ tuf/tests/test_updater.py | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/tuf/tests/aggregate_tests.py b/tuf/tests/aggregate_tests.py index 3ac9ecac..b5dd21b7 100755 --- a/tuf/tests/aggregate_tests.py +++ b/tuf/tests/aggregate_tests.py @@ -27,12 +27,15 @@ import tuf.keydb as keydb import tuf.repo.keystore as keystore import tuf.roledb as roledb +import random tests_list = glob.glob('test_*.py') # Remove '.py' from each filename. tests_list = [test[:-3] for test in tests_list] +random.shuffle(tests_list) + suite = unittest.TestLoader().loadTestsFromNames(tests_list) unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tuf/tests/test_signercli.py b/tuf/tests/test_signercli.py index aa91d862..ea9fccc5 100755 --- a/tuf/tests/test_signercli.py +++ b/tuf/tests/test_signercli.py @@ -1559,5 +1559,9 @@ def _mock_get_keyids(junk): signercli._get_metadata_directory = original_get_metadata_directory +def tearDownModule(): + unittest_toolbox.Modified_TestCase.clear_toolbox() + + if __name__ == '__main__': unittest.main() diff --git a/tuf/tests/test_signerlib.py b/tuf/tests/test_signerlib.py index dcb47993..8331ea74 100755 --- a/tuf/tests/test_signerlib.py +++ b/tuf/tests/test_signerlib.py @@ -975,6 +975,10 @@ def _get_signed_role_info(self, role, directory=None): filename) return signed_meta, role_info +def tearDownModule(): + unit_tbox.clear_toolbox() + tuf.repo.keystore.clear_keystore() + if __name__ == '__main__': unittest.main() diff --git a/tuf/tests/test_updater.py b/tuf/tests/test_updater.py index d32664e7..8386338e 100755 --- a/tuf/tests/test_updater.py +++ b/tuf/tests/test_updater.py @@ -1150,5 +1150,9 @@ def test_8_remove_obsolete_targets(self): tuf.download.download_url_to_tempfileobj = original_download +def tearDownModule(): + setup.remove_all_repositories(TestUpdater.repositories['main_repository']) + unittest_toolbox.Modified_TestCase.clear_toolbox() + if __name__ == '__main__': unittest.main() From 6f297650fd3895880665d8ef37d0bac636e3c065 Mon Sep 17 00:00:00 2001 From: zanefisher Date: Thu, 15 Aug 2013 17:40:48 -0400 Subject: [PATCH 2/7] Removed unnecessary imports from aggregate_tests.py. --- tuf/tests/aggregate_tests.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tuf/tests/aggregate_tests.py b/tuf/tests/aggregate_tests.py index b5dd21b7..42a1db18 100755 --- a/tuf/tests/aggregate_tests.py +++ b/tuf/tests/aggregate_tests.py @@ -24,9 +24,6 @@ import unittest import glob -import tuf.keydb as keydb -import tuf.repo.keystore as keystore -import tuf.roledb as roledb import random tests_list = glob.glob('test_*.py') From 817ad0735b927a10c9cb7588e44a1059af7318da Mon Sep 17 00:00:00 2001 From: zanefisher Date: Mon, 19 Aug 2013 17:10:13 -0400 Subject: [PATCH 3/7] Rewrote extraneous dependencies test, based on arbitrary package test. --- .../test_extraneous_dependencies_attack.py | 236 ++++++++---------- 1 file changed, 109 insertions(+), 127 deletions(-) mode change 100755 => 100644 tuf/tests/system_tests/test_extraneous_dependencies_attack.py diff --git a/tuf/tests/system_tests/test_extraneous_dependencies_attack.py b/tuf/tests/system_tests/test_extraneous_dependencies_attack.py old mode 100755 new mode 100644 index 72937d65..7492a070 --- a/tuf/tests/system_tests/test_extraneous_dependencies_attack.py +++ b/tuf/tests/system_tests/test_extraneous_dependencies_attack.py @@ -1,190 +1,172 @@ -#!/usr/bin/env python - """ test_extraneous_dependencies_attack.py - Konstantin Andrianov + Zane Fisher - February 19, 2012 + August 19, 2013 See LICENSE for licensing information. - Simulate an extraneous dependencies attack. + Simulate an extraneous dependencies attack. The client attempts to download + a file with one legitimate dependency, and one extraneous dependency. - In an extraneous dependencies attack, attacker is able to cause clients to - download software dependencies that are not the intended dependencies. +Note: The interposition provided by 'tuf.interposition' is used to intercept +all calls made by urllib/urillib2 to certain hostnames specified in +the interposition configuration file. Look up interposition.py for more +information and illustration of a sample contents of the interposition +configuration file. Interposition was meant to make TUF integration with an +existing software updater an easy process. This allows for more flexibility +to the existing software updater. However, if you are planning to solely use +TUF there should be no need for interposition, all necessary calls will be +generated from within TUF. + +Note: There is no difference between 'updates' and 'target' files. """ import os -import sys +import shutil import urllib import tempfile -import time - import util_test_tools -import tuf.repo.keystore -import tuf.repo.signerlib as signerlib -import tuf.repo.signercli as signercli + +import tuf from tuf.interposition import urllib_tuf + # Disable logging. util_test_tools.disable_logging() -version = 1 -class ExtraneousDependenciesAttackAlert(Exception): + +class ExtraneousDependencyAlert(Exception): pass -def test_extraneous_dependencies_attack(): +# Interprets the contents of the file it downloads as a list of dependent +# files from the same repository +def _download(url, filename, directory, tuf=False): + destination = os.path.join(directory, filename) + #print 'downloading to '+destination + #print 'from '+url + if tuf: + urllib_tuf.urlretrieve(url, destination) + else: + urllib.urlretrieve(url, destination) + + #print 'DDDDDD '+util_test_tools.read_file_content(destination) + if util_test_tools.read_file_content(destination) != '': + required_files = util_test_tools.read_file_content(destination).split(',') + for required_filename in required_files: + #print 'requires '+required_filename + required_file_url = os.path.dirname(url)+'/'+required_filename + _download(required_file_url, required_filename, directory, tuf) + + + +def test_extraneous_dependency_attack(TUF=False): + """ + + TUF: + If set to 'False' all directories that start with 'tuf_' are ignored, + indicating that tuf is not implemented. + + + Illustrate arbitrary package attack vulnerability. + + """ + + ERROR_MSG = 'Extraneous Dependency Attack was Successful!\n' - ERROR_MSG = '\tExtraneous Dependencies Attack Succeeded!\n\n' try: - # Setup. - root_repo, url, server_proc, keyids = util_test_tools.init_repo(tuf=True) + root_repo, url, server_proc, keyids = util_test_tools.init_repo(tuf=TUF) reg_repo = os.path.join(root_repo, 'reg_repo') tuf_repo = os.path.join(root_repo, 'tuf_repo') - keystore_dir = os.path.join(tuf_repo, 'keystore') - metadata_dir = os.path.join(tuf_repo, 'metadata') - downloads_dir = os.path.join(root_repo, 'downloads') + downloads = os.path.join(root_repo, 'downloads') targets_dir = os.path.join(tuf_repo, 'targets') - # 'roles' holds information about delegated roles. - roles = {'role1':{'password':['pass1']}, - 'role2':{'password':['pass2']}} + # Add files to 'repo' directory: {root_repo} + good_dependency_filepath = util_test_tools.add_file_to_repository(reg_repo, '') + good_dependency_basename = os.path.basename(good_dependency_filepath) - # Add files to 'reg_repo' directory: {root_repo} - role1_path = tempfile.mkdtemp(dir=reg_repo) - roles['role1']['filepath'] = \ - util_test_tools.add_file_to_repository(role1_path, 'Test A') + bad_dependency_filepath = util_test_tools.add_file_to_repository(reg_repo, '') + bad_dependency_basename = os.path.basename(bad_dependency_filepath) - role2_path = tempfile.mkdtemp(dir=reg_repo) - roles['role2']['filepath'] = \ - util_test_tools.add_file_to_repository(role2_path, 'Test B') + # The dependent file lists the good dependency + dependent_filepath = util_test_tools.add_file_to_repository(reg_repo, good_dependency_basename) + dependent_basename = os.path.basename(dependent_filepath) - # Update TUF repository. - util_test_tools.make_targets_meta(root_repo) - util_test_tools.make_release_meta(root_repo) - util_test_tools.make_timestamp_meta(root_repo) + url_to_repo = url+'reg_repo/'+dependent_basename + #downloaded_file = os.path.join(downloads, dependent_basename) + modified_dependency_list = good_dependency_basename+','+bad_dependency_basename - - def _make_delegation(rolename): - expiration_date = tuf.formats.format_time(time.time()+86400) - expiration_date = expiration_date[0:expiration_date.rfind(' UTC')] - # Indicate which file client downloads. - rel_filepath = os.path.relpath(roles[rolename]['filepath'], reg_repo) - roles[rolename]['target_path'] = os.path.join(targets_dir, rel_filepath) - rolepath, file_basename = os.path.split(roles[rolename]['filepath']) - junk, role_relpath = os.path.split(rolepath) - roles[rolename]['targets_dir'] = os.path.join(targets_dir, role_relpath) - roles[rolename]['metadata_dir'] = os.path.join(metadata_dir, 'targets') - - # Create a key to sign a new delegated role. - password = roles[rolename]['password'][0] - key = signerlib.generate_and_save_rsa_key(keystore_dir, password) - roles[rolename]['keyid'] = [key['keyid']] - roles[rolename]['dest_path'] = os.path.join(downloads_dir, file_basename) - - # Create delegation one. - util_test_tools.create_delegation(tuf_repo, - roles[rolename]['targets_dir'], - roles[rolename]['keyid'], password, - 'targets', rolename, expiration_date) - - # Update TUF repository. - # util_test_tools.make_targets_meta(root_repo) - util_test_tools.make_release_meta(root_repo) - util_test_tools.make_timestamp_meta(root_repo) + if TUF: + # Update TUF metadata before attacker modifies anything. + util_test_tools.tuf_refresh_repo(root_repo, keyids) # Modify the url. Remember that the interposition will intercept # urls that have 'localhost:9999' hostname, which was specified in # the json interposition configuration file. Look for 'hostname' # in 'util_test_tools.py'. Further, the 'file_basename' is the target # path relative to 'targets_dir'. - roles[rolename]['url'] = 'http://localhost:9999/'+rel_filepath + url_to_repo = 'http://localhost:9999/'+dependent_basename - # Perform a client download. - urllib_tuf.urlretrieve(roles[rolename]['url'], - roles[rolename]['dest_path']) + # Attacker adds the dependency in the targets repository. + target = os.path.join(targets_dir, dependent_basename) + util_test_tools.modify_file_at_repository(target, modified_dependency_list) + + # Attacker adds the dependency in the regular repository. + util_test_tools.modify_file_at_repository(dependent_filepath, modified_dependency_list) + + # End of Setup. - _make_delegation('role1') - _make_delegation('role2') - - - # The attacks. - - def _write_rogue_metadata(): - global version - version = version+1 - expiration_date = tuf.formats.format_time(time.time()+86400) - # Load the keystore before rebuilding the metadata. - tuf.repo.keystore.load_keystore_from_keyfiles(keystore_dir, - roles['role1']['keyid'], - roles['role1']['password']) - - # Rebuild the delegation role metadata. - signerlib.build_delegated_role_file(roles['role2']['targets_dir'], - roles['role1']['keyid'], metadata_dir, - roles['role1']['metadata_dir'], - 'role1.txt', version, expiration_date) - - # Update release and timestamp metadata. - util_test_tools.make_release_meta(root_repo) - util_test_tools.make_timestamp_meta(root_repo) - - - # Modify a target that was delegated to 'role2'. - util_test_tools.modify_file_at_repository(roles['role2']['target_path'], - 'Test NOT B') - - # Update rogue delegatee metadata. - _write_rogue_metadata() - - # Perform another client download. try: - urllib_tuf.urlretrieve(roles['role2']['url'], roles['role2']['dest_path']) - except tuf.MetadataNotAvailableError, e: + # Client downloads (tries to download) the file. + _download(url=url_to_repo, filename=dependent_basename, directory=downloads, tuf=TUF) + + except tuf.DownloadError: + # If tuf.DownloadError is raised, this means that TUF has prevented + # the download of an unrecognized file. Enable the logging to see, + # what actually happened. pass + else: - raise ExtraneousDependenciesAttackAlert(ERROR_MSG) - - - # Add a target file to the directory delegated to 'role2' but not 'role1'. - util_test_tools.add_file_to_repository(roles['role2']['targets_dir'], 'AAAA') - - # Update rogue delegatee metadata. - _write_rogue_metadata() - - # Perform another client download. - try: - urllib_tuf.urlretrieve(roles['role2']['url'], roles['role2']['dest_path']) - except tuf.MetadataNotAvailableError, e: - pass - else: - raise ExtraneousDependenciesAttackAlert(ERROR_MSG) + # Check if the legitimate dependency was downloaded + if not(os.path.exists(os.path.join(downloads, good_dependency_basename))): + raise tuf.DownloadError + # Check if the extraneous dependency was downloaded + if os.path.exists(os.path.join(downloads, bad_dependency_basename)): + raise ExtraneousDependencyAlert(ERROR_MSG) finally: - server_proc.kill() - #util_test_tools.cleanup(root_repo, server_proc) - - + util_test_tools.cleanup(root_repo, server_proc) +print 'Attempting extraneous dependency attack without TUF:' try: - test_extraneous_dependencies_attack() -except ExtraneousDependenciesAttackAlert, error: - print 'error' + test_extraneous_dependency_attack(TUF=False) + +except ExtraneousDependencyAlert, error: + print error + + + +print 'Attempting extraneous dependency attack with TUF:' +try: + test_extraneous_dependency_attack(TUF=True) + +except ExtraneousDependencyAlert, error: + print error From 71d4db8a96dac45b82b5d54be7110baf071687df Mon Sep 17 00:00:00 2001 From: zanefisher Date: Mon, 26 Aug 2013 14:18:20 -0400 Subject: [PATCH 4/7] Aggregate tests now passes. Moved various unit test code that ran on import into setup and teardown functions that are called at the appropriate time when run with other tests. --- tuf/tests/repository_setup.py | 18 +++++--- tuf/tests/test_signercli.py | 9 ++-- tuf/tests/test_signerlib.py | 7 +++- tuf/tests/test_updater.py | 78 +++++++++++++++++------------------ 4 files changed, 62 insertions(+), 50 deletions(-) diff --git a/tuf/tests/repository_setup.py b/tuf/tests/repository_setup.py index f355deb5..786889c9 100755 --- a/tuf/tests/repository_setup.py +++ b/tuf/tests/repository_setup.py @@ -31,17 +31,21 @@ import tuf.tests.unittest_toolbox as unittest_toolbox -# Populating 'rsa_keystore' and 'rsa_passwords' dictionaries. -# We will need them in creating keystore directory. -unittest_toolbox.Modified_TestCase.bind_keys_to_roles() - # Role:keyids dictionary. role_keyids = {} -for role in unittest_toolbox.Modified_TestCase.semi_roledict.keys(): - role_keyids[role] = unittest_toolbox.Modified_TestCase.semi_roledict[role]['keyids'] +def _init_role_keyids(): + # Populating 'rsa_keystore' and 'rsa_passwords' dictionaries. + # We will need them in creating keystore directory. + unittest_toolbox.Modified_TestCase.bind_keys_to_roles() + + global role_keyids + + for role in unittest_toolbox.Modified_TestCase.semi_roledict.keys(): + role_keyids[role] = unittest_toolbox.Modified_TestCase.semi_roledict[role]['keyids'] + @@ -54,6 +58,7 @@ def _create_keystore(keystore_directory): _rsa_keystore = unittest_toolbox.Modified_TestCase.rsa_keystore _rsa_passwords = unittest_toolbox.Modified_TestCase.rsa_passwords if not _rsa_keystore or not _rsa_passwords: + import pdb; pdb.set_trace() msg = 'Populate \'rsa_keystore\' and \'rsa_passwords\''+\ ' before invoking this method.' sys.exit(msg) @@ -275,6 +280,7 @@ def create_repositories(): """ + _init_role_keyids() # Make a temporary general repository directory. repository_dir = tempfile.mkdtemp() diff --git a/tuf/tests/test_signercli.py b/tuf/tests/test_signercli.py index 78fd467e..dd756d40 100755 --- a/tuf/tests/test_signercli.py +++ b/tuf/tests/test_signercli.py @@ -56,9 +56,6 @@ class guarantees the order of unit tests. So that, 'test_something_A' logger = logging.getLogger('tuf.test_signercli') -# Populating 'rsa_keystore' and 'rsa_passwords' dictionaries. -# We will need them when creating keystore directories. -unittest_toolbox.Modified_TestCase.bind_keys_to_roles() class TestSignercli(unittest_toolbox.Modified_TestCase): @@ -1556,6 +1553,12 @@ def _mock_get_keyids(junk): signercli._get_metadata_directory = original_get_metadata_directory +def setUpModule(): + # Populating 'rsa_keystore' and 'rsa_passwords' dictionaries. + # We will need them when creating keystore directories. + unittest_toolbox.Modified_TestCase.bind_keys_to_roles() + + def tearDownModule(): unittest_toolbox.Modified_TestCase.clear_toolbox() diff --git a/tuf/tests/test_signerlib.py b/tuf/tests/test_signerlib.py index 9bb8d759..4610f4b2 100755 --- a/tuf/tests/test_signerlib.py +++ b/tuf/tests/test_signerlib.py @@ -68,8 +68,6 @@ # 'unittest_toolbox.Modified_TestCase' is too long, I'll set it to 'unit_tbox'. unit_tbox = tuf.tests.unittest_toolbox.Modified_TestCase -# Generate rsa keys and roles dictionary dictionaries. -unit_tbox.bind_keys_to_roles() class TestSignerlib(unit_tbox): @@ -972,6 +970,11 @@ def _get_signed_role_info(self, role, directory=None): filename) return signed_meta, role_info + +def setUpModule(): + # Generate rsa keys and roles dictionary dictionaries. + unit_tbox.bind_keys_to_roles() + def tearDownModule(): unit_tbox.clear_toolbox() tuf.repo.keystore.clear_keystore() diff --git a/tuf/tests/test_updater.py b/tuf/tests/test_updater.py index d8d0ad2e..b4db02b0 100755 --- a/tuf/tests/test_updater.py +++ b/tuf/tests/test_updater.py @@ -56,10 +56,6 @@ class guarantees the order of unit tests. So that, 'test_something_A' logger = logging.getLogger('tuf.test_updater') -# References to roledb and keydb dictionaries (improve readability). -roledb = tuf.roledb -keydb = tuf.keydb - class TestUpdater_init_(unittest_toolbox.Modified_TestCase): @@ -105,39 +101,43 @@ def test__init__exceptions(self): os.remove(role_filepath) updater.Updater('Repo_Name', self.mirrors) - # Remove all created repositories. + # Remove all created repositories and roles. setup.remove_all_repositories(repositories['main_repository']) + tuf.roledb.clear_roledb() class TestUpdater(unittest_toolbox.Modified_TestCase): - # Create repositories. 'repositories' is a tuple that looks like this: - # (repository_dir, client_repository_dir, server_repository_dir), see - # repository_setup.py odule. - repositories = setup.create_repositories() - # Save references to repository directories and metadata. - # Server side references. - server_repo_dir = repositories['server_repository'] - server_meta_dir = os.path.join(server_repo_dir, 'metadata') - root_filepath = os.path.join(server_meta_dir, 'root.txt') - timestamp_filepath = os.path.join(server_meta_dir, 'timestamp.txt') - targets_filepath = os.path.join(server_meta_dir, 'targets.txt') - release_filepath = os.path.join(server_meta_dir, 'release.txt') + @classmethod + def setUpClass(cls): + # Create repositories. 'repositories' is a tuple that looks like this: + # (repository_dir, client_repository_dir, server_repository_dir), see + # repository_setup.py odule. + cls.repositories = setup.create_repositories() - # References to delegated metadata paths and directories. - delegated_dir1 = os.path.join(server_meta_dir, 'targets') - delegated_filepath1 = os.path.join(delegated_dir1, 'delegated_role1.txt') - delegated_dir2 = os.path.join(delegated_dir1, 'delegated_role1') - delegated_filepath2 = os.path.join(delegated_dir2, 'delegated_role2.txt') - targets_dir = os.path.join(server_repo_dir, 'targets') + # Save references to repository directories and metadata. + # Server side references. + cls.server_repo_dir = cls.repositories['server_repository'] + cls.server_meta_dir = os.path.join(cls.server_repo_dir, 'metadata') + cls.root_filepath = os.path.join(cls.server_meta_dir, 'root.txt') + cls.timestamp_filepath = os.path.join(cls.server_meta_dir, 'timestamp.txt') + cls.targets_filepath = os.path.join(cls.server_meta_dir, 'targets.txt') + cls.release_filepath = os.path.join(cls.server_meta_dir, 'release.txt') - # Client side references. - client_repo_dir = repositories['client_repository'] - client_meta_dir = os.path.join(client_repo_dir, 'metadata') - client_current_dir = os.path.join(client_meta_dir, 'current') - client_previous_dir = os.path.join(client_meta_dir, 'previous') + # References to delegated metadata paths and directories. + cls.delegated_dir1 = os.path.join(cls.server_meta_dir, 'targets') + cls.delegated_filepath1 = os.path.join(cls.delegated_dir1, 'delegated_role1.txt') + cls.delegated_dir2 = os.path.join(cls.delegated_dir1, 'delegated_role1') + cls.delegated_filepath2 = os.path.join(cls.delegated_dir2, 'delegated_role2.txt') + cls.targets_dir = os.path.join(cls.server_repo_dir, 'targets') + + # Client side references. + cls.client_repo_dir = cls.repositories['client_repository'] + cls.client_meta_dir = os.path.join(cls.client_repo_dir, 'metadata') + cls.client_current_dir = os.path.join(cls.client_meta_dir, 'current') + cls.client_previous_dir = os.path.join(cls.client_meta_dir, 'previous') @@ -176,8 +176,8 @@ def tearDown(self): unittest_toolbox.Modified_TestCase.tearDown(self) # Clear roledb and keydb dictionaries. - roledb.clear_roledb() - keydb.clear_keydb() + tuf.roledb.clear_roledb() + tuf.keydb.clear_keydb() @@ -379,14 +379,14 @@ def test_1__rebuild_key_and_role_db(self): # are populated. 'top_level_role_info' is a unittest_toolbox's dict # that contains top level role information it corresponds to a # ROLEDICT_SCHEMA where roles are keys and role information their values. - self.assertEqual(roledb._roledb_dict, self.top_level_role_info) - self.assertEqual(len(keydb._keydb_dict), 4) + self.assertEqual(tuf.roledb._roledb_dict, self.top_level_role_info) + self.assertEqual(len(tuf.keydb._keydb_dict), 4) # Verify that keydb dictionary was updated. for role in self.role_list: keyids = self.top_level_role_info[role]['keyids'] for keyid in keyids: - self.assertTrue(keyid in keydb._keydb_dict) + self.assertTrue(keyid in tuf.keydb._keydb_dict) @@ -408,22 +408,22 @@ def test_2__import_delegations(self): # Verify that there was no change in roledb and keydb dictionaries # by checking the number of elements in the dictionaries. - self.assertEqual(len(roledb._roledb_dict), 5) - self.assertEqual(len(keydb._keydb_dict), 5) + self.assertEqual(len(tuf.roledb._roledb_dict), 5) + self.assertEqual(len(tuf.keydb._keydb_dict), 5) # Test: normal case, first level delegation. self.Repository._import_delegations('targets/delegated_role1') - self.assertEqual(len(roledb._roledb_dict), 6) - self.assertEqual(len(keydb._keydb_dict), 6) + self.assertEqual(len(tuf.roledb._roledb_dict), 6) + self.assertEqual(len(tuf.keydb._keydb_dict), 6) # Verify that roledb dictionary was updated. - self.assertTrue('targets/delegated_role1' in roledb._roledb_dict) + self.assertTrue('targets/delegated_role1' in tuf.roledb._roledb_dict) # Verify that keydb dictionary was updated. keyids = self.semi_roledict['targets/delegated_role1']['keyids'] for keyid in keyids: - self.assertTrue(keyid in keydb._keydb_dict) + self.assertTrue(keyid in tuf.keydb._keydb_dict) From 4654f9b4a25ac1e249fc9236c7827a31d3929736 Mon Sep 17 00:00:00 2001 From: zanefisher Date: Mon, 26 Aug 2013 14:25:25 -0400 Subject: [PATCH 5/7] Removed stray debug code. --- tuf/tests/repository_setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tuf/tests/repository_setup.py b/tuf/tests/repository_setup.py index 786889c9..759704d1 100755 --- a/tuf/tests/repository_setup.py +++ b/tuf/tests/repository_setup.py @@ -58,7 +58,6 @@ def _create_keystore(keystore_directory): _rsa_keystore = unittest_toolbox.Modified_TestCase.rsa_keystore _rsa_passwords = unittest_toolbox.Modified_TestCase.rsa_passwords if not _rsa_keystore or not _rsa_passwords: - import pdb; pdb.set_trace() msg = 'Populate \'rsa_keystore\' and \'rsa_passwords\''+\ ' before invoking this method.' sys.exit(msg) From 4a85d4bba8a790ee44372a68613d7a05973b5b46 Mon Sep 17 00:00:00 2001 From: zanefisher Date: Mon, 26 Aug 2013 15:35:28 -0400 Subject: [PATCH 6/7] Make aggregate tests randomization optional. --- tuf/tests/aggregate_tests.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tuf/tests/aggregate_tests.py b/tuf/tests/aggregate_tests.py index 42a1db18..a8a56fda 100755 --- a/tuf/tests/aggregate_tests.py +++ b/tuf/tests/aggregate_tests.py @@ -19,9 +19,11 @@ Run all the unit tests from every .py file beginning with "test_" in 'tuf/tests'. + Use --random to run the tests in random order. """ +import sys import unittest import glob import random @@ -31,7 +33,8 @@ # Remove '.py' from each filename. tests_list = [test[:-3] for test in tests_list] -random.shuffle(tests_list) +if '--random' in sys.argv: + random.shuffle(tests_list) suite = unittest.TestLoader().loadTestsFromNames(tests_list) From 19fae3c9088eed77d05e8a44269a286946038beb Mon Sep 17 00:00:00 2001 From: vladdd Date: Fri, 30 Aug 2013 14:58:41 -0400 Subject: [PATCH 7/7] Review Zane's unit test fixes and resolve merge conflicts --- tuf/tests/aggregate_tests.py | 28 +++++++++++++++++++++------- tuf/tests/repository_setup.py | 4 +++- tuf/tests/test_signercli.py | 2 ++ tuf/tests/test_signerlib.py | 2 ++ tuf/tests/test_updater.py | 11 ++++++++--- 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/tuf/tests/aggregate_tests.py b/tuf/tests/aggregate_tests.py index a8a56fda..620c54de 100755 --- a/tuf/tests/aggregate_tests.py +++ b/tuf/tests/aggregate_tests.py @@ -11,31 +11,45 @@ January 26, 2013 - August 2013. Modified previous behavior that explicitly imported individual + August 2013. + Modified previous behavior that explicitly imported individual unit tests. -Zane Fisher See LICENSE for licensing information. - Run all the unit tests from every .py file beginning with "test_" in 'tuf/tests'. - Use --random to run the tests in random order. + Run all the unit tests from every .py file beginning with "test_" in + 'tuf/tests'. Use --random to run the tests in random order. """ + import sys import unittest import glob import random + +# Generate a list of pathnames that match a pattern (i.e., that begin with +# 'test_' and end with '.py'. A shell-style wildcard is used with glob() to +# match desired filenames. All the tests matching the pattern will be loaded +# and run in a test suite. tests_list = glob.glob('test_*.py') -# Remove '.py' from each filename. -tests_list = [test[:-3] for test in tests_list] +# Remove '.py' from each filename to allow loadTestsFromNames() (called below) +# to properly load the file as a module. +tests_without_extension = [] +for test in tests_list: + test = test[:-3] + tests_without_extension.append(test) +# Provide command-line option to randomize the order in which the tests run. +# Randomization might catch errors with unit tests that do not properly clean +# up or restore monkey-patched modules. if '--random' in sys.argv: - random.shuffle(tests_list) + random.shuffle(tests_without_extension) -suite = unittest.TestLoader().loadTestsFromNames(tests_list) +suite = unittest.TestLoader().loadTestsFromNames(tests_without_extension) unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tuf/tests/repository_setup.py b/tuf/tests/repository_setup.py index 759704d1..50a8a4e1 100755 --- a/tuf/tests/repository_setup.py +++ b/tuf/tests/repository_setup.py @@ -38,7 +38,7 @@ def _init_role_keyids(): # Populating 'rsa_keystore' and 'rsa_passwords' dictionaries. - # We will need them in creating keystore directory. + # We will need them in creating the keystore directory and metadata files. unittest_toolbox.Modified_TestCase.bind_keys_to_roles() global role_keyids @@ -279,6 +279,8 @@ def create_repositories(): """ + # Ensure the keyids for the required roles are loaded. Role keyids are + # needed for the creation of metadata file and the keystore. _init_role_keyids() # Make a temporary general repository directory. diff --git a/tuf/tests/test_signercli.py b/tuf/tests/test_signercli.py index dd756d40..bb4b33ee 100755 --- a/tuf/tests/test_signercli.py +++ b/tuf/tests/test_signercli.py @@ -1554,12 +1554,14 @@ def _mock_get_keyids(junk): def setUpModule(): + # setUpModule() is called before any test cases run. # Populating 'rsa_keystore' and 'rsa_passwords' dictionaries. # We will need them when creating keystore directories. unittest_toolbox.Modified_TestCase.bind_keys_to_roles() def tearDownModule(): + # tearDownModule() is called after all the test cases have run. unittest_toolbox.Modified_TestCase.clear_toolbox() diff --git a/tuf/tests/test_signerlib.py b/tuf/tests/test_signerlib.py index 4610f4b2..38e2f6bf 100755 --- a/tuf/tests/test_signerlib.py +++ b/tuf/tests/test_signerlib.py @@ -972,10 +972,12 @@ def _get_signed_role_info(self, role, directory=None): def setUpModule(): + # setUpModule() is called before any test cases run. # Generate rsa keys and roles dictionary dictionaries. unit_tbox.bind_keys_to_roles() def tearDownModule(): + # tearDownModule() is called after all the test cases have run. unit_tbox.clear_toolbox() tuf.repo.keystore.clear_keystore() diff --git a/tuf/tests/test_updater.py b/tuf/tests/test_updater.py index b4db02b0..ba7e234c 100755 --- a/tuf/tests/test_updater.py +++ b/tuf/tests/test_updater.py @@ -112,9 +112,10 @@ class TestUpdater(unittest_toolbox.Modified_TestCase): @classmethod def setUpClass(cls): + # setUpClass() is called before tests in an individual class run. # Create repositories. 'repositories' is a tuple that looks like this: # (repository_dir, client_repository_dir, server_repository_dir), see - # repository_setup.py odule. + # 'repository_setup.py' module. cls.repositories = setup.create_repositories() # Save references to repository directories and metadata. @@ -128,9 +129,11 @@ def setUpClass(cls): # References to delegated metadata paths and directories. cls.delegated_dir1 = os.path.join(cls.server_meta_dir, 'targets') - cls.delegated_filepath1 = os.path.join(cls.delegated_dir1, 'delegated_role1.txt') + cls.delegated_filepath1 = os.path.join(cls.delegated_dir1, + 'delegated_role1.txt') cls.delegated_dir2 = os.path.join(cls.delegated_dir1, 'delegated_role1') - cls.delegated_filepath2 = os.path.join(cls.delegated_dir2, 'delegated_role2.txt') + cls.delegated_filepath2 = os.path.join(cls.delegated_dir2, + 'delegated_role2.txt') cls.targets_dir = os.path.join(cls.server_repo_dir, 'targets') # Client side references. @@ -1150,6 +1153,8 @@ def test_8_remove_obsolete_targets(self): def tearDownModule(): + # tearDownModule() is called after all the tests have run. + # http://docs.python.org/2/library/unittest.html#class-and-module-fixtures setup.remove_all_repositories(TestUpdater.repositories['main_repository']) unittest_toolbox.Modified_TestCase.clear_toolbox()