1 /*
2  * Copyright (C) 2009 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 android.net.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNotSame;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.assertTrue;
25 
26 import android.net.UrlQuerySanitizer;
27 import android.net.UrlQuerySanitizer.IllegalCharacterValueSanitizer;
28 import android.net.UrlQuerySanitizer.ParameterValuePair;
29 import android.net.UrlQuerySanitizer.ValueSanitizer;
30 import android.os.Build;
31 
32 import androidx.test.ext.junit.runners.AndroidJUnit4;
33 import androidx.test.filters.SmallTest;
34 
35 import com.android.testutils.DevSdkIgnoreRule;
36 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
37 
38 import org.junit.Rule;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 
42 import java.util.List;
43 import java.util.Set;
44 
45 @SmallTest
46 @RunWith(AndroidJUnit4.class)
47 public class UrlQuerySanitizerTest {
48     @Rule
49     public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
50 
51     private static final int ALL_OK = IllegalCharacterValueSanitizer.ALL_OK;
52 
53     // URL for test.
54     private static final String TEST_URL = "http://example.com/?name=Joe+User&age=20&height=175";
55 
56     // Default sanitizer's change when "+".
57     private static final String EXPECTED_UNDERLINE_NAME = "Joe_User";
58 
59     // IllegalCharacterValueSanitizer sanitizer's change when "+".
60     private static final String EXPECTED_SPACE_NAME = "Joe User";
61     private static final String EXPECTED_AGE = "20";
62     private static final String EXPECTED_HEIGHT = "175";
63     private static final String NAME = "name";
64     private static final String AGE = "age";
65     private static final String HEIGHT = "height";
66 
67     @Test
testUrlQuerySanitizer()68     public void testUrlQuerySanitizer() {
69         MockUrlQuerySanitizer uqs = new MockUrlQuerySanitizer();
70         assertFalse(uqs.getAllowUnregisteredParamaters());
71 
72         final String query = "book=thinking in java&price=108";
73         final String book = "book";
74         final String bookName = "thinking in java";
75         final String price = "price";
76         final String bookPrice = "108";
77         final String notExistPar = "notExistParameter";
78         uqs.registerParameters(new String[]{book, price}, UrlQuerySanitizer.getSpaceLegal());
79         uqs.parseQuery(query);
80         assertTrue(uqs.hasParameter(book));
81         assertTrue(uqs.hasParameter(price));
82         assertFalse(uqs.hasParameter(notExistPar));
83         assertEquals(bookName, uqs.getValue(book));
84         assertEquals(bookPrice, uqs.getValue(price));
85         assertNull(uqs.getValue(notExistPar));
86         uqs.clear();
87         assertFalse(uqs.hasParameter(book));
88         assertFalse(uqs.hasParameter(price));
89 
90         uqs.parseEntry(book, bookName);
91         assertTrue(uqs.hasParameter(book));
92         assertEquals(bookName, uqs.getValue(book));
93         uqs.parseEntry(price, bookPrice);
94         assertTrue(uqs.hasParameter(price));
95         assertEquals(bookPrice, uqs.getValue(price));
96         assertFalse(uqs.hasParameter(notExistPar));
97         assertNull(uqs.getValue(notExistPar));
98 
99         uqs = new MockUrlQuerySanitizer(TEST_URL);
100         assertTrue(uqs.getAllowUnregisteredParamaters());
101 
102         assertTrue(uqs.hasParameter(NAME));
103         assertTrue(uqs.hasParameter(AGE));
104         assertTrue(uqs.hasParameter(HEIGHT));
105         assertFalse(uqs.hasParameter(notExistPar));
106 
107         assertEquals(EXPECTED_UNDERLINE_NAME, uqs.getValue(NAME));
108         assertEquals(EXPECTED_AGE, uqs.getValue(AGE));
109         assertEquals(EXPECTED_HEIGHT, uqs.getValue(HEIGHT));
110         assertNull(uqs.getValue(notExistPar));
111 
112         final int ContainerLen = 3;
113         Set<String> urlSet = uqs.getParameterSet();
114         assertEquals(ContainerLen, urlSet.size());
115         assertTrue(urlSet.contains(NAME));
116         assertTrue(urlSet.contains(AGE));
117         assertTrue(urlSet.contains(HEIGHT));
118         assertFalse(urlSet.contains(notExistPar));
119 
120         List<ParameterValuePair> urlList = uqs.getParameterList();
121         assertEquals(ContainerLen, urlList.size());
122         ParameterValuePair pvp = urlList.get(0);
123         assertEquals(NAME, pvp.mParameter);
124         assertEquals(EXPECTED_UNDERLINE_NAME, pvp.mValue);
125         pvp = urlList.get(1);
126         assertEquals(AGE, pvp.mParameter);
127         assertEquals(EXPECTED_AGE, pvp.mValue);
128         pvp = urlList.get(2);
129         assertEquals(HEIGHT, pvp.mParameter);
130         assertEquals(EXPECTED_HEIGHT, pvp.mValue);
131 
132         assertFalse(uqs.getPreferFirstRepeatedParameter());
133         uqs.addSanitizedEntry(HEIGHT, EXPECTED_HEIGHT + 1);
134         assertEquals(ContainerLen, urlSet.size());
135         assertEquals(ContainerLen + 1, urlList.size());
136         assertEquals(EXPECTED_HEIGHT + 1, uqs.getValue(HEIGHT));
137 
138         uqs.setPreferFirstRepeatedParameter(true);
139         assertTrue(uqs.getPreferFirstRepeatedParameter());
140         uqs.addSanitizedEntry(HEIGHT, EXPECTED_HEIGHT);
141         assertEquals(ContainerLen, urlSet.size());
142         assertEquals(ContainerLen + 2, urlList.size());
143         assertEquals(EXPECTED_HEIGHT + 1, uqs.getValue(HEIGHT));
144 
145         uqs.registerParameter(NAME, null);
146         assertNull(uqs.getValueSanitizer(NAME));
147         assertNotNull(uqs.getEffectiveValueSanitizer(NAME));
148 
149         uqs.setAllowUnregisteredParamaters(false);
150         assertFalse(uqs.getAllowUnregisteredParamaters());
151         uqs.registerParameter(NAME, null);
152         assertNull(uqs.getEffectiveValueSanitizer(NAME));
153 
154         ValueSanitizer vs = new IllegalCharacterValueSanitizer(ALL_OK);
155         uqs.registerParameter(NAME, vs);
156         uqs.parseUrl(TEST_URL);
157         assertEquals(EXPECTED_SPACE_NAME, uqs.getValue(NAME));
158         assertNotSame(EXPECTED_AGE, uqs.getValue(AGE));
159 
160         String[] register = {NAME, AGE};
161         uqs.registerParameters(register, vs);
162         uqs.parseUrl(TEST_URL);
163         assertEquals(EXPECTED_SPACE_NAME, uqs.getValue(NAME));
164         assertEquals(EXPECTED_AGE, uqs.getValue(AGE));
165         assertNotSame(EXPECTED_HEIGHT, uqs.getValue(HEIGHT));
166 
167         uqs.setUnregisteredParameterValueSanitizer(vs);
168         assertEquals(vs, uqs.getUnregisteredParameterValueSanitizer());
169 
170         vs = UrlQuerySanitizer.getAllIllegal();
171         assertEquals("Joe_User", vs.sanitize("Joe<User"));
172         vs = UrlQuerySanitizer.getAllButNulAndAngleBracketsLegal();
173         assertEquals("Joe   User", vs.sanitize("Joe<>\0User"));
174         vs = UrlQuerySanitizer.getAllButNulLegal();
175         assertEquals("Joe User", vs.sanitize("Joe\0User"));
176         vs = UrlQuerySanitizer.getAllButWhitespaceLegal();
177         assertEquals("Joe_User", vs.sanitize("Joe User"));
178         vs = UrlQuerySanitizer.getAmpAndSpaceLegal();
179         assertEquals("Joe User&", vs.sanitize("Joe User&"));
180         vs = UrlQuerySanitizer.getAmpLegal();
181         assertEquals("Joe_User&", vs.sanitize("Joe User&"));
182         vs = UrlQuerySanitizer.getSpaceLegal();
183         assertEquals("Joe User ", vs.sanitize("Joe User&"));
184         vs = UrlQuerySanitizer.getUrlAndSpaceLegal();
185         assertEquals("Joe User&Smith%B5'\'", vs.sanitize("Joe User&Smith%B5'\'"));
186         vs = UrlQuerySanitizer.getUrlLegal();
187         assertEquals("Joe_User&Smith%B5'\'", vs.sanitize("Joe User&Smith%B5'\'"));
188 
189         String escape = "Joe";
190         assertEquals(escape, uqs.unescape(escape));
191         String expectedPlus = "Joe User";
192         String expectedPercentSignHex = "title=" + Character.toString((char)181);
193         String initialPlus = "Joe+User";
194         String initialPercentSign = "title=%B5";
195         assertEquals(expectedPlus, uqs.unescape(initialPlus));
196         assertEquals(expectedPercentSignHex, uqs.unescape(initialPercentSign));
197         String expectedPlusThenPercentSign = "Joe Random, User";
198         String plusThenPercentSign = "Joe+Random%2C%20User";
199         assertEquals(expectedPlusThenPercentSign, uqs.unescape(plusThenPercentSign));
200         String expectedPercentSignThenPlus = "Joe, Random User";
201         String percentSignThenPlus = "Joe%2C+Random+User";
202         assertEquals(expectedPercentSignThenPlus, uqs.unescape(percentSignThenPlus));
203 
204         assertTrue(uqs.decodeHexDigit('0') >= 0);
205         assertTrue(uqs.decodeHexDigit('b') >= 0);
206         assertTrue(uqs.decodeHexDigit('F') >= 0);
207         assertTrue(uqs.decodeHexDigit('$') < 0);
208 
209         assertTrue(uqs.isHexDigit('0'));
210         assertTrue(uqs.isHexDigit('b'));
211         assertTrue(uqs.isHexDigit('F'));
212         assertFalse(uqs.isHexDigit('$'));
213 
214         uqs.clear();
215         assertEquals(0, urlSet.size());
216         assertEquals(0, urlList.size());
217 
218         uqs.setPreferFirstRepeatedParameter(true);
219         assertTrue(uqs.getPreferFirstRepeatedParameter());
220         uqs.setPreferFirstRepeatedParameter(false);
221         assertFalse(uqs.getPreferFirstRepeatedParameter());
222 
223         UrlQuerySanitizer uq = new UrlQuerySanitizer();
224         uq.setPreferFirstRepeatedParameter(true);
225         final String PARA_ANSWER = "answer";
226         uq.registerParameter(PARA_ANSWER, new MockValueSanitizer());
227         uq.parseUrl("http://www.google.com/question?answer=13&answer=42");
228         assertEquals("13", uq.getValue(PARA_ANSWER));
229 
230         uq.setPreferFirstRepeatedParameter(false);
231         uq.parseQuery("http://www.google.com/question?answer=13&answer=42");
232         assertEquals("42", uq.getValue(PARA_ANSWER));
233 
234     }
235 
236     @Test @IgnoreUpTo(Build.VERSION_CODES.Q) // Only fixed in R
237     public void testScriptUrlOk_73822755() {
238         ValueSanitizer sanitizer = new UrlQuerySanitizer.IllegalCharacterValueSanitizer(
239                 UrlQuerySanitizer.IllegalCharacterValueSanitizer.SCRIPT_URL_OK);
240         assertEquals("javascript:alert()", sanitizer.sanitize("javascript:alert()"));
241     }
242 
243     @Test @IgnoreUpTo(Build.VERSION_CODES.Q) // Only fixed in R
244     public void testScriptUrlBlocked_73822755() {
245         ValueSanitizer sanitizer = UrlQuerySanitizer.getUrlAndSpaceLegal();
246         assertEquals("", sanitizer.sanitize("javascript:alert()"));
247     }
248 
249     private static class MockValueSanitizer implements ValueSanitizer{
250 
251         public String sanitize(String value) {
252             return value;
253         }
254     }
255 
256     class MockUrlQuerySanitizer extends UrlQuerySanitizer {
257         public MockUrlQuerySanitizer() {
258             super();
259         }
260 
261         public MockUrlQuerySanitizer(String url) {
262             super(url);
263         }
264 
265         @Override
266         protected void addSanitizedEntry(String parameter, String value) {
267             super.addSanitizedEntry(parameter, value);
268         }
269 
270         @Override
271         protected void clear() {
272             super.clear();
273         }
274 
275         @Override
276         protected int decodeHexDigit(char c) {
277             return super.decodeHexDigit(c);
278         }
279 
280         @Override
281         protected boolean isHexDigit(char c) {
282             return super.isHexDigit(c);
283         }
284 
285         @Override
286         protected void parseEntry(String parameter, String value) {
287             super.parseEntry(parameter, value);
288         }
289     }
290 }
291