# has to be manually set in test_aapt_version()
MINIMUM_AAPT_VERSION = '26.0.0'
+VERCODE_OPERATION_RE = re.compile(r'^([ 0-9/*+-]|%c)+$')
+
# A signature block file with a .DSA, .RSA, or .EC extension
CERT_PATH_REGEX = re.compile(r'^META-INF/.*\.(DSA|EC|RSA)$')
APK_NAME_REGEX = re.compile(r'^([a-zA-Z][\w.]*)_(-?[0-9]+)_?([0-9a-f]{7})?\.apk')
r'^java-([6-9])-oracle$', # Debian WebUpd8
r'^jdk-([6-9])-oracle-.*$', # Debian make-jpkg
r'^java-([6-9])-openjdk-[^c][^o][^m].*$', # Debian
+ r'^oracle-jdk-bin-1\.([7-9]).*$', # Gentoo (oracle)
+ r'^icedtea-bin-([7-9]).*$', # Gentoo (openjdk)
]:
m = re.match(regex, j)
if not m:
pathlist += glob.glob('/usr/java/jdk1.[6-9]*')
pathlist += glob.glob('/System/Library/Java/JavaVirtualMachines/1.[6-9].0.jdk')
pathlist += glob.glob('/Library/Java/JavaVirtualMachines/*jdk*[6-9]*')
+ pathlist += glob.glob('/opt/oracle-jdk-*1.[7-9]*')
+ pathlist += glob.glob('/opt/icedtea-*[7-9]*')
if os.getenv('JAVA_HOME') is not None:
pathlist.append(os.getenv('JAVA_HOME'))
if os.getenv('PROGRAMFILES') is not None:
import requests
r = requests.head(remote)
r.raise_for_status()
+ location = r.headers.get('location')
+ if location and not location.startswith('https://'):
+ raise VCSException(_('Invalid redirect to non-HTTPS: {before} -> {after} ')
+ .format(before=remote, after=location))
gitsvn_args.extend(['--', remote, self.local])
p = self.git(gitsvn_args)
def gotorevisionx(self, rev):
if not os.path.exists(self.local):
- p = FDroidPopen(['hg', 'clone', '--ssh', 'false', '--', self.remote, self.local],
+ p = FDroidPopen(['hg', 'clone', '--ssh', '/bin/false', '--', self.remote, self.local],
output=False)
if p.returncode != 0:
self.clone_failed = True
raise VCSException("Unexpected output from hg status -uS: " + line)
FDroidPopen(['rm', '-rf', '--', line[2:]], cwd=self.local, output=False)
if not self.refreshed:
- p = FDroidPopen(['hg', 'pull', '--ssh', 'false'], cwd=self.local, output=False)
+ p = FDroidPopen(['hg', 'pull', '--ssh', '/bin/false'], cwd=self.local, output=False)
if p.returncode != 0:
raise VCSException("Hg pull failed", p.output)
self.refreshed = True
use_androguard.show_path = True
+def _get_androguard_APK(apkfile):
+ try:
+ from androguard.core.bytecodes.apk import APK
+ except ImportError:
+ raise FDroidException("androguard library is not installed and aapt not present")
+
+ return APK(apkfile)
+
+
+def ensure_final_value(packageName, arsc, value):
+ """Ensure incoming value is always the value, not the resid
+
+ androguard will sometimes return the Android "resId" aka
+ Resource ID instead of the actual value. This checks whether
+ the value is actually a resId, then performs the Android
+ Resource lookup as needed.
+
+ """
+ if value:
+ returnValue = value
+ if value[0] == '@':
+ try: # can be a literal value or a resId
+ res_id = int('0x' + value[1:], 16)
+ res_id = arsc.get_id(packageName, res_id)[1]
+ returnValue = arsc.get_string(packageName, res_id)[1]
+ except ValueError:
+ pass
+ return returnValue
+
+
def is_apk_and_debuggable_aapt(apkfile):
p = SdkToolsPopen(['aapt', 'dump', 'xmltree', apkfile, 'AndroidManifest.xml'],
output=False)
def is_apk_and_debuggable_androguard(apkfile):
- try:
- from androguard.core.bytecodes.apk import APK
- except ImportError:
- raise FDroidException("androguard library is not installed and aapt not present")
-
- apkobject = APK(apkfile)
+ apkobject = _get_androguard_APK(apkfile)
if apkobject.is_valid_APK():
debuggable = apkobject.get_element("application", "debuggable")
if debuggable is not None:
return is_apk_and_debuggable_aapt(apkfile)
-def get_apk_id_aapt(apkfile):
- """Extrat identification information from APK using aapt.
+def get_apk_id(apkfile):
+ """Extract identification information from APK using aapt.
:param apkfile: path to an APK file.
:returns: triplet (appid, version code, version name)
"""
- r = re.compile("^package: name='(?P<appid>.*)' versionCode='(?P<vercode>.*)' versionName='(?P<vername>.*)'.*")
+ if use_androguard():
+ return get_apk_id_androguard(apkfile)
+ else:
+ return get_apk_id_aapt(apkfile)
+
+
+def get_apk_id_androguard(apkfile):
+ if not os.path.exists(apkfile):
+ raise FDroidException(_("Reading packageName/versionCode/versionName failed, APK invalid: '{apkfilename}'")
+ .format(apkfilename=apkfile))
+ a = _get_androguard_APK(apkfile)
+ versionName = ensure_final_value(a.package, a.get_android_resources(), a.get_androidversion_name())
+ if not versionName:
+ versionName = '' # versionName is expected to always be a str
+ return a.package, a.get_androidversion_code(), versionName
+
+
+def get_apk_id_aapt(apkfile):
+ r = re.compile("^package: name='(?P<appid>.*)' versionCode='(?P<vercode>.*)' versionName='(?P<vername>.*?)'(?: platformBuildVersionName='.*')?")
p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile], output=False)
for line in p.output.splitlines():
m = r.match(line)
jarsigner passes unsigned APKs as "verified"! So this has to turn
on -strict then check for result 4.
+ Just to be safe, this never reuses the file, and locks down the
+ file permissions while in use. That should prevent a bad actor
+ from changing the settings during operation.
+
:returns: boolean whether the APK was verified
+
"""
_java_security = os.path.join(os.getcwd(), '.java.security')
+ if os.path.exists(_java_security):
+ os.remove(_java_security)
with open(_java_security, 'w') as fp:
fp.write('jdk.jar.disabledAlgorithms=MD2, RSA keySize < 1024')
+ os.chmod(_java_security, 0o400)
try:
cmd = [
else:
logging.debug(_('JAR signature verified: {path}').format(path=apk))
return True
+ finally:
+ if os.path.exists(_java_security):
+ os.chmod(_java_security, 0o600)
+ os.remove(_java_security)
logging.error(_('Old APK signature failed to verify: {path}').format(path=apk)
+ '\n' + output.decode('utf-8'))
log += '* ' + name + ' (' + version + ')\n'
return log
+
+
+def get_git_describe_link():
+ """Get a link to the current fdroiddata commit, to post to the wiki
+
+ """
+ try:
+ output = subprocess.check_output(['git', 'describe', '--always', '--dirty', '--abbrev=0'],
+ universal_newlines=True).strip()
+ except subprocess.CalledProcessError:
+ pass
+ if output:
+ commit = output.replace('-dirty', '')
+ return ('* fdroiddata: [https://gitlab.com/fdroid/fdroiddata/commit/{commit} {id}]\n'
+ .format(commit=commit, id=output))
+ else:
+ logging.error(_("'{path}' failed to execute!").format(path='git describe'))
+ return ''