diff --git a/MANIFEST.in b/MANIFEST.in index 98889a35..32018317 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -634,6 +634,7 @@ include tests/org.bitbucket.tickytacky.mirrormirror_2.apk include tests/org.bitbucket.tickytacky.mirrormirror_3.apk include tests/org.bitbucket.tickytacky.mirrormirror_4.apk include tests/org.dyndns.fules.ck_20.apk +include tests/org.sajeg.fallingblocks_3.apk include tests/publish.TestCase include tests/repo/categories.txt include tests/repo/com.example.test.helloworld_1.apk diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 06dd762f..222587a9 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -34,6 +34,7 @@ import json import time import yaml import copy +import defusedxml.ElementTree as ElementTree from datetime import datetime, timezone from argparse import ArgumentParser from pathlib import Path @@ -1394,13 +1395,16 @@ def scan_apk_androguard(apk, apkfile): apk['nativecode'].extend(sorted(list(arch))) for item in xml.findall('uses-permission'): - name = str(item.attrib[xmlns + 'name']) + name = item.attrib.get(xmlns + 'name') + if not name: + logging.debug( + _('Ignoring bad element in manifest: %s') + % ElementTree.tostring(item).decode() + ) + continue maxSdkVersion = item.attrib.get(xmlns + 'maxSdkVersion') maxSdkVersion = int(maxSdkVersion) if maxSdkVersion else None - permission = UsesPermission( - name, - maxSdkVersion - ) + permission = UsesPermission(str(name), maxSdkVersion) apk['uses-permission'].append(permission) for name, maxSdkVersion in apkobject.get_uses_implied_permission_list(): permission = UsesPermission( @@ -1410,20 +1414,26 @@ def scan_apk_androguard(apk, apkfile): apk['uses-permission'].append(permission) for item in xml.findall('uses-permission-sdk-23'): - name = str(item.attrib[xmlns + 'name']) + name = item.attrib.get(xmlns + 'name') + if not name: + logging.debug( + _('Ignoring bad element in manifest: %s') + % ElementTree.tostring(item).decode() + ) + continue maxSdkVersion = item.attrib.get(xmlns + 'maxSdkVersion') maxSdkVersion = int(maxSdkVersion) if maxSdkVersion else None - permission_sdk_23 = UsesPermissionSdk23( - name, - maxSdkVersion - ) + permission_sdk_23 = UsesPermissionSdk23(str(name), maxSdkVersion) apk['uses-permission-sdk-23'].append(permission_sdk_23) for item in xml.findall('uses-feature'): - key = xmlns + 'name' - if key not in item.attrib: + feature = str(item.attrib.get(xmlns + 'name', '')) + if not feature: + logging.debug( + _('Ignoring bad element in manifest: %s') + % ElementTree.tostring(item).decode() + ) continue - feature = str(item.attrib[key]) if feature not in ( 'android.hardware.screen.portrait', 'android.hardware.screen.landscape', diff --git a/tests/org.sajeg.fallingblocks_3.apk b/tests/org.sajeg.fallingblocks_3.apk new file mode 100644 index 00000000..98bfe770 Binary files /dev/null and b/tests/org.sajeg.fallingblocks_3.apk differ diff --git a/tests/update.TestCase b/tests/update.TestCase index 0a102dfd..b3234711 100755 --- a/tests/update.TestCase +++ b/tests/update.TestCase @@ -900,6 +900,29 @@ class UpdateTest(unittest.TestCase): shutil.copy(os.path.join(self.basedir, os.path.basename(apkfile)), apkfile) fdroidserver.update.scan_apk(apkfile) + def test_scan_apk_bad_namespace_in_manifest(self): + """Some APKs can produce an exception when parsing the AndroidManifest.xml + + This kind of parsing exception should be reported then ignored + so that working APKs can be included in the index. There are + so many weird things that make it into APKs, that does not + automatically disqualify them from inclusion. + + This APK has elements with messed up namespaces: + + + """ + # reset the state, perhaps this should be in setUp() + config = dict() + fdroidserver.common.fill_config_defaults(config) + fdroidserver.common.config = config + fdroidserver.update.config = config + with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): + os.mkdir('repo') + apkfile = 'repo/org.sajeg.fallingblocks_3.apk' + shutil.copy(os.path.join(self.basedir, os.path.basename(apkfile)), apkfile) + fdroidserver.update.scan_apk(apkfile) + def test_process_apk(self): def _build_yaml_representer(dumper, data): '''Creates a YAML representation of a Build instance'''