1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.providers.contacts; 18 19 import static com.android.providers.contacts.EvenMoreAsserts.assertThrows; 20 import static com.android.providers.contacts.TestUtils.cv; 21 22 import android.database.Cursor; 23 import android.net.Uri; 24 import android.net.Uri.Builder; 25 import android.provider.ContactsContract; 26 import android.provider.ContactsContract.CommonDataKinds.Phone; 27 import android.provider.ContactsContract.Contacts; 28 import android.provider.ContactsContract.Data; 29 import android.test.suitebuilder.annotation.MediumTest; 30 31 import com.android.providers.contacts.testutil.RawContactUtil; 32 33 /** 34 * Unit tests for {@link ContactsProvider2}, to make sure the queries don't allow sql injection. 35 * 36 * Run the test like this: 37 * <code> 38 * adb shell am instrument -e class com.android.providers.contacts.SqlInjectionDetectionTest -w \ 39 * com.android.providers.contacts.tests/android.test.InstrumentationTestRunner 40 * </code> 41 */ 42 @MediumTest 43 public class SqlInjectionDetectionTest extends BaseContactsProvider2Test { 44 private static final String[] PHONE_ID_PROJECTION = new String[] { Phone._ID }; 45 46 @Override 47 protected void setUp() throws Exception { 48 super.setUp(); 49 } 50 51 public void testQueryValid() { 52 assertQueryValid(Phone.CONTENT_URI, PHONE_ID_PROJECTION, 53 Phone.NUMBER + "='555-123-4567'", null); 54 55 // The following tables are whitelisted. 56 assertQueryValid(Data.CONTENT_URI, null, 57 "data._id in default_directory", null); 58 } 59 60 public void testPhoneQueryBadProjection() { 61 assertQueryThrows(Phone.CONTENT_URI, 62 new String[] { "0 UNION SELECT _id FROM view_data--" }, null, null); 63 64 // Invalid column names should be detected too. 65 assertQueryThrows(Phone.CONTENT_URI, new String[] { "a" }, null, null); 66 assertQueryThrows(Phone.CONTENT_URI, new String[] { " _id" }, null, null); 67 68 // This is still invalid because we only allow exact column names in projections. 69 assertQueryThrows(Phone.CONTENT_URI, new String[] { "[_id]" }, null, null); 70 } 71 72 public void testPhoneQueryBadSelection() { 73 assertQueryThrows(Phone.CONTENT_URI, PHONE_ID_PROJECTION, 74 "0=1) UNION SELECT _id FROM view_data--", null); 75 assertQueryThrows(Phone.CONTENT_URI, PHONE_ID_PROJECTION, ";delete from contacts", null); 76 if (ContactsDatabaseHelper.DISALLOW_SUB_QUERIES) { 77 assertQueryThrows(Phone.CONTENT_URI, PHONE_ID_PROJECTION, 78 "_id in data_usage_stat", null); 79 assertQueryThrows(Phone.CONTENT_URI, PHONE_ID_PROJECTION, 80 "_id in (select _id from default_directory)", null); 81 } 82 } 83 84 public void testPhoneQueryBadSortOrder() { 85 assertQueryThrows(Phone.CONTENT_URI, 86 PHONE_ID_PROJECTION, null, "_id UNION SELECT _id FROM view_data--"); 87 assertQueryThrows(Phone.CONTENT_URI, PHONE_ID_PROJECTION, null, ";delete from contacts"); 88 if (ContactsDatabaseHelper.DISALLOW_SUB_QUERIES) { 89 assertQueryThrows(Phone.CONTENT_URI, PHONE_ID_PROJECTION, null, 90 "_id in data_usage_stat"); 91 assertQueryThrows(Phone.CONTENT_URI, PHONE_ID_PROJECTION, 92 null, "exists (select _id from default_directory)"); 93 } 94 } 95 96 public void testPhoneQueryBadLimit() { 97 // Non-numeric query parameters are ignored by the provider 98 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale"); 99 insertPhoneNumber(rawContactId, "555-123-4567"); 100 101 Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon(); 102 builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, 103 "0 UNION SELECT -50"); 104 assertQueryValid(Phone.CONTENT_URI, 105 PHONE_ID_PROJECTION, null, null); 106 107 final Cursor c = mResolver.query(Phone.CONTENT_URI, PHONE_ID_PROJECTION, null, null, null); 108 // the current implementation ignores every non-numeric limit. so we should see the 109 // contact as the only result 110 assertEquals(1, c.getCount()); 111 c.moveToFirst(); 112 assertNotSame(-50, c.getLong(0)); 113 c.close(); 114 } 115 116 private void assertQueryValid(final Uri uri, final String[] projection, 117 final String selection, final String sortOrder) { 118 final Cursor c = mResolver.query(uri, projection, selection, null, sortOrder); 119 c.close(); 120 } 121 122 private <T extends Exception> void assertQueryThrows(final Uri uri, 123 final String[] projection, final String selection, final String sortOrder) { 124 assertThrows(IllegalArgumentException.class, () -> { 125 final Cursor c = mResolver.query(uri, projection, selection, null, sortOrder); 126 c.close(); 127 }); 128 } 129 130 public void testBadDelete() { 131 assertThrows(IllegalArgumentException.class, () -> { 132 mResolver.delete(Contacts.CONTENT_URI, ";delete from contacts;--", null); 133 }); 134 if (ContactsDatabaseHelper.DISALLOW_SUB_QUERIES) { 135 assertThrows(IllegalArgumentException.class, () -> { 136 mResolver.delete(Contacts.CONTENT_URI, "_id in data_usage_stat", null); 137 }); 138 } 139 } 140 141 public void testBadUpdate() { 142 assertThrows(IllegalArgumentException.class, () -> { 143 mResolver.update(Data.CONTENT_URI, cv(), ";delete from contacts;--", null); 144 }); 145 if (ContactsDatabaseHelper.DISALLOW_SUB_QUERIES) { 146 assertThrows(IllegalArgumentException.class, () -> { 147 mResolver.update(Data.CONTENT_URI, cv(), "_id in data_usage_stat", null); 148 }); 149 assertThrows(IllegalArgumentException.class, () -> { 150 mResolver.update(Data.CONTENT_URI, cv("_id/**/", 1), null, null); 151 }); 152 153 mResolver.update(Data.CONTENT_URI, cv("[data1]", 1), null, null); 154 } 155 } 156 157 public void testBadInsert() { 158 if (ContactsDatabaseHelper.DISALLOW_SUB_QUERIES) { 159 assertThrows(IllegalArgumentException.class, () -> { 160 mResolver.insert(Data.CONTENT_URI, cv("_id/**/", 1)); 161 }); 162 } 163 } 164 } 165