aboutsummaryrefslogtreecommitdiffstats
path: root/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java
diff options
context:
space:
mode:
Diffstat (limited to 'lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java')
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java427
1 files changed, 0 insertions, 427 deletions
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
deleted file mode 100644
index 57f4af4..0000000
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.checks;
-
-import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_EXPORTED;
-import static com.android.SdkConstants.ATTR_NAME;
-import static com.android.SdkConstants.ATTR_PATH;
-import static com.android.SdkConstants.ATTR_PATH_PATTERN;
-import static com.android.SdkConstants.ATTR_PATH_PREFIX;
-import static com.android.SdkConstants.ATTR_PERMISSION;
-import static com.android.SdkConstants.ATTR_READ_PERMISSION;
-import static com.android.SdkConstants.ATTR_WRITE_PERMISSION;
-import static com.android.SdkConstants.TAG_ACTIVITY;
-import static com.android.SdkConstants.TAG_APPLICATION;
-import static com.android.SdkConstants.TAG_GRANT_PERMISSION;
-import static com.android.SdkConstants.TAG_INTENT_FILTER;
-import static com.android.SdkConstants.TAG_PATH_PERMISSION;
-import static com.android.SdkConstants.TAG_PROVIDER;
-import static com.android.SdkConstants.TAG_RECEIVER;
-import static com.android.SdkConstants.TAG_SERVICE;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.JavaContext;
-import com.android.tools.lint.detector.api.LintUtils;
-import com.android.tools.lint.detector.api.Location;
-import com.android.tools.lint.detector.api.Scope;
-import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
-import com.android.tools.lint.detector.api.XmlContext;
-
-import org.w3c.dom.Attr;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-import lombok.ast.AstVisitor;
-import lombok.ast.Expression;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Identifier;
-import lombok.ast.MethodInvocation;
-import lombok.ast.StrictListAccessor;
-
-/**
- * Checks that exported services request a permission.
- */
-public class SecurityDetector extends Detector implements Detector.XmlScanner,
- Detector.JavaScanner {
-
- /** Exported services */
- public static final Issue EXPORTED_SERVICE = Issue.create(
- "ExportedService", //$NON-NLS-1$
- "Checks for exported services that do not require permissions",
- "Exported services (services 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 service " +
- "or bind to it. Without this, any application can use this service.",
- Category.SECURITY,
- 5,
- Severity.WARNING,
- SecurityDetector.class,
- Scope.MANIFEST_SCOPE);
-
- /** Exported content providers */
- public static final Issue EXPORTED_PROVIDER = Issue.create(
- "ExportedContentProvider", //$NON-NLS-1$
- "Checks for exported content providers that do not require permissions",
- "Content providers are exported by default and any application on the " +
- "system can potentially use them to read and write data. If the content" +
- "provider provides access to sensitive data, it should be protected by " +
- "specifying `export=false` in the manifest or by protecting it with a " +
- "permission that can be granted to other applications.",
- Category.SECURITY,
- 5,
- Severity.WARNING,
- 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$
- "Checks for <grant-uri-permission> elements where everything is shared",
- "The `<grant-uri-permission>` element allows specific paths to be shared. " +
- "This detector checks for a path URL of just '/' (everything), which is " +
- "probably not what you want; you should limit access to a subset.",
- Category.SECURITY,
- 7,
- Severity.WARNING,
- SecurityDetector.class,
- Scope.MANIFEST_SCOPE);
-
- /** Using the world-writable flag */
- public static final Issue WORLD_WRITEABLE = Issue.create(
- "WorldWriteableFiles", //$NON-NLS-1$
- "Checks for openFileOutput() and getSharedPreferences() calls passing " +
- "MODE_WORLD_WRITEABLE",
- "There are cases where it is appropriate for an application to write " +
- "world writeable files, but these should be reviewed carefully to " +
- "ensure that they contain no private data, and that if the file is " +
- "modified by a malicious application it does not trick or compromise " +
- "your application.",
- Category.SECURITY,
- 4,
- Severity.WARNING,
- SecurityDetector.class,
- Scope.JAVA_FILE_SCOPE);
-
-
- /** Using the world-readable flag */
- public static final Issue WORLD_READABLE = Issue.create(
- "WorldReadableFiles", //$NON-NLS-1$
- "Checks for openFileOutput() and getSharedPreferences() calls passing " +
- "MODE_WORLD_READABLE",
- "There are cases where it is appropriate for an application to write " +
- "world readable files, but these should be reviewed carefully to " +
- "ensure that they contain no private data that is leaked to other " +
- "applications.",
- Category.SECURITY,
- 4,
- Severity.WARNING,
- SecurityDetector.class,
- Scope.JAVA_FILE_SCOPE);
-
- /** Constructs a new {@link SecurityDetector} check */
- public SecurityDetector() {
- }
-
- @Override
- public @NonNull Speed getSpeed() {
- return Speed.FAST;
- }
- @Override
- public boolean appliesTo(@NonNull Context context, @NonNull File file) {
- return file.getName().equals(ANDROID_MANIFEST_XML);
- }
-
- // ---- Implements Detector.XmlScanner ----
-
- @Override
- public Collection<String> getApplicableElements() {
- return Arrays.asList(
- TAG_SERVICE,
- TAG_GRANT_PERMISSION,
- TAG_PROVIDER,
- TAG_ACTIVITY,
- TAG_RECEIVER
- );
- }
-
- @Override
- public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
- String tag = element.getTagName();
- if (tag.equals(TAG_SERVICE)) {
- checkService(context, element);
- } else if (tag.equals(TAG_GRANT_PERMISSION)) {
- 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 boolean getExported(Element element) {
- // Used to check whether an activity, service or broadcast receiver is exported.
- String exportValue = element.getAttributeNS(ANDROID_URI, ATTR_EXPORTED);
- if (exportValue != null && exportValue.length() > 0) {
- return Boolean.valueOf(exportValue);
- } else {
- for (Element child : LintUtils.getChildren(element)) {
- if (child.getTagName().equals(TAG_INTENT_FILTER)) {
- return true;
- }
- }
- }
-
- 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 boolean isLauncher(Element element) {
- // Checks whether an element is a launcher activity.
- for (Element child : LintUtils.getChildren(element)) {
- if (child.getTagName().equals(TAG_INTENT_FILTER)) {
- for (Element innerChild: LintUtils.getChildren(child)) {
- if (innerChild.getTagName().equals("category")) { //$NON-NLS-1$
- String categoryString = innerChild.getAttributeNS(ANDROID_URI, ATTR_NAME);
- return "android.intent.category.LAUNCHER".equals(categoryString); //$NON-NLS-1$
- }
- }
- }
- }
-
- return false;
- }
-
- private void checkActivity(XmlContext context, Element element) {
- // Do not flag launch activities. Even if not explicitly exported, it's
- // safe to assume that those activities should be exported.
- if (getExported(element) && isUnprotectedByPermission(element) && !isLauncher(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 boolean isStandardReceiver(Element element) {
- // Checks whether a broadcast receiver receives a standard Android action
- for (Element child : LintUtils.getChildren(element)) {
- if (child.getTagName().equals(TAG_INTENT_FILTER)) {
- for (Element innerChild : LintUtils.getChildren(child)) {
- if (innerChild.getTagName().equals("action")) { //$NON-NLS-1$
- String categoryString = innerChild.getAttributeNS(ANDROID_URI, ATTR_NAME);
- return categoryString.startsWith("android."); //$NON-NLS-1$
- }
- }
- }
- }
- return false;
- }
-
- private void checkReceiver(XmlContext context, Element element) {
- if (getExported(element) && isUnprotectedByPermission(element) &&
- !isStandardReceiver(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) {
- Attr path = element.getAttributeNodeNS(ANDROID_URI, ATTR_PATH);
- Attr prefix = element.getAttributeNodeNS(ANDROID_URI, ATTR_PATH_PREFIX);
- Attr pattern = element.getAttributeNodeNS(ANDROID_URI, ATTR_PATH_PATTERN);
-
- String msg = "Content provider shares everything; this is potentially dangerous.";
- if (path != null && path.getValue().equals("/")) { //$NON-NLS-1$
- context.report(OPEN_PROVIDER, path, context.getLocation(path), msg, null);
- }
- if (prefix != null && prefix.getValue().equals("/")) { //$NON-NLS-1$
- context.report(OPEN_PROVIDER, prefix, context.getLocation(prefix), msg, null);
- }
- if (pattern != null && (pattern.getValue().equals("/") //$NON-NLS-1$
- /* || pattern.getValue().equals(".*")*/)) {
- context.report(OPEN_PROVIDER, pattern, context.getLocation(pattern), msg, null);
- }
- }
-
- private void checkProvider(XmlContext context, Element element) {
- String exportValue = element.getAttributeNS(ANDROID_URI, ATTR_EXPORTED);
- // Content providers are exported by default
- boolean exported = true;
- if (exportValue != null && exportValue.length() > 0) {
- exported = Boolean.valueOf(exportValue);
- }
-
- if (exported) {
- // Just check for some use of permissions. Other Lint checks can check the saneness
- // of the permissions. We'll accept the permission, readPermission, or writePermission
- // attributes on the provider element, or a path-permission element.
- String permission = element.getAttributeNS(ANDROID_URI, ATTR_READ_PERMISSION);
- if (permission == null || permission.length() == 0) {
- permission = element.getAttributeNS(ANDROID_URI, ATTR_WRITE_PERMISSION);
- if (permission == null || permission.length() == 0) {
- permission = element.getAttributeNS(ANDROID_URI, ATTR_PERMISSION);
- if (permission == null || permission.length() == 0) {
- // No permission attributes? Check for path-permission.
-
- // TODO: Add a Lint check to ensure the path-permission is good, similar to
- // the grant-uri-permission check.
- boolean hasPermission = false;
- for (Element child : LintUtils.getChildren(element)) {
- String tag = child.getTagName();
- if (tag.equals(TAG_PATH_PERMISSION)) {
- hasPermission = true;
- break;
- }
- }
-
- if (!hasPermission) {
- context.report(EXPORTED_PROVIDER, element,
- context.getLocation(element),
- "Exported content providers can provide access to " +
- "potentially sensitive data",
- null);
- }
- }
- }
- }
- }
- }
-
- // ---- Implements Detector.JavaScanner ----
-
- @Override
- public List<String> getApplicableMethodNames() {
- // These are the API calls that can accept a MODE_WORLD_READABLE/MODE_WORLD_WRITABLE
- // argument.
- List<String> values = new ArrayList<String>(2);
- values.add("openFileOutput"); //$NON-NLS-1$
- values.add("getSharedPreferences"); //$NON-NLS-1$
- return values;
- }
-
- @Override
- public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
- @NonNull MethodInvocation node) {
- StrictListAccessor<Expression,MethodInvocation> args = node.astArguments();
- Iterator<Expression> iterator = args.iterator();
- while (iterator.hasNext()) {
- iterator.next().accept(visitor);
- }
- }
-
- @Override
- public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
- return new IdentifierVisitor(context);
- }
-
- private static class IdentifierVisitor extends ForwardingAstVisitor {
- private final JavaContext mContext;
-
- public IdentifierVisitor(JavaContext context) {
- super();
- mContext = context;
- }
-
- @Override
- public boolean visitIdentifier(Identifier node) {
- if ("MODE_WORLD_WRITEABLE".equals(node.getDescription())) { //$NON-NLS-1$
- Location location = mContext.getLocation(node);
- mContext.report(WORLD_WRITEABLE, node, location,
- "Using MODE_WORLD_WRITEABLE when creating files can be " +
- "risky, review carefully",
- null);
- } else if ("MODE_WORLD_READABLE".equals(node.getDescription())) { //$NON-NLS-1$
- Location location = mContext.getLocation(node);
- mContext.report(WORLD_READABLE, node, location,
- "Using MODE_WORLD_READABLE when creating files can be " +
- "risky, review carefully",
- null);
- }
-
- return false;
- }
- }
-}