mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-11-13 18:40:12 +01:00
Merge branch 'master' into 'master'
replace aapt with androguard See merge request !234
This commit is contained in:
commit
eb49c9e8f3
@ -483,15 +483,23 @@ def capitalize_intact(string):
|
||||
return string[0].upper() + string[1:]
|
||||
|
||||
|
||||
def get_metadata_from_apk(app, build, apkfile):
|
||||
"""get the required metadata from the built APK"""
|
||||
def has_native_code(apkobj):
|
||||
"""aapt checks if there are architecture folders under the lib/ folder
|
||||
so we are simulating the same behaviour"""
|
||||
arch_re = re.compile("^lib/(.*)/.*$")
|
||||
arch = [file for file in apkobj.get_files() if arch_re.match(file)]
|
||||
return False if not arch else True
|
||||
|
||||
p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile], output=False)
|
||||
|
||||
def get_apk_metadata_aapt(apkfile):
|
||||
"""aapt function to extract versionCode, versionName, packageName and nativecode"""
|
||||
vercode = None
|
||||
version = None
|
||||
foundid = None
|
||||
nativecode = None
|
||||
|
||||
p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile], output=False)
|
||||
|
||||
for line in p.output.splitlines():
|
||||
if line.startswith("package:"):
|
||||
pat = re.compile(".*name='([a-zA-Z0-9._]*)'.*")
|
||||
@ -509,6 +517,38 @@ def get_metadata_from_apk(app, build, apkfile):
|
||||
elif line.startswith("native-code:"):
|
||||
nativecode = line[12:]
|
||||
|
||||
return vercode, version, foundid, nativecode
|
||||
|
||||
|
||||
def get_apk_metadata_androguard(apkfile):
|
||||
"""androguard function to extract versionCode, versionName, packageName and nativecode"""
|
||||
try:
|
||||
from androguard.core.bytecodes.apk import APK
|
||||
apkobject = APK(apkfile)
|
||||
except ImportError:
|
||||
raise BuildException("androguard library is not installed and aapt binary not found")
|
||||
except FileNotFoundError:
|
||||
raise BuildException("Could not open apk file for metadata analysis")
|
||||
|
||||
if not apkobject.is_valid_APK():
|
||||
raise BuildException("Invalid APK provided")
|
||||
|
||||
foundid = apkobject.get_package()
|
||||
vercode = apkobject.get_androidversion_code()
|
||||
version = apkobject.get_androidversion_name()
|
||||
nativecode = has_native_code(apkobject)
|
||||
|
||||
return vercode, version, foundid, nativecode
|
||||
|
||||
|
||||
def get_metadata_from_apk(app, build, apkfile):
|
||||
"""get the required metadata from the built APK"""
|
||||
|
||||
if common.set_command_in_config('aapt'):
|
||||
vercode, version, foundid, nativecode = get_apk_metadata_aapt(apkfile)
|
||||
else:
|
||||
vercode, version, foundid, nativecode = get_apk_metadata_androguard(apkfile)
|
||||
|
||||
# Ignore empty strings or any kind of space/newline chars that we don't
|
||||
# care about
|
||||
if nativecode is not None:
|
||||
@ -533,7 +573,6 @@ def get_metadata_from_apk(app, build, apkfile):
|
||||
|
||||
def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, extlib_dir, tmp_dir, force, onserver, refresh):
|
||||
"""Do a build locally."""
|
||||
|
||||
ndk_path = build.ndk_path()
|
||||
if build.ndk or (build.buildjni and build.buildjni != ['no']):
|
||||
if not ndk_path:
|
||||
|
@ -46,6 +46,8 @@ from pyasn1.codec.der import decoder, encoder
|
||||
from pyasn1_modules import rfc2315
|
||||
from pyasn1.error import PyAsn1Error
|
||||
|
||||
from distutils.util import strtobool
|
||||
|
||||
import fdroidserver.metadata
|
||||
from .asynchronousfilereader import AsynchronousFileReader
|
||||
|
||||
@ -1690,14 +1692,7 @@ def get_file_extension(filename):
|
||||
return os.path.splitext(filename)[1].lower()[1:]
|
||||
|
||||
|
||||
def isApkAndDebuggable(apkfile, config):
|
||||
"""Returns True if the given file is an APK and is debuggable
|
||||
|
||||
:param apkfile: full path to the apk to check"""
|
||||
|
||||
if get_file_extension(apkfile) != 'apk':
|
||||
return False
|
||||
|
||||
def get_apk_debuggable_aapt(apkfile):
|
||||
p = SdkToolsPopen(['aapt', 'dump', 'xmltree', apkfile, 'AndroidManifest.xml'],
|
||||
output=False)
|
||||
if p.returncode != 0:
|
||||
@ -1709,6 +1704,35 @@ def isApkAndDebuggable(apkfile, config):
|
||||
return False
|
||||
|
||||
|
||||
def get_apk_debuggable_androguard(apkfile):
|
||||
try:
|
||||
from androguard.core.bytecodes.apk import APK
|
||||
except ImportError:
|
||||
logging.critical("androguard library is not installed and aapt not present")
|
||||
sys.exit(1)
|
||||
|
||||
apkobject = APK(apkfile)
|
||||
if apkobject.is_valid_APK():
|
||||
debuggable = apkobject.get_element("application", "debuggable")
|
||||
if debuggable is not None:
|
||||
return bool(strtobool(debuggable))
|
||||
return False
|
||||
|
||||
|
||||
def isApkAndDebuggable(apkfile, config):
|
||||
"""Returns True if the given file is an APK and is debuggable
|
||||
|
||||
:param apkfile: full path to the apk to check"""
|
||||
|
||||
if get_file_extension(apkfile) != 'apk':
|
||||
return False
|
||||
|
||||
if set_command_in_config('aapt'):
|
||||
return get_apk_debuggable_aapt(apkfile)
|
||||
else:
|
||||
return get_apk_debuggable_androguard(apkfile)
|
||||
|
||||
|
||||
class PopenResult:
|
||||
def __init__(self):
|
||||
self.returncode = None
|
||||
|
@ -41,7 +41,7 @@ from . import btlog
|
||||
from . import common
|
||||
from . import index
|
||||
from . import metadata
|
||||
from .common import SdkToolsPopen
|
||||
from .common import BuildException, SdkToolsPopen
|
||||
|
||||
METADATA_VERSION = 18
|
||||
|
||||
@ -60,6 +60,17 @@ APK_PERMISSION_PAT = \
|
||||
APK_FEATURE_PAT = re.compile(".*name='([^']*)'.*")
|
||||
|
||||
screen_densities = ['640', '480', '320', '240', '160', '120']
|
||||
screen_resolutions = {
|
||||
"xxxhdpi": '640',
|
||||
"xxhdpi": '480',
|
||||
"xhdpi": '320',
|
||||
"hdpi": '240',
|
||||
"mdpi": '160',
|
||||
"ldpi": '120',
|
||||
"undefined": '-1',
|
||||
"anydpi": '65534',
|
||||
"nodpi": '65535'
|
||||
}
|
||||
|
||||
all_screen_densities = ['0'] + screen_densities
|
||||
|
||||
@ -871,6 +882,196 @@ def scan_repo_files(apkcache, repodir, knownapks, use_date_from_file=False):
|
||||
return repo_files, cachechanged
|
||||
|
||||
|
||||
def scan_apk_aapt(apk, apkfile):
|
||||
p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile], output=False)
|
||||
if p.returncode != 0:
|
||||
if options.delete_unknown:
|
||||
if os.path.exists(apkfile):
|
||||
logging.error("Failed to get apk information, deleting " + apkfile)
|
||||
os.remove(apkfile)
|
||||
else:
|
||||
logging.error("Could not find {0} to remove it".format(apkfile))
|
||||
else:
|
||||
logging.error("Failed to get apk information, skipping " + apkfile)
|
||||
raise BuildException("Invaild APK")
|
||||
for line in p.output.splitlines():
|
||||
if line.startswith("package:"):
|
||||
try:
|
||||
apk['packageName'] = re.match(APK_NAME_PAT, line).group(1)
|
||||
apk['versionCode'] = int(re.match(APK_VERCODE_PAT, line).group(1))
|
||||
apk['versionName'] = re.match(APK_VERNAME_PAT, line).group(1)
|
||||
except Exception as e:
|
||||
logging.error("Package matching failed: " + str(e))
|
||||
logging.info("Line was: " + line)
|
||||
sys.exit(1)
|
||||
elif line.startswith("application:"):
|
||||
apk['name'] = re.match(APK_LABEL_PAT, line).group(1)
|
||||
# Keep path to non-dpi icon in case we need it
|
||||
match = re.match(APK_ICON_PAT_NODPI, line)
|
||||
if match:
|
||||
apk['icons_src']['-1'] = match.group(1)
|
||||
elif line.startswith("launchable-activity:"):
|
||||
# Only use launchable-activity as fallback to application
|
||||
if not apk['name']:
|
||||
apk['name'] = re.match(APK_LABEL_PAT, line).group(1)
|
||||
if '-1' not in apk['icons_src']:
|
||||
match = re.match(APK_ICON_PAT_NODPI, line)
|
||||
if match:
|
||||
apk['icons_src']['-1'] = match.group(1)
|
||||
elif line.startswith("application-icon-"):
|
||||
match = re.match(APK_ICON_PAT, line)
|
||||
if match:
|
||||
density = match.group(1)
|
||||
path = match.group(2)
|
||||
apk['icons_src'][density] = path
|
||||
elif line.startswith("sdkVersion:"):
|
||||
m = re.match(APK_SDK_VERSION_PAT, line)
|
||||
if m is None:
|
||||
logging.error(line.replace('sdkVersion:', '')
|
||||
+ ' is not a valid minSdkVersion!')
|
||||
else:
|
||||
apk['minSdkVersion'] = m.group(1)
|
||||
# if target not set, default to min
|
||||
if 'targetSdkVersion' not in apk:
|
||||
apk['targetSdkVersion'] = m.group(1)
|
||||
elif line.startswith("targetSdkVersion:"):
|
||||
m = re.match(APK_SDK_VERSION_PAT, line)
|
||||
if m is None:
|
||||
logging.error(line.replace('targetSdkVersion:', '')
|
||||
+ ' is not a valid targetSdkVersion!')
|
||||
else:
|
||||
apk['targetSdkVersion'] = m.group(1)
|
||||
elif line.startswith("maxSdkVersion:"):
|
||||
apk['maxSdkVersion'] = re.match(APK_SDK_VERSION_PAT, line).group(1)
|
||||
elif line.startswith("native-code:"):
|
||||
apk['nativecode'] = []
|
||||
for arch in line[13:].split(' '):
|
||||
apk['nativecode'].append(arch[1:-1])
|
||||
elif line.startswith('uses-permission:'):
|
||||
perm_match = re.match(APK_PERMISSION_PAT, line).groupdict()
|
||||
if perm_match['maxSdkVersion']:
|
||||
perm_match['maxSdkVersion'] = int(perm_match['maxSdkVersion'])
|
||||
permission = UsesPermission(
|
||||
perm_match['name'],
|
||||
perm_match['maxSdkVersion']
|
||||
)
|
||||
|
||||
apk['uses-permission'].append(permission)
|
||||
elif line.startswith('uses-permission-sdk-23:'):
|
||||
perm_match = re.match(APK_PERMISSION_PAT, line).groupdict()
|
||||
if perm_match['maxSdkVersion']:
|
||||
perm_match['maxSdkVersion'] = int(perm_match['maxSdkVersion'])
|
||||
permission_sdk_23 = UsesPermissionSdk23(
|
||||
perm_match['name'],
|
||||
perm_match['maxSdkVersion']
|
||||
)
|
||||
|
||||
apk['uses-permission-sdk-23'].append(permission_sdk_23)
|
||||
|
||||
elif line.startswith('uses-feature:'):
|
||||
feature = re.match(APK_FEATURE_PAT, line).group(1)
|
||||
# Filter out this, it's only added with the latest SDK tools and
|
||||
# causes problems for lots of apps.
|
||||
if feature != "android.hardware.screen.portrait" \
|
||||
and feature != "android.hardware.screen.landscape":
|
||||
if feature.startswith("android.feature."):
|
||||
feature = feature[16:]
|
||||
apk['features'].add(feature)
|
||||
|
||||
|
||||
def scan_apk_androguard(apk, apkfile):
|
||||
try:
|
||||
from androguard.core.bytecodes.apk import APK
|
||||
apkobject = APK(apkfile)
|
||||
if apkobject.is_valid_APK():
|
||||
arsc = apkobject.get_android_resources()
|
||||
else:
|
||||
if options.delete_unknown:
|
||||
if os.path.exists(apkfile):
|
||||
logging.error("Failed to get apk information, deleting " + apkfile)
|
||||
os.remove(apkfile)
|
||||
else:
|
||||
logging.error("Could not find {0} to remove it".format(apkfile))
|
||||
else:
|
||||
logging.error("Failed to get apk information, skipping " + apkfile)
|
||||
raise BuildException("Invaild APK")
|
||||
except ImportError:
|
||||
logging.critical("androguard library is not installed and aapt not present")
|
||||
sys.exit(1)
|
||||
except FileNotFoundError:
|
||||
logging.error("Could not open apk file for analysis")
|
||||
raise BuildException("Invaild APK")
|
||||
|
||||
apk['packageName'] = apkobject.get_package()
|
||||
apk['versionCode'] = int(apkobject.get_androidversion_code())
|
||||
apk['versionName'] = apkobject.get_androidversion_name()
|
||||
if apk['versionName'][0] == "@":
|
||||
version_id = int(apk['versionName'].replace("@", "0x"), 16)
|
||||
version_id = arsc.get_id(apk['packageName'], version_id)[1]
|
||||
apk['versionName'] = arsc.get_string(apk['packageName'], version_id)[1]
|
||||
apk['name'] = apkobject.get_app_name()
|
||||
|
||||
if apkobject.get_max_sdk_version() is not None:
|
||||
apk['maxSdkVersion'] = apkobject.get_max_sdk_version()
|
||||
apk['minSdkVersion'] = apkobject.get_min_sdk_version()
|
||||
apk['targetSdkVersion'] = apkobject.get_target_sdk_version()
|
||||
|
||||
icon_id = int(apkobject.get_element("application", "icon").replace("@", "0x"), 16)
|
||||
icon_name = arsc.get_id(apk['packageName'], icon_id)[1]
|
||||
|
||||
density_re = re.compile("^res/(.*)/" + icon_name + ".*$")
|
||||
|
||||
for file in apkobject.get_files():
|
||||
d_re = density_re.match(file)
|
||||
if d_re:
|
||||
folder = d_re.group(1).split('-')
|
||||
if len(folder) > 1:
|
||||
resolution = folder[1]
|
||||
else:
|
||||
resolution = 'mdpi'
|
||||
density = screen_resolutions[resolution]
|
||||
apk['icons_src'][density] = d_re.group(0)
|
||||
|
||||
if apk['icons_src'].get('-1') is None:
|
||||
apk['icons_src']['-1'] = apk['icons_src']['160']
|
||||
|
||||
arch_re = re.compile("^lib/(.*)/.*$")
|
||||
arch = set([arch_re.match(file).group(1) for file in apkobject.get_files() if arch_re.match(file)])
|
||||
if len(arch) >= 1:
|
||||
apk['nativecode'] = []
|
||||
apk['nativecode'].extend(sorted(list(arch)))
|
||||
|
||||
xml = apkobject.get_android_manifest_xml()
|
||||
|
||||
for item in xml.getElementsByTagName('uses-permission'):
|
||||
name = str(item.getAttribute("android:name"))
|
||||
maxSdkVersion = item.getAttribute("android:maxSdkVersion")
|
||||
maxSdkVersion = None if maxSdkVersion is '' else int(maxSdkVersion)
|
||||
permission = UsesPermission(
|
||||
name,
|
||||
maxSdkVersion
|
||||
)
|
||||
apk['uses-permission'].append(permission)
|
||||
|
||||
for item in xml.getElementsByTagName('uses-permission-sdk-23'):
|
||||
name = str(item.getAttribute("android:name"))
|
||||
maxSdkVersion = item.getAttribute("android:maxSdkVersion")
|
||||
maxSdkVersion = None if maxSdkVersion is '' else int(maxSdkVersion)
|
||||
permission_sdk_23 = UsesPermissionSdk23(
|
||||
name,
|
||||
maxSdkVersion
|
||||
)
|
||||
apk['uses-permission-sdk-23'].append(permission_sdk_23)
|
||||
|
||||
for item in xml.getElementsByTagName('uses-feature'):
|
||||
feature = str(item.getAttribute("android:name"))
|
||||
if feature != "android.hardware.screen.portrait" \
|
||||
and feature != "android.hardware.screen.landscape":
|
||||
if feature.startswith("android.feature."):
|
||||
feature = feature[16:]
|
||||
apk['features'].append(feature)
|
||||
|
||||
|
||||
def scan_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk):
|
||||
"""Scan the apk with the given filename in the given repo directory.
|
||||
|
||||
@ -888,7 +1089,7 @@ def scan_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk):
|
||||
|
||||
if ' ' in apkfilename:
|
||||
logging.critical("Spaces in filenames are not allowed.")
|
||||
sys.exit(1)
|
||||
return True, None, False
|
||||
|
||||
apkfile = os.path.join(repodir, apkfilename)
|
||||
shasum = sha256sum(apkfile)
|
||||
@ -921,100 +1122,16 @@ def scan_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk):
|
||||
apk['antiFeatures'] = set()
|
||||
if has_old_openssl(apkfile):
|
||||
apk['antiFeatures'].add('KnownVuln')
|
||||
p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile], output=False)
|
||||
if p.returncode != 0:
|
||||
if options.delete_unknown:
|
||||
if os.path.exists(apkfile):
|
||||
logging.error("Failed to get apk information, deleting " + apkfile)
|
||||
os.remove(apkfile)
|
||||
else:
|
||||
logging.error("Could not find {0} to remove it".format(apkfile))
|
||||
|
||||
try:
|
||||
if common.set_command_in_config('aapt'):
|
||||
logging.warning("Using AAPT for metadata")
|
||||
scan_apk_aapt(apk, apkfile)
|
||||
else:
|
||||
logging.error("Failed to get apk information, skipping " + apkfile)
|
||||
logging.warning("Using androguard for metadata")
|
||||
scan_apk_androguard(apk, apkfile)
|
||||
except BuildException:
|
||||
return True, None, False
|
||||
for line in p.output.splitlines():
|
||||
if line.startswith("package:"):
|
||||
try:
|
||||
apk['packageName'] = re.match(APK_NAME_PAT, line).group(1)
|
||||
apk['versionCode'] = int(re.match(APK_VERCODE_PAT, line).group(1))
|
||||
apk['versionName'] = re.match(APK_VERNAME_PAT, line).group(1)
|
||||
except Exception as e:
|
||||
logging.error("Package matching failed: " + str(e))
|
||||
logging.info("Line was: " + line)
|
||||
sys.exit(1)
|
||||
elif line.startswith("application:"):
|
||||
apk['name'] = re.match(APK_LABEL_PAT, line).group(1)
|
||||
# Keep path to non-dpi icon in case we need it
|
||||
match = re.match(APK_ICON_PAT_NODPI, line)
|
||||
if match:
|
||||
apk['icons_src']['-1'] = match.group(1)
|
||||
elif line.startswith("launchable-activity:"):
|
||||
# Only use launchable-activity as fallback to application
|
||||
if not apk['name']:
|
||||
apk['name'] = re.match(APK_LABEL_PAT, line).group(1)
|
||||
if '-1' not in apk['icons_src']:
|
||||
match = re.match(APK_ICON_PAT_NODPI, line)
|
||||
if match:
|
||||
apk['icons_src']['-1'] = match.group(1)
|
||||
elif line.startswith("application-icon-"):
|
||||
match = re.match(APK_ICON_PAT, line)
|
||||
if match:
|
||||
density = match.group(1)
|
||||
path = match.group(2)
|
||||
apk['icons_src'][density] = path
|
||||
elif line.startswith("sdkVersion:"):
|
||||
m = re.match(APK_SDK_VERSION_PAT, line)
|
||||
if m is None:
|
||||
logging.error(line.replace('sdkVersion:', '')
|
||||
+ ' is not a valid minSdkVersion!')
|
||||
else:
|
||||
apk['minSdkVersion'] = m.group(1)
|
||||
# if target not set, default to min
|
||||
if 'targetSdkVersion' not in apk:
|
||||
apk['targetSdkVersion'] = m.group(1)
|
||||
elif line.startswith("targetSdkVersion:"):
|
||||
m = re.match(APK_SDK_VERSION_PAT, line)
|
||||
if m is None:
|
||||
logging.error(line.replace('targetSdkVersion:', '')
|
||||
+ ' is not a valid targetSdkVersion!')
|
||||
else:
|
||||
apk['targetSdkVersion'] = m.group(1)
|
||||
elif line.startswith("maxSdkVersion:"):
|
||||
apk['maxSdkVersion'] = re.match(APK_SDK_VERSION_PAT, line).group(1)
|
||||
elif line.startswith("native-code:"):
|
||||
apk['nativecode'] = []
|
||||
for arch in line[13:].split(' '):
|
||||
apk['nativecode'].append(arch[1:-1])
|
||||
elif line.startswith('uses-permission:'):
|
||||
perm_match = re.match(APK_PERMISSION_PAT, line).groupdict()
|
||||
if perm_match['maxSdkVersion']:
|
||||
perm_match['maxSdkVersion'] = int(perm_match['maxSdkVersion'])
|
||||
permission = UsesPermission(
|
||||
perm_match['name'],
|
||||
perm_match['maxSdkVersion']
|
||||
)
|
||||
|
||||
apk['uses-permission'].append(permission)
|
||||
elif line.startswith('uses-permission-sdk-23:'):
|
||||
perm_match = re.match(APK_PERMISSION_PAT, line).groupdict()
|
||||
if perm_match['maxSdkVersion']:
|
||||
perm_match['maxSdkVersion'] = int(perm_match['maxSdkVersion'])
|
||||
permission_sdk_23 = UsesPermissionSdk23(
|
||||
perm_match['name'],
|
||||
perm_match['maxSdkVersion']
|
||||
)
|
||||
|
||||
apk['uses-permission-sdk-23'].append(permission_sdk_23)
|
||||
|
||||
elif line.startswith('uses-feature:'):
|
||||
feature = re.match(APK_FEATURE_PAT, line).group(1)
|
||||
# Filter out this, it's only added with the latest SDK tools and
|
||||
# causes problems for lots of apps.
|
||||
if feature != "android.hardware.screen.portrait" \
|
||||
and feature != "android.hardware.screen.landscape":
|
||||
if feature.startswith("android.feature."):
|
||||
feature = feature[16:]
|
||||
apk['features'].add(feature)
|
||||
|
||||
if 'minSdkVersion' not in apk:
|
||||
logging.warn("No SDK version information found in {0}".format(apkfile))
|
||||
@ -1029,7 +1146,7 @@ def scan_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk):
|
||||
apk['sig'] = getsig(os.path.join(os.getcwd(), apkfile))
|
||||
if not apk['sig']:
|
||||
logging.critical("Failed to get apk signature")
|
||||
sys.exit(1)
|
||||
return True, None, False
|
||||
|
||||
apkzip = zipfile.ZipFile(apkfile, 'r')
|
||||
|
||||
@ -1068,10 +1185,8 @@ def scan_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk):
|
||||
with open(icondest, 'wb') as f:
|
||||
f.write(get_icon_bytes(apkzip, iconsrc))
|
||||
apk['icons'][density] = iconfilename
|
||||
|
||||
except Exception as e:
|
||||
logging.warn("Error retrieving icon file: %s" % (e))
|
||||
del apk['icons'][density]
|
||||
except (zipfile.BadZipFile, ValueError, KeyError) as e:
|
||||
logging.warning("Error retrieving icon file: %s" % (icondest))
|
||||
del apk['icons_src'][density]
|
||||
empty_densities.append(density)
|
||||
|
||||
|
92
tests/androguard_test.py
Normal file
92
tests/androguard_test.py
Normal file
@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import inspect
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import yaml
|
||||
from binascii import unhexlify
|
||||
|
||||
localmodule = os.path.realpath(
|
||||
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
|
||||
print('localmodule: ' + localmodule)
|
||||
if localmodule not in sys.path:
|
||||
sys.path.insert(0, localmodule)
|
||||
|
||||
import fdroidserver.common
|
||||
import fdroidserver.metadata
|
||||
import fdroidserver.update
|
||||
|
||||
|
||||
class UpdateTest(unittest.TestCase):
|
||||
'''fdroid androguard manual tests'''
|
||||
|
||||
def testScanMetadataAndroguardAAPT(self):
|
||||
|
||||
def _create_apkmetadata_object(apkName):
|
||||
'''Create an empty apk metadata object'''
|
||||
apk = {}
|
||||
apk['apkName'] = apkName
|
||||
apk['uses-permission'] = []
|
||||
apk['uses-permission-sdk-23'] = []
|
||||
apk['features'] = []
|
||||
apk['icons_src'] = {}
|
||||
return apk
|
||||
|
||||
config = dict()
|
||||
fdroidserver.common.fill_config_defaults(config)
|
||||
fdroidserver.update.config = config
|
||||
os.chdir(os.path.dirname(__file__))
|
||||
if os.path.basename(os.getcwd()) != 'tests':
|
||||
raise Exception('This test must be run in the "tests/" subdir')
|
||||
|
||||
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
|
||||
|
||||
self.assertTrue(fdroidserver.common.set_command_in_config('aapt'))
|
||||
try:
|
||||
from androguard.core.bytecodes.apk import APK
|
||||
except ImportError:
|
||||
raise Exception("androguard not installed!")
|
||||
|
||||
apkList = ['../info.guardianproject.urzip.apk', '../org.dyndns.fules.ck_20.apk']
|
||||
|
||||
for apkName in apkList:
|
||||
logging.debug("Processing " + apkName)
|
||||
apkfile = os.path.join('repo', apkName)
|
||||
|
||||
apkaapt = _create_apkmetadata_object(apkName)
|
||||
logging.debug("Using AAPT for metadata")
|
||||
fdroidserver.update.scan_apk_aapt(apkaapt, apkfile)
|
||||
# avoid AAPT application name bug
|
||||
del apkaapt['name']
|
||||
|
||||
apkandroguard = _create_apkmetadata_object(apkName)
|
||||
logging.debug("Using androguard for metadata")
|
||||
fdroidserver.update.scan_apk_androguard(apkandroguard, apkfile)
|
||||
# avoid AAPT application name bug
|
||||
del apkandroguard['name']
|
||||
|
||||
self.maxDiff = None
|
||||
self.assertEqual(apkaapt, apkandroguard)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option("-v", "--verbose", action="store_true", default=False,
|
||||
help="Spew out even more information than normal")
|
||||
(fdroidserver.common.options, args) = parser.parse_args(['--verbose'])
|
||||
|
||||
newSuite = unittest.TestSuite()
|
||||
newSuite.addTest(unittest.makeSuite(UpdateTest))
|
||||
unittest.main()
|
20
tests/metadata/apk/info.guardianproject.urzip.yaml
Normal file
20
tests/metadata/apk/info.guardianproject.urzip.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
antiFeatures: !!set {}
|
||||
features: []
|
||||
hash: abfb3adb7496611749e7abfb014c5c789e3a02489e48a5c3665110d1b1acd931
|
||||
hashType: sha256
|
||||
icon: info.guardianproject.urzip.100.png
|
||||
icons:
|
||||
'0': info.guardianproject.urzip.100.png
|
||||
'160': info.guardianproject.urzip.100.png
|
||||
icons_src:
|
||||
'-1': res/drawable/ic_launcher.png
|
||||
'160': res/drawable/ic_launcher.png
|
||||
minSdkVersion: '4'
|
||||
packageName: info.guardianproject.urzip
|
||||
sig: e0ecb5fc2d63088e4a07ae410a127722
|
||||
size: 9969
|
||||
targetSdkVersion: '18'
|
||||
uses-permission: []
|
||||
uses-permission-sdk-23: []
|
||||
versionCode: 100
|
||||
versionName: '0.1'
|
41
tests/metadata/apk/org.dyndns.fules.ck.yaml
Normal file
41
tests/metadata/apk/org.dyndns.fules.ck.yaml
Normal file
@ -0,0 +1,41 @@
|
||||
antiFeatures: !!set {}
|
||||
features: []
|
||||
hash: 897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8
|
||||
hashType: sha256
|
||||
icon: org.dyndns.fules.ck.20.png
|
||||
icons:
|
||||
'0': org.dyndns.fules.ck.20.png
|
||||
'120': org.dyndns.fules.ck.20.png
|
||||
'160': org.dyndns.fules.ck.20.png
|
||||
'240': org.dyndns.fules.ck.20.png
|
||||
icons_src:
|
||||
'-1': res/drawable-mdpi-v4/icon_launcher.png
|
||||
'120': res/drawable-ldpi-v4/icon_launcher.png
|
||||
'160': res/drawable-mdpi-v4/icon_launcher.png
|
||||
'240': res/drawable-hdpi-v4/icon_launcher.png
|
||||
minSdkVersion: '7'
|
||||
nativecode:
|
||||
- arm64-v8a
|
||||
- armeabi
|
||||
- armeabi-v7a
|
||||
- mips
|
||||
- mips64
|
||||
- x86
|
||||
- x86_64
|
||||
packageName: org.dyndns.fules.ck
|
||||
sig: 9bf7a6a67f95688daec75eab4b1436ac
|
||||
size: 132453
|
||||
targetSdkVersion: '8'
|
||||
uses-permission:
|
||||
- !!python/object/new:fdroidserver.update.UsesPermission
|
||||
- android.permission.BIND_INPUT_METHOD
|
||||
- null
|
||||
- !!python/object/new:fdroidserver.update.UsesPermission
|
||||
- android.permission.READ_EXTERNAL_STORAGE
|
||||
- null
|
||||
- !!python/object/new:fdroidserver.update.UsesPermission
|
||||
- android.permission.VIBRATE
|
||||
- null
|
||||
uses-permission-sdk-23: []
|
||||
versionCode: 20
|
||||
versionName: v1.6pre2
|
BIN
tests/org.dyndns.fules.ck_20.apk
Normal file
BIN
tests/org.dyndns.fules.ck_20.apk
Normal file
Binary file not shown.
@ -224,6 +224,54 @@ class UpdateTest(unittest.TestCase):
|
||||
self.assertIsNone(apk.get('obbMainFile'))
|
||||
self.assertIsNone(apk.get('obbPatchFile'))
|
||||
|
||||
def testScanApkMetadata(self):
|
||||
|
||||
def _build_yaml_representer(dumper, data):
|
||||
'''Creates a YAML representation of a Build instance'''
|
||||
return dumper.represent_dict(data)
|
||||
|
||||
config = dict()
|
||||
fdroidserver.common.fill_config_defaults(config)
|
||||
fdroidserver.update.config = config
|
||||
os.chdir(os.path.dirname(__file__))
|
||||
if os.path.basename(os.getcwd()) != 'tests':
|
||||
raise Exception('This test must be run in the "tests/" subdir')
|
||||
|
||||
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
|
||||
|
||||
for icon_dir in fdroidserver.update.get_all_icon_dirs('repo'):
|
||||
if not os.path.exists(icon_dir):
|
||||
os.makedirs(icon_dir)
|
||||
|
||||
knownapks = fdroidserver.common.KnownApks()
|
||||
apkList = ['../urzip.apk', '../org.dyndns.fules.ck_20.apk']
|
||||
|
||||
for apkName in apkList:
|
||||
_, apk, cachechanged = fdroidserver.update.scan_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']
|
||||
|
||||
savepath = os.path.join('metadata', 'apk', apk['packageName'] + '.yaml')
|
||||
# Uncomment to save APK metadata
|
||||
# with open(savepath, 'w') as f:
|
||||
# yaml.add_representer(fdroidserver.metadata.Build, _build_yaml_representer)
|
||||
# yaml.dump(apk, f, default_flow_style=False)
|
||||
|
||||
with open(savepath, 'r') as f:
|
||||
frompickle = yaml.load(f)
|
||||
self.maxDiff = None
|
||||
self.assertEqual(apk, frompickle)
|
||||
|
||||
def test_scan_invalid_apk(self):
|
||||
os.chdir(os.path.join(localmodule, 'tests'))
|
||||
if os.path.basename(os.getcwd()) != 'tests':
|
||||
|
Loading…
Reference in New Issue
Block a user