• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.os.cts;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.os.Bundle;
24 import android.util.Log;
25 
26 import java.net.URISyntaxException;
27 
28 /**
29  * Activity used to verify an UnsafeIntentLaunch StrictMode violation is reported when an Intent
30  * is unparceled from the delivered Intent and used to start another activity.
31  */
32 public class IntentLaunchActivity extends Activity {
33     private static final String TAG = "IntentLaunchActivity";
34 
35     protected static final String EXTRA_INNER_INTENT = "inner-intent";
36     private static final String EXTRA_INNER_INTENT_URI_STRING = "inner-intent-uri-string";
37 
38     private static final String ACTION_UNSAFE_INTENT_LAUNCH = "android.os.cts.UNSAFE_INTENT_LAUNCH";
39     private static final String ACTION_UNSAFE_DATA_COPY_FROM_INTENT =
40             "android.os.cts.UNSAFE_DATA_COPY_FROM_INTENT";
41     private static final String ACTION_DATA_COPY_FROM_DELIVERED_INTENT_WITH_UNPARCELED_EXTRAS =
42             "android.os.cts.DATA_COPY_FROM_DELIVERED_INTENT_WITH_UNPARCELED_EXTRAS";
43     private static final String ACTION_UNSAFE_DATA_COPY_FROM_EXTRAS =
44             "android.os.cts.UNSAFE_DATA_COPY_FROM_EXTRAS";
45     private static final String ACTION_UNSAFE_INTENT_FROM_URI_LAUNCH =
46             "android.os.cts.UNSAFE_INTENT_FROM_URI_LAUNCH";
47     private static final String ACTION_SAFE_INTENT_FROM_URI_LAUNCH =
48             "android.os.cts.SAFE_INTENT_FROM_URI_LAUNCH";
49 
50     private static final String ACTION_BROWSABLE_INTENT_LAUNCH =
51             "android.os.cts.BROWSABLE_INTENT_LAUNCH";
52 
53     private static final String EXTRA_TEST_KEY = "android.os.cts.TEST_KEY";
54 
55     /**
56      * Returns an Intent containing a parceled inner Intent that can be used to start this Activity
57      * and verify the StrictMode UnsafeIntentLaunch violation is reported as expected.
58      */
getUnsafeIntentLaunchTestIntent(Context context)59     public static Intent getUnsafeIntentLaunchTestIntent(Context context) {
60         return getTestIntent(context, ACTION_UNSAFE_INTENT_LAUNCH);
61     }
62 
63     /**
64      * Returns an Intent containing a parceled Intent with data in the extras; the returned Intent
65      * can be used to start this Activity and verify the StrictMode UnsafeIntentLaunch violation
66      * is reported as expected when copying data from an unparceled Intent.
67      */
getUnsafeDataCopyFromIntentTestIntent(Context context)68     public static Intent getUnsafeDataCopyFromIntentTestIntent(Context context) {
69         return getTestIntentWithExtrasInParceledIntent(context,
70                 ACTION_UNSAFE_DATA_COPY_FROM_INTENT);
71     }
72 
73     /**
74      * Returns an Intent containing a parceled Intent with data in the extras; the returned Intent
75      * can be used to start this Activity and verify the StrictMode UnsafeIntentLaunch violation is
76      * reported as expected when copying data from an Intent's extras without sanitation /
77      * validation.
78      */
getUnsafeDataCopyFromExtrasTestIntent(Context context)79     public static Intent getUnsafeDataCopyFromExtrasTestIntent(Context context) {
80         return getTestIntentWithExtrasInParceledIntent(context,
81                 ACTION_UNSAFE_DATA_COPY_FROM_EXTRAS);
82     }
83 
84     /**
85      * Returns an Intent containing data in the extras; the returned Intent can be used to start
86      * this Activity and verify the StrictMode UnsafeIntentLaunch violation is not reported since
87      * the extras of the Intent delivered to a protected component are considered safe.
88      */
getDataCopyFromDeliveredIntentWithUnparceledExtrasTestIntent( Context context)89     public static Intent getDataCopyFromDeliveredIntentWithUnparceledExtrasTestIntent(
90             Context context) {
91         // When an Intent is delivered to a protected component this delivered Intent's extras
92         // can be copied unfiltered to another Intent since only a trusted component could send
93         // this Intent. If the sending component were to attempt an unfiltered copy of extras from
94         // an unparceled Intent or Bundle the violation would be triggered by that call.
95         return getTestIntent(context,
96                 ACTION_DATA_COPY_FROM_DELIVERED_INTENT_WITH_UNPARCELED_EXTRAS);
97     }
98 
99     /**
100      * Returns an Intent with the specified {@code action} set and a parceled Intent with data in
101      * the extras.
102      */
getTestIntentWithExtrasInParceledIntent(Context context, String action)103     private static Intent getTestIntentWithExtrasInParceledIntent(Context context, String action) {
104         Intent intent = getTestIntent(context, action);
105         Intent innerIntent = intent.getParcelableExtra(EXTRA_INNER_INTENT);
106         // Add an extra to the Intent so that it contains the extras Bundle; the data itself is not
107         // important, just the fact that there is data to be copied without sanitation / validation.
108         innerIntent.putExtra(EXTRA_TEST_KEY, "TEST_VALUE");
109         return intent;
110     }
111 
112     /**
113      * Returns an Intent containing an Intent encoded as a URI in the extras; the returned Intent
114      * can be used to start this Activity and verify the StrictMode UnsafeIntentLaunch violation is
115      * reported as expected when launching an Intent parsed from a URI.
116      */
getUnsafeIntentFromUriLaunchTestIntent(Context context)117     public static Intent getUnsafeIntentFromUriLaunchTestIntent(Context context) {
118         return getTestIntentWithUriIntentInExtras(context, ACTION_UNSAFE_INTENT_FROM_URI_LAUNCH);
119     }
120 
121     /**
122      * Returns an Intent containing an Intent encoded as a URI in the extras; the returned Intent
123      * can be used to start this Activity and verify the StrictMode UnsafeIntentLaunch violation is
124      * not reported when launching an Intent parsed from a URI with the browsable category and
125      * without an explicit component.
126      */
getSafeIntentFromUriLaunchTestIntent(Context context)127     public static Intent getSafeIntentFromUriLaunchTestIntent(Context context) {
128         return getTestIntentWithUriIntentInExtras(context, ACTION_SAFE_INTENT_FROM_URI_LAUNCH);
129     }
130 
131     /**
132      * Returns an Intent with the specified {@code action} set and containing an Intent encoded as a
133      * URI in the extras.
134      */
getTestIntentWithUriIntentInExtras(Context context, String action)135     private static Intent getTestIntentWithUriIntentInExtras(Context context, String action) {
136         Intent intent = getTestIntent(context, action);
137         Intent innerIntent = intent.getParcelableExtra(EXTRA_INNER_INTENT);
138         innerIntent.addCategory(Intent.CATEGORY_BROWSABLE);
139         innerIntent.setAction(ACTION_BROWSABLE_INTENT_LAUNCH);
140         innerIntent.setPackage(context.getPackageName());
141         String intentUriString = innerIntent.toUri(Intent.URI_ANDROID_APP_SCHEME);
142         intent.putExtra(EXTRA_INNER_INTENT_URI_STRING, intentUriString);
143         intent.removeExtra(EXTRA_INNER_INTENT);
144         return intent;
145     }
146 
147     /**
148      * Returns an Intent with the specified {@code action} set and a parceled Intent.
149      */
getTestIntent(Context context, String action)150     private static Intent getTestIntent(Context context, String action) {
151         Intent intent = new Intent(context, IntentLaunchActivity.class);
152         intent.setAction(action);
153         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
154         Intent innerIntent = new Intent(context, SimpleTestActivity.class);
155         intent.putExtra(EXTRA_INNER_INTENT, innerIntent);
156         return intent;
157     }
158 
159     @Override
onCreate(Bundle savedInstanceState)160     protected void onCreate(Bundle savedInstanceState) {
161         super.onCreate(savedInstanceState);
162         Intent deliveredIntent = getIntent();
163         Intent innerIntent = deliveredIntent.getParcelableExtra(EXTRA_INNER_INTENT);
164         String action = deliveredIntent.getAction();
165         switch (action) {
166             case ACTION_UNSAFE_INTENT_LAUNCH: {
167                 if (innerIntent != null) {
168                     startActivity(innerIntent);
169                 }
170                 break;
171             }
172             case ACTION_UNSAFE_DATA_COPY_FROM_INTENT: {
173                 if (innerIntent != null) {
174                     // Instantiate a new Intent to be used as the target of the unfiltered data
175                     // copy.
176                     Intent intent = new Intent(getApplicationContext(), SimpleTestActivity.class);
177                     intent.putExtras(innerIntent);
178                     startActivity(intent);
179                 }
180                 break;
181             }
182             case ACTION_UNSAFE_DATA_COPY_FROM_EXTRAS: {
183                 if (innerIntent != null) {
184                     Intent intent = new Intent(getApplicationContext(), SimpleTestActivity.class);
185                     intent.putExtras(innerIntent.getExtras());
186                     startActivity(intent);
187                 }
188                 break;
189             }
190             case ACTION_DATA_COPY_FROM_DELIVERED_INTENT_WITH_UNPARCELED_EXTRAS: {
191                 Intent intent = new Intent(getApplicationContext(), SimpleTestActivity.class);
192                 intent.putExtras(deliveredIntent);
193                 startActivity(intent);
194                 break;
195             }
196             case ACTION_UNSAFE_INTENT_FROM_URI_LAUNCH:
197             case ACTION_SAFE_INTENT_FROM_URI_LAUNCH: {
198                 String intentUriString = deliveredIntent.getStringExtra(
199                         EXTRA_INNER_INTENT_URI_STRING);
200                 if (intentUriString != null) {
201                     try {
202                         Intent intent = Intent.parseUri(intentUriString,
203                                 Intent.URI_ANDROID_APP_SCHEME);
204                         // If this is a safe intent from URI launch then clear the component as a
205                         // browsable Intent without a component set should not result in a
206                         // violation.
207                         if (ACTION_SAFE_INTENT_FROM_URI_LAUNCH.equals(action)) {
208                             intent.setComponent(null);
209                         }
210                         startActivity(intent);
211                     } catch (URISyntaxException e) {
212                         Log.e(TAG, "Exception parsing URI: " + intentUriString, e);
213                     }
214                 }
215                 break;
216             }
217             default:
218                 throw new IllegalArgumentException(
219                         "An unexpected action of " + deliveredIntent.getAction()
220                                 + " was specified");
221         }
222         finish();
223     }
224 }
225