Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54416f51 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Error prone checks for mutability flags on PI"

parents 4969a9a4 1f0c0813
Loading
Loading
Loading
Loading
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.google.errorprone.bugpatterns.android;

import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.FieldMatchers.staticField;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.contains;
import static com.google.errorprone.matchers.Matchers.methodInvocation;
import static com.google.errorprone.matchers.Matchers.staticMethod;


import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;

import java.util.regex.Pattern;

/**
 * Any method calls to create a PendingIntent require that one of the
 * mutability flags, FLAG_MUTABLE or FLAG_IMMUTABLE, be explicitly specified.
 * This checker verifies that one of these mutability flags are used when
 * creating PendingIntents.
 */
@AutoService(BugChecker.class)
@BugPattern(
        name = "AndroidFrameworkPendingIntentMutability",
        summary = "Verifies that FLAG_MUTABLE or FLAG_IMMUTABLE is always set",
        severity = WARNING)
public final class PendingIntentMutabilityChecker extends BugChecker
        implements MethodInvocationTreeMatcher {

    private static final Matcher<ExpressionTree> PENDING_INTENT_METHOD = methodInvocation(
            staticMethod()
            .onClass("android.app.PendingIntent")
            .withNameMatching(Pattern.compile(
                    "^(getActivity|getActivityAsUser|getActivities|getActivitiesAsUser|"
                    + "getBroadcast|getBroadcastAsUser|getService|getForegroundService).*")));

    private static final Matcher<ExpressionTree> VALID_FLAGS = anyOf(
            staticField("android.app.PendingIntent", "FLAG_MUTABLE"),
            staticField("android.app.PendingIntent", "FLAG_IMMUTABLE"));

    private static final Matcher<ExpressionTree> CONTAINS_VALID_FLAGS = contains(
            ExpressionTree.class, VALID_FLAGS);

    @Override
    public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
        if (PENDING_INTENT_METHOD.matches(tree, state)) {
            final ExpressionTree arg = tree.getArguments().get(3);
            if (!(VALID_FLAGS.matches(arg, state) || CONTAINS_VALID_FLAGS.matches(arg, state))) {
                return buildDescription(arg)
                        .setMessage("To improve security, PendingIntents must declare one of"
                                + " FLAG_MUTABLE or FLAG_IMMUTABLE explicitly; see"
                                + " go/immutable-pendingintents for more details")
                        .build();
            }
        }
        return Description.NO_MATCH;
    }
}
+292 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.google.errorprone.bugpatterns.android;

import com.google.errorprone.CompilationTestHelper;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class PendingIntentMutabilityCheckerTest {
    private CompilationTestHelper mCompilationHelper;

    @Before
    public void setUp() {
        mCompilationHelper = CompilationTestHelper.newInstance(
                PendingIntentMutabilityChecker.class, getClass());
    }

    @Test
    public void testGetActivity() {
        mCompilationHelper
                .addSourceFile("/android/app/PendingIntent.java")
                .addSourceFile("/android/content/Context.java")
                .addSourceFile("/android/content/Intent.java")
                .addSourceFile("/android/os/UserHandle.java")
                .addSourceFile("/android/os/Bundle.java")
                .addSourceLines("Example.java",
                        "import android.app.PendingIntent;",
                        "import android.content.Context;",
                        "import android.content.Intent;",
                        "public class Example {",
                        "  Context context;",
                        "  Intent intent;",
                        "  void example() {",
                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_MUTABLE);",
                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_IMMUTABLE);",
                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
                        "    PendingIntent.getActivity(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_ONE_SHOT);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivity(context, 42, intent, 0);",
                        "  }",
                        "}")
                .doTest();
    }

    @Test
    public void testGetActivityAsUser() {
        mCompilationHelper
                .addSourceFile("/android/app/PendingIntent.java")
                .addSourceFile("/android/content/Context.java")
                .addSourceFile("/android/content/Intent.java")
                .addSourceFile("/android/os/UserHandle.java")
                .addSourceFile("/android/os/Bundle.java")
                .addSourceLines("Example.java",
                        "import android.app.PendingIntent;",
                        "import android.content.Context;",
                        "import android.content.Intent;",
                        "public class Example {",
                        "  Context context;",
                        "  Intent intent;",
                        "  void example() {",
                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_MUTABLE, null, null);",
                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_IMMUTABLE, null, null);",
                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null, null);",
                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null, null);",
                        "    PendingIntent.getActivityAsUser(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE, null, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_ONE_SHOT, null, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE, null, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivityAsUser(context, 42, intent, 0, null, null);",
                        "  }",
                        "}")
                .doTest();
    }

    @Test
    public void testGetActivities() {
        mCompilationHelper
                .addSourceFile("/android/app/PendingIntent.java")
                .addSourceFile("/android/content/Context.java")
                .addSourceFile("/android/content/Intent.java")
                .addSourceFile("/android/os/UserHandle.java")
                .addSourceFile("/android/os/Bundle.java")
                .addSourceLines("Example.java",
                        "import android.app.PendingIntent;",
                        "import android.content.Context;",
                        "import android.content.Intent;",
                        "public class Example {",
                        "  Context context;",
                        "  Intent[] intents;",
                        "  void example() {",
                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_MUTABLE, null);",
                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_IMMUTABLE, null);",
                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null);",
                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null);",
                        "    PendingIntent.getActivities(context, 42, intents, 0 | PendingIntent.FLAG_MUTABLE, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_ONE_SHOT, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivities(context, 42, intents, 0, null);",
                        "  }",
                        "}")
                .doTest();
    }

    @Test
    public void testGetActivitiesAsUser() {
        mCompilationHelper
                .addSourceFile("/android/app/PendingIntent.java")
                .addSourceFile("/android/content/Context.java")
                .addSourceFile("/android/content/Intent.java")
                .addSourceFile("/android/os/UserHandle.java")
                .addSourceFile("/android/os/Bundle.java")
                .addSourceLines("Example.java",
                        "import android.app.PendingIntent;",
                        "import android.content.Context;",
                        "import android.content.Intent;",
                        "public class Example {",
                        "  Context context;",
                        "  Intent[] intents;",
                        "  void example() {",
                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_MUTABLE, null, null);",
                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_IMMUTABLE, null, null);",
                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null, null);",
                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null, null);",
                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, 0 | PendingIntent.FLAG_MUTABLE, null, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_ONE_SHOT, null, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE, null, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, 0, null, null);",
                        "  }",
                        "}")
                .doTest();
    }


    @Test
    public void testGetBroadcast() {
        mCompilationHelper
                .addSourceFile("/android/app/PendingIntent.java")
                .addSourceFile("/android/content/Context.java")
                .addSourceFile("/android/content/Intent.java")
                .addSourceFile("/android/os/UserHandle.java")
                .addSourceFile("/android/os/Bundle.java")
                .addSourceLines("Example.java",
                        "import android.app.PendingIntent;",
                        "import android.content.Context;",
                        "import android.content.Intent;",
                        "public class Example {",
                        "  Context context;",
                        "  Intent intent;",
                        "  void example() {",
                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_MUTABLE);",
                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_IMMUTABLE);",
                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
                        "    PendingIntent.getBroadcast(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_ONE_SHOT);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getBroadcast(context, 42, intent, 0);",
                        "  }",
                        "}")
                .doTest();
    }

    @Test
    public void testGetBroadcastAsUser() {
        mCompilationHelper
                .addSourceFile("/android/app/PendingIntent.java")
                .addSourceFile("/android/content/Context.java")
                .addSourceFile("/android/content/Intent.java")
                .addSourceFile("/android/os/UserHandle.java")
                .addSourceFile("/android/os/Bundle.java")
                .addSourceLines("Example.java",
                        "import android.app.PendingIntent;",
                        "import android.content.Context;",
                        "import android.content.Intent;",
                        "public class Example {",
                        "  Context context;",
                        "  Intent intent;",
                        "  void example() {",
                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_MUTABLE, null);",
                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_IMMUTABLE, null);",
                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null);",
                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null);",
                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_ONE_SHOT, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE, null);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, 0, null);",
                        "  }",
                        "}")
                .doTest();
    }

    @Test
    public void testGetService() {
        mCompilationHelper
                .addSourceFile("/android/app/PendingIntent.java")
                .addSourceFile("/android/content/Context.java")
                .addSourceFile("/android/content/Intent.java")
                .addSourceFile("/android/os/UserHandle.java")
                .addSourceFile("/android/os/Bundle.java")
                .addSourceLines("Example.java",
                        "import android.app.PendingIntent;",
                        "import android.content.Context;",
                        "import android.content.Intent;",
                        "public class Example {",
                        "  Context context;",
                        "  Intent intent;",
                        "  void example() {",
                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_MUTABLE);",
                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_IMMUTABLE);",
                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
                        "    PendingIntent.getService(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_ONE_SHOT);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getService(context, 42, intent, 0);",
                        "  }",
                        "}")
                .doTest();
    }

    @Test
    public void testGetForegroundService() {
        mCompilationHelper
                .addSourceFile("/android/app/PendingIntent.java")
                .addSourceFile("/android/content/Context.java")
                .addSourceFile("/android/content/Intent.java")
                .addSourceFile("/android/os/UserHandle.java")
                .addSourceFile("/android/os/Bundle.java")
                .addSourceLines("Example.java",
                        "import android.app.PendingIntent;",
                        "import android.content.Context;",
                        "import android.content.Intent;",
                        "public class Example {",
                        "  Context context;",
                        "  Intent intent;",
                        "  void example() {",
                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_MUTABLE);",
                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_IMMUTABLE);",
                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
                        "    PendingIntent.getForegroundService(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_ONE_SHOT);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE);",
                        "    // BUG: Diagnostic contains:",
                        "    PendingIntent.getForegroundService(context, 42, intent, 0);",
                        "  }",
                        "}")
                .doTest();
    }
}
+69 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 android.app;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;

public class PendingIntent {
    public static final int FLAG_ONE_SHOT = 1<<30;
    public static final int FLAG_IMMUTABLE = 1<<26;
    public static final int FLAG_MUTABLE = 1<<25;
    public static final int FLAG_NO_CREATE = 1<<29;

    public static PendingIntent getActivity(Context context, int requestCode,
            Intent intent, int flags) {
        throw new UnsupportedOperationException();
    }

    public static PendingIntent getActivityAsUser(Context context, int requestCode,
            Intent intent, int flags, Bundle options, UserHandle user) {
        throw new UnsupportedOperationException();
    }

    public static PendingIntent getActivities(Context context, int requestCode,
            Intent[] intents, int flags, Bundle options) {
        throw new UnsupportedOperationException();
    }

    public static PendingIntent getActivitiesAsUser(Context context, int requestCode,
            Intent[] intents, int flags, Bundle options, UserHandle user) {
        throw new UnsupportedOperationException();
    }

    public static PendingIntent getBroadcast(Context context, int requestCode,
            Intent intent, int flags) {
        throw new UnsupportedOperationException();
    }

    public static PendingIntent getBroadcastAsUser(Context context, int requestCode,
            Intent intent, int flags, UserHandle userHandle) {
        throw new UnsupportedOperationException();
    }

    public static PendingIntent getService(Context context, int requestCode,
            Intent intent, int flags) {
        throw new UnsupportedOperationException();
    }

    public static PendingIntent getForegroundService(Context context, int requestCode,
            Intent intent, int flags) {
        throw new UnsupportedOperationException();
    }
}
+20 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 android.content;

public class Intent {
}
+20 −0

File added.

Preview size limit exceeded, changes collapsed.