diff options
author | Tor Norbye <tnorbye@google.com> | 2012-06-13 10:28:00 -0700 |
---|---|---|
committer | android code review <noreply-gerritcodereview@google.com> | 2012-06-13 10:28:01 -0700 |
commit | fa1f0bd43a50ea3e25b48607b7ef723f8f1365cd (patch) | |
tree | 53eacf59c666b1e4699c3e1c9f74c169cfd60db7 | |
parent | 2ff4badd3e877b4ab290f582d6bfd3eb7ce2de9c (diff) | |
parent | 43a8ebf6d5bbf1ed2baf28b2a5a60fbe2a1b08b4 (diff) | |
download | sdk-fa1f0bd43a50ea3e25b48607b7ef723f8f1365cd.zip sdk-fa1f0bd43a50ea3e25b48607b7ef723f8f1365cd.tar.gz sdk-fa1f0bd43a50ea3e25b48607b7ef723f8f1365cd.tar.bz2 |
Merge "Added lint checks for unprotected activities and export receivers, analogous to the existing lint check for unprotected services."
14 files changed, 404 insertions, 28 deletions
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java index b975ec1..d829398 100644 --- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java +++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java @@ -54,7 +54,7 @@ public class BuiltinIssueRegistry extends IssueRegistry { private static final List<Issue> sIssues; static { - final int initialCapacity = 93; + final int initialCapacity = 95; List<Issue> issues = new ArrayList<Issue>(initialCapacity); issues.add(AccessibilityDetector.ISSUE); @@ -110,6 +110,8 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(ManifestOrderDetector.DUPLICATE_ACTIVITY); issues.add(SecurityDetector.EXPORTED_PROVIDER); issues.add(SecurityDetector.EXPORTED_SERVICE); + issues.add(SecurityDetector.EXPORTED_ACTIVITY); + issues.add(SecurityDetector.EXPORTED_RECEIVER); issues.add(SecurityDetector.OPEN_PROVIDER); issues.add(SecurityDetector.WORLD_READABLE); issues.add(SecurityDetector.WORLD_WRITEABLE); diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java index c9848eb..0e802b8 100644 --- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java +++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java @@ -25,11 +25,13 @@ import static com.android.tools.lint.detector.api.LintConstants.ATTR_PATH_PREFIX import static com.android.tools.lint.detector.api.LintConstants.ATTR_PERMISSION; import static com.android.tools.lint.detector.api.LintConstants.ATTR_READ_PERMISSION; import static com.android.tools.lint.detector.api.LintConstants.ATTR_WRITE_PERMISSION; +import static com.android.tools.lint.detector.api.LintConstants.TAG_ACTIVITY; import static com.android.tools.lint.detector.api.LintConstants.TAG_APPLICATION; import static com.android.tools.lint.detector.api.LintConstants.TAG_GRANT_PERMISSION; import static com.android.tools.lint.detector.api.LintConstants.TAG_INTENT_FILTER; import static com.android.tools.lint.detector.api.LintConstants.TAG_PATH_PERMISSION; import static com.android.tools.lint.detector.api.LintConstants.TAG_PROVIDER; +import static com.android.tools.lint.detector.api.LintConstants.TAG_RECEIVER; import static com.android.tools.lint.detector.api.LintConstants.TAG_SERVICE; import com.android.annotations.NonNull; @@ -99,6 +101,34 @@ public class SecurityDetector extends Detector implements Detector.XmlScanner, SecurityDetector.class, Scope.MANIFEST_SCOPE); + /** Exported activities */ + public static final Issue EXPORTED_ACTIVITY = Issue.create( + "ExportedActivity", //$NON-NLS-1$ + "Checks for exported activities that do not require permissions", + "Exported activities (activities which either set exported=true or contain " + + "an intent-filter and do not specify exported=false) should define a " + + "permission that an entity must have in order to launch the activity " + + "or bind to it. Without this, any application can use this activity.", + Category.SECURITY, + 2, + Severity.WARNING, + SecurityDetector.class, + Scope.MANIFEST_SCOPE); + + /** Exported receivers */ + public static final Issue EXPORTED_RECEIVER = Issue.create( + "ExportedReceiver", //$NON-NLS-1$ + "Checks for exported receivers that do not require permissions", + "Exported receivers (receivers which either set exported=true or contain " + + "an intent-filter and do not specify exported=false) should define a " + + "permission that an entity must have in order to launch the receiver " + + "or bind to it. Without this, any application can use this receiver.", + Category.SECURITY, + 5, + Severity.WARNING, + SecurityDetector.class, + Scope.MANIFEST_SCOPE); + /** Content provides which grant all URIs access */ public static final Issue OPEN_PROVIDER = Issue.create( "GrantAllUris", //$NON-NLS-1$ @@ -164,7 +194,9 @@ public class SecurityDetector extends Detector implements Detector.XmlScanner, return Arrays.asList( TAG_SERVICE, TAG_GRANT_PERMISSION, - TAG_PROVIDER + TAG_PROVIDER, + TAG_ACTIVITY, + TAG_RECEIVER ); } @@ -177,43 +209,68 @@ public class SecurityDetector extends Detector implements Detector.XmlScanner, checkGrantPermission(context, element); } else if (tag.equals(TAG_PROVIDER)) { checkProvider(context, element); + } else if (tag.equals(TAG_ACTIVITY)) { + checkActivity(context, element); + } else if (tag.equals(TAG_RECEIVER)) { + checkReceiver(context, element); } } - private void checkService(XmlContext context, Element element) { + private boolean getExported(Element element) { + // Used to check whether an activity, service or broadcast receiver is exported. String exportValue = element.getAttributeNS(ANDROID_URI, ATTR_EXPORTED); - boolean exported; if (exportValue != null && exportValue.length() > 0) { - exported = Boolean.valueOf(exportValue); + return Boolean.valueOf(exportValue); } else { - boolean haveIntentFilters = false; for (Element child : LintUtils.getChildren(element)) { if (child.getTagName().equals(TAG_INTENT_FILTER)) { - haveIntentFilters = true; - break; + return true; } } - exported = haveIntentFilters; } - if (exported) { - // Make sure this service has a permission - String permission = element.getAttributeNS(ANDROID_URI, ATTR_PERMISSION); - if (permission == null || permission.length() == 0) { - Node parent = element.getParentNode(); - if (parent.getNodeType() == Node.ELEMENT_NODE - && parent.getNodeName().equals(TAG_APPLICATION)) { - Element application = (Element) parent; - permission = application.getAttributeNS(ANDROID_URI, ATTR_PERMISSION); - if (permission == null || permission.length() == 0) { - // No declared permission for this exported service: complain - context.report(EXPORTED_SERVICE, element, - context.getLocation(element), - "Exported service does not require permission", null); - } - } + return false; + } + + private boolean isUnprotectedByPermission(Element element) { + // Used to check whether an activity, service or broadcast receiver are + // protected by a permission. + String permission = element.getAttributeNS(ANDROID_URI, ATTR_PERMISSION); + if (permission == null || permission.length() == 0) { + Node parent = element.getParentNode(); + if (parent.getNodeType() == Node.ELEMENT_NODE + && parent.getNodeName().equals(TAG_APPLICATION)) { + Element application = (Element) parent; + permission = application.getAttributeNS(ANDROID_URI, ATTR_PERMISSION); + return permission == null || permission.length() == 0; } } + + return false; + } + + private void checkActivity(XmlContext context, Element element) { + if (getExported(element) && isUnprotectedByPermission(element)) { + // No declared permission for this exported activity: complain + context.report(EXPORTED_ACTIVITY, element, context.getLocation(element), + "Exported activity does not require permission", null); + } + } + + private void checkReceiver(XmlContext context, Element element) { + if (getExported(element) && isUnprotectedByPermission(element)) { + // No declared permission for this exported receiver: complain + context.report(EXPORTED_RECEIVER, element, context.getLocation(element), + "Exported receiver does not require permission", null); + } + } + + private void checkService(XmlContext context, Element element) { + if (getExported(element) && isUnprotectedByPermission(element)) { + // No declared permission for this exported service: complain + context.report(EXPORTED_SERVICE, element, context.getLocation(element), + "Exported service does not require permission", null); + } } private void checkGrantPermission(XmlContext context, Element element) { diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecurityDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecurityDetectorTest.java index a23b297..0978826 100644 --- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecurityDetectorTest.java +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecurityDetectorTest.java @@ -70,8 +70,8 @@ public class SecurityDetectorTest extends AbstractCheckTest { public void testUri() throws Exception { assertEquals( - "AndroidManifest.xml:24: Warning: Content provider shares everything; this is potentially dangerous.\n" + - "AndroidManifest.xml:25: Warning: Content provider shares everything; this is potentially dangerous.", + "AndroidManifest.xml:25: Warning: Content provider shares everything; this is potentially dangerous.\n" + + "AndroidManifest.xml:26: Warning: Content provider shares everything; this is potentially dangerous.", lintProject( "grantpermission.xml=>AndroidManifest.xml", @@ -108,4 +108,92 @@ public class SecurityDetectorTest extends AbstractCheckTest { // Java files must be renamed in source tree "src/test/pkg/WorldWriteableFile.java.txt=>src/test/pkg/WorldWriteableFile.java")); } + + public void testActivity0() throws Exception { + // Activities that do not have intent-filters do not need warnings + assertEquals( + "No warnings.", + lintProject( + "exportactivity0.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testActivity1() throws Exception { + assertEquals( + "AndroidManifest.xml:12: Warning: Exported activity does not require permission", + lintProject( + "exportactivity1.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testActivity2() throws Exception { + // Defines a permission on the <activity> element + assertEquals( + "No warnings.", + lintProject( + "exportactivity2.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testActivity3() throws Exception { + // Defines a permission on the parent <application> element + assertEquals( + "No warnings.", + lintProject( + "exportactivity3.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testActivity4() throws Exception { + // Not defining exported, but have intent-filters + assertEquals( + "AndroidManifest.xml:12: Warning: Exported activity does not require permission", + lintProject( + "exportactivity4.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testReceiver0() throws Exception { + // Activities that do not have intent-filters do not need warnings + assertEquals( + "No warnings.", + lintProject( + "exportreceiver0.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testReceiver1() throws Exception { + assertEquals( + "AndroidManifest.xml:12: Warning: Exported receiver does not require permission", + lintProject( + "exportreceiver1.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testReceiver2() throws Exception { + // Defines a permission on the <activity> element + assertEquals( + "No warnings.", + lintProject( + "exportreceiver2.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testReceiver3() throws Exception { + // Defines a permission on the parent <application> element + assertEquals( + "No warnings.", + lintProject( + "exportreceiver3.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } + + public void testReceiver4() throws Exception { + // Not defining exported, but have intent-filters + assertEquals( + "AndroidManifest.xml:12: Warning: Exported receiver does not require permission", + lintProject( + "exportreceiver4.xml=>AndroidManifest.xml", + "res/values/strings.xml")); + } } diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity0.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity0.xml new file mode 100644 index 0000000..cc436d0 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity0.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <activity + android:label="@string/app_name" + android:name="com.sample.service.serviceClass" > + </activity> + </application> + +</manifest> + diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity1.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity1.xml new file mode 100644 index 0000000..46d5efb --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity1.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <activity + android:label="@string/app_name" + android:name="com.sample.service.serviceClass" > + <intent-filter > + <action android:name="com.sample.service.serviceClass" > + </action> + </intent-filter> + </activity> + </application> + +</manifest> + diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity2.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity2.xml new file mode 100644 index 0000000..2b4cf6a --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity2.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <activity + android:label="@string/app_name" + android:name="com.sample.service.serviceClass" + android:permission="android.permission.RECEIVE_BOOT_COMPLETED" + android:process=":remote" > + <intent-filter > + <action android:name="com.sample.service.serviceClass" > + </action> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity3.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity3.xml new file mode 100644 index 0000000..191c699 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity3.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <application + android:icon="@drawable/ic_launcher" + android:permission="android.permission.RECEIVE_BOOT_COMPLETED" + android:label="@string/app_name" > + <activity + android:label="@string/app_name" + android:name="com.sample.service.serviceClass" + android:process=":remote" > + <intent-filter > + <action android:name="com.sample.service.serviceClass" > + </action> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity4.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity4.xml new file mode 100644 index 0000000..31345cc --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportactivity4.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <activity + android:label="@string/app_name" + android:name="com.sample.service.serviceClass" + android:process=":remote" > + <intent-filter > + <action android:name="com.sample.service.serviceClass" > + </action> + </intent-filter> + </activity> + </application> + +</manifest> + diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver0.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver0.xml new file mode 100644 index 0000000..f9c5190 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver0.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <receiver + android:label="@string/app_name" + android:name="com.sample.service.serviceClass" > + </receiver> + </application> + +</manifest> + diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver1.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver1.xml new file mode 100644 index 0000000..0652d49 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver1.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <receiver + android:label="@string/app_name" + android:name="com.sample.service.serviceClass" > + <intent-filter > + <action android:name="com.sample.service.serviceClass" > + </action> + </intent-filter> + </receiver> + </application> + +</manifest> + diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver2.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver2.xml new file mode 100644 index 0000000..46d5dcb --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver2.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <receiver + android:label="@string/app_name" + android:name="com.sample.service.serviceClass" + android:permission="android.permission.RECEIVE_BOOT_COMPLETED" + android:process=":remote" > + <intent-filter > + <action android:name="com.sample.service.serviceClass" > + </action> + </intent-filter> + </receiver> + </application> + +</manifest> diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver3.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver3.xml new file mode 100644 index 0000000..bc3ec8a --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver3.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <application + android:icon="@drawable/ic_launcher" + android:permission="android.permission.RECEIVE_BOOT_COMPLETED" + android:label="@string/app_name" > + <receiver + android:label="@string/app_name" + android:name="com.sample.service.serviceClass" + android:process=":remote" > + <intent-filter > + <action android:name="com.sample.service.serviceClass" > + </action> + </intent-filter> + </receiver> + </application> + +</manifest> diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver4.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver4.xml new file mode 100644 index 0000000..5d9a826 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/exportreceiver4.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="14" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <receiver + android:label="@string/app_name" + android:name="com.sample.service.serviceClass" + android:process=":remote" > + <intent-filter > + <action android:name="com.sample.service.serviceClass" > + </action> + </intent-filter> + </receiver> + </application> + +</manifest> + diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/grantpermission.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/grantpermission.xml index b75f712..e07c370 100644 --- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/grantpermission.xml +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/grantpermission.xml @@ -11,7 +11,8 @@ android:label="@string/app_name" > <activity android:label="@string/app_name" - android:name=".Foo2Activity" > + android:name=".Foo2Activity" + android:permission="Foo" > <intent-filter > <action android:name="android.intent.action.MAIN" /> |