Skip to content

Commit cec6cd0

Browse files
authored
Merge pull request github#6724 from atorralba/atorralba/android-contentprovider-sources
Java: Add sources for content providers in Android
2 parents fc8b439 + 46eb27c commit cec6cd0

File tree

24 files changed

+1604
-0
lines changed

24 files changed

+1604
-0
lines changed

java/ql/lib/semmle/code/java/dataflow/FlowSources.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,20 @@ class ExportedAndroidIntentInput extends RemoteFlowSource, AndroidIntentInput {
247247

248248
override string getSourceType() { result = "Exported Android intent source" }
249249
}
250+
251+
/** A parameter of an entry-point method declared in a `ContentProvider` class. */
252+
class AndroidContentProviderInput extends DataFlow::Node {
253+
AndroidContentProvider declaringType;
254+
255+
AndroidContentProviderInput() {
256+
sourceNode(this, "contentprovider") and
257+
this.getEnclosingCallable().getDeclaringType() = declaringType
258+
}
259+
}
260+
261+
/** A parameter of an entry-point method declared in an exported `ContentProvider` class. */
262+
class ExportedAndroidContentProviderInput extends RemoteFlowSource, AndroidContentProviderInput {
263+
ExportedAndroidContentProviderInput() { declaringType.isExported() }
264+
265+
override string getSourceType() { result = "Exported Android content provider source" }
266+
}

java/ql/lib/semmle/code/java/frameworks/android/Android.qll

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ class AndroidContentProvider extends ExportableAndroidComponent {
7272
AndroidContentProvider() {
7373
this.getASupertype*().hasQualifiedName("android.content", "ContentProvider")
7474
}
75+
76+
/**
77+
* Holds if this content provider requires read and write permissions
78+
* in an `AndroidManifest.xml` file.
79+
*/
80+
predicate requiresPermissions() {
81+
getAndroidComponentXmlElement().(AndroidProviderXmlElement).requiresPermissions()
82+
}
7583
}
7684

7785
/** An Android content resolver. */
@@ -148,3 +156,39 @@ private class UriModel extends SummaryModelCsv {
148156
]
149157
}
150158
}
159+
160+
private class ContentProviderSourceModels extends SourceModelCsv {
161+
override predicate row(string row) {
162+
row =
163+
[
164+
// ContentInterface models are here for backwards compatibility (it was removed in API 28)
165+
"android.content;ContentInterface;true;call;(String,String,String,Bundle);;Parameter[0..3];contentprovider",
166+
"android.content;ContentProvider;true;call;(String,String,String,Bundle);;Parameter[0..3];contentprovider",
167+
"android.content;ContentProvider;true;call;(String,String,Bundle);;Parameter[0..2];contentprovider",
168+
"android.content;ContentProvider;true;delete;(Uri,String,String[]);;Parameter[0..2];contentprovider",
169+
"android.content;ContentInterface;true;delete;(Uri,Bundle);;Parameter[0..1];contentprovider",
170+
"android.content;ContentProvider;true;delete;(Uri,Bundle);;Parameter[0..1];contentprovider",
171+
"android.content;ContentInterface;true;getType;(Uri);;Parameter[0];contentprovider",
172+
"android.content;ContentProvider;true;getType;(Uri);;Parameter[0];contentprovider",
173+
"android.content;ContentInterface;true;insert;(Uri,ContentValues,Bundle);;Parameter[0];contentprovider",
174+
"android.content;ContentProvider;true;insert;(Uri,ContentValues,Bundle);;Parameter[0..2];contentprovider",
175+
"android.content;ContentProvider;true;insert;(Uri,ContentValues);;Parameter[0..1];contentprovider",
176+
"android.content;ContentInterface;true;openAssetFile;(Uri,String,CancellationSignal);;Parameter[0];contentprovider",
177+
"android.content;ContentProvider;true;openAssetFile;(Uri,String,CancellationSignal);;Parameter[0];contentprovider",
178+
"android.content;ContentProvider;true;openAssetFile;(Uri,String);;Parameter[0];contentprovider",
179+
"android.content;ContentInterface;true;openTypedAssetFile;(Uri,String,Bundle,CancellationSignal);;Parameter[0..2];contentprovider",
180+
"android.content;ContentProvider;true;openTypedAssetFile;(Uri,String,Bundle,CancellationSignal);;Parameter[0..2];contentprovider",
181+
"android.content;ContentProvider;true;openTypedAssetFile;(Uri,String,Bundle);;Parameter[0..2];contentprovider",
182+
"android.content;ContentInterface;true;openFile;(Uri,String,CancellationSignal);;Parameter[0];contentprovider",
183+
"android.content;ContentProvider;true;openFile;(Uri,String,CancellationSignal);;Parameter[0];contentprovider",
184+
"android.content;ContentProvider;true;openFile;(Uri,String);;Parameter[0];contentprovider",
185+
"android.content;ContentInterface;true;query;(Uri,String[],Bundle,CancellationSignal);;Parameter[0..2];contentprovider",
186+
"android.content;ContentProvider;true;query;(Uri,String[],Bundle,CancellationSignal);;Parameter[0..2];contentprovider",
187+
"android.content;ContentProvider;true;query;(Uri,String[],String,String[],String);;Parameter[0..4];contentprovider",
188+
"android.content;ContentProvider;true;query;(Uri,String[],String,String[],String,CancellationSignal);;Parameter[0..4];contentprovider",
189+
"android.content;ContentInterface;true;update;(Uri,ContentValues,Bundle);;Parameter[0..2];contentprovider",
190+
"android.content;ContentProvider;true;update;(Uri,ContentValues,Bundle);;Parameter[0..2];contentprovider",
191+
"android.content;ContentProvider;true;update;(Uri,ContentValues,String,String[]);;Parameter[0..3];contentprovider"
192+
]
193+
}
194+
}

java/ql/lib/semmle/code/xml/AndroidManifest.qll

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,47 @@ class AndroidReceiverXmlElement extends AndroidComponentXmlElement {
7979
*/
8080
class AndroidProviderXmlElement extends AndroidComponentXmlElement {
8181
AndroidProviderXmlElement() { this.getName() = "provider" }
82+
83+
/**
84+
* Holds if this provider element has explicitly set a value for either its
85+
* `android:permission` attribute or its `android:readPermission` and `android:writePermission`
86+
* attributes.
87+
*/
88+
predicate requiresPermissions() {
89+
this.getAnAttribute().(AndroidPermissionXmlAttribute).isFull()
90+
or
91+
this.getAnAttribute().(AndroidPermissionXmlAttribute).isWrite() and
92+
this.getAnAttribute().(AndroidPermissionXmlAttribute).isRead()
93+
}
94+
}
95+
96+
/**
97+
* The attribute `android:perrmission`, `android:readPermission`, or `android:writePermission`.
98+
*/
99+
class AndroidPermissionXmlAttribute extends XMLAttribute {
100+
AndroidPermissionXmlAttribute() {
101+
this.getNamespace().getPrefix() = "android" and
102+
this.getName() = ["permission", "readPermission", "writePermission"]
103+
}
104+
105+
/** Holds if this is an `android:permission` attribute. */
106+
predicate isFull() { this.getName() = "permission" }
107+
108+
/** Holds if this is an `android:readPermission` attribute. */
109+
predicate isRead() { this.getName() = "readPermission" }
110+
111+
/** Holds if this is an `android:writePermission` attribute. */
112+
predicate isWrite() { this.getName() = "writePermission" }
113+
}
114+
115+
/**
116+
* The `<path-permission`> element of a `<provider>` in an Android manifest file.
117+
*/
118+
class AndroidPathPermissionXmlElement extends XMLElement {
119+
AndroidPathPermissionXmlElement() {
120+
this.getParent() instanceof AndroidProviderXmlElement and
121+
this.hasName("path-permission")
122+
}
82123
}
83124

84125
/**
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
android:versionCode="1"
5+
android:versionName="1.0"
6+
package="com.example.app">
7+
8+
<application
9+
android:allowBackup="true"
10+
android:icon="@mipmap/ic_launcher"
11+
android:roundIcon="@mipmap/ic_launcher_round"
12+
android:label="@string/app_name"
13+
android:supportsRtl="true"
14+
android:theme="@style/AppTheme">
15+
16+
<activity
17+
android:name=".MainActivity"
18+
android:icon="@drawable/ic_launcher"
19+
android:label="@string/app_name">
20+
<intent-filter>
21+
<action android:name="android.intent.action.MAIN" />
22+
<category android:name="android.intent.category.LAUNCHER" />
23+
</intent-filter>
24+
</activity>
25+
26+
<provider
27+
android:name=".Test"
28+
android:authority="com.example.myapp.Test"
29+
android:exported="true" />
30+
31+
<provider
32+
android:name=".Safe"
33+
android:authority="com.example.myapp.Safe"
34+
android:exported="false" />
35+
</application>
36+
</manifest>
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package com.example.app;
2+
3+
import java.io.FileNotFoundException;
4+
import android.content.ContentProvider;
5+
import android.content.ContentValues;
6+
import android.content.res.AssetFileDescriptor;
7+
import android.database.Cursor;
8+
import android.net.Uri;
9+
import android.os.Bundle;
10+
import android.os.CancellationSignal;
11+
import android.os.ParcelFileDescriptor;
12+
import android.os.RemoteException;
13+
14+
// This Content Provider isn't exported, so there shouldn't be any flow
15+
public class Safe extends ContentProvider {
16+
17+
void sink(Object o) {}
18+
19+
@Override
20+
public Bundle call(String authority, String method, String arg, Bundle extras) {
21+
sink(authority);
22+
sink(method);
23+
sink(arg);
24+
sink(extras.get("some_key"));
25+
return null;
26+
}
27+
28+
public Bundle call(String method, String arg, Bundle extras) {
29+
sink(method);
30+
sink(arg);
31+
sink(extras.get("some_key"));
32+
return null;
33+
}
34+
35+
@Override
36+
public int delete(Uri uri, String selection, String[] selectionArgs) {
37+
sink(uri);
38+
sink(selection);
39+
sink(selectionArgs);
40+
return 0;
41+
}
42+
43+
@Override
44+
public int delete(Uri uri, Bundle extras) {
45+
sink(uri);
46+
sink(extras.get("some_key"));
47+
return 0;
48+
}
49+
50+
@Override
51+
public String getType(Uri uri) {
52+
sink(uri);
53+
return null;
54+
}
55+
56+
@Override
57+
public Uri insert(Uri uri, ContentValues values, Bundle extras) {
58+
sink(uri);
59+
sink(values);
60+
sink(extras.get("some_key"));
61+
return null;
62+
}
63+
64+
@Override
65+
public Uri insert(Uri uri, ContentValues values) {
66+
sink(uri);
67+
sink(values);
68+
return null;
69+
}
70+
71+
@Override
72+
public AssetFileDescriptor openAssetFile(Uri uri, String mode, CancellationSignal signal) {
73+
sink(uri);
74+
sink(mode);
75+
sink(signal);
76+
return null;
77+
}
78+
79+
@Override
80+
public AssetFileDescriptor openAssetFile(Uri uri, String mode) {
81+
sink(uri);
82+
sink(mode);
83+
return null;
84+
}
85+
86+
@Override
87+
public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts,
88+
CancellationSignal signal) throws RemoteException, FileNotFoundException {
89+
sink(uri);
90+
sink(mimeTypeFilter);
91+
sink(opts.get("some_key"));
92+
sink(signal);
93+
return null;
94+
}
95+
96+
@Override
97+
public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
98+
throws FileNotFoundException {
99+
sink(uri);
100+
sink(mimeTypeFilter);
101+
sink(opts.get("some_key"));
102+
return null;
103+
}
104+
105+
@Override
106+
public ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal) {
107+
sink(uri);
108+
sink(mode);
109+
sink(signal);
110+
return null;
111+
}
112+
113+
@Override
114+
public ParcelFileDescriptor openFile(Uri uri, String mode) {
115+
sink(uri);
116+
sink(mode);
117+
return null;
118+
}
119+
120+
@Override
121+
public Cursor query(Uri uri, String[] projection, Bundle queryArgs,
122+
CancellationSignal cancellationSignal) {
123+
sink(uri);
124+
sink(projection);
125+
sink(queryArgs.get("some_key"));
126+
sink(cancellationSignal);
127+
return null;
128+
}
129+
130+
@Override
131+
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
132+
String sortOrder) {
133+
sink(uri);
134+
sink(projection);
135+
sink(selection);
136+
sink(selectionArgs);
137+
return null;
138+
}
139+
140+
@Override
141+
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
142+
String sortOrder, CancellationSignal cancellationSignal) {
143+
sink(uri);
144+
sink(projection);
145+
sink(selection);
146+
sink(selectionArgs);
147+
sink(sortOrder);
148+
sink(cancellationSignal);
149+
return null;
150+
}
151+
152+
@Override
153+
public int update(Uri uri, ContentValues values, Bundle extras) {
154+
sink(uri);
155+
sink(values);
156+
sink(extras.get("some_key"));
157+
return 0;
158+
}
159+
160+
@Override
161+
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
162+
sink(uri);
163+
sink(values);
164+
sink(selection);
165+
sink(selectionArgs);
166+
return 0;
167+
}
168+
169+
170+
@Override
171+
public boolean onCreate() {
172+
return false;
173+
}
174+
}

0 commit comments

Comments
 (0)