Tests for ApkVerifier
This commit is contained in:
parent
2837a235b4
commit
9235462e34
BIN
app/src/androidTest/assets/org.fdroid.permissions.minmax.apk
Normal file
BIN
app/src/androidTest/assets/org.fdroid.permissions.minmax.apk
Normal file
Binary file not shown.
BIN
app/src/androidTest/assets/org.fdroid.permissions.minmax.zip
Normal file
BIN
app/src/androidTest/assets/org.fdroid.permissions.minmax.zip
Normal file
Binary file not shown.
BIN
app/src/androidTest/assets/org.fdroid.permissions.sdk14.apk
Normal file
BIN
app/src/androidTest/assets/org.fdroid.permissions.sdk14.apk
Normal file
Binary file not shown.
BIN
app/src/androidTest/assets/org.fdroid.permissions.sdk14.zip
Normal file
BIN
app/src/androidTest/assets/org.fdroid.permissions.sdk14.zip
Normal file
Binary file not shown.
39
app/src/androidTest/java/org/fdroid/fdroid/AssetUtils.java
Normal file
39
app/src/androidTest/java/org/fdroid/fdroid/AssetUtils.java
Normal file
@ -0,0 +1,39 @@
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class AssetUtils {
|
||||
|
||||
private static final String TAG = "Utils";
|
||||
|
||||
@Nullable
|
||||
public static File copyAssetToDir(Context context, String assetName, File directory) {
|
||||
File tempFile = null;
|
||||
InputStream input = null;
|
||||
OutputStream output = null;
|
||||
try {
|
||||
tempFile = File.createTempFile(assetName, ".testasset", directory);
|
||||
Log.i(TAG, "Copying asset file " + assetName + " to directory " + directory);
|
||||
input = context.getAssets().open(assetName);
|
||||
output = new FileOutputStream(tempFile);
|
||||
Utils.copy(input, output);
|
||||
} catch (IOException e) {
|
||||
fail(e.getMessage());
|
||||
} finally {
|
||||
Utils.closeQuietly(output);
|
||||
Utils.closeQuietly(input);
|
||||
}
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
}
|
@ -4,12 +4,10 @@ import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.Log;
|
||||
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.AssetUtils;
|
||||
import org.fdroid.fdroid.data.SanitizedFile;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@ -17,10 +15,6 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@ -36,8 +30,6 @@ import static org.junit.Assume.assumeTrue;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FileCompatTest {
|
||||
|
||||
private static final String TAG = "FileCompatTest";
|
||||
|
||||
private SanitizedFile sourceFile;
|
||||
private SanitizedFile destFile;
|
||||
|
||||
@ -45,7 +37,8 @@ public class FileCompatTest {
|
||||
public void setUp() {
|
||||
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
|
||||
File dir = getWriteableDir(instrumentation);
|
||||
sourceFile = SanitizedFile.knownSanitized(copyAssetToDir(instrumentation.getContext(), "simpleIndex.jar", dir));
|
||||
sourceFile = SanitizedFile.knownSanitized(
|
||||
AssetUtils.copyAssetToDir(instrumentation.getContext(), "simpleIndex.jar", dir));
|
||||
destFile = new SanitizedFile(dir, "dest-" + UUID.randomUUID() + ".testproduct");
|
||||
assertFalse(destFile.exists());
|
||||
assertTrue(sourceFile.getAbsolutePath() + " should exist.", sourceFile.exists());
|
||||
@ -82,26 +75,6 @@ public class FileCompatTest {
|
||||
assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static File copyAssetToDir(Context context, String assetName, File directory) {
|
||||
File tempFile;
|
||||
InputStream input = null;
|
||||
OutputStream output = null;
|
||||
try {
|
||||
tempFile = File.createTempFile(assetName + "-", ".testasset", directory);
|
||||
Log.i(TAG, "Copying asset file " + assetName + " to directory " + directory);
|
||||
input = context.getAssets().open(assetName);
|
||||
output = new FileOutputStream(tempFile);
|
||||
Utils.copy(input, output);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
Utils.closeQuietly(output);
|
||||
Utils.closeQuietly(input);
|
||||
}
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer internal over external storage, because external tends to be FAT filesystems,
|
||||
|
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.fdroid.fdroid.installer;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.net.Uri;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.fdroid.fdroid.AssetUtils;
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* This test checks the ApkVerifier by parsing a repo from permissionsRepo.xml
|
||||
* and checking the listed permissions against the ones specified in apks' AndroidManifest,
|
||||
* which have been specifically generated for this test.
|
||||
* - the apk file name must match the package name in the xml
|
||||
* - the versionName of listed apks inside the repo have either a good or bad outcome.
|
||||
* this must be defined in GOOD_VERSION_NAMES and BAD_VERSION_NAMES.
|
||||
* <p/>
|
||||
* NOTE: This androidTest cannot run as a Robolectric test because the
|
||||
* required methods from PackageManger are not included in Robolectric's Android API.
|
||||
* java.lang.NoClassDefFoundError: java/util/jar/StrictJarFile
|
||||
* at android.content.pm.PackageManager.getPackageArchiveInfo(PackageManager.java:3545)
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ApkVerifierTest {
|
||||
|
||||
Instrumentation instrumentation;
|
||||
|
||||
File sdk14Apk;
|
||||
File minMaxApk;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
instrumentation = InstrumentationRegistry.getInstrumentation();
|
||||
File dir = null;
|
||||
try {
|
||||
dir = tempFolder.newFolder("apks");
|
||||
} catch (IOException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
sdk14Apk = AssetUtils.copyAssetToDir(instrumentation.getContext(),
|
||||
"org.fdroid.permissions.sdk14.apk",
|
||||
dir
|
||||
);
|
||||
minMaxApk = AssetUtils.copyAssetToDir(instrumentation.getContext(),
|
||||
"org.fdroid.permissions.minmax.apk",
|
||||
dir
|
||||
);
|
||||
assertTrue(sdk14Apk.exists());
|
||||
assertTrue(minMaxApk.exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithoutPrefix() {
|
||||
Apk apk = new Apk();
|
||||
apk.packageName = "org.fdroid.permissions.sdk14";
|
||||
apk.targetSdkVersion = 14;
|
||||
apk.permissions = new String[]{
|
||||
"AUTHENTICATE_ACCOUNTS",
|
||||
"MANAGE_ACCOUNTS",
|
||||
"READ_PROFILE",
|
||||
"WRITE_PROFILE",
|
||||
"GET_ACCOUNTS",
|
||||
"READ_CONTACTS",
|
||||
"WRITE_CONTACTS",
|
||||
"WRITE_EXTERNAL_STORAGE",
|
||||
"READ_EXTERNAL_STORAGE",
|
||||
"INTERNET",
|
||||
"ACCESS_NETWORK_STATE",
|
||||
"NFC",
|
||||
"READ_SYNC_SETTINGS",
|
||||
"WRITE_SYNC_SETTINGS",
|
||||
"WRITE_CALL_LOG", // implied-permission!
|
||||
"READ_CALL_LOG", // implied-permission!
|
||||
};
|
||||
|
||||
Uri uri = Uri.fromFile(sdk14Apk);
|
||||
|
||||
ApkVerifier apkVerifier = new ApkVerifier(instrumentation.getContext(), uri, apk);
|
||||
|
||||
try {
|
||||
apkVerifier.verifyApk();
|
||||
} catch (ApkVerifier.ApkVerificationException | ApkVerifier.ApkPermissionUnequalException e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithPrefix() {
|
||||
Apk apk = new Apk();
|
||||
apk.packageName = "org.fdroid.permissions.sdk14";
|
||||
apk.targetSdkVersion = 14;
|
||||
apk.permissions = new String[]{
|
||||
"android.permission.AUTHENTICATE_ACCOUNTS",
|
||||
"android.permission.MANAGE_ACCOUNTS",
|
||||
"android.permission.READ_PROFILE",
|
||||
"android.permission.WRITE_PROFILE",
|
||||
"android.permission.GET_ACCOUNTS",
|
||||
"android.permission.READ_CONTACTS",
|
||||
"android.permission.WRITE_CONTACTS",
|
||||
"android.permission.WRITE_EXTERNAL_STORAGE",
|
||||
"android.permission.READ_EXTERNAL_STORAGE",
|
||||
"android.permission.INTERNET",
|
||||
"android.permission.ACCESS_NETWORK_STATE",
|
||||
"android.permission.NFC",
|
||||
"android.permission.READ_SYNC_SETTINGS",
|
||||
"android.permission.WRITE_SYNC_SETTINGS",
|
||||
"android.permission.WRITE_CALL_LOG", // implied-permission!
|
||||
"android.permission.READ_CALL_LOG", // implied-permission!
|
||||
};
|
||||
|
||||
Uri uri = Uri.fromFile(sdk14Apk);
|
||||
|
||||
ApkVerifier apkVerifier = new ApkVerifier(instrumentation.getContext(), uri, apk);
|
||||
|
||||
try {
|
||||
apkVerifier.verifyApk();
|
||||
} catch (ApkVerifier.ApkVerificationException | ApkVerifier.ApkPermissionUnequalException e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional permissions are okay. The user is simply
|
||||
* warned about a permission that is not used inside the apk
|
||||
*/
|
||||
@Test
|
||||
public void testAdditionalPermission() {
|
||||
Apk apk = new Apk();
|
||||
apk.packageName = "org.fdroid.permissions.sdk14";
|
||||
apk.targetSdkVersion = 14;
|
||||
apk.permissions = new String[]{
|
||||
"android.permission.AUTHENTICATE_ACCOUNTS",
|
||||
"android.permission.MANAGE_ACCOUNTS",
|
||||
"android.permission.READ_PROFILE",
|
||||
"android.permission.WRITE_PROFILE",
|
||||
"android.permission.GET_ACCOUNTS",
|
||||
"android.permission.READ_CONTACTS",
|
||||
"android.permission.WRITE_CONTACTS",
|
||||
"android.permission.WRITE_EXTERNAL_STORAGE",
|
||||
"android.permission.READ_EXTERNAL_STORAGE",
|
||||
"android.permission.INTERNET",
|
||||
"android.permission.ACCESS_NETWORK_STATE",
|
||||
"android.permission.NFC",
|
||||
"android.permission.READ_SYNC_SETTINGS",
|
||||
"android.permission.WRITE_SYNC_SETTINGS",
|
||||
"android.permission.WRITE_CALL_LOG", // implied-permission!
|
||||
"android.permission.READ_CALL_LOG", // implied-permission!
|
||||
"NEW_PERMISSION",
|
||||
};
|
||||
|
||||
Uri uri = Uri.fromFile(sdk14Apk);
|
||||
|
||||
ApkVerifier apkVerifier = new ApkVerifier(instrumentation.getContext(), uri, apk);
|
||||
|
||||
try {
|
||||
apkVerifier.verifyApk();
|
||||
} catch (ApkVerifier.ApkVerificationException | ApkVerifier.ApkPermissionUnequalException e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Missing permissions are not okay!
|
||||
* The user is then not warned about a permission that the apk uses!
|
||||
*/
|
||||
@Test
|
||||
public void testMissingPermission() {
|
||||
Apk apk = new Apk();
|
||||
apk.packageName = "org.fdroid.permissions.sdk14";
|
||||
apk.targetSdkVersion = 14;
|
||||
apk.permissions = new String[]{
|
||||
//"android.permission.AUTHENTICATE_ACCOUNTS",
|
||||
"android.permission.MANAGE_ACCOUNTS",
|
||||
"android.permission.READ_PROFILE",
|
||||
"android.permission.WRITE_PROFILE",
|
||||
"android.permission.GET_ACCOUNTS",
|
||||
"android.permission.READ_CONTACTS",
|
||||
"android.permission.WRITE_CONTACTS",
|
||||
"android.permission.WRITE_EXTERNAL_STORAGE",
|
||||
"android.permission.READ_EXTERNAL_STORAGE",
|
||||
"android.permission.INTERNET",
|
||||
"android.permission.ACCESS_NETWORK_STATE",
|
||||
"android.permission.NFC",
|
||||
"android.permission.READ_SYNC_SETTINGS",
|
||||
"android.permission.WRITE_SYNC_SETTINGS",
|
||||
"android.permission.WRITE_CALL_LOG", // implied-permission!
|
||||
"android.permission.READ_CALL_LOG", // implied-permission!
|
||||
};
|
||||
|
||||
Uri uri = Uri.fromFile(sdk14Apk);
|
||||
|
||||
ApkVerifier apkVerifier = new ApkVerifier(instrumentation.getContext(), uri, apk);
|
||||
|
||||
try {
|
||||
apkVerifier.verifyApk();
|
||||
fail();
|
||||
} catch (ApkVerifier.ApkVerificationException e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage());
|
||||
} catch (ApkVerifier.ApkPermissionUnequalException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -45,6 +45,10 @@ class ApkVerifier {
|
||||
private final Apk expectedApk;
|
||||
private final PackageManager pm;
|
||||
|
||||
/**
|
||||
* IMPORTANT: localApkUri must be available as a File on the file system with an absolute path
|
||||
* to be readable by Android's internal PackageParser.
|
||||
*/
|
||||
ApkVerifier(Context context, Uri localApkUri, Apk expectedApk) {
|
||||
this.localApkUri = localApkUri;
|
||||
this.expectedApk = expectedApk;
|
||||
@ -52,11 +56,18 @@ class ApkVerifier {
|
||||
}
|
||||
|
||||
public void verifyApk() throws ApkVerificationException, ApkPermissionUnequalException {
|
||||
Utils.debugLog(TAG, "localApkUri.getPath: " + localApkUri.getPath());
|
||||
|
||||
// parse downloaded apk file locally
|
||||
PackageInfo localApkInfo = pm.getPackageArchiveInfo(
|
||||
localApkUri.getPath(), PackageManager.GET_PERMISSIONS);
|
||||
if (localApkInfo == null) {
|
||||
throw new ApkVerificationException("Parsing apk file failed!");
|
||||
// Unfortunately, more specific errors are not forwarded to us
|
||||
// but the internal PackageParser sometimes shows warnings in logcat such as
|
||||
// "Requires newer sdk version #14 (current version is #11)"
|
||||
throw new ApkVerificationException("Parsing apk file failed!" +
|
||||
"Maybe minSdk of apk is lower than current Sdk?" +
|
||||
"Look into logcat for more specific warnings of Android's PackageParser");
|
||||
}
|
||||
|
||||
// check if the apk has the expected packageName
|
||||
|
Loading…
x
Reference in New Issue
Block a user