2017-03-29 23:33:09 +02:00
|
|
|
#!/usr/bin/env python3
|
2017-04-03 19:19:37 +02:00
|
|
|
|
2020-10-01 10:02:05 +02:00
|
|
|
import datetime
|
2017-04-03 19:19:37 +02:00
|
|
|
import inspect
|
2017-11-30 10:14:38 +01:00
|
|
|
import logging
|
2017-03-29 23:33:09 +02:00
|
|
|
import optparse
|
|
|
|
import os
|
2017-04-03 19:19:37 +02:00
|
|
|
import sys
|
2017-03-29 23:33:09 +02:00
|
|
|
import unittest
|
|
|
|
import zipfile
|
2017-05-02 17:05:48 +02:00
|
|
|
from unittest.mock import patch
|
|
|
|
import requests
|
2017-09-20 00:16:13 +02:00
|
|
|
import tempfile
|
|
|
|
import json
|
|
|
|
import shutil
|
2017-03-29 23:33:09 +02:00
|
|
|
|
2017-04-03 19:19:37 +02:00
|
|
|
localmodule = os.path.realpath(
|
2021-06-07 11:49:21 +02:00
|
|
|
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..')
|
|
|
|
)
|
2017-04-03 19:19:37 +02:00
|
|
|
print('localmodule: ' + localmodule)
|
|
|
|
if localmodule not in sys.path:
|
|
|
|
sys.path.insert(0, localmodule)
|
|
|
|
|
2017-03-29 23:33:09 +02:00
|
|
|
import fdroidserver.common
|
|
|
|
import fdroidserver.index
|
2021-04-02 14:36:42 +02:00
|
|
|
import fdroidserver.net
|
2017-03-29 23:33:09 +02:00
|
|
|
import fdroidserver.signindex
|
2017-09-20 00:16:13 +02:00
|
|
|
import fdroidserver.publish
|
|
|
|
from testcommon import TmpCwd
|
2021-09-13 13:18:21 +02:00
|
|
|
from pathlib import Path
|
2017-03-29 23:33:09 +02:00
|
|
|
|
|
|
|
|
2017-05-02 17:05:48 +02:00
|
|
|
GP_FINGERPRINT = 'B7C2EEFD8DAC7806AF67DFCD92EB18126BC08312A7F2D6F3862E46013C7A6135'
|
|
|
|
|
|
|
|
|
2020-10-01 10:02:05 +02:00
|
|
|
class Options:
|
|
|
|
nosign = True
|
|
|
|
pretty = False
|
|
|
|
verbose = False
|
|
|
|
|
|
|
|
|
2017-03-29 23:33:09 +02:00
|
|
|
class IndexTest(unittest.TestCase):
|
|
|
|
def setUp(self):
|
2017-11-30 10:14:38 +01:00
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
self.basedir = os.path.join(localmodule, 'tests')
|
2020-10-01 10:02:05 +02:00
|
|
|
os.chmod(os.path.join(self.basedir, 'config.py'), 0o600)
|
2017-11-30 10:14:38 +01:00
|
|
|
self.tmpdir = os.path.abspath(os.path.join(self.basedir, '..', '.testfiles'))
|
|
|
|
if not os.path.exists(self.tmpdir):
|
|
|
|
os.makedirs(self.tmpdir)
|
|
|
|
os.chdir(self.basedir)
|
|
|
|
|
2017-03-29 23:33:09 +02:00
|
|
|
fdroidserver.common.config = None
|
2020-10-01 10:02:05 +02:00
|
|
|
fdroidserver.common.options = Options
|
2017-03-29 23:33:09 +02:00
|
|
|
config = fdroidserver.common.read_config(fdroidserver.common.options)
|
|
|
|
config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
fdroidserver.signindex.config = config
|
|
|
|
|
2020-10-22 14:39:18 +02:00
|
|
|
if not os.path.exists('repo/index-v1.jar'):
|
2022-05-23 12:39:17 +02:00
|
|
|
fdroidserver.signindex.sign_index(
|
2021-06-07 11:49:21 +02:00
|
|
|
os.path.join(self.basedir, 'repo'), 'index-v1.json'
|
|
|
|
)
|
2020-10-22 14:39:18 +02:00
|
|
|
|
2017-03-29 23:33:09 +02:00
|
|
|
def test_get_public_key_from_jar_succeeds(self):
|
2017-11-30 10:14:38 +01:00
|
|
|
source_dir = os.path.join(self.basedir, 'signindex')
|
2017-03-29 23:33:09 +02:00
|
|
|
for f in ('testy.jar', 'guardianproject.jar'):
|
|
|
|
testfile = os.path.join(source_dir, f)
|
|
|
|
jar = zipfile.ZipFile(testfile)
|
|
|
|
_, fingerprint = fdroidserver.index.get_public_key_from_jar(jar)
|
|
|
|
# comparing fingerprints should be sufficient
|
|
|
|
if f == 'testy.jar':
|
2021-06-07 11:49:21 +02:00
|
|
|
self.assertEqual(
|
|
|
|
fingerprint,
|
|
|
|
'818E469465F96B704E27BE2FEE4C63AB'
|
|
|
|
+ '9F83DDF30E7A34C7371A4728D83B0BC1',
|
|
|
|
)
|
2017-03-29 23:33:09 +02:00
|
|
|
if f == 'guardianproject.jar':
|
2017-05-02 17:05:48 +02:00
|
|
|
self.assertTrue(fingerprint == GP_FINGERPRINT)
|
2017-03-29 23:33:09 +02:00
|
|
|
|
|
|
|
def test_get_public_key_from_jar_fails(self):
|
2017-11-30 10:14:38 +01:00
|
|
|
source_dir = os.path.join(self.basedir, 'signindex')
|
2017-03-29 23:33:09 +02:00
|
|
|
testfile = os.path.join(source_dir, 'unsigned.jar')
|
|
|
|
jar = zipfile.ZipFile(testfile)
|
|
|
|
with self.assertRaises(fdroidserver.index.VerificationException):
|
|
|
|
fdroidserver.index.get_public_key_from_jar(jar)
|
|
|
|
|
|
|
|
def test_download_repo_index_no_fingerprint(self):
|
|
|
|
with self.assertRaises(fdroidserver.index.VerificationException):
|
|
|
|
fdroidserver.index.download_repo_index("http://example.org")
|
|
|
|
|
|
|
|
def test_download_repo_index_no_jar(self):
|
2020-02-05 20:28:48 +01:00
|
|
|
with self.assertRaises(requests.exceptions.RequestException):
|
2021-06-07 11:49:21 +02:00
|
|
|
fdroidserver.index.download_repo_index(
|
|
|
|
"http://example.org?fingerprint=nope"
|
|
|
|
)
|
2017-03-29 23:33:09 +02:00
|
|
|
|
2020-10-22 14:39:18 +02:00
|
|
|
def test_get_repo_key_fingerprint(self):
|
|
|
|
pubkey, fingerprint = fdroidserver.index.extract_pubkey()
|
2021-06-07 11:49:21 +02:00
|
|
|
data, public_key, public_key_fingerprint = fdroidserver.index.get_index_from_jar(
|
|
|
|
'repo/index-v1.jar', fingerprint
|
|
|
|
)
|
2020-10-22 14:39:18 +02:00
|
|
|
self.assertIsNotNone(data)
|
|
|
|
self.assertIsNotNone(public_key)
|
|
|
|
self.assertIsNotNone(public_key_fingerprint)
|
|
|
|
|
|
|
|
def test_get_index_from_jar_with_bad_fingerprint(self):
|
|
|
|
pubkey, fingerprint = fdroidserver.index.extract_pubkey()
|
|
|
|
fingerprint = fingerprint[:-1] + 'G'
|
|
|
|
with self.assertRaises(fdroidserver.exception.VerificationException):
|
|
|
|
fdroidserver.index.get_index_from_jar('repo/index-v1.jar', fingerprint)
|
|
|
|
|
|
|
|
def test_get_index_from_jar_with_chars_to_be_stripped(self):
|
|
|
|
fingerprint = 'NOOOO F4 9A F3 F1 1E FD DF 20 DF FD 70 F5 E3 11 7B 99 76 67 41 67 AD CA 28 0E 6B 19 32 A0 60 1B 26 F6'
|
2021-06-07 11:49:21 +02:00
|
|
|
data, public_key, public_key_fingerprint = fdroidserver.index.get_index_from_jar(
|
|
|
|
'repo/index-v1.jar', fingerprint
|
|
|
|
)
|
2020-10-22 14:39:18 +02:00
|
|
|
|
2017-05-02 17:05:48 +02:00
|
|
|
@patch('requests.head')
|
|
|
|
def test_download_repo_index_same_etag(self, head):
|
|
|
|
url = 'http://example.org?fingerprint=test'
|
|
|
|
etag = '"4de5-54d840ce95cb9"'
|
|
|
|
|
|
|
|
head.return_value.headers = {'ETag': etag}
|
|
|
|
index, new_etag = fdroidserver.index.download_repo_index(url, etag=etag)
|
|
|
|
|
|
|
|
self.assertIsNone(index)
|
|
|
|
self.assertEqual(etag, new_etag)
|
|
|
|
|
|
|
|
@patch('requests.get')
|
|
|
|
@patch('requests.head')
|
|
|
|
def test_download_repo_index_new_etag(self, head, get):
|
|
|
|
url = 'http://example.org?fingerprint=' + GP_FINGERPRINT
|
|
|
|
etag = '"4de5-54d840ce95cb9"'
|
|
|
|
|
|
|
|
# fake HTTP answers
|
|
|
|
head.return_value.headers = {'ETag': 'new_etag'}
|
|
|
|
get.return_value.headers = {'ETag': 'new_etag'}
|
|
|
|
get.return_value.status_code = 200
|
2018-08-14 10:08:03 +02:00
|
|
|
testfile = os.path.join('signindex', 'guardianproject-v1.jar')
|
2017-05-02 17:05:48 +02:00
|
|
|
with open(testfile, 'rb') as file:
|
|
|
|
get.return_value.content = file.read()
|
|
|
|
|
|
|
|
index, new_etag = fdroidserver.index.download_repo_index(url, etag=etag)
|
|
|
|
|
|
|
|
# assert that the index was retrieved properly
|
|
|
|
self.assertEqual('Guardian Project Official Releases', index['repo']['name'])
|
|
|
|
self.assertEqual(GP_FINGERPRINT, index['repo']['fingerprint'])
|
|
|
|
self.assertTrue(len(index['repo']['pubkey']) > 500)
|
|
|
|
self.assertEqual(10, len(index['apps']))
|
|
|
|
self.assertEqual(10, len(index['packages']))
|
|
|
|
self.assertEqual('new_etag', new_etag)
|
2017-03-29 23:33:09 +02:00
|
|
|
|
2021-04-02 14:36:42 +02:00
|
|
|
@patch('fdroidserver.net.http_get')
|
|
|
|
def test_download_repo_index_url_parsing(self, mock_http_get):
|
|
|
|
mock_http_get.side_effect = lambda url, etag, timeout: (None, url)
|
|
|
|
repo_url = 'https://example.org/fdroid/repo'
|
|
|
|
index_url = 'https://example.org/fdroid/repo/index-v1.jar'
|
|
|
|
fingerprint_url = 'https://example.org/fdroid/repo?fingerprint=' + GP_FINGERPRINT
|
|
|
|
slash_url = 'https://example.org/fdroid/repo//?fingerprint=' + GP_FINGERPRINT
|
|
|
|
for url in (repo_url, index_url, fingerprint_url, slash_url):
|
|
|
|
_ignored, returned_url = fdroidserver.index.download_repo_index(url, verify_fingerprint=False)
|
|
|
|
self.assertEqual(index_url, returned_url)
|
|
|
|
|
2017-09-20 00:16:13 +02:00
|
|
|
def test_v1_sort_packages(self):
|
|
|
|
|
|
|
|
i = [{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'org.smssecure.smssecure_134.apk',
|
|
|
|
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
|
|
|
|
'versionCode': 134},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'org.smssecure.smssecure_134_b30bb97.apk',
|
|
|
|
'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
|
|
|
|
'versionCode': 134},
|
|
|
|
{'packageName': 'b075b32b4ef1e8a869e00edb136bd48e34a0382b85ced8628f164d1199584e4e'},
|
|
|
|
{'packageName': '43af70d1aca437c2f9974c4634cc5abe45bdc4d5d71529ac4e553488d3bb3ff6'},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'org.smssecure.smssecure_135_b30bb97.apk',
|
|
|
|
'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
|
|
|
|
'versionCode': 135},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'org.smssecure.smssecure_135.apk',
|
|
|
|
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
|
|
|
|
'versionCode': 135},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'org.smssecure.smssecure_133.apk',
|
|
|
|
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
|
|
|
|
'versionCode': 133},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'smssecure-weird-version.apk',
|
|
|
|
'signer': '99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff',
|
|
|
|
'versionCode': 133},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'smssecure-custom.apk',
|
|
|
|
'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
|
|
|
|
'versionCode': 133},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'smssecure-new-custom.apk',
|
|
|
|
'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
|
|
|
|
'versionCode': 135}]
|
|
|
|
|
|
|
|
o = [{'packageName': '43af70d1aca437c2f9974c4634cc5abe45bdc4d5d71529ac4e553488d3bb3ff6'},
|
|
|
|
{'packageName': 'b075b32b4ef1e8a869e00edb136bd48e34a0382b85ced8628f164d1199584e4e'},
|
|
|
|
# app test data
|
|
|
|
# # packages with reproducible developer signature
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'org.smssecure.smssecure_135_b30bb97.apk',
|
|
|
|
'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
|
|
|
|
'versionCode': 135},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'org.smssecure.smssecure_134_b30bb97.apk',
|
|
|
|
'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
|
|
|
|
'versionCode': 134},
|
|
|
|
# # packages build and signed by fdroid
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'org.smssecure.smssecure_135.apk',
|
|
|
|
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
|
|
|
|
'versionCode': 135},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'org.smssecure.smssecure_134.apk',
|
|
|
|
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
|
|
|
|
'versionCode': 134},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'org.smssecure.smssecure_133.apk',
|
|
|
|
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
|
|
|
|
'versionCode': 133},
|
|
|
|
# # packages signed with unkown keys
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'smssecure-new-custom.apk',
|
|
|
|
'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
|
|
|
|
'versionCode': 135},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'smssecure-custom.apk',
|
|
|
|
'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
|
|
|
|
'versionCode': 133},
|
|
|
|
{'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'smssecure-weird-version.apk',
|
|
|
|
'signer': '99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff',
|
|
|
|
'versionCode': 133}]
|
|
|
|
|
|
|
|
fdroidserver.common.config = {}
|
|
|
|
fdroidserver.common.fill_config_defaults(fdroidserver.common.config)
|
|
|
|
fdroidserver.publish.config = fdroidserver.common.config
|
|
|
|
fdroidserver.publish.config['keystorepass'] = '123456'
|
|
|
|
fdroidserver.publish.config['keypass'] = '123456'
|
|
|
|
fdroidserver.publish.config['keystore'] = os.path.join(os.getcwd(),
|
|
|
|
'dummy-keystore.jks')
|
|
|
|
fdroidserver.publish.config['repo_keyalias'] = 'repokey'
|
|
|
|
|
|
|
|
testsmetadir = os.path.join(os.getcwd(), 'metadata')
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
|
|
|
|
shutil.copytree(testsmetadir, 'metadata')
|
|
|
|
sigkeyfps = {
|
|
|
|
"org.smssecure.smssecure": {
|
|
|
|
"signer": "b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6"
|
|
|
|
}
|
|
|
|
}
|
2017-09-05 15:17:22 +02:00
|
|
|
os.makedirs('stats')
|
|
|
|
jarfile = 'stats/publishsigkeys.jar'
|
2017-09-20 00:16:13 +02:00
|
|
|
with zipfile.ZipFile(jarfile, 'w', zipfile.ZIP_DEFLATED) as jar:
|
|
|
|
jar.writestr('publishsigkeys.json', json.dumps(sigkeyfps))
|
|
|
|
fdroidserver.publish.sign_sig_key_fingerprint_list(jarfile)
|
|
|
|
with open('config.py', 'w'):
|
|
|
|
pass
|
|
|
|
|
|
|
|
fdroidserver.index.v1_sort_packages(
|
2021-06-07 11:49:21 +02:00
|
|
|
i, fdroidserver.common.load_stats_fdroid_signing_key_fingerprints()
|
|
|
|
)
|
2017-09-20 00:16:13 +02:00
|
|
|
self.maxDiff = None
|
|
|
|
self.assertEqual(json.dumps(i, indent=2), json.dumps(o, indent=2))
|
|
|
|
|
2020-01-13 15:43:42 +01:00
|
|
|
def test_make_v0_repo_only(self):
|
2021-06-07 11:49:21 +02:00
|
|
|
tmptestsdir = tempfile.mkdtemp(
|
|
|
|
prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir
|
|
|
|
)
|
2020-01-13 15:43:42 +01:00
|
|
|
os.chdir(tmptestsdir)
|
|
|
|
os.mkdir('repo')
|
|
|
|
repo_icons_dir = os.path.join('repo', 'icons')
|
|
|
|
self.assertFalse(os.path.isdir(repo_icons_dir))
|
|
|
|
repodict = {
|
|
|
|
'address': 'https://example.com/fdroid/repo',
|
|
|
|
'description': 'This is just a test',
|
|
|
|
'icon': 'blahblah',
|
|
|
|
'name': 'test',
|
|
|
|
'timestamp': datetime.datetime.now(),
|
|
|
|
'version': 12,
|
|
|
|
}
|
|
|
|
requestsdict = {'install': [], 'uninstall': []}
|
|
|
|
fdroidserver.common.config['repo_pubkey'] = 'ffffffffffffffffffffffffffffffffff'
|
|
|
|
fdroidserver.index.make_v0({}, [], 'repo', repodict, requestsdict, {})
|
|
|
|
self.assertTrue(os.path.isdir(repo_icons_dir))
|
2021-06-07 11:49:21 +02:00
|
|
|
self.assertTrue(
|
|
|
|
os.path.exists(
|
|
|
|
os.path.join(
|
|
|
|
repo_icons_dir, fdroidserver.common.default_config['repo_icon']
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
2020-01-13 15:43:42 +01:00
|
|
|
self.assertTrue(os.path.exists(os.path.join('repo', 'index.xml')))
|
|
|
|
|
2020-10-01 10:02:05 +02:00
|
|
|
def test_make_v0(self):
|
2021-06-07 11:49:21 +02:00
|
|
|
tmptestsdir = tempfile.mkdtemp(
|
|
|
|
prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir
|
|
|
|
)
|
2020-10-01 10:02:05 +02:00
|
|
|
os.chdir(tmptestsdir)
|
2020-01-13 15:43:42 +01:00
|
|
|
os.mkdir('metadata')
|
2020-10-01 10:02:05 +02:00
|
|
|
os.mkdir('repo')
|
2020-01-13 15:43:42 +01:00
|
|
|
metadatafile = 'metadata/info.zwanenburg.caffeinetile.yml'
|
2021-06-07 11:49:21 +02:00
|
|
|
shutil.copy(os.path.join(self.basedir, metadatafile), metadatafile)
|
2020-10-01 10:02:05 +02:00
|
|
|
repo_icons_dir = os.path.join('repo', 'icons')
|
|
|
|
self.assertFalse(os.path.isdir(repo_icons_dir))
|
|
|
|
repodict = {
|
|
|
|
'address': 'https://example.com/fdroid/repo',
|
|
|
|
'description': 'This is just a test',
|
|
|
|
'icon': 'blahblah',
|
|
|
|
'name': 'test',
|
|
|
|
'timestamp': datetime.datetime.now(),
|
|
|
|
'version': 12,
|
|
|
|
}
|
2021-03-16 09:22:55 +01:00
|
|
|
app = fdroidserver.metadata.parse_metadata(metadatafile)
|
2020-01-13 15:43:42 +01:00
|
|
|
app['icon'] = 'info.zwanenburg.caffeinetile.4.xml'
|
|
|
|
app['CurrentVersionCode'] = '4'
|
|
|
|
apps = {app.id: app}
|
|
|
|
apk = {
|
|
|
|
'hash': 'dbbdd7deadb038862f426b71efe4a64df8c3edf25d669e935f349510e16f65db',
|
|
|
|
'hashType': 'sha256',
|
2021-06-07 11:49:21 +02:00
|
|
|
'uses-permission': [['android.permission.WAKE_LOCK', None]],
|
2020-01-13 15:43:42 +01:00
|
|
|
'uses-permission-sdk-23': [],
|
|
|
|
'features': [],
|
|
|
|
'icons_src': {
|
|
|
|
'160': 'res/drawable/ic_coffee_on.xml',
|
2021-06-07 11:49:21 +02:00
|
|
|
'-1': 'res/drawable/ic_coffee_on.xml',
|
2020-01-13 15:43:42 +01:00
|
|
|
},
|
2021-06-07 11:49:21 +02:00
|
|
|
'icons': {'160': 'info.zwanenburg.caffeinetile.4.xml'},
|
2020-01-13 15:43:42 +01:00
|
|
|
'antiFeatures': [],
|
|
|
|
'packageName': 'info.zwanenburg.caffeinetile',
|
|
|
|
'versionCode': 4,
|
|
|
|
'name': 'Caffeine Tile',
|
|
|
|
'versionName': '1.3',
|
|
|
|
'minSdkVersion': 24,
|
|
|
|
'targetSdkVersion': 25,
|
|
|
|
'sig': '03f9b2f848d22fd1d8d1331e8b1b486d',
|
|
|
|
'signer': '51cfa5c8a743833ad89acf81cb755936876a5c8b8eca54d1ffdcec0cdca25d0e',
|
|
|
|
'size': 11740,
|
|
|
|
'apkName': 'info.zwanenburg.caffeinetile_4.apk',
|
|
|
|
'icon': 'info.zwanenburg.caffeinetile.4.xml',
|
|
|
|
'added': datetime.datetime.fromtimestamp(1539122400),
|
|
|
|
}
|
2020-10-01 10:02:05 +02:00
|
|
|
requestsdict = {'install': [], 'uninstall': []}
|
|
|
|
fdroidserver.common.config['repo_pubkey'] = 'ffffffffffffffffffffffffffffffffff'
|
2020-01-13 15:43:42 +01:00
|
|
|
fdroidserver.common.config['make_current_version_link'] = True
|
|
|
|
fdroidserver.index.make_v0(apps, [apk], 'repo', repodict, requestsdict, {})
|
2020-10-01 10:02:05 +02:00
|
|
|
self.assertTrue(os.path.isdir(repo_icons_dir))
|
2021-06-07 11:49:21 +02:00
|
|
|
self.assertTrue(
|
|
|
|
os.path.exists(
|
|
|
|
os.path.join(
|
|
|
|
repo_icons_dir, fdroidserver.common.default_config['repo_icon']
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
2020-10-01 10:02:05 +02:00
|
|
|
self.assertTrue(os.path.exists(os.path.join('repo', 'index.xml')))
|
|
|
|
|
2022-06-07 13:15:36 +02:00
|
|
|
def test_v0_invalid_config_exception(self):
|
|
|
|
"""Index v0 needs additional config values when using --nosign
|
|
|
|
|
|
|
|
index.xml aka Index v0 includes the full repo public key in
|
|
|
|
the XML itself. So when running `fdroid update --nosign`,
|
|
|
|
there needs to be either repo_pubkey or a full keystore config
|
|
|
|
present.
|
|
|
|
|
|
|
|
"""
|
|
|
|
tmptestsdir = tempfile.mkdtemp(
|
|
|
|
prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir
|
|
|
|
)
|
|
|
|
os.chdir(tmptestsdir)
|
|
|
|
os.mkdir('repo')
|
|
|
|
repo_icons_dir = os.path.join('repo', 'icons')
|
|
|
|
self.assertFalse(os.path.isdir(repo_icons_dir))
|
|
|
|
repodict = {
|
|
|
|
'address': 'https://example.com/fdroid/repo',
|
|
|
|
'description': 'This is just a test',
|
|
|
|
'icon': 'blahblah',
|
|
|
|
'name': 'test',
|
|
|
|
'timestamp': datetime.datetime.now(),
|
|
|
|
'version': 12,
|
|
|
|
}
|
|
|
|
requestsdict = {'install': [], 'uninstall': []}
|
|
|
|
|
|
|
|
fdroidserver.common.options.nosign = False
|
|
|
|
with self.assertRaises(fdroidserver.exception.FDroidException):
|
|
|
|
fdroidserver.index.make_v0({}, [], 'repo', repodict, requestsdict, {})
|
|
|
|
|
|
|
|
fdroidserver.common.options.nosign = True
|
|
|
|
with self.assertRaises(fdroidserver.exception.FDroidException):
|
|
|
|
fdroidserver.index.make_v0({}, [], 'repo', repodict, requestsdict, {})
|
|
|
|
|
|
|
|
fdroidserver.common.config['repo_pubkey'] = 'ffffffffffffffffffffffffffffffffff'
|
|
|
|
self.assertFalse(os.path.exists(os.path.join('repo', 'index.xml')))
|
|
|
|
self.assertFalse(os.path.exists(os.path.join('repo', 'index_unsigned.jar')))
|
|
|
|
self.assertFalse(os.path.exists(os.path.join('repo', 'index.jar')))
|
|
|
|
fdroidserver.index.make_v0({}, [], 'repo', repodict, requestsdict, {})
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('repo', 'index.xml')))
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('repo', 'index_unsigned.jar')))
|
|
|
|
self.assertFalse(os.path.exists(os.path.join('repo', 'index.jar')))
|
|
|
|
|
2021-08-23 12:45:03 +02:00
|
|
|
def test_github_get_mirror_service_urls(self):
|
2020-12-08 19:44:39 +01:00
|
|
|
for url in [
|
2021-06-07 11:49:21 +02:00
|
|
|
'git@github.com:foo/bar',
|
|
|
|
'git@github.com:foo/bar.git',
|
|
|
|
'https://github.com/foo/bar',
|
|
|
|
'https://github.com/foo/bar.git',
|
2020-12-08 19:44:39 +01:00
|
|
|
]:
|
2021-06-07 11:49:21 +02:00
|
|
|
self.assertEqual(
|
|
|
|
['https://raw.githubusercontent.com/foo/bar/master/fdroid'],
|
|
|
|
fdroidserver.index.get_mirror_service_urls(url),
|
|
|
|
)
|
2020-12-08 19:44:39 +01:00
|
|
|
|
2021-08-23 12:45:03 +02:00
|
|
|
def test_gitlab_get_mirror_service_urls(self):
|
2021-09-13 13:18:21 +02:00
|
|
|
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
|
|
|
|
os.mkdir('fdroid')
|
|
|
|
with Path('fdroid/placeholder').open('w') as fp:
|
|
|
|
fp.write(' ')
|
|
|
|
for url in [
|
|
|
|
'git@gitlab.com:group/project',
|
|
|
|
'git@gitlab.com:group/project.git',
|
|
|
|
'https://gitlab.com/group/project',
|
|
|
|
'https://gitlab.com/group/project.git',
|
|
|
|
]:
|
|
|
|
with patch('fdroidserver.common.GITLAB_COM_PAGES_MAX_SIZE', 1000):
|
|
|
|
self.assertEqual(
|
|
|
|
[
|
|
|
|
'https://group.gitlab.io/project/fdroid',
|
|
|
|
'https://gitlab.com/group/project/-/raw/master/fdroid',
|
|
|
|
],
|
|
|
|
fdroidserver.index.get_mirror_service_urls(url),
|
|
|
|
)
|
|
|
|
with patch('fdroidserver.common.GITLAB_COM_PAGES_MAX_SIZE', 10):
|
|
|
|
self.assertEqual(
|
|
|
|
[
|
|
|
|
'https://gitlab.com/group/project/-/raw/master/fdroid',
|
|
|
|
],
|
|
|
|
fdroidserver.index.get_mirror_service_urls(url),
|
|
|
|
)
|
2020-12-08 19:44:39 +01:00
|
|
|
|
2021-02-01 07:54:48 +01:00
|
|
|
def test_make_website(self):
|
2021-06-07 11:49:21 +02:00
|
|
|
tmptestsdir = tempfile.mkdtemp(
|
|
|
|
prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir
|
|
|
|
)
|
2021-02-01 07:54:48 +01:00
|
|
|
os.chdir(tmptestsdir)
|
|
|
|
os.mkdir('metadata')
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
|
|
|
repodict = {
|
|
|
|
'address': 'https://example.com/fdroid/repo',
|
|
|
|
'description': 'This is just a test',
|
|
|
|
'icon': 'blahblah',
|
|
|
|
'name': 'test',
|
|
|
|
'timestamp': datetime.datetime.now(),
|
|
|
|
'version': 12,
|
|
|
|
}
|
|
|
|
|
|
|
|
fdroidserver.common.config['repo_pubkey'] = 'ffffffffffffffffffffffffffffffffff'
|
|
|
|
|
|
|
|
fdroidserver.index.make_website([], "repo", repodict)
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('repo', 'index.html')))
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('repo', 'index.css')))
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('repo', 'index.png')))
|
|
|
|
|
2021-02-08 06:37:44 +01:00
|
|
|
try:
|
|
|
|
from html5print import CSSBeautifier, HTMLBeautifier
|
|
|
|
except ImportError:
|
2021-02-08 11:56:41 +01:00
|
|
|
print('WARNING: skipping rest of test since html5print is missing!')
|
2021-02-08 06:37:44 +01:00
|
|
|
return
|
|
|
|
|
2021-02-01 07:54:48 +01:00
|
|
|
with open(os.path.join("repo", "index.html")) as f:
|
|
|
|
html = f.read()
|
|
|
|
pretty_html = HTMLBeautifier.beautify(html)
|
|
|
|
self.maxDiff = None
|
|
|
|
self.assertEquals(html, pretty_html)
|
|
|
|
|
|
|
|
with open(os.path.join("repo", "index.css")) as f:
|
|
|
|
css = f.read()
|
|
|
|
pretty_css = CSSBeautifier.beautify(css)
|
|
|
|
self.maxDiff = None
|
|
|
|
self.assertEquals(css, pretty_css)
|
|
|
|
|
index: raise error rather than crash on bad repo file
If a non-APK is added with the appid/packageName that matches some APKs, it
should through an error.
Traceback (most recent call last):
File "/home/hans/code/fdroid/server/fdroid", line 22, in <module>
fdroidserver.__main__.main()
File "/home/hans/code/fdroid/server/fdroidserver/__main__.py", line 211, in main
mod.main()
File "/home/hans/code/fdroid/server/fdroidserver/update.py", line 2343, in main
index.make(apps, sortedids, apks, repodirs[0], False)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 142, in make
fdroid_signing_key_fingerprints)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 166, in make_v1
v1_sort_packages(packages, fdroid_signing_key_fingerprints)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 292, in v1_sort_packages
packages.sort(key=v1_sort_keys)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 288, in v1_sort_keys
.format(apkfilename=package['apkName']))
fdroidserver.exception.FDroidException: at.roteskreuz.stopcorona_8.jobf does not have a valid signature!
2020-06-16 17:28:01 +02:00
|
|
|
def test_v1_sort_packages_with_invalid(self):
|
2021-06-07 11:49:21 +02:00
|
|
|
i = [
|
|
|
|
{
|
|
|
|
'packageName': 'org.smssecure.smssecure',
|
|
|
|
'apkName': 'smssecure-custom.fake',
|
|
|
|
'signer': None,
|
|
|
|
'versionCode': 11111,
|
|
|
|
}
|
|
|
|
]
|
index: raise error rather than crash on bad repo file
If a non-APK is added with the appid/packageName that matches some APKs, it
should through an error.
Traceback (most recent call last):
File "/home/hans/code/fdroid/server/fdroid", line 22, in <module>
fdroidserver.__main__.main()
File "/home/hans/code/fdroid/server/fdroidserver/__main__.py", line 211, in main
mod.main()
File "/home/hans/code/fdroid/server/fdroidserver/update.py", line 2343, in main
index.make(apps, sortedids, apks, repodirs[0], False)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 142, in make
fdroid_signing_key_fingerprints)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 166, in make_v1
v1_sort_packages(packages, fdroid_signing_key_fingerprints)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 292, in v1_sort_packages
packages.sort(key=v1_sort_keys)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 288, in v1_sort_keys
.format(apkfilename=package['apkName']))
fdroidserver.exception.FDroidException: at.roteskreuz.stopcorona_8.jobf does not have a valid signature!
2020-06-16 17:28:01 +02:00
|
|
|
|
|
|
|
fdroidserver.index.v1_sort_packages(
|
2021-06-07 11:49:21 +02:00
|
|
|
i, fdroidserver.common.load_stats_fdroid_signing_key_fingerprints()
|
|
|
|
)
|
index: raise error rather than crash on bad repo file
If a non-APK is added with the appid/packageName that matches some APKs, it
should through an error.
Traceback (most recent call last):
File "/home/hans/code/fdroid/server/fdroid", line 22, in <module>
fdroidserver.__main__.main()
File "/home/hans/code/fdroid/server/fdroidserver/__main__.py", line 211, in main
mod.main()
File "/home/hans/code/fdroid/server/fdroidserver/update.py", line 2343, in main
index.make(apps, sortedids, apks, repodirs[0], False)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 142, in make
fdroid_signing_key_fingerprints)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 166, in make_v1
v1_sort_packages(packages, fdroid_signing_key_fingerprints)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 292, in v1_sort_packages
packages.sort(key=v1_sort_keys)
File "/home/hans/code/fdroid/server/fdroidserver/index.py", line 288, in v1_sort_keys
.format(apkfilename=package['apkName']))
fdroidserver.exception.FDroidException: at.roteskreuz.stopcorona_8.jobf does not have a valid signature!
2020-06-16 17:28:01 +02:00
|
|
|
|
2017-03-29 23:33:09 +02:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2018-08-14 10:08:03 +02:00
|
|
|
os.chdir(os.path.dirname(__file__))
|
|
|
|
|
2017-03-29 23:33:09 +02:00
|
|
|
parser = optparse.OptionParser()
|
2021-06-07 11:49:21 +02:00
|
|
|
parser.add_option(
|
|
|
|
"-v",
|
|
|
|
"--verbose",
|
|
|
|
action="store_true",
|
|
|
|
default=False,
|
|
|
|
help="Spew out even more information than normal",
|
|
|
|
)
|
2020-10-01 10:02:05 +02:00
|
|
|
(options, args) = parser.parse_args()
|
|
|
|
Options.verbose = options.verbose
|
2017-03-29 23:33:09 +02:00
|
|
|
|
|
|
|
newSuite = unittest.TestSuite()
|
|
|
|
newSuite.addTest(unittest.makeSuite(IndexTest))
|
2017-10-20 11:51:59 +02:00
|
|
|
unittest.main(failfast=False)
|