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

Commit 1f0c0813 authored by Ashwini Oruganti's avatar Ashwini Oruganti
Browse files

Error prone checks for mutability flags on PI

Add a mutability flag check for all method calls that create a PendingIntent.

Bug: 160794467
Test: atest error_prone_android_framework_test:com.google.errorprone.bugpatterns.android.PendingIntentMutabilityCheckerTest
Change-Id: I26a51a6dddb2793e9a56e72876f3f9d2aea4e3fb
parent 65d966e1
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.