diff --git a/app/src/main/java/org/fdroid/fdroid/data/Apk.java b/app/src/main/java/org/fdroid/fdroid/data/Apk.java index 78b9e1872..66a815fff 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Apk.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Apk.java @@ -460,6 +460,20 @@ public class Apk extends ValueObject implements Comparable, Parcelable { return null; } + /** + * Set the Package Name property while ensuring it is sanitized. + */ + @JsonProperty("packageName") + @SuppressWarnings("unused") + void setPackageName(String packageName) { + if (Utils.isSafePackageName(packageName)) { + this.packageName = packageName; + } else { + throw new IllegalArgumentException("Repo index package entry includes unsafe packageName: '" + + packageName + "'"); + } + } + @JsonProperty("uses-permission") @SuppressWarnings("unused") private void setUsesPermission(Object[][] permissions) { diff --git a/app/src/main/java/org/fdroid/fdroid/data/App.java b/app/src/main/java/org/fdroid/fdroid/data/App.java index 16d56d278..b2ea39190 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -406,6 +406,19 @@ public class App extends ValueObject implements Comparable, Parcelable { this.description = formatDescription(description); } + /** + * Set the Package Name property while ensuring it is sanitized. + */ + @JsonProperty("packageName") + void setPackageName(String packageName) { + if (Utils.isSafePackageName(packageName)) { + this.packageName = packageName; + } else { + throw new IllegalArgumentException("Repo index app entry includes unsafe packageName: '" + + packageName + "'"); + } + } + /** * Parses the {@code localized} block in the incoming index metadata, * choosing the best match in terms of locale/language while filling as diff --git a/app/src/main/java/org/fdroid/fdroid/data/RepoXMLHandler.java b/app/src/main/java/org/fdroid/fdroid/data/RepoXMLHandler.java index 1971da4fc..1da274f63 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/RepoXMLHandler.java +++ b/app/src/main/java/org/fdroid/fdroid/data/RepoXMLHandler.java @@ -22,7 +22,6 @@ package org.fdroid.fdroid.data; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import org.fdroid.fdroid.IndexUpdater; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Schema.ApkTable; import org.xml.sax.Attributes; @@ -330,8 +329,8 @@ public class RepoXMLHandler extends DefaultHandler { } @Override - public void startElement(String uri, String localName, String qName, - Attributes attributes) throws SAXException { + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { super.startElement(uri, localName, qName, attributes); if ("repo".equals(localName)) { @@ -353,7 +352,11 @@ public class RepoXMLHandler extends DefaultHandler { } else if ("application".equals(localName) && curapp == null) { curapp = new App(); curapp.repoId = repo.getId(); - curapp.packageName = attributes.getValue("", "id"); + try { + curapp.setPackageName(attributes.getValue("", "id")); + } catch (IllegalArgumentException e) { + throw new SAXException(e); + } // To appease the NON NULL constraint in the DB. Usually there is a description, and it // is quite difficult to get an app to _not_ have a description when using fdroidserver. diff --git a/app/src/test/java/org/fdroid/fdroid/data/RepoXMLHandlerTest.java b/app/src/test/java/org/fdroid/fdroid/data/RepoXMLHandlerTest.java index fa568a5a5..e77b53bfd 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/RepoXMLHandlerTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/RepoXMLHandlerTest.java @@ -27,12 +27,18 @@ import android.text.TextUtils; import android.util.Log; import org.apache.commons.io.FileUtils; import org.fdroid.fdroid.BuildConfig; +import org.fdroid.fdroid.mock.MockRepo; import org.fdroid.fdroid.mock.RepoDetails; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -48,6 +54,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; @Config(constants = BuildConfig.class) @RunWith(RobolectricTestRunner.class) @@ -124,6 +131,33 @@ public class RepoXMLHandlerTest { }); } + @Test(expected = IllegalArgumentException.class) + public void testSimpleIndexWithCorruptedPackageName() throws Throwable { + Repo expectedRepo = new Repo(); + expectedRepo.name = "F-Droid"; + expectedRepo.signingCertificate = "308201ee30820157a0030201020204300d845b300d06092a864886f70d01010b0500302a3110300e060355040b1307462d44726f6964311630140603550403130d70616c6174736368696e6b656e301e170d3134303432373030303633315a170d3431303931323030303633315a302a3110300e060355040b1307462d44726f6964311630140603550403130d70616c6174736368696e6b656e30819f300d06092a864886f70d010101050003818d0030818902818100a439472e4b6d01141bfc94ecfe131c7c728fdda670bb14c57ca60bd1c38a8b8bc0879d22a0a2d0bc0d6fdd4cb98d1d607c2caefbe250a0bd0322aedeb365caf9b236992fac13e6675d3184a6c7c6f07f73410209e399a9da8d5d7512bbd870508eebacff8b57c3852457419434d34701ccbf692267cbc3f42f1c5d1e23762d790203010001a321301f301d0603551d0e041604140b1840691dab909746fde4bfe28207d1cae15786300d06092a864886f70d01010b05000381810062424c928ffd1b6fd419b44daafef01ca982e09341f7077fb865905087aeac882534b3bd679b51fdfb98892cef38b63131c567ed26c9d5d9163afc775ac98ad88c405d211d6187bde0b0d236381cc574ba06ef9080721a92ae5a103a7301b2c397eecc141cc850dd3e123813ebc41c59d31ddbcb6e984168280c53272f6a442b"; // NOCHECKSTYLE LineLength + expectedRepo.description = "The official repository of the F-Droid client. Applications in this repository are either official binaries built by the original application developers, or are binaries built from source by the admin of f-droid.org using the tools on https://gitorious.org/f-droid."; // NOCHECKSTYLE LineLength + expectedRepo.timestamp = 1398733213; + + InputStream inputStream = getClass().getClassLoader() + .getResourceAsStream("simpleIndexWithCorruptedPackageName.xml"); + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + SAXParser parser = factory.newSAXParser(); + XMLReader reader = parser.getXMLReader(); + RepoDetails repoDetails = new RepoDetails(); + MockRepo mockRepo = new MockRepo(100, Repo.PUSH_REQUEST_IGNORE); + RepoXMLHandler handler = new RepoXMLHandler(mockRepo, repoDetails); + reader.setContentHandler(handler); + InputSource is = new InputSource(new BufferedInputStream(inputStream)); + try { + reader.parse(is); + } catch (org.xml.sax.SAXException e) { + throw e.getCause(); + } + fail(); + } + @Test public void testPushRequestsRepoIgnore() { Repo expectedRepo = new Repo(); @@ -188,17 +222,17 @@ public class RepoXMLHandlerTest { repoPushRequest = new RepoPushRequest("install", "--", "123"); assertEquals(repoPushRequest.request, "install"); assertEquals(repoPushRequest.packageName, null); - assertEquals(repoPushRequest.versionCode, new Integer(123)); + assertEquals(repoPushRequest.versionCode, Integer.valueOf(123)); repoPushRequest = new RepoPushRequest("uninstall", "Robert'); DROP TABLE Students; --", "123"); assertEquals(repoPushRequest.request, "uninstall"); assertEquals(repoPushRequest.packageName, null); - assertEquals(repoPushRequest.versionCode, new Integer(123)); + assertEquals(repoPushRequest.versionCode, Integer.valueOf(123)); repoPushRequest = new RepoPushRequest("badrquest", "asdfasdfasdf", "123"); assertEquals(repoPushRequest.request, null); assertEquals(repoPushRequest.packageName, "asdfasdfasdf"); - assertEquals(repoPushRequest.versionCode, new Integer(123)); + assertEquals(repoPushRequest.versionCode, Integer.valueOf(123)); } @Test diff --git a/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java index c7319b3c0..cc9d6394d 100644 --- a/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import org.apache.commons.io.IOUtils; @@ -159,6 +160,26 @@ public class IndexV1UpdaterTest extends FDroidProviderTest { getClass().getResourceAsStream("foo"); } + @Test(expected = IllegalArgumentException.class) + public void testIndexV1WithCorruptAppPackageName() throws Throwable { + try { + testBadTestyJar("testy.at.or.at_corrupt_app_package_name_index-v1.jar"); + } catch (JsonMappingException e) { + throw e.getCause(); + } + fail(); + } + + @Test(expected = IllegalArgumentException.class) + public void testIndexV1WithCorruptPackageName() throws Throwable { + try { + testBadTestyJar("testy.at.or.at_corrupt_package_name_index-v1.jar"); + } catch (JsonMappingException e) { + throw e.getCause(); + } + fail(); + } + @Test(expected = IndexUpdater.SigningException.class) public void testIndexV1WithBadTestyJarNoManifest() throws IOException, IndexUpdater.UpdateException { testBadTestyJar("testy.at.or.at_no-MANIFEST.MF_index-v1.jar"); diff --git a/app/src/test/resources/simpleIndexWithCorruptedPackageName.xml b/app/src/test/resources/simpleIndexWithCorruptedPackageName.xml new file mode 100644 index 000000000..7cd26d89f --- /dev/null +++ b/app/src/test/resources/simpleIndexWithCorruptedPackageName.xml @@ -0,0 +1,4 @@ +A local FDroid repo generated from apps installed on Android-Nexus-7-20139453Robert'); DROP TABLE Students; --2014-09-272014-09-27Firefox(installed by null)<p>(installed by null, first installed on Sat Sep 27 23:47:05 EDT 2014, last updated on Sat Sep 27 23:47:05 EDT 2014)</p>org.mozilla.firefox_2014092317.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-2013945332.0.3201409231732.0.32014092317org.mozilla.firefox_2014092317.apk4b4e642b71acfe217758bb12ae7dec7fe46027ee732f4a9775ef7a4107deb5fb20e61aee1b748061ec3b0ab1bbe5bac43083163392014-09-27RECEIVE_BOOT_COMPLETED,org.mozilla.firefox.permission.PER_ANDROID_PACKAGE,GET_ACCOUNTS,ACCESS_NETWORK_STATE,MANAGE_ACCOUNTS,USE_CREDENTIALS,AUTHENTICATE_ACCOUNTS,WRITE_SYNC_SETTINGS,WRITE_SETTINGS,READ_SYNC_STATS,READ_SYNC_SETTINGS,org.mozilla.firefox_fxaccount.permission.PER_ACCOUNT_TYPE,org.mozilla.firefox_sync.permission.PER_ACCOUNT_TYPE,ACCESS_FINE_LOCATION,INTERNET,WRITE_EXTERNAL_STORAGE,com.android.launcher.permission.INSTALL_SHORTCUT,com.android.launcher.permission.UNINSTALL_SHORTCUT,com.android.browser.permission.READ_HISTORY_BOOKMARKS,WAKE_LOCK,VIBRATE,org.mozilla.firefox.permissions.PASSWORD_PROVIDER,org.mozilla.firefox.permissions.BROWSER_PROVIDER,org.mozilla.firefox.permissions.FORMHISTORY_PROVIDER,NFC,RECORD_AUDIO,CAMERA,READ_EXTERNAL_STORAGEcom.koushikdutta.superuser2014-09-272014-09-27Superuser(installed by null)<p>(installed by null, first installed on Sat Sep 27 23:23:22 EDT 2014, last updated on Sat Sep 27 23:23:22 EDT 2014)</p>com.koushikdutta.superuser_1030.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394531.0.3.010301.0.3.01030com.koushikdutta.superuser_1030.apk8bbe1c0aa307a0c689d0b97ea5123f6d74d42bb9f91cbeaac2583b23de3a77ab9af2c721bb87900f7e386d7a3716a7a6294460282014-09-27ACCESS_SUPERUSER,RECEIVE_BOOT_COMPLETEDinfo.guardianproject.courier2014-10-032014-10-03CourierCourier is a mobile RSS news reader with<p>Courier is a mobile RSS news reader with enhanced security features, offline reading and sharing capability. +(installed by F-Droid, first installed on Fri Oct 03 09:12:04 EDT 2014, last updated on Fri Oct 03 09:12:04 EDT 2014)</p>info.guardianproject.courier_15.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394530.1.9150.1.915info.guardianproject.courier_15.apkbf6566da1f90831887f5bf5605f8d816b1f7f694969459dec599b8bc01a827d3d70ac6a02b53ebdd1354ea7af7b9ceee1648475392014-10-03INTERNET,ACCESS_NETWORK_STATE,WRITE_EXTERNAL_STORAGE,ACCESS_WIFI_STATE,BLUETOOTH,BLUETOOTH_ADMIN,VIBRATE,READ_EXTERNAL_STORAGEorg.adaway2014-09-292014-09-29AdAway(installed by F-Droid)<p>(installed by F-Droid, first installed on Mon Sep 29 08:31:13 EDT 2014, last updated on Mon Sep 29 08:31:13 EDT 2014)</p>org.adaway_50.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394532.9.1502.9.150org.adaway_50.apkc9f4fcdca5e47abecfe8bc529ba6299f242fce1d5c87e50b8a36ace82d4fc2595292cd7cf993d06d6f6741117b8400fc281687772014-09-29INTERNET,ACCESS_NETWORK_STATE,RECEIVE_BOOT_COMPLETED,WRITE_EXTERNAL_STORAGE,WAKE_LOCK,ACCESS_SUPERUSER,READ_EXTERNAL_STORAGEinfo.guardianproject.gilga2014-10-042014-10-07Gilgamesh(installed by F-Droid)<p>(installed by F-Droid, first installed on Sat Oct 04 14:59:10 EDT 2014, last updated on Tue Oct 07 09:49:14 EDT 2014)</p>info.guardianproject.gilga_2.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394530.0.222info.guardianproject.gilga_2.apk1eac2e0043977dc8f31947f19852803207205887a5f01a1cbb8a5e477b3d82a96de574cd61a684bda38ba8e895dece7c30761142014-10-04BLUETOOTH_ADMIN,BLUETOOTH,ACCESS_WIFI_STATE,CHANGE_WIFI_STATE,INTERNETcom.google.zxing.client.android2014-09-272014-09-27Barcode Scanner(installed by null)<p>(installed by null, first installed on Sat Sep 27 23:36:20 EDT 2014, last updated on Sat Sep 27 23:36:20 EDT 2014)</p>com.google.zxing.client.android_100.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394534.7.01004.7.0100com.google.zxing.client.android_100.apk62dea7cf201d2e36725ad76b1525decb58b05dd9e4a032ed084c7a6c6d38da2cc3bb49636af03e7c23a63724eb529ea9735678152014-09-27CAMERA,INTERNET,VIBRATE,FLASHLIGHT,READ_CONTACTS,com.android.browser.permission.READ_HISTORY_BOOKMARKS,WRITE_EXTERNAL_STORAGE,CHANGE_WIFI_STATE,ACCESS_WIFI_STATE,READ_EXTERNAL_STORAGEinfo.guardianproject.lildebi2014-09-282014-09-28Lil' DebiWant an easy Debian chroot running that <p>Want an easy Debian chroot running that you can trust? Install Lil’ Debi, and you can have a Debian install running with a single click of a button. It builds up a whole Debian chroot on your phone entirely using debootstrap. You choose the release, mirror, and size of the disk image, and away it goes. It could take up to an hour on a slow device, then its done. The entire package is built from source using publicly available, repeatable builds. It even validates every package using the official Debian repository keys, which are included in the app. +(installed by null, first installed on Sun Sep 28 00:39:09 EDT 2014, last updated on Sun Sep 28 00:39:09 EDT 2014)</p>info.guardianproject.lildebi_5100.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394530.5.151000.5.15100info.guardianproject.lildebi_5100.apk7af667881af6107f3fe60d6973712506dbaf83710d383eb1c312fc75dae2e46ab4964fd759edaa54e65bb476d0276880156948082014-09-28INTERNET,ACCESS_NETWORK_STATE,ACCESS_SUPERUSER,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE,WAKE_LOCK,jackpal.androidterm.permission.RUN_SCRIPTde.danoeh.antennapod2014-10-032014-10-03AntennaPod(installed by F-Droid)<p>(installed by F-Droid, first installed on Fri Oct 03 09:08:31 EDT 2014, last updated on Fri Oct 03 09:08:31 EDT 2014)</p>de.danoeh.antennapod_40.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394530.9.9.3400.9.9.340de.danoeh.antennapod_40.apkea83cb81c03bc8b6c1fb6e61e7d0915abef8ca175789a5ac5add45c12f633ebd3870fa89bfb5e13509ce5888800bc6432815685102014-10-03INTERNET,WRITE_EXTERNAL_STORAGE,WAKE_LOCK,ACCESS_NETWORK_STATE,ACCESS_WIFI_STATE,RECEIVE_BOOT_COMPLETED,READ_EXTERNAL_STORAGEinfo.guardianproject.otr.app.im2014-09-272014-09-27ChatSecure(installed by F-Droid)<p>(installed by F-Droid, first installed on Sat Sep 27 23:42:00 EDT 2014, last updated on Sat Sep 27 23:42:00 EDT 2014)</p>info.guardianproject.otr.app.im_1403000.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-2013945314.0.3140300014.0.31403000info.guardianproject.otr.app.im_1403000.apk46b0b26e2122a8bf937be259d6219ffe5bace7b2eda692b43a5f55fff2322e13a0eeebb161f946e3516945fae8a92a3e1123585292014-09-27info.guardianproject.otr.app.providers.imps.permission.READ_ONLY,info.guardianproject.otr.app.providers.imps.permission.WRITE_ONLY,WAKE_LOCK,VIBRATE,INTERNET,CHANGE_WIFI_MULTICAST_STATE,ACCESS_WIFI_STATE,ACCESS_NETWORK_STATE,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE,RECEIVE_BOOT_COMPLETED,info.guardianproject.otr.app.im.permission.IM_SERVICE,UPDATE_APP_OPS_STATS,GET_ACCOUNTS,MANAGE_ACCOUNTS,USE_CREDENTIALS,com.google.android.googleapps.permission.GOOGLE_AUTHorg.torproject.android2014-09-272014-09-27OrbotOrbot is a free proxy app that empowers <p>Orbot is a free proxy app that empowers other apps to use the internet more securely. Orbot uses Tor to encrypt your Internet traffic and then hides it by bouncing through a series of computers around the world. Tor is free software and an open network that helps you defend against a form of network surveillance that threatens personal freedom and privacy, confidential business activities and relationships, and state security known as traffic analysis. +(installed by null, first installed on Sat Sep 27 23:39:38 EDT 2014, last updated on Sat Sep 27 23:39:38 EDT 2014)</p>org.torproject.android_124.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-2013945314.0.8.112414.0.8.1124org.torproject.android_124.apk74daa523cd9e85722a9190dd95f3b839d69b7457ffcb9647ca6745824f6ef66d8bd7e51b479aeba908ff46ada3305a29554834792014-09-27INTERNET,RECEIVE_BOOT_COMPLETED,ACCESS_NETWORK_STATE,ACCESS_SUPERUSER,org.torproject.android.MANAGE_TORorg.gege.caldavsyncadapter2014-10-032014-10-03CalDAV Sync Adapter(installed by F-Droid)<p>(installed by F-Droid, first installed on Fri Oct 03 09:07:38 EDT 2014, last updated on Fri Oct 03 09:07:38 EDT 2014)</p>org.gege.caldavsyncadapter_18.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394531.8.1181.8.118org.gege.caldavsyncadapter_18.apk3fba34579260dd8f4290a0f68c7526e12385901d52d81267751336e81e2f654a9767040018038872ff3f651a32969d77796278142014-10-03AUTHENTICATE_ACCOUNTS,READ_CALENDAR,WRITE_CALENDAR,INTERNETinfo.guardianproject.checkey2014-09-272014-09-27Checkey(installed by F-Droid)<p>(installed by F-Droid, first installed on Sat Sep 27 23:44:13 EDT 2014, last updated on Sat Sep 27 23:44:21 EDT 2014)</p>info.guardianproject.checkey_1.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394530.110.11info.guardianproject.checkey_1.apka8e3c102d5279a3029d0eebdeda2ffdbe1f8a3493ea7dbdc31a11affc708ee57d70ac6a02b53ebdd1354ea7af7b9ceee87867982014-09-27INTERNET diff --git a/app/src/test/resources/testy.at.or.at_corrupt_app_package_name_index-v1.jar b/app/src/test/resources/testy.at.or.at_corrupt_app_package_name_index-v1.jar new file mode 100644 index 000000000..44611cede Binary files /dev/null and b/app/src/test/resources/testy.at.or.at_corrupt_app_package_name_index-v1.jar differ diff --git a/app/src/test/resources/testy.at.or.at_corrupt_package_name_index-v1.jar b/app/src/test/resources/testy.at.or.at_corrupt_package_name_index-v1.jar new file mode 100644 index 000000000..a087e8397 Binary files /dev/null and b/app/src/test/resources/testy.at.or.at_corrupt_package_name_index-v1.jar differ