sys.path.insert(0, localmodule)
import fdroidserver.common
+import fdroidserver.exception
import fdroidserver.metadata
import fdroidserver.update
from fdroidserver.common import FDroidPopen
self.assertTrue(False, 'TypeError!')
def testBadGetsig(self):
+ """getsig() should still be able to fetch the fingerprint of bad signatures"""
# config needed to use jarsigner and keytool
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.update.config = config
+
apkfile = os.path.join(os.path.dirname(__file__), 'urzip-badsig.apk')
- sig = self.javagetsig(apkfile)
- self.assertIsNone(sig, "sig should be None: " + str(sig))
- pysig = fdroidserver.update.getsig(apkfile)
- self.assertIsNone(pysig, "python sig should be None: " + str(sig))
+ sig = fdroidserver.update.getsig(apkfile)
+ self.assertEqual(sig, 'e0ecb5fc2d63088e4a07ae410a127722',
+ "python sig should be: " + str(sig))
apkfile = os.path.join(os.path.dirname(__file__), 'urzip-badcert.apk')
- sig = self.javagetsig(apkfile)
- self.assertIsNone(sig, "sig should be None: " + str(sig))
- pysig = fdroidserver.update.getsig(apkfile)
- self.assertIsNone(pysig, "python sig should be None: " + str(sig))
+ sig = fdroidserver.update.getsig(apkfile)
+ self.assertEqual(sig, 'e0ecb5fc2d63088e4a07ae410a127722',
+ "python sig should be: " + str(sig))
def testScanApksAndObbs(self):
os.chdir(os.path.join(localmodule, 'tests'))
fdroidserver.update.options.clean = True
fdroidserver.update.options.delete_unknown = True
fdroidserver.update.options.rename_apks = False
+ fdroidserver.update.options.allow_disabled_algorithms = False
apps = fdroidserver.metadata.read_metadata(xref=True)
knownapks = fdroidserver.common.KnownApks()
- apks, cachechanged = fdroidserver.update.scan_apks({}, 'repo', knownapks, False)
- self.assertEqual(len(apks), 7)
+ apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False)
+ self.assertEqual(len(apks), 11)
apk = apks[0]
+ self.assertEqual(apk['packageName'], 'com.politedroid')
+ self.assertEqual(apk['versionCode'], 3)
+ self.assertEqual(apk['minSdkVersion'], '3')
+ self.assertEqual(apk['targetSdkVersion'], '3')
+ self.assertFalse('maxSdkVersion' in apk)
+ apk = apks[4]
+ self.assertEqual(apk['packageName'], 'obb.main.oldversion')
+ self.assertEqual(apk['versionCode'], 1444412523)
self.assertEqual(apk['minSdkVersion'], '4')
self.assertEqual(apk['targetSdkVersion'], '18')
self.assertFalse('maxSdkVersion' in apk)
self.assertIsNone(apk.get('obbMainFile'))
self.assertIsNone(apk.get('obbPatchFile'))
- def testScanApkMetadata(self):
+ def test_scan_apk(self):
+ config = dict()
+ fdroidserver.common.fill_config_defaults(config)
+ fdroidserver.update.config = config
+ os.chdir(os.path.join(localmodule, 'tests'))
+ if os.path.basename(os.getcwd()) != 'tests':
+ raise Exception('This test must be run in the "tests/" subdir')
+
+ apk_info = fdroidserver.update.scan_apk('org.dyndns.fules.ck_20.apk')
+
+ self.assertEqual(apk_info['icons_src'], {'240': 'res/drawable-hdpi-v4/icon_launcher.png',
+ '120': 'res/drawable-ldpi-v4/icon_launcher.png',
+ '160': 'res/drawable-mdpi-v4/icon_launcher.png',
+ '-1': 'res/drawable-mdpi-v4/icon_launcher.png'})
+ self.assertEqual(apk_info['icons'], {})
+ self.assertEqual(apk_info['features'], [])
+ self.assertEqual(apk_info['antiFeatures'], set())
+ self.assertEqual(apk_info['versionName'], 'v1.6pre2')
+ self.assertEqual(apk_info['hash'],
+ '897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8')
+ self.assertEqual(apk_info['packageName'], 'org.dyndns.fules.ck')
+ self.assertEqual(apk_info['versionCode'], 20)
+ self.assertEqual(apk_info['size'], 132453)
+ self.assertEqual(apk_info['nativecode'],
+ ['arm64-v8a', 'armeabi', 'armeabi-v7a', 'mips', 'mips64', 'x86', 'x86_64'])
+ self.assertEqual(apk_info['minSdkVersion'], '7')
+ self.assertEqual(apk_info['sig'], '9bf7a6a67f95688daec75eab4b1436ac')
+ self.assertEqual(apk_info['hashType'], 'sha256')
+ self.assertEqual(apk_info['targetSdkVersion'], '8')
+
+ def test_scan_apk_no_sig(self):
+ config = dict()
+ fdroidserver.common.fill_config_defaults(config)
+ fdroidserver.update.config = config
+ os.chdir(os.path.join(localmodule, 'tests'))
+ if os.path.basename(os.getcwd()) != 'tests':
+ raise Exception('This test must be run in the "tests/" subdir')
+
+ with self.assertRaises(fdroidserver.exception.BuildException):
+ fdroidserver.update.scan_apk('urzip-release-unsigned.apk')
+
+ def test_process_apk(self):
def _build_yaml_representer(dumper, data):
'''Creates a YAML representation of a Build instance'''
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.update.config = config
- os.chdir(os.path.dirname(__file__))
+ os.chdir(os.path.join(localmodule, 'tests'))
if os.path.basename(os.getcwd()) != 'tests':
raise Exception('This test must be run in the "tests/" subdir')
fdroidserver.update.options.clean = True
fdroidserver.update.options.rename_apks = False
fdroidserver.update.options.delete_unknown = True
+ fdroidserver.update.options.allow_disabled_algorithms = False
for icon_dir in fdroidserver.update.get_all_icon_dirs('repo'):
if not os.path.exists(icon_dir):
apkList = ['../urzip.apk', '../org.dyndns.fules.ck_20.apk']
for apkName in apkList:
- _, apk, cachechanged = fdroidserver.update.scan_apk({}, apkName, 'repo', knownapks, False)
+ _, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo', knownapks,
+ False)
# Don't care about the date added to the repo and relative apkName
del apk['added']
del apk['apkName']
# avoid AAPT application name bug
del apk['name']
+ # ensure that icons have been extracted properly
+ if apkName == '../urzip.apk':
+ self.assertEqual(apk['icon'], 'info.guardianproject.urzip.100.png')
+ if apkName == '../org.dyndns.fules.ck_20.apk':
+ self.assertEqual(apk['icon'], 'org.dyndns.fules.ck.20.png')
+ for density in fdroidserver.update.screen_densities:
+ icon_path = os.path.join(fdroidserver.update.get_icon_dir('repo', density),
+ apk['icon'])
+ self.assertTrue(os.path.isfile(icon_path))
+ self.assertTrue(os.path.getsize(icon_path) > 1)
+
savepath = os.path.join('metadata', 'apk', apk['packageName'] + '.yaml')
# Uncomment to save APK metadata
# with open(savepath, 'w') as f:
self.maxDiff = None
self.assertEqual(apk, frompickle)
- def test_scan_invalid_apk(self):
+ def test_process_apk_signed_by_disabled_algorithms(self):
+ os.chdir(os.path.join(localmodule, 'tests'))
+ if os.path.basename(os.getcwd()) != 'tests':
+ raise Exception('This test must be run in the "tests/" subdir')
+
+ config = dict()
+ fdroidserver.common.fill_config_defaults(config)
+ fdroidserver.update.config = config
+
+ config['ndk_paths'] = dict()
+ config['accepted_formats'] = ['json', 'txt', 'yml']
+ fdroidserver.common.config = config
+ fdroidserver.update.config = config
+
+ fdroidserver.update.options = type('', (), {})()
+ fdroidserver.update.options.clean = True
+ fdroidserver.update.options.verbose = True
+ fdroidserver.update.options.rename_apks = False
+ fdroidserver.update.options.delete_unknown = True
+ fdroidserver.update.options.allow_disabled_algorithms = False
+
+ knownapks = fdroidserver.common.KnownApks()
+ apksourcedir = os.getcwd()
+ tmpdir = os.path.join(localmodule, '.testfiles')
+ if not os.path.exists(tmpdir):
+ os.makedirs(tmpdir)
+ tmptestsdir = tempfile.mkdtemp(prefix='test_process_apk_signed_by_disabled_algorithms-',
+ dir=tmpdir)
+ print('tmptestsdir', tmptestsdir)
+ os.chdir(tmptestsdir)
+ os.mkdir('repo')
+ os.mkdir('archive')
+ # setup the repo, create icons dirs, etc.
+ fdroidserver.update.process_apks({}, 'repo', knownapks)
+ fdroidserver.update.process_apks({}, 'archive', knownapks)
+
+ disabledsigs = ['org.bitbucket.tickytacky.mirrormirror_2.apk', ]
+ for apkName in disabledsigs:
+ shutil.copy(os.path.join(apksourcedir, apkName),
+ os.path.join(tmptestsdir, 'repo'))
+
+ skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo',
+ knownapks,
+ allow_disabled_algorithms=True,
+ archive_bad_sig=False)
+ self.assertFalse(skip)
+ self.assertIsNotNone(apk)
+ self.assertTrue(cachechanged)
+ self.assertFalse(os.path.exists(os.path.join('archive', apkName)))
+ self.assertTrue(os.path.exists(os.path.join('repo', apkName)))
+
+ # this test only works on systems with fully updated Java/jarsigner
+ # that has MD5 listed in jdk.jar.disabledAlgorithms in java.security
+ skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo',
+ knownapks,
+ allow_disabled_algorithms=False,
+ archive_bad_sig=True)
+ self.assertTrue(skip)
+ self.assertIsNone(apk)
+ self.assertFalse(cachechanged)
+ self.assertTrue(os.path.exists(os.path.join('archive', apkName)))
+ self.assertFalse(os.path.exists(os.path.join('repo', apkName)))
+
+ skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'archive',
+ knownapks,
+ allow_disabled_algorithms=False,
+ archive_bad_sig=False)
+ self.assertFalse(skip)
+ self.assertIsNotNone(apk)
+ self.assertTrue(cachechanged)
+ self.assertTrue(os.path.exists(os.path.join('archive', apkName)))
+ self.assertFalse(os.path.exists(os.path.join('repo', apkName)))
+
+ # ensure that icons have been moved to the archive as well
+ for density in fdroidserver.update.screen_densities:
+ icon_path = os.path.join(fdroidserver.update.get_icon_dir('archive', density),
+ apk['icon'])
+ self.assertTrue(os.path.isfile(icon_path))
+ self.assertTrue(os.path.getsize(icon_path) > 1)
+
+ badsigs = ['urzip-badcert.apk', 'urzip-badsig.apk', 'urzip-release-unsigned.apk', ]
+ for apkName in badsigs:
+ shutil.copy(os.path.join(apksourcedir, apkName),
+ os.path.join(tmptestsdir, 'repo'))
+
+ skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo',
+ knownapks,
+ allow_disabled_algorithms=False,
+ archive_bad_sig=False)
+ self.assertTrue(skip)
+ self.assertIsNone(apk)
+ self.assertFalse(cachechanged)
+
+ def test_process_invalid_apk(self):
os.chdir(os.path.join(localmodule, 'tests'))
if os.path.basename(os.getcwd()) != 'tests':
raise Exception('This test must be run in the "tests/" subdir')
knownapks = fdroidserver.common.KnownApks()
apk = 'fake.ota.update_1234.zip' # this is not an APK, scanning should fail
- (skip, apk, cachechanged) = fdroidserver.update.scan_apk({}, apk, 'repo', knownapks, False)
+ (skip, apk, cachechanged) = fdroidserver.update.process_apk({}, apk, 'repo', knownapks,
+ False)
self.assertTrue(skip)
self.assertIsNone(apk)
self.assertFalse(cachechanged)
+ def test_translate_per_build_anti_features(self):
+ os.chdir(os.path.join(localmodule, 'tests'))
+ if os.path.basename(os.getcwd()) != 'tests':
+ raise Exception('This test must be run in the "tests/" subdir')
+
+ config = dict()
+ fdroidserver.common.fill_config_defaults(config)
+ config['ndk_paths'] = dict()
+ config['accepted_formats'] = ['json', 'txt', 'yml']
+ fdroidserver.common.config = config
+ fdroidserver.update.config = config
+
+ fdroidserver.update.options = type('', (), {})()
+ fdroidserver.update.options.clean = True
+ fdroidserver.update.options.delete_unknown = True
+ fdroidserver.update.options.rename_apks = False
+ fdroidserver.update.options.allow_disabled_algorithms = False
+
+ apps = fdroidserver.metadata.read_metadata(xref=True)
+ knownapks = fdroidserver.common.KnownApks()
+ apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False)
+ fdroidserver.update.translate_per_build_anti_features(apps, apks)
+ self.assertEqual(len(apks), 11)
+ foundtest = False
+ for apk in apks:
+ if apk['packageName'] == 'com.politedroid' and apk['versionCode'] == 3:
+ antiFeatures = apk.get('antiFeatures')
+ self.assertTrue('KnownVuln' in antiFeatures)
+ self.assertEqual(3, len(antiFeatures))
+ foundtest = True
+ self.assertTrue(foundtest)
+
+ def test_create_metadata_from_template(self):
+ tmpdir = os.path.join(localmodule, '.testfiles')
+ if not os.path.exists(tmpdir):
+ os.makedirs(tmpdir)
+ tmptestsdir = tempfile.mkdtemp(prefix='test_create_metadata_from_template-',
+ dir=tmpdir)
+ print('tmptestsdir', tmptestsdir)
+ os.chdir(tmptestsdir)
+ os.mkdir('repo')
+ os.mkdir('metadata')
+ shutil.copy(os.path.join(localmodule, 'tests', 'urzip.apk'), 'repo')
+
+ config = dict()
+ fdroidserver.common.fill_config_defaults(config)
+ config['ndk_paths'] = dict()
+ config['accepted_formats'] = ['json', 'txt', 'yml']
+ fdroidserver.common.config = config
+ fdroidserver.update.config = config
+
+ fdroidserver.update.options = type('', (), {})()
+ fdroidserver.update.options.clean = True
+ fdroidserver.update.options.delete_unknown = False
+ fdroidserver.update.options.rename_apks = False
+ fdroidserver.update.options.allow_disabled_algorithms = False
+
+ knownapks = fdroidserver.common.KnownApks()
+ apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False)
+ self.assertEqual(1, len(apks))
+ apk = apks[0]
+
+ testfile = 'metadata/info.guardianproject.urzip.yml'
+ # create empty 0 byte .yml file, run read_metadata, it should work
+ open(testfile, 'a').close()
+ apps = fdroidserver.metadata.read_metadata(xref=True)
+ self.assertEqual(1, len(apps))
+ os.remove(testfile)
+
+ # test using internal template
+ apps = fdroidserver.metadata.read_metadata(xref=True)
+ self.assertEqual(0, len(apps))
+ fdroidserver.update.create_metadata_from_template(apk)
+ self.assertTrue(os.path.exists(testfile))
+ apps = fdroidserver.metadata.read_metadata(xref=True)
+ self.assertEqual(1, len(apps))
+ for app in apps.values():
+ self.assertEqual('urzip', app['Name'])
+ self.assertEqual(1, len(app['Categories']))
+ break
+
+ # test using external template.yml
+ os.remove(testfile)
+ self.assertFalse(os.path.exists(testfile))
+ shutil.copy(os.path.join(localmodule, 'examples', 'template.yml'), tmptestsdir)
+ fdroidserver.update.create_metadata_from_template(apk)
+ self.assertTrue(os.path.exists(testfile))
+ apps = fdroidserver.metadata.read_metadata(xref=True)
+ self.assertEqual(1, len(apps))
+ for app in apps.values():
+ self.assertEqual('urzip', app['Name'])
+ self.assertEqual(1, len(app['Categories']))
+ self.assertEqual('Internet', app['Categories'][0])
+ break
+ with open(testfile) as fp:
+ data = yaml.load(fp)
+ self.assertEqual('urzip', data['Name'])
+ self.assertEqual('urzip', data['Summary'])
+
if __name__ == "__main__":
parser = optparse.OptionParser()